diff --git a/docs/dynamic.md b/docs/dynamic.md index 99fd68ec..21fc34fa 100644 --- a/docs/dynamic.md +++ b/docs/dynamic.md @@ -119,7 +119,7 @@ Each line is a JSON object with a versioned envelope: { "schema_version": 1, "nyx_version": "0.7.0", - "corpus_version": "4", + "corpus_version": "15", "kind": "verdict", "ts": "2026-05-15T18:42:09Z", "finding_id": "a3b1...", @@ -134,6 +134,8 @@ Each line is a JSON object with a versioned envelope: } ``` +The literal `nyx_version` and `corpus_version` values shift between releases; see `crate::dynamic::telemetry::CORPUS_VERSION` for the active payload-corpus version your binary writes. + | Field | Meaning | | --- | --- | | `schema_version` | Event schema version. Readers reject mismatches. | diff --git a/docs/output.md b/docs/output.md index dc2d9936..c4a5e077 100644 --- a/docs/output.md +++ b/docs/output.md @@ -19,9 +19,9 @@ Human-readable, color-coded output to stdout. Status messages go to stderr. | Tag | Color | Meaning | |-----|-------|---------| -| `[HIGH]` | Red, bold | Critical -- likely exploitable | -| `[MEDIUM]` | Orange, bold | Important -- may be exploitable | -| `[LOW]` | Muted blue-gray | Informational -- code quality or weak signal | +| `[HIGH]` | Red, bold | Critical, likely exploitable | +| `[MEDIUM]` | Orange, bold | Important, may be exploitable | +| `[LOW]` | Muted blue-gray | Informational: code quality or weak signal | ### Evidence fields @@ -139,9 +139,9 @@ Fields marked "no" are omitted when empty/null/false to keep output compact. | Level | Meaning | |-------|---------| -| `High` | Strong signal -- taint-confirmed flow, definite state violation | -| `Medium` | Moderate signal -- resource leak, path-validated taint, CFG structural | -| `Low` | Weak signal -- AST pattern match, possible resource leak, degraded analysis | +| `High` | Strong signal: taint-confirmed flow, definite state violation | +| `Medium` | Moderate signal: resource leak, path-validated taint, CFG structural | +| `Low` | Weak signal: AST pattern match, possible resource leak, degraded analysis | ### Evidence object @@ -192,12 +192,12 @@ nyx scan . --format sarif > results.sarif The SARIF output includes: -- **Tool metadata** -- Nyx name and version -- **Rules** -- Rule ID, description, severity mapping -- **Results** -- One result per finding with location, message, and properties -- **Properties** -- Each result includes `category` and optionally `confidence` and `rollup.count` -- **Related locations** -- Rollup findings include example locations in `relatedLocations` -- **Artifacts** -- File paths referenced by findings +- **Tool metadata**: Nyx name and version +- **Rules**: Rule ID, description, severity mapping +- **Results**: One result per finding with location, message, and properties +- **Properties**: Each result includes `category` and optionally `confidence` and `rollup.count` +- **Related locations**: Rollup findings include example locations in `relatedLocations` +- **Artifacts**: File paths referenced by findings ### GitHub Code Scanning integration @@ -219,9 +219,10 @@ The SARIF output includes: |------|---------| | `0` | Scan completed successfully; no findings matched `--fail-on` threshold | | `1` | `--fail-on` threshold breached (at least one finding meets or exceeds the specified severity) | -| Non-zero | Error (I/O, config, database, parse error) | +| `2` | `--gate` policy tripped (e.g. `no-new-confirmed` saw a new Confirmed finding, or `resolve-all-confirmed` saw a previously Confirmed finding still open) | +| Other non-zero | Error (I/O, config, database, parse error) | -Without `--fail-on`, Nyx always exits `0` on a successful scan regardless of findings count. +Without `--fail-on` or `--gate`, Nyx always exits `0` on a successful scan regardless of findings count. --- @@ -229,9 +230,9 @@ Without `--fail-on`, Nyx always exits `0` on a successful scan regardless of fin | Level | Description | Typical rules | |-------|-------------|---------------| -| **High** | Critical vulnerabilities -- likely exploitable | Command injection, unsafe deserialization, banned C functions, taint-confirmed flows with user input sources | -| **Medium** | Important issues -- may be exploitable with additional context | SQL concatenation, XSS sinks, reflection, unguarded sinks, resource leaks | -| **Low** | Informational -- code quality or weak signals | Weak crypto algorithms, insecure randomness, `unwrap()`/`panic!()`, type-safety escapes | +| **High** | Critical vulnerabilities, likely exploitable | Command injection, unsafe deserialization, banned C functions, taint-confirmed flows with user input sources | +| **Medium** | Important issues, may be exploitable with additional context | SQL concatenation, XSS sinks, reflection, unguarded sinks, resource leaks | +| **Low** | Informational: code quality or weak signals | Weak crypto algorithms, insecure randomness, `unwrap()`/`panic!()`, type-safety escapes | ### Non-production severity downgrade @@ -260,13 +261,13 @@ Suppress specific findings directly in source code using `nyx:ignore` comments. ### Directive forms ```python -x = dangerous() # nyx:ignore taint-unsanitised-flow ← suppresses this line +x = dangerous() # nyx:ignore taint-unsanitised-flow (suppresses this line) # nyx:ignore-next-line taint-unsanitised-flow -x = dangerous() ← suppresses this line +x = dangerous() (suppressed by the comment above) ``` -- `nyx:ignore ` -- suppresses findings on the **same line** as the comment. -- `nyx:ignore-next-line ` -- suppresses findings on the **next line**. +- `nyx:ignore `: suppresses findings on the **same line** as the comment. +- `nyx:ignore-next-line `: suppresses findings on the **next line**. - For taint findings, the primary line is the **sink line** (the `line` field in output). ### Rule ID matching diff --git a/src/ast.rs b/src/ast.rs index a13b0d1d..7bb2bfb6 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1893,7 +1893,6 @@ impl<'a> ParsedFile<'a> { cfg: &body.graph, entry: body.entry, lang: caller_lang, - file_path: &self.source.file_path_str, source_bytes: self.source.bytes, func_summaries: self.local_summaries(), global_summaries, diff --git a/src/cfg_analysis/auth.rs b/src/cfg_analysis/auth.rs index d521d48f..34e5e9a5 100644 --- a/src/cfg_analysis/auth.rs +++ b/src/cfg_analysis/auth.rs @@ -157,10 +157,6 @@ fn find_auth_nodes(ctx: &AnalysisContext) -> Vec { } impl CfgAnalysis for AuthGap { - fn name(&self) -> &'static str { - "auth-gap" - } - fn run(&self, ctx: &AnalysisContext) -> Vec { // Decorator/annotation/attribute auth on the body declaration // already gates every sink in the body, skip the @@ -218,7 +214,6 @@ impl CfgAnalysis for AuthGap { findings.push(CfgFinding { rule_id: "cfg-auth-gap".to_string(), - title: "Missing auth check".to_string(), severity: Severity::High, confidence: Confidence::Medium, span: info.ast.span, diff --git a/src/cfg_analysis/error_handling.rs b/src/cfg_analysis/error_handling.rs index 09e2819a..c9a40c5b 100644 --- a/src/cfg_analysis/error_handling.rs +++ b/src/cfg_analysis/error_handling.rs @@ -306,10 +306,6 @@ fn find_post_if_sinks(cfg: &crate::cfg::Cfg, if_node: NodeIndex) -> Vec &'static str { - "incomplete-error-handling" - } - fn run(&self, ctx: &AnalysisContext) -> Vec { let mut findings = Vec::new(); @@ -369,7 +365,6 @@ impl CfgAnalysis for IncompleteErrorHandling { if has_dangerous_successor { findings.push(CfgFinding { rule_id: "cfg-error-fallthrough".to_string(), - title: "Error check without return".to_string(), severity: Severity::Medium, confidence: Confidence::Medium, span: info.ast.span, diff --git a/src/cfg_analysis/guards.rs b/src/cfg_analysis/guards.rs index 5d5141f0..57fa1dcc 100644 --- a/src/cfg_analysis/guards.rs +++ b/src/cfg_analysis/guards.rs @@ -2715,10 +2715,6 @@ fn sink_in_entrypoint(ctx: &AnalysisContext, sink: NodeIndex) -> bool { } impl CfgAnalysis for UnguardedSink { - fn name(&self) -> &'static str { - "unguarded-sink" - } - fn run(&self, ctx: &AnalysisContext) -> Vec { let doms = dominators::compute_dominators(ctx.cfg, ctx.entry); let sink_nodes = dominators::find_sink_nodes(ctx.cfg); @@ -2976,7 +2972,6 @@ impl CfgAnalysis for UnguardedSink { findings.push(CfgFinding { rule_id: "cfg-unguarded-sink".to_string(), - title: "Unguarded sink".to_string(), severity, confidence, span: sink_info.ast.span, diff --git a/src/cfg_analysis/mod.rs b/src/cfg_analysis/mod.rs index 34808fe2..66a9bde1 100644 --- a/src/cfg_analysis/mod.rs +++ b/src/cfg_analysis/mod.rs @@ -140,8 +140,6 @@ pub enum Confidence { #[derive(Debug, Clone)] pub struct CfgFinding { pub rule_id: String, - #[allow(dead_code)] - pub title: String, pub severity: Severity, pub confidence: Confidence, pub span: (usize, usize), @@ -154,12 +152,8 @@ pub struct AnalysisContext<'a> { pub cfg: &'a crate::cfg::Cfg, pub entry: NodeIndex, pub lang: Lang, - #[allow(dead_code)] - pub file_path: &'a str, - #[allow(dead_code)] pub source_bytes: &'a [u8], pub func_summaries: &'a FuncSummaries, - #[allow(dead_code)] pub global_summaries: Option<&'a GlobalSummaries>, /// Per-file SSA summaries map produced by /// `lower_all_functions_from_bodies` (after both the augment pass @@ -170,7 +164,6 @@ pub struct AnalysisContext<'a> { /// suppress structural findings whose taint flow has been proven /// validated through helper summaries (CVE-2026-25544 patched /// counterpart). - #[allow(dead_code)] pub ssa_summaries: Option< &'a std::collections::HashMap< crate::symbol::FuncKey, @@ -218,8 +211,6 @@ pub struct AnalysisContext<'a> { } pub trait CfgAnalysis { - #[allow(dead_code)] - fn name(&self) -> &'static str; fn run(&self, ctx: &AnalysisContext) -> Vec; } diff --git a/src/cfg_analysis/resources.rs b/src/cfg_analysis/resources.rs index d815568e..c41c7c47 100644 --- a/src/cfg_analysis/resources.rs +++ b/src/cfg_analysis/resources.rs @@ -531,10 +531,6 @@ fn has_explicit_lock_acquire(ctx: &AnalysisContext, acquire: NodeIndex) -> bool } impl CfgAnalysis for ResourceMisuse { - fn name(&self) -> &'static str { - "resource-misuse" - } - fn run(&self, ctx: &AnalysisContext) -> Vec { let pairs = rules::resource_pairs(ctx.lang); let exit = match dominators::find_exit_node(ctx.cfg) { @@ -631,7 +627,6 @@ impl CfgAnalysis for ResourceMisuse { } else { "cfg-resource-leak".to_string() }, - title: format!("{} may leak", pair.resource_name), severity: Severity::Medium, confidence: Confidence::Medium, span: info.ast.span, diff --git a/src/cfg_analysis/tests.rs b/src/cfg_analysis/tests.rs index 6d6801ea..e3accd17 100644 --- a/src/cfg_analysis/tests.rs +++ b/src/cfg_analysis/tests.rs @@ -23,7 +23,6 @@ fn parse_and_analyse( cfg, entry, lang, - file_path: "test.rs", source_bytes: src, func_summaries: summaries, global_summaries: None, @@ -54,7 +53,6 @@ fn parse_and_run_all(src: &[u8], lang_str: &str, ts_lang: Language) -> Vec( cfg: &body.graph, entry: body.entry, lang, - file_path: "test.rs", source_bytes: src, func_summaries: &file_cfg.summaries, global_summaries: None, @@ -1227,7 +1223,6 @@ fn config_sanitizer_suppresses_unguarded_sink() { cfg, entry, lang, - file_path: "test.rs", source_bytes: src, func_summaries: summaries, global_summaries: None, @@ -1708,7 +1703,6 @@ fn cfg_only_no_taint_produces_low_severity() { cfg, entry, lang, - file_path: "test.rs", source_bytes: src, func_summaries: summaries, global_summaries: None, diff --git a/src/cfg_analysis/unreachable.rs b/src/cfg_analysis/unreachable.rs index 7968f095..c11d3cd1 100644 --- a/src/cfg_analysis/unreachable.rs +++ b/src/cfg_analysis/unreachable.rs @@ -38,10 +38,6 @@ fn event_handler_callbacks(ctx: &AnalysisContext) -> HashSet { } impl CfgAnalysis for UnreachableCode { - fn name(&self) -> &'static str { - "unreachable-code" - } - fn run(&self, ctx: &AnalysisContext) -> Vec { let reachable = dominators::reachable_set(ctx.cfg, ctx.entry); let handler_callbacks = event_handler_callbacks(ctx); @@ -122,7 +118,6 @@ impl CfgAnalysis for UnreachableCode { findings.push(CfgFinding { rule_id: rule_id.to_string(), - title: title.to_string(), severity, confidence: Confidence::High, span: info.ast.span,