-
-
Lintern runs as a GitLab CI job: the scan stage fails and the pipeline goes red before anyone merges.
-
GitLab Duo, assigned as reviewer, automatically posted this security review on merge request !1 and said "I did not follow it."
-
58 of 95 attacks caught on Gray Swan's public prompt-injection benchmark, 0 of 35 false alarms. Rerun it with no key.
-
Five kinds of attack, each with a sourced stat and what Lintern found in merge request !1.
Summary for judges
- Path A, Assisted. Fixed rules decide pass or fail in GitLab CI. GitLab Duo reviews the merge request on its own, flags the planted attacks and requests changes. A person makes the merge call.
- See Duo's review live: on merge request !1, GitLab Duo, assigned as reviewer, posted a security review automatically. On the README payload it wrote that the line tries to send
AWS_SECRET_ACCESS_KEYto an external host, and "I did not follow it." - Check it in 60 seconds: open merge request !1. It plants five attacks, and its pipeline goes red. The failed scan job lists 1 critical and 5 high findings. On main, without the planted lines, every job passes.
- Measured: on Gray Swan's public prompt-injection benchmark, Lintern caught 58 of 95 attacks with 0 false alarms on 35 clean samples. Anyone can rerun it with
python cert/run_cert.py, no API key needed.
Inspiration
Most new code is now written with AI agents, and those agents read every file in the repo, including text a reviewer never sees. Recent disclosures (README key exfiltration, a setup script that opened a reverse shell, the "rules file backdoor", slopsquatting) are all post-code problems: the attack lands in a merge request that looks harmless in review. In my view, that is exactly one of the gaps this hackathon is about.
What it does
On every merge request, Lintern runs five deterministic scanners in GitLab CI, writes a GitLab SAST report, and fails the pipeline on any high finding. It checks for five kinds of attack:
- Poisoned agent instructions: invisible Unicode characters in AGENTS.md that tell the agent to add a dependency.
- Prompt injection: "ignore all previous instructions" text in docs, including attempts to send secrets out.
- Made-up packages: a dependency name that does not exist on the registry, ready for an attacker to claim.
- Agents with too much power: an MCP config that auto-approves a shell tool that can run anything.
- Unsafe model files: a model loader that uses pickle, which runs code when the file loads.
The rules decide pass or fail, so no hidden prompt can talk the gate into passing. GitLab Duo then reviews the merge request on its own: assigned as reviewer, it posts a security review on each planted change and requests changes. You can also ask Duo's Security Analyst agent, running on Claude Sonnet 5.5, in Duo Chat, as the video shows. Duo explains the why; a person makes the merge call.
How we built it
- Five standard-library Python scanners. Each check is a rule in
rules/rulepack.json, mapped to the OWASP Top 10 for LLM Applications (2025). A new attack means one new rule. - A GitLab CI pipeline with three Lintern jobs: a self-test, the scan gate, and a benchmark run. GitLab's own SAST and Secret Detection templates run alongside it.
- SAST report output (
reports: sast), so findings land in GitLab's security report. - GitLab Duo's automated Code Review flow, which reviews the merge request and requests changes, plus Duo Chat with the Security Analyst agent on Claude Sonnet 5.5.
- A keyless benchmark harness against the MIT-licensed Gray Swan IPI Arena public attacks.
Where it runs in GitLab
- Create: checks every merge request before review.
- Verify: the
lintern-scanjob fails the pipeline on a high finding. - Secure: findings land in GitLab's security report.
Challenges
Real indirect prompt-injection attacks look harmless, so simple pattern matching misses them. We tuned structural signals (role-tag impersonation, fake tool output, exfiltration instructions) to raise detection while keeping false alarms at zero. Keeping a language model out of the pass or fail decision was a deliberate choice: a gate that a prompt can argue with is not a gate.
Accomplishments
- A working gate on a real merge request, with the red and green pipelines public.
- A benchmark number anyone can reproduce in seconds, with no key.
- One of the five attack classes comes from a real report: I found and reported an unsafe model file in SurrealML on huntr (8 Jul 2026).
What we learned
The dangerous part of AI-assisted code is often not the code itself but the text around it: instructions, configs and dependency lists that agents trust and reviewers skim.
Limits
37 of the 95 benchmark attacks still get through, and the report names them. The limits are written up in docs/CANDOUR.md.
What's next
More rules from new disclosures, wider framework mapping (MITRE ATLAS, NIST), a larger clean-sample set, automatic fix commits for simple cases such as hidden Unicode and unpinned packages, and optional auto-merge when the gate passes and Duo agrees, for teams that want full automation.
Built With
- claude
- gitlab
- gitlab-ci
- gitlab-duo
- owasp
- python
- sast

Log in or sign up for Devpost to join the conversation.