Forge-CRS — Autonomous Cyber Reasoning System

User Guide

← Back to outcome

Forge-CRS — User Guide

Running a campaign

From app/:

node bin/crs.mjs run

You'll get a table like:

  STATUS TARGET          CWE        EXECS   PoV        PATCH/REG
  ------------------------------------------------------------------------------
  OK     config-merge    CWE-1321   3       53->31B    fixed 2/2
  OK     path-store      CWE-22     189     14->2B     fixed 2/2
  OK     task-runner     CWE-78     14      22->1B     fixed 1/1
  OK     regex-validate  CWE-1333   40      31->23B    fixed 4/4
  OK     binary-reader   CWE-125    5       6->2B      fixed 2/2

How to read it:

  • STATUSOK = REMEDIATED (discovered + classified + patched + PoV

neutralized + regression preserved). PART = partial, MISS = not found, FAIL = patch failed.

  • EXECS — how many test cases the fuzzer executed before it tripped the

oracle. Lower means the bug was easy to reach; this is a real search, so it varies by target.

  • PoV — proof-of-vulnerability size *before → after* minimization. The

minimizer shrinks the random crashing input down to the essential trigger (e.g. path traversal minimizes to just ..).

  • PATCH/REG — whether the synthesized patch neutralized the PoV, and how

many functional regression cases still pass.

Useful flags

FlagEffect
--seed <n>Set the fuzzing seed (campaigns are fully deterministic per seed).
--target <id>Run a single target (config-merge, path-store, task-runner, regex-validate, binary-reader).
--reportWrite the full structured campaign to .work/campaign-report.json.
--quietPrint only a machine-readable summary line (for CI).

Exit code is 0 only if the full pipeline remediated every target.

The full report

--report emits, per target: discovery stats (executions, iterations, coverage blocks), the minimized PoV, the classified CWE, the **unified-style patch diff**, and the per-case regression results. This is the artifact to attach to a ticket or audit.

Adding your own target

The engine is target-agnostic; all knowledge lives in app/src/registry.mjs. A target entry provides:

  • file / entry — the source file and exported function under test.
  • kindin-process (coverage-guided) or worker (time-bounded, for hangs).
  • mutateKindstring | buffer | json (selects the mutator family).
  • seeds — initial corpus.
  • makeContext() / makeArgs(value, ctx) — turn a fuzz value into a real call.
  • oracle(exec, ctx, value) — decide if a result is a real vulnerability

(return { vuln, signal, evidence }).

  • regression — functional cases any patch must keep passing.
  • patch{ find, replace, summary } semantic source rewrite.

Drop a vulnerable module in app/targets/, add a registry entry, and node bin/crs.mjs run --target <id> will attack and repair it. The oracle and the patch are deliberately decoupled: discovery never sees the fix, and the patch is never trusted until the validator re-proves the PoV is dead.

Safety

No real exploit is ever executed: the filesystem read and process spawn for the traversal/injection targets are dependency-injected recorders, so the CRS observes what *would* happen without touching your machine. ReDoS candidates run in a disposable worker the parent can hard-kill.