<?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: ragnaroast</title>
    <description>The latest articles on DEV Community by ragnaroast (@ragnaroast).</description>
    <link>https://dev.to/ragnaroast</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%2F4192651%2F71ff0ffc-ae08-4a8a-ac5a-633af631cdf1.jpg</url>
      <title>DEV Community: ragnaroast</title>
      <link>https://dev.to/ragnaroast</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ragnaroast"/>
    <language>en</language>
    <item>
      <title>How We Cut 3,500 CI/CD Security Alerts Down to 5 Fixes Using FalkorDB, Graph Theory, and Too Much Caffeine</title>
      <dc:creator>ragnaroast</dc:creator>
      <pubDate>Sun, 18 Oct 2026 18:12:54 +0000</pubDate>
      <link>https://dev.to/ragnaroast/how-we-cut-3500-cicd-security-alerts-down-to-5-fixes-using-falkordb-graph-theory-and-too-much-4818</link>
      <guid>https://dev.to/ragnaroast/how-we-cut-3500-cicd-security-alerts-down-to-5-fixes-using-falkordb-graph-theory-and-too-much-4818</guid>
      <description>&lt;p&gt;It was 2:00 AM, our terminal was glowing with an angry shade of red, and my monitor looked like it had caught digital chickenpox.&lt;/p&gt;

&lt;p&gt;We had just run a popular CI/CD linter on an open-source repository’s GitHub Actions workflows. The output? &lt;strong&gt;3,541 warnings across 165 files.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Thirty-five hundred alerts. &lt;/p&gt;

&lt;p&gt;If you’ve ever worked in software engineering, you know exactly what happens when a developer sees 3,500 security alerts: &lt;strong&gt;they close the tab, make a cup of coffee, and pretend they never saw it.&lt;/strong&gt; That’s not security; that’s pure, distilled alert fatigue.&lt;/p&gt;

&lt;p&gt;Sitting there in the dim glow of our laptops, our team had a collective realization: &lt;strong&gt;CI/CD security isn’t broken because we lack scanners. It’s broken because existing scanners treat complex pipelines like flat text files.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the story of how we built &lt;strong&gt;ChokePoint (Alepheus)&lt;/strong&gt; during the hackathon—a graph-powered CI/CD attack-path agent that uses FalkorDB’s graph algorithms to turn an overwhelming swamp of warnings into mathematically proven, minimal fixes.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Anatomy of the Nightmare: Strangers Meet Your Crown Jewels
&lt;/h3&gt;

&lt;p&gt;Think about what a GitHub Actions workflow actually does. &lt;/p&gt;

&lt;p&gt;It’s the one place in your entire infrastructure where code and text submitted by complete strangers on the internet (forked pull requests, issue titles, casual comments, third-party action tags) directly share computing resources with your most sensitive production secrets: AWS deployment roles, npm publishing tokens, and Docker registry keys.&lt;/p&gt;

&lt;p&gt;Remember the &lt;em&gt;tj-actions&lt;/em&gt; incident? A compromised third-party action tag meant that every pipeline running that action handed over all of its environment secrets on a silver platter. &lt;/p&gt;

&lt;p&gt;Or consider the classic &lt;strong&gt;"Pwn-Request"&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An external contributor opens a pull request.&lt;/li&gt;
&lt;li&gt;The workflow triggers on &lt;code&gt;pull_request_target&lt;/code&gt; (which grants write access).&lt;/li&gt;
&lt;li&gt;A step checks out the untrusted PR branch.&lt;/li&gt;
&lt;li&gt;A bash script interpolates &lt;code&gt;${{ github.event.issue.title }}&lt;/code&gt; into an &lt;code&gt;echo&lt;/code&gt; command.&lt;/li&gt;
&lt;li&gt;Three steps down, a deploy job uses that runner’s token to push to production.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Existing linters flag step 4 as a "script injection" and step 2 as an "untrusted trigger." But they evaluate each file in isolation. They have zero clue whether that injection actually flows into a job with production credentials, or if it dies harmlessly in an empty test runner.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Lightbulb Moment: "Wait... Pipelines Are Graphs!"
&lt;/h3&gt;

&lt;p&gt;At about 3:30 AM, after our third energy drink, the whiteboard came alive.&lt;/p&gt;

