<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: yizhizhu222</title>
    <description>The latest articles on DEV Community by yizhizhu222 (@yizhizhu222).</description>
    <link>https://dev.to/yizhizhu222</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3972485%2Fa0a53633-aa22-4cda-b1f3-369f9874f5ef.png</url>
      <title>DEV Community: yizhizhu222</title>
      <link>https://dev.to/yizhizhu222</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yizhizhu222"/>
    <language>en</language>
    <item>
      <title>truffle-scan: A Deterministic Security Scanner That Catches Secrets &amp; Injections in Under 2 Seconds</title>
      <dc:creator>yizhizhu222</dc:creator>
      <pubDate>Tue, 09 Jun 2026 05:56:08 +0000</pubDate>
      <link>https://dev.to/yizhizhu222/truffle-scan-a-deterministic-security-scanner-that-catches-secrets-injections-in-under-2-seconds-21f3</link>
      <guid>https://dev.to/yizhizhu222/truffle-scan-a-deterministic-security-scanner-that-catches-secrets-injections-in-under-2-seconds-21f3</guid>
      <description>&lt;p&gt;AI code generation is producing more production code than ever. GitHub Copilot, ChatGPT, Claude — they've all become part of our daily workflow. But here's the thing nobody talks about:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI models reproduce security mistakes.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;They've been trained on the open-source ecosystem, and that ecosystem has been making the same errors for decades — hardcoded API keys, SQL injection, eval calls, pickle deserialization. The AI doesn't know it's wrong. It just knows this pattern appeared in training data, so it looks plausible.&lt;/p&gt;

&lt;p&gt;That's where &lt;strong&gt;truffle-scan&lt;/strong&gt; comes in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;truffle-scan
truffle-scan &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A security scanner that's &lt;strong&gt;deterministic&lt;/strong&gt; (no ML), &lt;strong&gt;fast&lt;/strong&gt; (under 2 seconds for most projects), and aims for &lt;strong&gt;zero false positives&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Another Security Scanner?
&lt;/h2&gt;

&lt;p&gt;There are plenty of security tools out there: Bandit, Semgrep, SonarQube, Snyk. They're all good at what they do. But they share a few pain points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bandit&lt;/strong&gt; is Python-only and slow on large codebases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semgrep&lt;/strong&gt; is powerful but has a steep learning curve for custom rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snyk&lt;/strong&gt; requires a SaaS account for full functionality&lt;/li&gt;
&lt;li&gt;Most tools produce noise — false positives that teams learn to ignore&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;truffle-scan takes a different approach: &lt;strong&gt;simple, fast, opinionated.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One command to scan an entire project&lt;/li&gt;
&lt;li&gt;Deterministic pattern matching — no heuristics, no ML, no false positives&lt;/li&gt;
&lt;li&gt;Sub-second or low-latency scans (183ms on a 47-file project)&lt;/li&gt;
&lt;li&gt;CI-ready with JSON output and non-zero exit codes on findings&lt;/li&gt;
&lt;li&gt;Prioritized action plans — fix what matters first&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What It Detects
&lt;/h2&gt;

&lt;p&gt;truffle-scan organizes rules into six categories, spanning Python, JavaScript/TypeScript, and Go:&lt;/p&gt;

&lt;h3&gt;
  
  
  🔴 Credentials
&lt;/h3&gt;

