codatrace: runtime confirmation

codatrace runs inside your application and shows which codafort findings were actually reached by untrusted data at runtime.

It does not look for new vulnerabilities: it confirms or fails to confirm what the code analysis reported. That confirmation can go into the counter-signed declaration (the coda-attestation/1 artifact that attest create emits).

Ethical boundary. The agent observes and does not alter: it injects no payload, changes no call result, blocks no request, terminates no process. It is neither a WAF nor a sandbox. When the agent fails, it stops observing: at boot it switches itself off and says why in one stderr line; at runtime it stops sending events.
Licence. In the public binary, the codatrace collector needs a Pro, Verified or Platform licence (Vibe does not include runtime; without one it refuses and says how to activate). Only schema is free. The agent loaded into the application is never blocked: a missing licence does not take the application down. See plans.

The three verdicts

VerdictMeaningWhat to do
confirmed-at-runtimean event without a sanitizer correlated with the finding: the flow happened, undefendedtop priority; this is what the counter-signed declaration can state
sanitized-at-runtimethe sink was reached with a policy sanitizer of the right category on the paththere is a defence there
unreachedno flow with request data was observed at that sitenever read it as "safe": it is "not measured"

unreached has three causes the collector cannot tell apart: route not exercised, sink called with internal data, or a category the agent does not observe. That is why the evidence literally says not measured, never safe.

Install

The binary ships with the Python, Node and JVM agents in the same package. There is no PyPI or npm package: agent and collector must be the same version. Download from Download or install the codatrace.rb formula published as a release asset.

codatrace install python     # materialises the agent and prints how to load it
codatrace install node
codatrace install jvm        # outside the matrix (linux-arm64/Windows): prints the cc line to recompile

The flow

# 1. The policy says what the agent observes; it comes from the codafort taxonomy.
#    The release package ships the policy generated from the same commit:
codatrace policy --output policy.json

# 2. Start the application with the agent (`codatrace install <runtime>` prints the exact lines).
export CODATRACE_POLICY=policy.json CODATRACE_EVENTS=events.jsonl
node --import .codatrace/agents/node/codatrace_agent.mjs app.js    # Node: --import guarantees ESM order
#    Python: at process start `import codatrace_agent; codatrace_agent.install()` and
#    `app = codatrace_agent.wsgi(app)` (Django; in Flask, `app.wsgi_app`) or `codatrace_agent.asgi(app)`.
#    JVM: java -agentpath:.codatrace/agents/jvm/libcodatrace.so=events.jsonl …

# 3. Exercise the application (integration tests, staging, real acceptance traffic).

# 4. The code analysis, to cross-match:
codafort engine analyze --source . --output static.json

# 5. Collect and correlate by file, line and category:
codatrace collect --events events.jsonl --static static.json --policy-file policy.json --output report.json
SOCK="$(mktemp -d)/codatrace.sock"   # multi-worker: a private directory, CODATRACE_EVENTS="$SOCK" in the workers
codatrace collect --socket "$SOCK" --timeout 600 --static static.json --policy-file policy.json --output report.json

# 6. The evidence contribution for the counter-signed declaration (coda-evidence/1, modality: iast):
codatrace coverage > coverage.json
codatrace evidence --report report.json --coverage coverage.json > iast-ev.json
codafort attest create --evidence iast-ev.json

With --socket, the agents only connect to a socket owned by the same user. --timeout is the collection window (default 30 s); the report says how many connections the deadline cut (socket_connections_cut_by_deadline). Without --socket, the collector reads from --events or stdin, which is the path for CI with no live application. Whoever can write to the event channel can change what will be signed.

codatrace evidence --verdicts publishes the (finding, verdict) pairs summarised in the verdict_digest, with only an opaque id and verdict, never file or line. Without --coverage, the agent's coverage is absent from the evidence: "not measured", never zero.

What it does not see

  • The agent checks whether some request value appears inside the sink argument. A value transformed before the sink (hashed, compressed, encoded) goes unconfirmed: this can hide a flow, but it does not produce a false positive. Values under 3 characters are ignored, and partial concatenation escapes.
  • Sink coverage is partial. codatrace coverage lists what each agent observes.
  • Part of the taxonomy has no event to observe at runtime: numeric and structural rules (index, division, loop bound, allocation, overflow, ReDoS) and, for now, categories such as log, CORS, NoSQL, XPath, template and header splitting. These stay unreached.
  • Python and Node: the automatic source reads only the query string; for the body, call begin_request in your own middleware.
  • Python: a database driver whose connect is not a writable attribute gets no coverage.
  • Node: a loose exported function from an ESM package is not observed; a prototype method is.
  • JVM: no SQL or XSS coverage. Framework frames (Spring, Tomcat, Jackson…) do not count as application code. The sanitizer is observed at method entry.
  • Emission ceiling: CODATRACE_MAX_EVENTS (default 10,000). Drops appear in the report under events_over_cap.

Since codatrace only reports observed flows, a false positive is a bug.