Documentation menu

Introduction

locac is an autonomous vulnerability-research agent harness. You point it at a repository, give it a goal, and it maps the attack surface, traces untrusted input to dangerous sinks, builds a proof-of-concept, and reports a small set of cross-verified High/Critical findings.

It is a harness, not a scanner. The deterministic tools do the mechanical work: AST outlines, sink scanners, call-graph tracing, a DAST module runner, a crash oracle. The model reasons, triages, and proves. Neither half is useful without the other.

What locac optimises for

Most security tooling optimises for recall: emit everything that pattern-matches, let a human sort it out. locac optimises for the opposite. A run that produces two findings you can hand to a vendor without a follow-up question beats a run that produces two hundred you have to triage.

That trade-off is enforced by the harness, not by prompting. A finding cannot be recorded at High or Critical unless it clears the mechanical gate described below, and the gate is code, so the model cannot talk its way past it.

The five invariants

These are non-negotiable. They are in the system prompt, and the ones that can be checked mechanically are also enforced in the tool layer.

  1. Default deployment only. A bug that needs debug flags, example credentials, or an administrator mistake to fire is not a finding.
  2. No app sink. The bug must live in official, supported code. Never build or wrap a deliberately vulnerable app around the target to demonstrate one.
  3. High/Critical only. Informational, low, medium, hardening and version-disclosure issues are out of scope by construction.
  4. Cross-verify. A finding is accepted only after independent agents reproduce it and false-positive elimination fails to break it.
  5. Resume from memory. Prior findings and the scratchpad are read before any expensive tool runs again. A run picks up where the last one stopped instead of restarting.

How a run works

Every run drives the same four-phase loop:

  1. Orient. outline, ls, glob, code_search map entry points, routes, parsers, config, and trust boundaries.
  2. Hunt. grok_sink and scan_sinks locate candidates for a vulnerability class, then trace_callers follows the flow from an untrusted source (request params, body, headers, env, file reads, deserialization) to a dangerous sink.
  3. Verify. A tool hit is a candidate, not a finding. Five conditions must all hold: reachable from an untrusted entry point in the default deployment; the attacker controls the tainted value; nothing on the path sanitises it; no debug flag or example credential is required; and a concrete PoC reproduces it. Then the agent tries to refute the path.
  4. Report. record_finding, then cross_verify, then score_finding, which computes CVSS and applies the severity gate so that severity is never guessed, then report_findings.

The evidence gate

This is locac's actual differentiator, so it is worth being precise about.

To record a finding at High or Critical, two independent conditions must both be satisfied:

  • Semantic proof, bound to this finding. The finding must cite, via artifact_ref, an artifact that carries a structured vulnerability claim (location, line, class, the security effect to observe) and a machine-checked assertion that passed. Three tools mint one: reproduce, when its oracle actually triggered; run_module, per confirmed detection; and replay_request, when a marker appears in the mutated response and is absent from the benign baseline. The claim's location must match the finding's, which is why a High/Critical finding must carry a file (or an exact DAST URL) and, for a source finding, a line.
    Running something is not enough. A execute script that exits zero, a plain replay, a scan that confirmed nothing, and a coverage report are all recorded and auditable, but they are diagnostics: none of them evaluated an exploit assertion, so none can promote a severity. A real artifact minted for a different finding cannot either.
  • A 2-of-3 cross-verify quorum. cross_verify(finding_id) spawns three verifiers with different lenses: reproduce (build the PoC from scratch and run it), refute (find the sanitiser that makes this a false positive), and boundary (confirm it fires in the default deployment). A vote counts only if that verifier minted its artifact during this round, named the exact existing finding_id, matched its location, and used an artifact distinct from the other two lenses'. Re-citing the candidate's own artifact is not independent evidence. Two of the three must promote.

Fail either condition and score_finding will not let the severity through. The finding can still be recorded. It just cannot claim a severity it has not earned.

Trust model in one paragraph

The target repository is untrusted input. It is treated as attacker-authored, including its comments, its README, and any output its code produces. Every tool result carrying target-derived bytes is wrapped in ⟦untrusted-output <nonce>⟧ fences, and the agent is instructed, in the harness invariants that survive every prompt override, to read fenced text strictly as evidence and never as an instruction. Configuration, skills, roles and keybindings are read only from LOCAC_HOME (~/.locac by default), never from the repository under audit. The session database also lives outside the target root, because sandboxed target code has write access to that root.

The Security model page covers the full picture, including the OS sandbox and approval modes.

What locac is not

  • Not a CI linter. It is expensive, it runs commands, and it is designed for a research session rather than a per-commit hook.
  • Not a fuzzer. It drives a fuzzer or a PoC when one is warranted; it does not replace one.
  • Not a hosted service. It is a single binary that runs on your machine, talks to whichever model provider you configure, and stores everything locally.

Where to go next