Why I Built a Deterministic CPG-to-Nuclei Compiler in Rust
Author's Perspective on AI and Security Automation
My background is in mechanical engineering. When I transitioned into cybersecurity, I brought a physical mindset with me: a system only works when the right forces are applied to the right mechanisms. Today, the tech industry tends to treat AI like a magic wand. I see it differently. Large Language Models are a brilliant new technological commodity-much like electricity. You don't ask electricity to build a car; you use it to power the machines that do. You treat it with respect, implement security boundaries, and remember the golden rule of engineering: use the right tool for the right job. When you apply this to automated security testing and agentic workflows, the current approach is fundamentally broken. We are asking language models to do precision machining. Agents get stuck in trial-and-error loops-guessing payload syntaxes, hitting CLI flag errors, and hallucinating schema parameters. It burns massive amounts of execution credits. Even worse, when an agent finally produces a functional template, it often causes state-mutation friction. It modifies application settings or pollutes enterprise databases without cleaning up, forcing maintainers to reject the pull requests to protect production environments.
The cpg-nuclei-compiler Solution
I built and open-sourced cpg-nuclei-compiler (v1.0.0): a Rust-native compiler that converts Code Property Graphs (CPGs) into syntactically perfect ProjectDiscovery Nuclei YAML templates, backed by an isolated Docker execution harness. The conversion pipeline follows these stages:
- Source (C/C++, Java, Go, Python) โโโบ Joern CPG โโโบ DataFlowSlice โโโบ Nuclei YAML โโโบ Docker Harness (TP/TN)
The 3-Tier Hybrid Model
Rather than forcing an LLM to guess low-level exploit structures, this pipeline decouples high-level intent from exact code generation:
- The Intent (AI Layer): High-level reasoning maps target objectives. The AI acts as the electricity, deciding what to look for without wrestling with YAML formatting.
- The Machine (Rust Engine): The core compiler parses Joern CPG schemas and Single Static Assignment (SSA) dataflow paths. It computes taint graphs natively in Rust, generating 0%-error Nuclei YAML templates in milliseconds with minimal memory overhead.
- The Quality Control (Docker): The generated template runs inside an isolated container fixture to produce verifiable True Positive (TP) and True Negative (TN) reachability proofs before a pull request is ever submitted.
Real-World Proof: Solving Issue #11635 (PR #17007)
To validate this approach on a live target, I refactored the template for CVE-2024-51483 (changedetection.io Local File Inclusion) in ProjectDiscovery's official template repository (PR #17007). Legacy automated checks were mutating settings (e.g., forcing html_webdriver mode) and leaving orphaned watches in the database. Using my compiled harness, I standardized an enterprise-safe lifecycle:
- Snapshot: Captures CSRF tokens and records original application settings via GET requests.
- Execute: Temporarily enables required drivers and executes the LFI path check.
- Teardown: Issues explicit DELETE API calls and posts the original settings snapshot back to the server-leaving zero database artifacts.
Summary
By pairing deterministic graph compilation with closed-loop container verification, the goal is to clear template review backlogs, protect live environments, and give security engineers a reliable path from source code to verified security checks.
Comments
No comments yet. Start the discussion.