nyx/docs/dynamic.md

3.8 KiB
Raw Blame History

Dynamic verification

As of M7, nyx verifies every Confidence >= Medium finding by default: it builds a minimal harness, runs your code's entry point against a curated payload corpus inside a sandbox, and records the verdict in each finding's evidence block.

Default-on semantics

nyx scan                 # verifies Medium+ findings (default)
nyx scan --no-verify     # static analysis only, no harness execution
nyx scan --verify        # same as default; explicit for clarity in scripts

--no-verify is the escape hatch. It overrides the config default for a single run without changing nyx.toml.

What "verified" means

A finding with dynamic_verdict.status: Confirmed was successfully triggered by at least one payload in nyx's corpus. The corpus covers common patterns for each vulnerability class (SQL injection, XSS, command injection, SSRF, etc.) per language.

A finding with dynamic_verdict.status: NotConfirmed was attempted but no payload fired. This is not a false-positive signal — it means the corpus did not have a payload that matched the specific sink variant or the execution path was not reachable in the test harness.

A finding with dynamic_verdict.status: Unsupported could not be attempted. Common reasons: confidence below threshold, no flow steps, language or sink type not yet supported by the harness layer.

Confidence gate

Only Confidence >= Medium findings are verified by default (§5.1). To also verify low-confidence findings — for corpus building or backfill — pass --verify-all-confidence:

nyx scan --verify-all-confidence

This is not recommended for production scans because low-confidence findings have a higher false-positive rate and the harness may produce unreliable verdicts.

nyx.toml opt-out

If you want static-only scans permanently, set verify = false in nyx.toml:

[scanner]
verify = false

This survives upgrades — the M7 default flip only changes the inherited default for projects that have not explicitly set the field.

Sandbox backends

nyx uses docker when available, then falls back to an in-process runner:

nyx scan --backend docker    # require docker; fail if unavailable
nyx scan --backend process   # in-process runner (no container; less isolation)
nyx scan --unsafe-sandbox    # alias for --backend process

The docker backend mounts only the entry file's directory and blocks all outbound network by default. When out-of-band detection is enabled (oob_listener in config), the container gets --network bridge with a host-gateway route.

Repro artifacts

When a finding is Confirmed, nyx writes a repro artifact to ~/.cache/nyx/repro/<stable_hash>/. The artifact contains the harness spec and the triggering payload. You can regenerate the verdict with:

nyx scan --verify <path>    # re-scans and re-verifies

See docs/output.md for the dynamic_verdict field schema.

Wall-clock cost

Verification adds harness build + sandbox startup time per finding. On typical codebases with 1050 Medium+ findings, end-to-end overhead is 25× static-only.

If scan time is unacceptable for a given workflow (e.g. IDE integration, quick pre-commit check), use --no-verify for that workflow and rely on the full scan in CI.

Opting in to feedback

False positives (nyx says Confirmed but you disagree) can be recorded:

nyx verify-feedback <finding_id> --wrong "reason"

This writes to the local telemetry log (~/.cache/nyx/dynamic/events.jsonl) and contributes to precision monitoring. Feedback is never uploaded automatically.

nyx serve integration

The browser UI shows dynamic_verdict in each finding's detail panel and uses the verdict in ranking (Confirmed findings surface first). The scan compare page has a Verdict Diff tab that shows which findings changed verification status between two scans.