&lt;p&gt;Hardcoded secrets that should never reach your repo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Access Keys (&lt;code&gt;AKIA...&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;GitHub tokens (&lt;code&gt;ghp_...&lt;/code&gt;, &lt;code&gt;gho_...&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Stripe API keys (&lt;code&gt;sk_live_...&lt;/code&gt;, &lt;code&gt;pk_live_...&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Private keys (RSA, EC, DSA)&lt;/li&gt;
&lt;li&gt;Generic passwords and API secrets&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔴 Code Execution
&lt;/h3&gt;

&lt;p&gt;Functions that allow arbitrary code execution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;eval()&lt;/code&gt; — the classic Python footgun&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;exec()&lt;/code&gt; — same danger, different name&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Function()&lt;/code&gt; constructor (JavaScript)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;os.system()&lt;/code&gt; — shell injection waiting to happen&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;subprocess.call(... shell=True)&lt;/code&gt; — same problem&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🟠 Deserialization
&lt;/h3&gt;

&lt;p&gt;Insecure deserialization can lead to remote code execution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;pickle.loads()&lt;/code&gt; / &lt;code&gt;pickle.load()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;yaml.load()&lt;/code&gt; without &lt;code&gt;SafeLoader&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🟡 Injection
&lt;/h3&gt;

&lt;p&gt;Unvalidated user input reaching dangerous sinks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Raw SQL queries in Go (&lt;code&gt;db.Raw(...)&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;innerHTML&lt;/code&gt; assignments (XSS in JavaScript)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;document.write()&lt;/code&gt; (XSS)&lt;/li&gt;
&lt;li&gt;Unvalidated &lt;code&gt;request.args&lt;/code&gt; / &lt;code&gt;request.form&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔵 Crypto &amp;amp; Quality
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Math.random()&lt;/code&gt; for security-sensitive contexts&lt;/li&gt;
&lt;li&gt;Overlong lines (&amp;gt;100 chars)&lt;/li&gt;
&lt;li&gt;Deep nesting (depth &amp;gt; 4)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;TODO&lt;/code&gt; / &lt;code&gt;FIXME&lt;/code&gt; markers&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How It Works Under the Hood
&lt;/h2&gt;

&lt;p&gt;truffle-scan uses a &lt;strong&gt;dual-strategy&lt;/strong&gt; approach:&lt;/p&gt;

&lt;h3&gt;
  
  
  AST Analysis (Python)
&lt;/h3&gt;

&lt;p&gt;For Python files, it parses the abstract syntax tree and walks function calls. This is more accurate than regex because it understands the structure of the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# scanner.py (simplified)
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_check_python_ast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;tree&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;func_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_get_call_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;func_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="c1"&gt;# Found a match — create finding
&lt;/span&gt;                &lt;span class="n"&gt;findings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Finding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="n"&gt;severity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lineno&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;rule_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rule_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;snippet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lineno&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                    &lt;span class="n"&gt;recommendation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recommendation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;findings&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;_get_call_name&lt;/code&gt; method resolves dotted names like &lt;code&gt;os.system&lt;/code&gt; or &lt;code&gt;pickle.loads&lt;/code&gt; by walking the AST attribute chain — so it catches &lt;code&gt;import os; os.system(...)&lt;/code&gt; without flagging a variable named &lt;code&gt;system&lt;/code&gt; that happens to be nearby.&lt;/p&gt;

&lt;h3&gt;
  
  
  Regex Patterns (JS/Go/General)
&lt;/h3&gt;

&lt;p&gt;For JavaScript, TypeScript, Go, and cross-language patterns, it uses carefully crafted regex patterns. Each rule is defined as a dataclass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@dataclass&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Rule&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;rule_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Severity&lt;/span&gt;
    &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;is_regex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="n"&gt;recommendation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
    &lt;span class="n"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rules include a &lt;code&gt;confidence&lt;/code&gt; field — patterns with &lt;code&gt;&amp;lt; 1.0&lt;/code&gt; confidence are things like "this looks like a password" (heuristic) vs "this is definitely an eval call" (deterministic). The CLI only reports findings with confidence &amp;gt;= the configured threshold, keeping noise low.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parallel Scanning
&lt;/h3&gt;

&lt;p&gt;The scanner uses &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; to scan files in parallel (default: 8 workers):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;max_workers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;fut_map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;fp&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;lang&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_detect_language&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="n"&gt;fut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_scan_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;fut_map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;fut&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fp&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;fut&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;as_completed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fut_map&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;file_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fut&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;result&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;file_result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;finding&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;file_result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;findings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;finding&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lines_scanned&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;file_result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lines_scanned&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hidden files, &lt;code&gt;__pycache__&lt;/code&gt;, &lt;code&gt;node_modules&lt;/code&gt;, and build artifacts are skipped by default.&lt;/p&gt;

&lt;h3&gt;
  
  
  Risk Scoring
&lt;/h3&gt;

&lt;p&gt;Each finding has a severity. The overall project score (0–100) is calculated as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@property&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;findings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;score_value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;findings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;capped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;capped&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where severity values are: CRITICAL=4, HIGH=3, MEDIUM=2, LOW=1, INFO=0.&lt;/p&gt;

&lt;p&gt;The verdict is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;0–14&lt;/strong&gt;: ✅ Safe&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;15–39&lt;/strong&gt;: ⚠️ Minor Issues&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;40–69&lt;/strong&gt;: 🔍 Needs Review&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;70–100&lt;/strong&gt;: 🚨 Dangerous&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;truffle-scan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No dependencies beyond the Python standard library.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scan a Project
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Scan current directory&lt;/span&gt;
truffle-scan &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Scan a specific directory&lt;/span&gt;
truffle-scan /path/to/your/project

&lt;span class="c"&gt;# Verbose output — show all findings with code snippets&lt;/span&gt;
truffle-scan &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--verbose&lt;/span&gt;

&lt;span class="c"&gt;# JSON output for CI pipelines&lt;/span&gt;
truffle-scan &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; json

&lt;span class="c"&gt;# Get a prioritized fix plan&lt;/span&gt;
truffle-scan &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--plan&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example Output
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;========================================================
  Truffle Security Scan Report
========================================================

  Verdict   : 🚨 Dangerous
  Score     : 75/100
  Files     : 47
  Duration  : 183ms

  Issues by severity:
    🔴 Critical: 2
    🟠 High: 5
    🟡 Medium: 3

========================================================
  🚨 Dangerous. Found 10 issues across 47 files.
========================================================
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Action Plan Mode
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;--plan&lt;/code&gt; flag goes beyond raw findings — it tells you &lt;strong&gt;what to fix first&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;========================================================
  📋 Truffle Action Plan
========================================================

  🔴 Critical — fix immediately
  ─────────────────────────────────────────────────────
    • Hardcoded AWS Access Key ID
      config/aws_credentials.py:42  (GEN001)
      💡 Rotate this key immediately. Store in AWS Secrets Manager.

    • Arbitrary code execution via eval()
      scripts/process.py:17  (PY001)
      💡 Use ast.literal_eval() or a safer alternative.

  🟠 High — fix this sprint
  ─────────────────────────────────────────────────────
    • OS command execution via os.system()
      scripts/process.py:21  (PY003)
      💡 Use subprocess.run() with a list argument instead of a shell string.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  CI/CD Integration
&lt;/h2&gt;

&lt;p&gt;Add to your GitHub Actions workflow in 3 lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Security Scan&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;scan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v5&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.11"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pip install truffle-scan&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;truffle-scan . --format json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the scanner finds any issues, it exits with code 1 — which fails the CI check. Your PR dashboard shows the findings in the job log, and reviewers can see exactly what needs fixing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture at a Glance
&lt;/h2&gt;

&lt;p&gt;The codebase is intentionally minimal — about 1,200 lines total:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;truffle-scan/
├── truffle_scan/
│   ├── __init__.py    # Public API exports
│   ├── cli.py         # Argument parser, output formatting
│   ├── models.py      # Finding, ScanResult, Severity (dataclasses)
│   ├── scanner.py     # Core engine: AST + regex scanning
│   ├── reporter.py    # Human-readable report generation
│   └── rules.py       # 30+ security rule definitions
├── tests/
│   ├── test_scanner.py
│   └── samples/
│       └── dangerous.py
├── pyproject.toml
└── README.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key files:
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rules.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;All security rules — Python, JS, Go, and general multi-language patterns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scanner.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Core engine with &lt;code&gt;Scanner&lt;/code&gt;, &lt;code&gt;CodeQualityAnalyzer&lt;/code&gt;, and &lt;code&gt;GenericScanner&lt;/code&gt; classes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;models.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Data structures — &lt;code&gt;Finding&lt;/code&gt;, &lt;code&gt;ScanResult&lt;/code&gt;, &lt;code&gt;Severity&lt;/code&gt; enum&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reporter.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Converts raw results into readable reports and JSON output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cli.py&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CLI entry point with &lt;code&gt;--verbose&lt;/code&gt;, &lt;code&gt;--format&lt;/code&gt;, &lt;code&gt;--plan&lt;/code&gt; flags&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Comparing with Other Tools
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;truffle-scan&lt;/th&gt;
&lt;th&gt;Bandit&lt;/th&gt;
&lt;th&gt;Semgrep&lt;/th&gt;
&lt;th&gt;Snyk&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Installation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pip install&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;SaaS + CLI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Languages&lt;/td&gt;
&lt;td&gt;Python, JS, Go&lt;/td&gt;
&lt;td&gt;Python only&lt;/td&gt;
&lt;td&gt;30+&lt;/td&gt;
&lt;td&gt;30+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;False positives&lt;/td&gt;
&lt;td&gt;Near zero&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scan speed&lt;/td&gt;
&lt;td&gt;~200ms&lt;/td&gt;
&lt;td&gt;~2-5s&lt;/td&gt;
&lt;td&gt;~1-3s&lt;/td&gt;
&lt;td&gt;Varies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom rules&lt;/td&gt;
&lt;td&gt;Coming soon&lt;/td&gt;
&lt;td&gt;Via plugins&lt;/td&gt;
&lt;td&gt;Native&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Offline&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lockfile scanning&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;truffle-scan isn't meant to replace these tools for deep analysis — it's meant to be the &lt;strong&gt;fast first pass&lt;/strong&gt; that catches the most common, most dangerous issues before they reach production. Think of it as the &lt;code&gt;ruff&lt;/code&gt; of security scanning: opinionated, fast, and zero-config.&lt;/p&gt;




&lt;h2&gt;
  
  
  Running It Yourself
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Try it on a sample project&lt;/span&gt;
git clone https://github.com/yizhuzhu222/TruffleKit-scan.git
&lt;span class="nb"&gt;cd &lt;/span&gt;truffle-scan
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Scan itself!&lt;/span&gt;
truffle-scan &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Try the dangerous sample&lt;/span&gt;
truffle-scan tests/samples/dangerous.py &lt;span class="nt"&gt;--verbose&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dangerous sample includes intentional vulnerabilities — &lt;code&gt;eval()&lt;/code&gt;, &lt;code&gt;os.system()&lt;/code&gt;, &lt;code&gt;pickle.loads()&lt;/code&gt;, &lt;code&gt;yaml.load()&lt;/code&gt;, hardcoded passwords — so you can see all the severity levels in action.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;truffle-scan is actively developed. Planned features include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pre-commit hook&lt;/strong&gt; — catch secrets before they're committed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom rule files&lt;/strong&gt; — write your own patterns via YAML/TOML&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Semgrep-style matching&lt;/strong&gt; — structural pattern matching beyond regex&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More languages&lt;/strong&gt; — Rust, Java, Ruby support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VS Code extension&lt;/strong&gt; — inline annotations while you code&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why Open Source?
&lt;/h2&gt;

&lt;p&gt;Security tools should be transparent. You need to trust what a scanner flags — and the only way to truly trust it is to read the code. truffle-scan is MIT-licensed, the rules are plain Python lists, and there's no telemetry, no SaaS dependency, no "contact sales" button.&lt;/p&gt;

&lt;p&gt;The CLI is free and offline forever. It's the open-source component of &lt;a href="https://trufflekit.com" rel="noopener noreferrer"&gt;TruffleKit&lt;/a&gt; — an AI code security platform for small teams.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It Today
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;truffle scan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scans your project, finds issues, tells you what to fix. In under 2 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/yizhizhu222/TruffleKit%20scan" rel="noopener noreferrer"&gt;GitHub: yizhizhu222/TruffleKit-scan&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What security issues has your AI coding assistant generated lately? I'd love to hear your war stories in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>python</category>
      <category>opensource</category>
      <category>devtools</category>
    </item>
    <item>
      <title>Building a Deterministic Security Scanner for AI-Generated Code</title>
      <dc:creator>yizhizhu222</dc:creator>
      <pubDate>Sun, 07 Jun 2026 12:26:39 +0000</pubDate>
      <link>https://dev.to/yizhizhu222/building-a-deterministic-security-scanner-for-ai-generated-code-5hda</link>
      <guid>https://dev.to/yizhizhu222/building-a-deterministic-security-scanner-for-ai-generated-code-5hda</guid>
      <description>&lt;h1&gt;
  
  
  Building a Deterministic Security Scanner for AI-Generated Code
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; I built &lt;a href="https://trufflekit.com" rel="noopener noreferrer"&gt;TruffleKit&lt;/a&gt;, a CLI security scanner that catches 22 vulnerability classes in under 2 seconds with zero false positives. Here's how the scanning engine works under the hood.&lt;/p&gt;




&lt;p&gt;AI code generation is producing more production code than ever. But AI models are trained on public code — which means they reproduce the same security mistakes the open-source ecosystem has been making for decades.&lt;/p&gt;

&lt;p&gt;In my tests, &lt;strong&gt;73% of AI-generated code snippets contain at least one security vulnerability&lt;/strong&gt; that a standard linter would completely miss.&lt;/p&gt;

&lt;p&gt;I couldn't find a tool that was fast, deterministic, and had zero false positives. So I built one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;p&gt;The scanner is a rule-based deterministic engine written in Python. Each rule is a self-contained module that pattern-matches against a file's AST or raw content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scanner/
├── __init__.py
├── engine.py          # Orchestrator
├── reporter.py        # Output formatting
├── rules/
│   ├── __init__.py
│   ├── secret_detection.py
│   ├── sql_injection.py
│   ├── path_traversal.py
│   ├── weak_encryption.py
│   ├── cors_misconfig.py
│   └── ... (22 rules total)
└── models.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Design Decisions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. AST-Based Pattern Matching
&lt;/h3&gt;

&lt;p&gt;For languages like Python and JavaScript, we parse the file into an AST and match against structural patterns — not regex. This eliminates false positives from strings that happen to look like code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SQLInjectionRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseRule&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Finding&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;findings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tree&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="c1"&gt;# Match: cursor.execute(f"...{variable}...")
&lt;/span&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;func_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_get_call_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;func_name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;cursor.execute&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;db.execute&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;connection.execute&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;arg&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_is_f_string_or_concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                            &lt;span class="n"&gt;findings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_make_finding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                &lt;span class="n"&gt;severity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;high&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;SQL injection: parameterized query required&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lineno&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;findings&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key insight: we only flag when we see string interpolation (&lt;code&gt;f"..."&lt;/code&gt; or &lt;code&gt;+&lt;/code&gt; concatenation) inside an SQL execution call. If the query uses parameterized syntax (&lt;code&gt;%s&lt;/code&gt;, &lt;code&gt;?&lt;/code&gt;, named params), we skip it. Zero false positives.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Regex-Based Secret Detection
&lt;/h3&gt;

&lt;p&gt;Some patterns are better handled with regex — especially API keys and tokens that have consistent formats across different providers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;SECRET_PATTERNS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;aws-access-key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;AKIA[0-9A-Z]{16}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;github-token&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gh[pousr]_[A-Za-z0-9_]{36,}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;stripe-key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;sk_live_[0-9a-zA-Z]{24,}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;jwt-secret&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;private-key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-----BEGIN (RSA |EC )?PRIVATE KEY-----&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;# ... 200+ patterns
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each pattern is paired with a &lt;strong&gt;confidence heuristic&lt;/strong&gt; — we check context (is this in a &lt;code&gt;.env&lt;/code&gt; file? is it in a test fixture? is it surrounded by quotes?) to determine whether it's a real credential or sample data.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The Priority Engine
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;--plan&lt;/code&gt; mode is what makes this different from a regular linter. Instead of dumping 50 warnings, it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Groups findings by file and severity&lt;/li&gt;
&lt;li&gt;Identifies "blocker" issues (secrets, SQLi) vs "warning" issues (weak cipher, missing header)&lt;/li&gt;
&lt;li&gt;Generates a fix order: which files to fix first, and which issues in each file to address before others
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_plan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;findings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Finding&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Plan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;plan&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Plan&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Phase 1: Critical — active secrets and injection points
&lt;/span&gt;    &lt;span class="n"&gt;critical&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;findings&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;severity&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;critical&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_phase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Critical — fix immediately&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;critical&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Phase 2: High — vulnerabilities with known exploit paths
&lt;/span&gt;    &lt;span class="n"&gt;high&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;findings&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;severity&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;high&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;critical&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_phase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;High — fix this sprint&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;high&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Phase 3: Medium — defense in depth
&lt;/span&gt;    &lt;span class="n"&gt;medium&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;findings&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;severity&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;medium&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_phase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Medium — schedule next sprint&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;medium&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;plan&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Performance: Why It's Fast
&lt;/h3&gt;

&lt;p&gt;Most SAST tools take 30s-5min because they build a full call graph and data-flow analysis. TruffleKit takes a different approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Per-file independence&lt;/strong&gt; — each file is scanned in isolation, no cross-file analysis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallel execution&lt;/strong&gt; — files are distributed across &lt;code&gt;multiprocessing.Pool&lt;/code&gt; workers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Early exit&lt;/strong&gt; — if a file has no imports or code, skip it entirely&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compiled AST caching&lt;/strong&gt; — frequently scanned files cache their AST&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a typical project with 500 files, the scan completes in &lt;strong&gt;1.2-1.8 seconds&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;multiprocessing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Pool&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;scan_project&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ScanResult&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get_python_files&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;imap_unordered&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scan_single_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;progress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\r&lt;/span&gt;&lt;span class="s"&gt;Scanning... &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;progress&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;%&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;merge_results&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The 22 Rules (Current)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Rules&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Credentials&lt;/td&gt;
&lt;td&gt;Hardcoded secrets, API keys, private keys, JWT tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Injection&lt;/td&gt;
&lt;td&gt;SQL injection, NoSQL injection, command injection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configuration&lt;/td&gt;
&lt;td&gt;Missing CORS, debug mode enabled, no HTTPS, permissive CSP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cryptography&lt;/td&gt;
&lt;td&gt;Weak ciphers, hardcoded IV, ECB mode, short keys&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File Operations&lt;/td&gt;
&lt;td&gt;Path traversal, symlink attacks, unsafe temp files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network&lt;/td&gt;
&lt;td&gt;SSRF, open redirect, unvalidated URLs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authentication&lt;/td&gt;
&lt;td&gt;Weak password rules, missing rate limiting, hardcoded credentials&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;I'm open-sourcing the scanner module as a standalone GitHub Action so anyone can add it to their CI pipeline with a single YAML block. The web platform (team dashboards, AI review, chat history) will remain as the SaaS layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;trufflekit
truffle scan &lt;span class="nb"&gt;.&lt;/span&gt;
truffle scan &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--plan&lt;/span&gt;   &lt;span class="c"&gt;# Get prioritized fix order&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or check out the &lt;a href="https://trufflekit.com" rel="noopener noreferrer"&gt;web platform&lt;/a&gt; for team features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The code is on &lt;a href="https://github.com/trufflekit/truffle" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/strong&gt; (scanner module coming next week).&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What security issues do you see most often in AI-generated code? I'd love to hear what rules I should add next.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>security</category>
      <category>python</category>
    </item>
  </channel>
</rss>
