<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[JSON Web Tools]]></title><description><![CDATA[JSON Web Tools]]></description><link>https://jsonwebtools.hashnode.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/69a5d306e8e1f9df72ceece6/f11a442e-2834-47a3-a81a-047dffdc81c6.svg</url><title>JSON Web Tools</title><link>https://jsonwebtools.hashnode.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Sun, 20 Sep 2026 22:48:10 GMT</lastBuildDate><atom:link href="https://jsonwebtools.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[ How I Built a Client-Side JSON Security Scanner (XSS, SQL Injection, PII
  Detection)]]></title><description><![CDATA[Most JSON tools send your data to their servers. That always bothered me.
When I was building jsonwebtools.com, I decided the security scanner had to run entirely in the browser — no API calls, no log]]></description><link>https://jsonwebtools.hashnode.dev/jsonwebtools</link><guid isPermaLink="true">https://jsonwebtools.hashnode.dev/jsonwebtools</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[json]]></category><category><![CDATA[webdev]]></category><category><![CDATA[Security]]></category><category><![CDATA[AWS]]></category><dc:creator><![CDATA[admin]]></dc:creator><pubDate>Mon, 02 Mar 2026 18:22:51 GMT</pubDate><content:encoded><![CDATA[<p>Most JSON tools send your data to their servers. That always bothered me.</p>
<p>When I was building <strong>jsonwebtools.com</strong>, I decided the security scanner had to run entirely in the browser — no API calls, no logging, no server.</p>
<p>Here’s how I built it.</p>
<hr />
<h2>Why a JSON Security Scanner?</h2>
<p>Developers paste JSON everywhere — API responses, config files, database exports.</p>
<p>That JSON often contains things it shouldn’t:</p>
<ul>
<li><p>Hardcoded API keys and passwords</p>
</li>
<li><p>User PII (emails, phone numbers, SSNs)</p>
</li>
<li><p>XSS payloads injected into user data</p>
</li>
<li><p>SQL injection strings hiding in values</p>
</li>
<li><p>Internal file paths and system info</p>
</li>
</ul>
<p>None of the existing JSON tools checked for this. So I built one.</p>
<hr />
<h2>How It Works</h2>
<p>The scanner recursively walks the entire JSON tree and runs each value through a set of detection patterns.</p>
<pre><code class="language-javascript">function scanJSON(obj, path = '') {
  if (typeof obj === 'string') {
    return runPatterns(obj, path);
  }

  if (Array.isArray(obj)) {
    return obj.flatMap((item, i) =&gt;
      scanJSON(item, `\({path}[\){i}]`)
    );
  }

  if (typeof obj === 'object' &amp;&amp; obj !== null) {
    return Object.entries(obj).flatMap(([key, val]) =&gt;
      scanJSON(val, path ? `\({path}.\){key}` : key)
    );
  }

  return [];
}
</code></pre>
<p>For each string value, it runs pattern matching against known threat signatures.</p>
<hr />
<h2>Detection Patterns</h2>
<h3>XSS Detection</h3>
<pre><code class="language-javascript">const xssPatterns = [
  /&lt;script[\s\S]*?&gt;[\s\S]*?&lt;\/script&gt;/gi,
  /javascript\s*:/gi,
  /on\w+\s*=\s*["'][^"']*["']/gi, // onerror=, onclick=, etc.
  /&lt;iframe[\s\S]*?&gt;/gi,
  /eval\s*\(/gi,
  /document\.(cookie|write|location)/gi
];
</code></pre>
<h3>SQL Injection Detection</h3>
<pre><code class="language-javascript">const sqlPatterns = [
  /(\b(SELECT|INSERT|UPDATE|DELETE|DROP|UNION|ALTER)\b.*\b(FROM|INTO|WHERE|TABLE)\b)/gi,
  /'\s*(OR|AND)\s*'?\d+\s*'?\s*=\s*'?\d+/gi, // ' OR 1=1
  /;\s*(DROP|DELETE|INSERT|UPDATE)\s/gi,
  /--\s*$/gm, // SQL comment
  /\/\*[\s\S]*?\*\//g // block comment
];
</code></pre>
<h3>PII Detection</h3>
<pre><code class="language-javascript">const piiPatterns = [
  { pattern: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g, type: 'Email' },
  { pattern: /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g, type: 'Phone number' },
  { pattern: /\b\d{3}-\d{2}-\d{4}\b/g, type: 'SSN' },
  { pattern: /\b4[0-9]{12}(?:[0-9]{3})?\b/g, type: 'Visa card' },
  { pattern: /\b5[1-5][0-9]{14}\b/g, type: 'Mastercard' }
];
</code></pre>
<h3>Credential Detection</h3>
<pre><code class="language-javascript">const credentialPatterns = [
  /["']?(api[_-]?key|apikey|api[_-]?secret)["']?\s*[:=]\s*["']([^"']{8,})["']/gi,
  /["']?(password|passwd|pwd)["']?\s*[:=]\s*["']([^"']{4,})["']/gi,
  /["']?(secret|token|auth)["']?\s*[:=]\s*["']([^"']{8,})["']/gi,
  /bearer\s+[a-zA-Z0-9\-._~+/]+=*/gi,
  /ghp_[a-zA-Z0-9]{36}/g, // GitHub personal access token
  /sk-[a-zA-Z0-9]{48}/g   // OpenAI API key
];
</code></pre>
<hr />
<h2>Severity Classification</h2>
<p>Each finding gets a severity level:</p>
<pre><code class="language-javascript">function classifySeverity(finding) {
  if (finding.type === 'XSS' || finding.type === 'SQL Injection') {
    return 'HIGH';
  }

  if (finding.type === 'API Key' || finding.type === 'Password') {
    return 'HIGH';
  }

  if (finding.type === 'SSN' || finding.type === 'Credit Card') {
    return 'MEDIUM';
  }

  if (finding.type === 'Email' || finding.type === 'Phone') {
    return 'LOW';
  }
}
</code></pre>
<hr />
<h2>Path Tracking</h2>
<p>Every finding includes the full JSON path so you know exactly where the issue is:</p>
<ul>
<li><p><strong>HIGH</strong>: XSS pattern detected at <code>users[2].bio</code></p>
</li>
<li><p><strong>MEDIUM</strong>: Email address found at <code>orders[0].customer.email</code></p>
</li>
<li><p><strong>HIGH</strong>: API key detected at <code>config.integrations.stripe.secret</code></p>
</li>
</ul>
<hr />
<h2>Performance Considerations</h2>
<p>For large JSON files (1MB+), the recursive scan can be slow.</p>
<p>I added:</p>
<ul>
<li><p>A debounce so it only runs after the user stops typing</p>
</li>
<li><p>A size check to warn about files over 500KB before scanning</p>
</li>
</ul>
<pre><code class="language-javascript">const debouncedScan = debounce((json) =&gt; {
  const parsed = JSON.parse(json);
  const findings = scanJSON(parsed);
  displayFindings(findings);
}, 500);
</code></pre>
<hr />
<h2>What I Found While Testing</h2>
<p>While building the scanner, I ran it on my own test fixtures and found:</p>
<ul>
<li><p>A forgotten Stripe test key in a mock API response</p>
</li>
<li><p>Email addresses in a dataset I thought was anonymized</p>
</li>
<li><p>A <code>password: "admin123"</code> in a config template</p>
</li>
</ul>
<p>The scanner paid for itself before launch.</p>
<hr />
<h2>False Positives</h2>
<p>Regex-based detection isn’t perfect.</p>
<ul>
<li><p>Email pattern triggers on <code>test@example.com</code></p>
</li>
<li><p>Phone pattern triggers on version numbers like <code>123.456.7890</code></p>
</li>
<li><p>SQL pattern can trigger on legitimate strings containing <code>SELECT</code> or <code>WHERE</code></p>
</li>
</ul>
<p>I consider these acceptable false positives — better to flag and let the developer decide than to miss a real credential.</p>
<p><strong>Future improvement:</strong> add a whitelist/ignore system for known-safe patterns.</p>
<hr />
<h2>Try It</h2>
<p>The scanner runs automatically on every JSON validation at <strong>jsonwebtools.com</strong> — paste any JSON and it scans immediately.</p>
<p>No account. No server. No data logging.</p>
<p>The full platform includes <strong>68+ tools</strong>: JWT decoder, JSON → TypeScript/Go/Rust/Python/C#, diff tool, batch validator, performance benchmarking, and more.</p>
]]></content:encoded></item></channel></rss>