Skip to content

Commit c7057a0

Browse files
authored
Add fallback location for results without file locations (#819)
1 parent 7f5d4a9 commit c7057a0

File tree

7 files changed

+488
-46
lines changed

7 files changed

+488
-46
lines changed

deny.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ all-features = true
1313
[advisories]
1414
unmaintained = "workspace"
1515
ignore = [
16+
# gix-date 0.11.0 vulnerability via tame-index 0.25.0 -> gix 0.75.0
17+
# Waiting for tame-index to update to gix 0.77+
18+
{ id = "RUSTSEC-2025-0140", reason = "tame-index 0.25.0 pins gix 0.75.0; no updated version available yet" },
1619
]
1720

1821
[bans]

src/cargo-deny/check.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,8 @@ fn print_diagnostics(
548548
use cargo_deny::diag::Check;
549549

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

553554
for pack in rx {
554555
sc.add_diagnostics(pack, files);

src/sarif/collector.rs

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::diag::Extra;
22
use crate::sarif::model::{
3-
DefaultConfiguration, Driver, Help, Location, Message, Result as SarifResult, Rule,
4-
RuleProperties, Run, SarifLog, TextContent, Tool,
3+
ArtifactLocation, DefaultConfiguration, Driver, Help, Location, Message, PhysicalLocation,
4+
Region, Result as SarifResult, Rule, RuleProperties, Run, SarifLog, TextContent, Tool,
55
};
66
use crate::{
77
Kid,
@@ -14,6 +14,7 @@ use std::fmt::Write as _;
1414
pub struct SarifCollector {
1515
diagnostics: Vec<DiagnosticData>,
1616
rules: BTreeMap<DiagnosticCode, RuleData>,
17+
workspace_root: String,
1718
}
1819

1920
struct DiagnosticData {
@@ -31,17 +32,15 @@ struct RuleData {
3132
description: &'static str,
3233
}
3334

34-
#[allow(clippy::derivable_impls)]
35-
impl Default for SarifCollector {
36-
fn default() -> Self {
35+
impl SarifCollector {
36+
pub fn new(workspace_root: impl Into<String>) -> Self {
3737
Self {
3838
diagnostics: Vec::new(),
3939
rules: BTreeMap::new(),
40+
workspace_root: workspace_root.into(),
4041
}
4142
}
42-
}
4343

44-
impl SarifCollector {
4544
pub fn add_diagnostics(&mut self, pack: Pack, files: &crate::diag::Files) {
4645
for diag in pack {
4746
let Some(code) = diag.code else {
@@ -255,11 +254,37 @@ impl SarifCollector {
255254
}
256255
}
257256

257+
// GitHub Code Scanning requires at least one location per result.
258+
// If no locations were found (e.g., for dependency advisories that only
259+
// reference Cargo.lock which is filtered out), add a fallback location
260+
// pointing to the workspace Cargo.toml since that's where dependencies are declared.
261+
let locations = if diag.locations.is_empty() {
262+
let fallback_uri = if self.workspace_root.is_empty() {
263+
"Cargo.toml".to_string()
264+
} else {
265+
format!("{}/Cargo.toml", self.workspace_root)
266+
};
267+
vec![Location {
268+
physical_location: PhysicalLocation {
269+
artifact_location: ArtifactLocation { uri: fallback_uri },
270+
region: Region {
271+
start_line: 1,
272+
byte_offset: 0,
273+
byte_length: 0,
274+
snippet: None,
275+
message: None,
276+
},
277+
},
278+
}]
279+
} else {
280+
diag.locations
281+
};
282+
258283
SarifResult {
259284
rule_id,
260285
message: diag.message,
261286
level: severity_to_sarif_level(diag.severity),
262-
locations: diag.locations,
287+
locations,
263288
partial_fingerprints: fingerprints,
264289
}
265290
})

tests/sarif.rs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ where
2121
runner(cctx, tx);
2222
},
2323
|| {
24-
let mut sarif = cargo_deny::sarif::SarifCollector::default();
24+
let workspace_root = ctx.krates.workspace_root().as_str();
25+
let mut sarif = cargo_deny::sarif::SarifCollector::new(workspace_root);
2526

2627
let default = if std::env::var_os("CI").is_some() {
2728
60
@@ -64,11 +65,20 @@ where
6465
// so rather than try and fail, just redact manually
6566
for res in &mut sl.runs[0].results {
6667
for loc in &mut res.locations {
67-
loc.physical_location.artifact_location.uri = loc
68-
.physical_location
69-
.artifact_location
70-
.uri
71-
.replace(root.as_str(), "{CWD}");
68+
let uri = &mut loc.physical_location.artifact_location.uri;
69+
// First try standard CWD replacement
70+
*uri = uri.replace(root.as_str(), "{CWD}");
71+
72+
// Handle stale absolute paths from pre-computed test metadata (e.g., sarif_advisories)
73+
// by normalizing any remaining absolute path containing known project subdirectories
74+
if uri.starts_with('/') {
75+
for marker in ["/cargo-deny/examples/", "/cargo-deny/tests/"] {
76+
if let Some(pos) = uri.find(marker) {
77+
*uri = format!("{{CWD}}{}", &uri[pos + "/cargo-deny".len()..]);
78+
break;
79+
}
80+
}
81+
}
7282
}
7383

7484
for fp in res.partial_fingerprints.values_mut() {

0 commit comments

Comments
 (0)