&lt;p&gt;A CI/CD pipeline isn't a list of lines. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Steps depend on Jobs. &lt;/li&gt;
&lt;li&gt;Jobs depend on Workflows. &lt;/li&gt;
&lt;li&gt;Workflows consume Actions and output Artifacts. &lt;/li&gt;
&lt;li&gt;And data &lt;em&gt;flows&lt;/em&gt; from untrusted inputs down to crown-jewel capabilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;It’s a directed graph.&lt;/strong&gt; And if it’s a graph, we shouldn't be using regex and AST grep. We should be using a graph database.&lt;/p&gt;

&lt;p&gt;Enter &lt;strong&gt;FalkorDB&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  The FalkorDB Adventure: Building the Brain
&lt;/h3&gt;

&lt;p&gt;We spun up FalkorDB in Docker (&lt;code&gt;docker compose up -d&lt;/code&gt;), pinned our Redis ports, and got down to business. &lt;/p&gt;

&lt;p&gt;We designed a &lt;strong&gt;dual-layer graph model&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Structural Layer:&lt;/strong&gt; Real-world entities (&lt;code&gt;Workflow&lt;/code&gt;, &lt;code&gt;Job&lt;/code&gt;, &lt;code&gt;Step&lt;/code&gt;, &lt;code&gt;Action&lt;/code&gt;, &lt;code&gt;Secret&lt;/code&gt;, &lt;code&gt;Permission&lt;/code&gt;) connected by structural relationships (&lt;code&gt;HAS_JOB&lt;/code&gt;, &lt;code&gt;USES&lt;/code&gt;, &lt;code&gt;HOLDS&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Flow Layer (&lt;code&gt;FLOWS&lt;/code&gt;):&lt;/strong&gt; Derived dynamic edges representing attacker movement (&lt;code&gt;inject&lt;/code&gt;, &lt;code&gt;exec&lt;/code&gt;, &lt;code&gt;expose&lt;/code&gt;, &lt;code&gt;poison&lt;/code&gt;, &lt;code&gt;consume&lt;/code&gt;) annotated with exploit difficulty, line numbers, and remediation costs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Writing the Cypher query to find multi-hop attack chains felt like watching magic happen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cypher"&gt;&lt;code&gt;&lt;span class="k"&gt;MATCH&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;i:&lt;/span&gt;&lt;span class="n"&gt;Input&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;attacker:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;:FLOWS&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="ss"&gt;]&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="py"&gt;c:&lt;/span&gt;&lt;span class="n"&gt;Capability&lt;/span&gt; &lt;span class="ss"&gt;{&lt;/span&gt;&lt;span class="py"&gt;crown:&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="ss"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="ss"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="ow"&gt;IN&lt;/span&gt; &lt;span class="nf"&gt;relationships&lt;/span&gt;&lt;span class="ss"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;cost&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;e.difficulty&lt;/span&gt;&lt;span class="ss"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_difficulty&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;total_difficulty&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suddenly, with variable-depth traversal, we weren't looking at 3,500 isolated line warnings. We were looking at &lt;strong&gt;5 actual, exploitable end-to-end paths&lt;/strong&gt; leading from an attacker to AWS root.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Mathematical Secret Sauce: Min-Cut with &lt;code&gt;algo.maxFlow&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Now came the coolest part of the project—the part where computer science theory saved our sanity.&lt;/p&gt;

&lt;p&gt;Once you know there are 25 attack paths in your system, do you really need to rewrite 25 workflows? &lt;br&gt;
&lt;strong&gt;No!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Think of attack paths like water flowing through pipes from an attacker source to your crown-jewel sink. To stop the flood, you don’t need to replace every pipe; you just need to close the &lt;strong&gt;minimum cut&lt;/strong&gt;—the narrowest bottleneck of edges whose removal completely disconnects the attacker from your secrets.&lt;/p&gt;

&lt;p&gt;FalkorDB has native graph algorithms built right in. We fed our pipeline graph into &lt;strong&gt;&lt;code&gt;algo.maxFlow&lt;/code&gt;&lt;/strong&gt;, using our &lt;code&gt;fix_cost&lt;/code&gt; property as the edge capacity:&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;# Run FalkorDB max-flow and residual BFS
&lt;/span&gt;&lt;span class="n"&gt;cut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;min_fix_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The algorithm solved the bottleneck in &lt;strong&gt;60 milliseconds&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Instead of telling the developer to fix 25 disparate items across 10 files, FalkorDB pointed directly to &lt;strong&gt;two high-leverage choke points&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Move one unquoted issue title into an &lt;code&gt;env:&lt;/code&gt; variable block.&lt;/li&gt;
&lt;li&gt;Pin one third-party action to an immutable commit SHA.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Boom. All 25 attack chains severed. An 80% reduction in developer fix fatigue.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  "What-If" Verification: The Power of &lt;code&gt;GRAPH.COPY&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;One of our biggest hurdles was: &lt;em&gt;How do we prove to a skeptical security team that our fix actually works without pushing broken code to production?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;FalkorDB had our back with &lt;strong&gt;&lt;code&gt;GRAPH.COPY&lt;/code&gt;&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Whenever our agent drafted a YAML patch (using &lt;code&gt;ruamel.yaml&lt;/code&gt; so we didn't wipe out developers' carefully crafted comments):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It copied the live graph into a temporary &lt;code&gt;whatif:*&lt;/code&gt; sandbox in memory.&lt;/li&gt;
&lt;li&gt;Re-parsed the candidate YAML and re-ingested it into the clone.&lt;/li&gt;
&lt;li&gt;Re-ran the attack chain traversal queries.&lt;/li&gt;
&lt;li&gt;If and only if the active chains dropped from $N \to 0$, it marked the patch as &lt;strong&gt;Verified&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No production mutations. No guessing. Mathematical proof in sub-second time.&lt;/p&gt;




&lt;h3&gt;
  
  
  Bringing It All Together: Autonomous Gemini Agent + React Web Console
&lt;/h3&gt;

&lt;p&gt;To make this feel like a tool from 2026, we wired everything together:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;LLM Engine:&lt;/strong&gt; We hooked up &lt;strong&gt;Google Gemini 2.5 Flash&lt;/strong&gt; using its native function-calling SDK on the free tier (zero API costs, super fast response times!).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FastMCP Server:&lt;/strong&gt; We exposed the whole FalkorDB toolset (&lt;code&gt;list_chains&lt;/code&gt;, &lt;code&gt;min_fix_set&lt;/code&gt;, &lt;code&gt;blast_radius&lt;/code&gt;) over the Model Context Protocol, so AI IDEs like Cursor and Claude Desktop can run our queries natively.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React + Vite Dashboard:&lt;/strong&gt; We built a dark-mode cybersecurity console with real-time SSE event streaming so you can watch the AI agent "think", inspect hop-by-hop evidence, compare PR snapshot diffs, and review unified patch diffs.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  What We Learned (The Real Takeaways)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Graphs turn messy security panic into clean math:&lt;/strong&gt; Trying to trace multi-file, reusable CI/CD workflows with regex is a fool's errand. Graphs don't care how deeply nested your composite actions are; traversal just works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alert fatigue is a data modeling problem:&lt;/strong&gt; Linters drown developers in warnings because they lack reachability context. Graph reachability filters out 80% of the noise automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FalkorDB algorithms are a superpower:&lt;/strong&gt; Having &lt;code&gt;algo.maxFlow&lt;/code&gt;, &lt;code&gt;algo.pageRank&lt;/code&gt;, and &lt;code&gt;GRAPH.COPY&lt;/code&gt; built directly into the database engine meant we didn't have to export data into NetworkX or write complex graph logic from scratch in Python. It all ran at C-speed in-engine.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  The Finish Line
&lt;/h3&gt;

&lt;p&gt;Building ChokePoint during this hackathon was an absolute blast. We went from staring at an unreadable wall of 3,500 linter errors to watching a live dashboard prove that two targeted patches could secure an entire enterprise repository.&lt;/p&gt;

&lt;p&gt;If you want to check out the project, explore the Cypher queries, or try out the CLI on your own workflows, check out our repository on GitHub:&lt;br&gt;
👉 &lt;strong&gt;&lt;a href="https://github.com/ragnaroast/alepheus" rel="noopener noreferrer"&gt;https://github.com/ragnaroast/alepheus&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Built with ❤️, Python, React, Google Gemini, and FalkorDB.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>flakordb</category>
      <category>ai</category>
      <category>cybersecurity</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
