Security memory for your codebase. Cairn turns every vulnerability you fix into a proven rule, finds the siblings you missed, and stops the same mistake from coming back.
Status: pre-alpha. The lesson format, CLI and GitHub Action work end to end; quality numbers on real advisories are being measured on VariantBench before the first release.
$ cairn learn --commit 416196f
Learning from 416196fbee80 Scope order lookup to the current user (fixes HackerOne #1234)
app/orders.py (1 changed line)
attempt 1 ✗ too broad: still matches the fixed code orders.py:12
attempt 2 ✓ matches the vulnerable code, silent on the fix
✓ New lesson: Record loaded by URL ID without scoping to the current user
python-flask-idor-get-by-id · CWE-639, CWE-862 · high
Found 1 possible sibling in the current code:
HIGH Record loaded by URL ID without scoping to the current user
python-flask-idor-get-by-id · CWE-639, CWE-862 · proposed
app/orders.py:19
18 │ def update_address(address_id):
❱ 19 │ address = Address.query.get(address_id)
20 │ address.update(request.json)
The fix closed one IDOR. The lesson found the one next to it, and once approved it blocks the pattern on every future pull request.
Finding vulnerabilities is getting cheap. Fixing them once and for all is not. The same root cause comes back in another file, another service, another quarter, and AI coding agents now reproduce old mistakes faster than reviewers can catch them.
Cairn keeps what your team has learned as lessons: small, reviewable files containing a rule plus the evidence that the rule works. Lessons are:
- Proven. A lesson is accepted only if its rule matches the vulnerable code and stays silent on the fixed code (the differential test). No proof, no enforcement.
- Open and portable. Plain YAML in your repo, using the Opengrep/Semgrep rule language. Share packs between teams; read every line.
- Yours. Runs locally. Bring any model, including a local one, for writing new lessons. Nothing is sent anywhere unless you configure it to.
pipx install git+https://github.com/twomuchlabs/cairn # `pip install cairn-security` from the first releaseThen open a terminal in any project and run:
cairnThat's it. The first time, Cairn asks once to install its rule engine (Semgrep CE, pinned) and then scans the project. Already have Opengrep or Semgrep? Cairn uses it. Setting up a script or CI? cairn doctor --fix installs the engine without asking.
cairn # scan this project for known mistakes (same as cairn hunt)
cairn learn --commit <fix-sha> # turn a security fix into a lesson, then hunt its siblings
cairn validate # re-prove every lesson (run this in CI)
cairn check --baseline-ref origin/main --sarif cairn.sarif # enforce approved lessons on a PRScanning, checking and validating need no model and no network. learn needs one model. If ANTHROPIC_API_KEY is set, Cairn uses Claude Sonnet automatically. To choose another:
export CAIRN_MODEL=anthropic:claude-sonnet-5-5 ANTHROPIC_API_KEY=...
export CAIRN_MODEL=openai:<model> OPENAI_API_KEY=... # or any OpenAI-compatible API via OPENAI_BASE_URL
export CAIRN_MODEL=ollama:<model> # local; code never leaves your machineLessons that learn writes are saved to .cairn/lessons/ as proposed. A person reviews them and sets status: approved (with approved_by and approved). Only approved lessons can fail CI.
Cairn ships a Claude Code plugin. After every edit the agent makes, it checks the change against your approved lessons and tells the agent what to fix (only for mistakes the edit introduced, not old debt). It also blocks shell commands that launch another coding agent with its safety checks switched off.
/plugin install cairn --marketplace TwoMuchLabs/cairn
The plugin also starts Cairn's MCP server, so the agent can search your lessons before writing risky code ("how do we handle file downloads here?") and check files on request. All its tools are read-only and limited to the project.
The plugin calls the cairn CLI, so install that first, with the MCP extra: pip install "cairn-security[mcp]".
# .github/workflows/cairn.yml
name: cairn
on: pull_request
permissions:
contents: read
security-events: write # only needed for the SARIF upload
jobs:
cairn:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with: { fetch-depth: 0, persist-credentials: false }
- uses: twomuchlabs/cairn@<commit-sha> # pin to a full commit SHA
- uses: github/codeql-action/upload-sarif@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # v4.39.1
if: always()
with: { sarif_file: cairn.sarif }On pull requests the action reports only findings introduced by the PR, so adopting Cairn on an existing codebase does not flood anyone.
fix commit
┌──────┴──────┐
before (vulnerable) after (patched)
│ │
rule must match rule must be silent
near the changed near the changed
lines lines
└──────┬──────┘
+ clean code stays under the false-positive budget
+ every `cairn-expect` line is matched
▼
lesson accepted as "proposed" → human approves → enforced
If the fix itself left an unfixed sibling in the same file, Cairn records it (cairn-unfixed) and tells you, instead of rejecting a correct rule. See the lesson format spec.
We publish how we measure, including where Cairn fails:
- Every bundled lesson passes its acceptance test in CI.
- VariantBench runs
learnon real advisories with single-commit fixes in Python, JavaScript/TypeScript, Go and Java, with a held-out split we never tune on. - Findings are quiet by default: low-confidence lessons are hidden, overlapping matches are reported once, and nothing is suppressed automatically.
- Rules are as good as the Opengrep/Semgrep rule language: intra-file taint, no cross-service data flow.
learnworks best on focused fixes (1–4 files). Broad refactors make poor lessons and are refused.- Fix generation, exploitability (VEX) analysis and coding-agent guardrails are on the roadmap, not in this release.
Lessons are the most valuable contribution. See CONTRIBUTING.md. Report vulnerabilities in Cairn itself via SECURITY.md.
Apache-2.0, including the lesson format and every bundled lesson. Community lessons stay under the same license; we will not relicense them.