mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-15 20:05:13 +02:00
Dynamic (#77)
This commit is contained in:
parent
55247b7fcd
commit
991c84a1eb
1464 changed files with 225448 additions and 1985 deletions
|
|
@ -1,5 +1,5 @@
|
|||
use crate::commands::scan::Diag;
|
||||
use crate::evidence::{Confidence, Evidence};
|
||||
use crate::evidence::{Confidence, Evidence, VerifyResult, VerifyStatus};
|
||||
use crate::patterns::{FindingCategory, Severity};
|
||||
use crate::utils::path::{DEFAULT_UI_MAX_FILE_BYTES, open_repo_text_file};
|
||||
use serde::Serialize;
|
||||
|
|
@ -26,6 +26,15 @@ pub const VALID_TRIAGE_STATES: &[&str] = &[
|
|||
"fixed",
|
||||
];
|
||||
|
||||
/// Valid dynamic verification states for findings.
|
||||
pub const VALID_DYNAMIC_VERIFICATION_STATES: &[&str] = &[
|
||||
"Confirmed",
|
||||
"NotConfirmed",
|
||||
"Inconclusive",
|
||||
"Unsupported",
|
||||
"Unverified",
|
||||
];
|
||||
|
||||
/// Check if a string is a valid triage state.
|
||||
pub fn is_valid_triage_state(s: &str) -> bool {
|
||||
VALID_TRIAGE_STATES.contains(&s)
|
||||
|
|
@ -38,6 +47,10 @@ pub struct FindingView {
|
|||
pub fingerprint: String,
|
||||
#[serde(skip_serializing_if = "String::is_empty")]
|
||||
pub portable_fingerprint: String,
|
||||
/// Blake3-derived stable cross-commit identity hash (M6.5). Zero when not
|
||||
/// yet computed (server-side scans always compute it post-analysis).
|
||||
#[serde(skip_serializing_if = "crate::server::models::is_zero_u64")]
|
||||
pub stable_hash: u64,
|
||||
pub path: String,
|
||||
pub line: usize,
|
||||
pub col: usize,
|
||||
|
|
@ -60,6 +73,8 @@ pub struct FindingView {
|
|||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub evidence: Option<Evidence>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub dynamic_verdict: Option<VerifyResult>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub guard_kind: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub rank_reason: Option<Vec<(String, String)>>,
|
||||
|
|
@ -195,6 +210,7 @@ pub struct FilterValues {
|
|||
pub languages: Vec<String>,
|
||||
pub rules: Vec<String>,
|
||||
pub statuses: Vec<String>,
|
||||
pub verification_statuses: Vec<String>,
|
||||
}
|
||||
|
||||
/// Collect distinct filter values from a slice of diagnostics.
|
||||
|
|
@ -205,6 +221,7 @@ pub fn collect_filter_values(findings: &[Diag]) -> FilterValues {
|
|||
let mut languages = BTreeSet::new();
|
||||
let mut rules = BTreeSet::new();
|
||||
let mut statuses = BTreeSet::new();
|
||||
let mut verification_statuses = BTreeSet::new();
|
||||
|
||||
for d in findings {
|
||||
severities.insert(d.severity.as_db_str().to_string());
|
||||
|
|
@ -217,12 +234,20 @@ pub fn collect_filter_values(findings: &[Diag]) -> FilterValues {
|
|||
}
|
||||
rules.insert(d.id.clone());
|
||||
statuses.insert(status_for_diag(d).to_string());
|
||||
verification_statuses.insert(
|
||||
dynamic_status_for_diag(d)
|
||||
.unwrap_or("Unverified")
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
// Always include all valid triage states so the filter dropdown is complete
|
||||
for s in VALID_TRIAGE_STATES {
|
||||
statuses.insert(s.to_string());
|
||||
}
|
||||
for s in VALID_DYNAMIC_VERIFICATION_STATES {
|
||||
verification_statuses.insert(s.to_string());
|
||||
}
|
||||
|
||||
FilterValues {
|
||||
severities: severities.into_iter().collect(),
|
||||
|
|
@ -231,6 +256,7 @@ pub fn collect_filter_values(findings: &[Diag]) -> FilterValues {
|
|||
languages: languages.into_iter().collect(),
|
||||
rules: rules.into_iter().collect(),
|
||||
statuses: statuses.into_iter().collect(),
|
||||
verification_statuses: verification_statuses.into_iter().collect(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -263,12 +289,36 @@ fn status_for_diag(d: &Diag) -> &'static str {
|
|||
}
|
||||
}
|
||||
|
||||
/// Human-readable dynamic status used by API filters and table rows.
|
||||
pub fn dynamic_status_label(status: VerifyStatus) -> &'static str {
|
||||
match status {
|
||||
VerifyStatus::Confirmed => "Confirmed",
|
||||
VerifyStatus::PartiallyConfirmed => "PartiallyConfirmed",
|
||||
VerifyStatus::NotConfirmed => "NotConfirmed",
|
||||
VerifyStatus::Inconclusive => "Inconclusive",
|
||||
VerifyStatus::Unsupported => "Unsupported",
|
||||
}
|
||||
}
|
||||
|
||||
/// Dynamic verification status for a diagnostic, when a verdict exists.
|
||||
pub fn dynamic_status_for_diag(d: &Diag) -> Option<&'static str> {
|
||||
d.evidence
|
||||
.as_ref()
|
||||
.and_then(|ev| ev.dynamic_verdict.as_ref())
|
||||
.map(|verdict| dynamic_status_label(verdict.status))
|
||||
}
|
||||
|
||||
pub(crate) fn is_zero_u64(v: &u64) -> bool {
|
||||
*v == 0
|
||||
}
|
||||
|
||||
/// Convert a Diag to a FindingView at a given index.
|
||||
pub fn finding_from_diag(index: usize, d: &Diag) -> FindingView {
|
||||
FindingView {
|
||||
index,
|
||||
fingerprint: compute_fingerprint(d),
|
||||
portable_fingerprint: String::new(), // set by caller with scan_root
|
||||
stable_hash: d.stable_hash,
|
||||
path: d.path.clone(),
|
||||
line: d.line,
|
||||
col: d.col,
|
||||
|
|
@ -287,6 +337,10 @@ pub fn finding_from_diag(index: usize, d: &Diag) -> FindingView {
|
|||
triage_note: String::new(),
|
||||
code_context: None,
|
||||
evidence: None,
|
||||
dynamic_verdict: d
|
||||
.evidence
|
||||
.as_ref()
|
||||
.and_then(|ev| ev.dynamic_verdict.clone()),
|
||||
guard_kind: None,
|
||||
rank_reason: None,
|
||||
sanitizer_status: None,
|
||||
|
|
@ -394,6 +448,10 @@ pub struct CompareResponse {
|
|||
pub fixed_findings: Vec<ComparedFinding>,
|
||||
pub changed_findings: Vec<ChangedFinding>,
|
||||
pub unchanged_findings: Vec<ComparedFinding>,
|
||||
/// Verdict-level diff entries (M6.5). Populated when findings in both
|
||||
/// scans carry `stable_hash` values.
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub verdict_diff: Vec<crate::baseline::VerdictDiffEntry>,
|
||||
}
|
||||
|
||||
/// Minimal scan metadata for comparison headers.
|
||||
|
|
@ -704,6 +762,8 @@ pub struct ScannerQuality {
|
|||
pub symex_verified_rate: f64,
|
||||
/// Count broken down by symbolic verdict label.
|
||||
pub symex_breakdown: HashMap<String, usize>,
|
||||
/// Dynamic verifier verdict counts from the latest scan.
|
||||
pub dynamic_verification: crate::commands::scan::DynamicVerificationSummary,
|
||||
}
|
||||
|
||||
/// One issue-category bucket (rule-family derived). Broader than OWASP, with
|
||||
|
|
@ -880,6 +940,7 @@ mod tests {
|
|||
rollup: None,
|
||||
finding_id: String::new(),
|
||||
alternative_finding_ids: Vec::new(),
|
||||
stable_hash: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue