Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions deny.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ all-features = true
[advisories]
unmaintained = "workspace"
ignore = [
# gix-date 0.11.0 vulnerability via tame-index 0.25.0 -> gix 0.75.0
# Waiting for tame-index to update to gix 0.77+
{ id = "RUSTSEC-2025-0140", reason = "tame-index 0.25.0 pins gix 0.75.0; no updated version available yet" },
]

[bans]
Expand Down
3 changes: 2 additions & 1 deletion src/cargo-deny/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,8 @@ fn print_diagnostics(
use cargo_deny::diag::Check;

if log_ctx.format == crate::Format::Sarif {
let mut sc = cargo_deny::sarif::SarifCollector::default();
let workspace_root = krates.map_or("", |k| k.workspace_root().as_str());
let mut sc = cargo_deny::sarif::SarifCollector::new(workspace_root);

for pack in rx {
sc.add_diagnostics(pack, files);
Expand Down
41 changes: 33 additions & 8 deletions src/sarif/collector.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::diag::Extra;
use crate::sarif::model::{
DefaultConfiguration, Driver, Help, Location, Message, Result as SarifResult, Rule,
RuleProperties, Run, SarifLog, TextContent, Tool,
ArtifactLocation, DefaultConfiguration, Driver, Help, Location, Message, PhysicalLocation,
Region, Result as SarifResult, Rule, RuleProperties, Run, SarifLog, TextContent, Tool,
};
use crate::{
Kid,
Expand All @@ -14,6 +14,7 @@ use std::fmt::Write as _;
pub struct SarifCollector {
diagnostics: Vec<DiagnosticData>,
rules: BTreeMap<DiagnosticCode, RuleData>,
workspace_root: String,
}

struct DiagnosticData {
Expand All @@ -31,17 +32,15 @@ struct RuleData {
description: &'static str,
}

#[allow(clippy::derivable_impls)]
impl Default for SarifCollector {
fn default() -> Self {
impl SarifCollector {
pub fn new(workspace_root: impl Into<String>) -> Self {
Self {
diagnostics: Vec::new(),
rules: BTreeMap::new(),
workspace_root: workspace_root.into(),
}
}
}

impl SarifCollector {
pub fn add_diagnostics(&mut self, pack: Pack, files: &crate::diag::Files) {
for diag in pack {
let Some(code) = diag.code else {
Expand Down Expand Up @@ -255,11 +254,37 @@ impl SarifCollector {
}
}

// GitHub Code Scanning requires at least one location per result.
// If no locations were found (e.g., for dependency advisories that only
// reference Cargo.lock which is filtered out), add a fallback location
// pointing to the workspace Cargo.toml since that's where dependencies are declared.
let locations = if diag.locations.is_empty() {
let fallback_uri = if self.workspace_root.is_empty() {
"Cargo.toml".to_string()
} else {
format!("{}/Cargo.toml", self.workspace_root)
};
vec![Location {
physical_location: PhysicalLocation {
artifact_location: ArtifactLocation { uri: fallback_uri },
region: Region {
start_line: 1,
byte_offset: 0,
byte_length: 0,
snippet: None,
message: None,
},
},
}]
} else {
diag.locations
};

SarifResult {
rule_id,
message: diag.message,
level: severity_to_sarif_level(diag.severity),
locations: diag.locations,
locations,
partial_fingerprints: fingerprints,
}
})
Expand Down
22 changes: 16 additions & 6 deletions tests/sarif.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ where
runner(cctx, tx);
},
|| {
let mut sarif = cargo_deny::sarif::SarifCollector::default();
let workspace_root = ctx.krates.workspace_root().as_str();
let mut sarif = cargo_deny::sarif::SarifCollector::new(workspace_root);

let default = if std::env::var_os("CI").is_some() {
60
Expand Down Expand Up @@ -64,11 +65,20 @@ where
// so rather than try and fail, just redact manually
for res in &mut sl.runs[0].results {
for loc in &mut res.locations {
loc.physical_location.artifact_location.uri = loc
.physical_location
.artifact_location
.uri
.replace(root.as_str(), "{CWD}");
let uri = &mut loc.physical_location.artifact_location.uri;
// First try standard CWD replacement
*uri = uri.replace(root.as_str(), "{CWD}");

// Handle stale absolute paths from pre-computed test metadata (e.g., sarif_advisories)
// by normalizing any remaining absolute path containing known project subdirectories
if uri.starts_with('/') {
for marker in ["/cargo-deny/examples/", "/cargo-deny/tests/"] {
if let Some(pos) = uri.find(marker) {
*uri = format!("{{CWD}}{}", &uri[pos + "/cargo-deny".len()..]);
break;
}
}
}
}

for fp in res.partial_fingerprints.values_mut() {
Expand Down
Loading