<?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: RustDev</title>
    <description>The latest articles on DEV Community by RustDev (@bumahkib7).</description>
    <link>https://dev.to/bumahkib7</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3755556%2F8cba2dc3-fc8d-44d8-a6c8-3b4a7ad8eb72.jpeg</url>
      <title>DEV Community: RustDev</title>
      <link>https://dev.to/bumahkib7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bumahkib7"/>
    <language>en</language>
    <item>
      <title>I was tired of writing Dockerfiles so I built something</title>
      <dc:creator>RustDev</dc:creator>
      <pubDate>Wed, 04 Mar 2026 01:14:57 +0000</pubDate>
      <link>https://dev.to/bumahkib7/i-was-tired-of-writing-dockerfiles-so-i-built-something-240d</link>
      <guid>https://dev.to/bumahkib7/i-was-tired-of-writing-dockerfiles-so-i-built-something-240d</guid>
      <description>&lt;p&gt;Every time I needed to deploy a Spring Boot app I wanted to quit.&lt;br&gt;
Not because Spring Boot is bad. Because the moment you leave the JavaScript world, deployment becomes your second job. Vercel doesn't run your JAR file. Railway wants a Dockerfile. Render wants a Dockerfile. You spend 2 hours on infrastructure before writing a single line of actual business logic.&lt;br&gt;
I built Runix because I was genuinely annoyed.&lt;br&gt;
You connect your GitHub repo. It figures out what you're running. It deploys it. You get a URL. That's it.&lt;br&gt;
No Dockerfile. No nixpacks config. No render.yaml. Nothing.&lt;br&gt;
It works for Java, Rust, Go, Python, Elixir, .NET, Node. The stuff Vercel pretends doesn't exist.&lt;br&gt;
It's free to start. Probably has bugs. I'm fixing them daily.&lt;br&gt;
&lt;a href="https://runixcloud.dev" rel="noopener noreferrer"&gt;https://runixcloud.dev&lt;/a&gt;&lt;br&gt;
Brutal feedback welcome.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>programming</category>
      <category>docker</category>
    </item>
    <item>
      <title>How I Compiled 647 Semgrep Rules to Native Rust</title>
      <dc:creator>RustDev</dc:creator>
      <pubDate>Thu, 05 Feb 2026 22:31:50 +0000</pubDate>
      <link>https://dev.to/bumahkib7/how-i-compiled-647-semgrep-rules-to-native-rust-1mk3</link>
      <guid>https://dev.to/bumahkib7/how-i-compiled-647-semgrep-rules-to-native-rust-1mk3</guid>
      <description>&lt;h1&gt;
  
  
  I Love Semgrep. So I Compiled Its Rules to Native Code.
&lt;/h1&gt;

&lt;p&gt;I love Semgrep. It has thousands of community-contributed security rules that catch real vulnerabilities. But every time I ran it on a large codebase, I'd wait... and wait.&lt;/p&gt;

&lt;p&gt;The problem? Semgrep interprets YAML rules at runtime using Python. For a 500K line monorepo, that meant 4+ minutes per scan.&lt;/p&gt;

&lt;p&gt;So I asked myself: &lt;strong&gt;what if I compiled those rules to native code instead?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Idea
&lt;/h2&gt;

&lt;p&gt;Semgrep rules are just pattern matching. A rule like this:&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;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sql-injection&lt;/span&gt;
    &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;execute($QUERY)&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Possible&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;SQL&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;injection"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Says "find any call to &lt;code&gt;execute()&lt;/code&gt; with one argument." That's not fundamentally different from what tree-sitter does with its query language.&lt;/p&gt;

&lt;p&gt;What if I translated Semgrep patterns into tree-sitter queries at build time, embedded them in the binary, and matched against ASTs directly?&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hard Part: Metavariables
&lt;/h2&gt;

&lt;p&gt;Semgrep uses &lt;code&gt;$VARIABLES&lt;/code&gt; to capture arbitrary code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eval($USER_INPUT)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This matches &lt;code&gt;eval(x)&lt;/code&gt;, &lt;code&gt;eval(foo.bar)&lt;/code&gt;, &lt;code&gt;eval(getInput())&lt;/code&gt; — anything.&lt;/p&gt;

&lt;p&gt;Tree-sitter queries don't have metavariables. They have captures:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scheme"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;call_expression&lt;/span&gt;
  &lt;span class="nv"&gt;function:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;@func&lt;/span&gt;
  &lt;span class="nv"&gt;arguments:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;arguments&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;@arg&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;@func&lt;/code&gt; and &lt;code&gt;@arg&lt;/code&gt; are captures — they grab whatever matches that position.&lt;/p&gt;

&lt;p&gt;So I built a translator. It parses Semgrep patterns, identifies metavariables, and generates tree-sitter queries with captures in the right places.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified version of the pattern compiler&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;compile_pattern&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;semgrep&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;TreeSitterQuery&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;parse_semgrep_pattern&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;semgrep&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&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="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="nf"&gt;.walk&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;Metavar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// $X becomes (_) @x&lt;/span&gt;
                &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"(_) @{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="nf"&gt;.to_lowercase&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nf"&gt;Literal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;{}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="c1"&gt;// ... more cases&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nn"&gt;TreeSitterQuery&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Ellipsis Problem
&lt;/h2&gt;

&lt;p&gt;Semgrep's &lt;code&gt;...&lt;/code&gt; operator matches "zero or more of anything":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func($ARG, ...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This matches &lt;code&gt;func(a)&lt;/code&gt;, &lt;code&gt;func(a, b)&lt;/code&gt;, &lt;code&gt;func(a, b, c, d, e)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Tree-sitter queries can't express this directly. For these patterns, I fall back to walking the AST manually and checking if the structure matches.&lt;/p&gt;

&lt;p&gt;Not as fast as native queries, but still faster than Python interpretation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build-Time Compilation
&lt;/h2&gt;

&lt;p&gt;The magic happens in &lt;code&gt;build.rs&lt;/code&gt;. At compile time:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parse all 647 Semgrep YAML files&lt;/li&gt;
&lt;li&gt;Translate each pattern to a tree-sitter query (or AST walker)&lt;/li&gt;
&lt;li&gt;Serialize everything to a binary blob&lt;/li&gt;
&lt;li&gt;Embed it with &lt;code&gt;include_bytes!()&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In the compiled binary&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;RULES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;include_bytes!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"compiled_rules.bin"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// At runtime - instant loading&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;load_rules&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;RuleSet&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;bincode&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;deserialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RULES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No file I/O. No YAML parsing. No pattern compilation. The rules are just &lt;em&gt;there&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;On a 500K LOC monorepo:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Semgrep&lt;/td&gt;
&lt;td&gt;4m 12s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RMA&lt;/td&gt;
&lt;td&gt;23s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;About &lt;strong&gt;10x faster&lt;/strong&gt;. The difference gets bigger as codebases grow.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Still Rough
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;False positives on generated code (working on better heuristics)&lt;/li&gt;
&lt;li&gt;Some Semgrep features aren't supported yet (taint mode is partial)&lt;/li&gt;
&lt;li&gt;Error messages could be clearer&lt;/li&gt;
&lt;/ul&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;cargo &lt;span class="nb"&gt;install &lt;/span&gt;rma-cli
rma scan &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or with the interactive TUI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rma scan &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--interactive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's MIT licensed: &lt;a href="https://github.com/bumahkib7/rust-monorepo-analyzer" rel="noopener noreferrer"&gt;github.com/bumahkib7/rust-monorepo-analyzer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Would love feedback, especially if you try it on your own projects. What rules are missing? Too many false positives? Let me know.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you're interested in the pattern compiler implementation, check out &lt;code&gt;crates/rules/build.rs&lt;/code&gt; in the repo.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>security</category>
      <category>opensource</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
