From a18b3fa1bc9e18e0693affb8087379436f2d3b1c Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Tue, 31 Mar 2026 21:33:04 -0400 Subject: [PATCH 01/18] Use codeflows for more context in SARIF output Signed-off-by: William Woodruff --- crates/zizmor/src/output/sarif.rs | 192 ++++++++----- crates/zizmor/tests/integration/cli.rs | 109 +++++++- ...gration__e2e__sarif_zizmor_properties.snap | 255 +++++++++++++++++- 3 files changed, 487 insertions(+), 69 deletions(-) diff --git a/crates/zizmor/src/output/sarif.rs b/crates/zizmor/src/output/sarif.rs index 6f63c09c9..78809e94e 100644 --- a/crates/zizmor/src/output/sarif.rs +++ b/crates/zizmor/src/output/sarif.rs @@ -3,9 +3,10 @@ use std::collections::HashSet; use serde_sarif::sarif::{ - ArtifactContent, ArtifactLocation, Invocation, Location as SarifLocation, LogicalLocation, - Message, MultiformatMessageString, PhysicalLocation, PropertyBag, Region, ReportingDescriptor, - Result as SarifResult, ResultKind, ResultLevel, Run, Sarif, Tool, ToolComponent, + ArtifactContent, ArtifactLocation, CodeFlow, Invocation, Location as SarifLocation, + LogicalLocation, Message, MultiformatMessageString, PhysicalLocation, PropertyBag, Region, + ReportingDescriptor, Result as SarifResult, ResultKind, ResultLevel, Run, Sarif, + ThreadFlow, ThreadFlowLocation, Tool, ToolComponent, }; use crate::finding::{Finding, Severity, location::Location}; @@ -96,6 +97,67 @@ fn build_results(findings: &[Finding]) -> Vec { fn build_result(finding: &Finding<'_>) -> SarifResult { let primary = finding.primary_location(); + let related: Vec<_> = finding + .visible_locations() + .filter(|l| !l.symbolic.is_primary()) + .collect(); + + // Build the message with back-links to related locations. + // SARIF embedded links use [text](id) syntax, where id references + // a related location's integer ID. GitHub renders these as clickable + // modals that users can click through to see more context. + let message = if related.is_empty() { + finding.desc.to_string() + } else { + use std::fmt::Write; + let mut msg = finding.desc.to_string(); + for (i, loc) in related.iter().enumerate() { + write!( + &mut msg, + " [{annotation}]({id})", + annotation = loc.symbolic.annotation.as_ref(), + id = i + 1 + ) + .unwrap(); + } + msg + }; + + // Build related locations with sequential IDs for back-linking. + let related_locations: Vec = related + .iter() + .enumerate() + .map(|(i, loc)| build_location(loc, Some((i + 1) as i64))) + .collect(); + + // Build code flows for better visualization of location chains. + // GitHub renders these as step-by-step traces in security alerts. + let all_locations: Vec<_> = std::iter::once(primary).chain(related.iter().copied()).collect(); + let code_flows = if all_locations.len() > 1 { + let thread_flow_locations: Vec = all_locations + .iter() + .map(|loc| { + let importance = if loc.symbolic.is_primary() { + "essential" + } else { + "important" + }; + ThreadFlowLocation::builder() + .location(build_location(loc, None)) + .importance(serde_json::Value::String(importance.into())) + .build() + }) + .collect(); + vec![CodeFlow::builder() + .thread_flows(vec![ + ThreadFlow::builder() + .locations(thread_flow_locations) + .build(), + ]) + .build()] + } else { + vec![] + }; SarifResult::builder() .rule_id(format!("zizmor/{id}", id = finding.ident)) @@ -105,20 +167,10 @@ fn build_result(finding: &Finding<'_>) -> SarifResult { // code security alert titles when the primary annotation was // terse. So now we use the finding's description again, like // we did before 1.4.0. - .message(finding.desc) - .locations(build_locations(std::iter::once(primary))) - // TODO: Evaluate including the related locations via CodeFlows - // instead -- GitHub seems to do a better job of rendering these, - // but still doesn't do a great job of putting all of the locations - // into the same render. - // TODO: Give related locations IDs and back-link to them in the - // main location's message -- GitHub renders these as modals that - // users can click through to see more context. - .related_locations(build_locations( - finding - .visible_locations() - .filter(|l| !l.symbolic.is_primary()), - )) + .message(Message::builder().text(message).build()) + .locations(vec![build_location(primary, None)]) + .related_locations(related_locations) + .code_flows(code_flows) .level(ResultLevel::from(finding.determinations.severity)) .kind(ResultKind::from(finding.determinations.severity)) .properties( @@ -145,57 +197,65 @@ fn build_result(finding: &Finding<'_>) -> SarifResult { .build() } -fn build_locations<'a>(locations: impl Iterator>) -> Vec { - locations - .map(|location| { - SarifLocation::builder() - .logical_locations([LogicalLocation::builder() - .properties( - PropertyBag::builder() - .additional_properties([( - "symbolic".into(), - serde_json::value::to_value(location.symbolic.clone()) - .expect("failed to serialize symbolic location"), - )]) - .build(), - ) - .build()]) - .physical_location( - PhysicalLocation::builder() - .artifact_location( - ArtifactLocation::builder() - .uri(location.symbolic.key.sarif_path()) - .build(), - ) - .region( - Region::builder() - // NOTE: SARIF lines/columns are 1-based. - .start_line((location.concrete.location.start_point.row as i64) + 1) - .end_line((location.concrete.location.end_point.row as i64) + 1) - .start_column( - (location.concrete.location.start_point.column as i64) + 1, - ) - .end_column( - (location.concrete.location.end_point.column as i64) + 1, - ) - .source_language("yaml") - .snippet( - ArtifactContent::builder() - .text(location.concrete.feature) - .build(), - ) - .build(), - ) - .build(), - ) - .message( - Message::builder() - .text(location.symbolic.annotation.as_ref()) +fn build_physical_location(location: &Location<'_>) -> PhysicalLocation { + PhysicalLocation::builder() + .artifact_location( + ArtifactLocation::builder() + .uri(location.symbolic.key.sarif_path()) + .build(), + ) + .region( + Region::builder() + // NOTE: SARIF lines/columns are 1-based. + .start_line((location.concrete.location.start_point.row as i64) + 1) + .end_line((location.concrete.location.end_point.row as i64) + 1) + .start_column((location.concrete.location.start_point.column as i64) + 1) + .end_column((location.concrete.location.end_point.column as i64) + 1) + .source_language("yaml") + .snippet( + ArtifactContent::builder() + .text(location.concrete.feature) .build(), ) - .build() - }) - .collect() + .build(), + ) + .build() +} + +fn build_logical_locations(location: &Location<'_>) -> Vec { + vec![LogicalLocation::builder() + .properties( + PropertyBag::builder() + .additional_properties([( + "symbolic".into(), + serde_json::value::to_value(location.symbolic.clone()) + .expect("failed to serialize symbolic location"), + )]) + .build(), + ) + .build()] +} + +fn build_location(location: &Location<'_>, id: Option) -> SarifLocation { + let message = Message::builder() + .text(location.symbolic.annotation.as_ref()) + .build(); + let physical = build_physical_location(location); + let logical = build_logical_locations(location); + + match id { + Some(id) => SarifLocation::builder() + .id(id) + .logical_locations(logical) + .physical_location(physical) + .message(message) + .build(), + None => SarifLocation::builder() + .logical_locations(logical) + .physical_location(physical) + .message(message) + .build(), + } } #[cfg(test)] diff --git a/crates/zizmor/tests/integration/cli.rs b/crates/zizmor/tests/integration/cli.rs index a0863086a..e8b0174b7 100644 --- a/crates/zizmor/tests/integration/cli.rs +++ b/crates/zizmor/tests/integration/cli.rs @@ -277,6 +277,7 @@ jobs: ], "results": [ { + "codeFlows": [], "kind": "fail", "level": "warning", "locations": [ @@ -343,6 +344,110 @@ jobs: "ruleId": "zizmor/artipacked" }, { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "importance": "essential", + "location": { + "logicalLocations": [ + { + "properties": { + "symbolic": { + "annotation": "default permissions used due to no permissions: block", + "feature_kind": "Normal", + "key": { + "Stdin": {} + }, + "kind": "Primary", + "route": { + "route": [ + { + "Key": "jobs" + }, + { + "Key": "test" + } + ] + } + } + } + } + ], + "message": { + "text": "default permissions used due to no permissions: block" + }, + "physicalLocation": { + "artifactLocation": { + "uri": "" + }, + "region": { + "endColumn": 1, + "endLine": 7, + "snippet": { + "text": " test:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v3\n" + }, + "sourceLanguage": "yaml", + "startColumn": 3, + "startLine": 3 + } + } + } + }, + { + "importance": "important", + "location": { + "logicalLocations": [ + { + "properties": { + "symbolic": { + "annotation": "this job", + "feature_kind": "Normal", + "key": { + "Stdin": {} + }, + "kind": "Related", + "route": { + "route": [ + { + "Key": "jobs" + }, + { + "Key": "test" + } + ] + } + } + } + } + ], + "message": { + "text": "this job" + }, + "physicalLocation": { + "artifactLocation": { + "uri": "" + }, + "region": { + "endColumn": 1, + "endLine": 7, + "snippet": { + "text": " test:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v3\n" + }, + "sourceLanguage": "yaml", + "startColumn": 3, + "startLine": 3 + } + } + } + } + ] + } + ] + } + ], "kind": "fail", "level": "warning", "locations": [ @@ -392,7 +497,7 @@ jobs: } ], "message": { - "text": "overly broad permissions" + "text": "overly broad permissions [this job](1)" }, "properties": { "zizmor/confidence": "Medium", @@ -401,6 +506,7 @@ jobs: }, "relatedLocations": [ { + "id": 1, "logicalLocations": [ { "properties": { @@ -448,6 +554,7 @@ jobs: "ruleId": "zizmor/excessive-permissions" }, { + "codeFlows": [], "kind": "fail", "level": "error", "locations": [ diff --git a/crates/zizmor/tests/integration/snapshots/integration__e2e__sarif_zizmor_properties.snap b/crates/zizmor/tests/integration/snapshots/integration__e2e__sarif_zizmor_properties.snap index 7cefd8dff..816040f0b 100644 --- a/crates/zizmor/tests/integration/snapshots/integration__e2e__sarif_zizmor_properties.snap +++ b/crates/zizmor/tests/integration/snapshots/integration__e2e__sarif_zizmor_properties.snap @@ -13,6 +13,119 @@ expression: "zizmor().offline(true).input(input_under_test(\"several-vulnerabili ], "results": [ { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "importance": "essential", + "location": { + "logicalLocations": [ + { + "properties": { + "symbolic": { + "annotation": "uses write-all permissions", + "feature_kind": "Normal", + "key": { + "Local": { + "given_path": "@@INPUT@@", + "prefix": null + } + }, + "kind": "Primary", + "route": { + "route": [ + { + "Key": "jobs" + }, + { + "Key": "hackme" + }, + { + "Key": "permissions" + } + ] + } + } + } + } + ], + "message": { + "text": "uses write-all permissions" + }, + "physicalLocation": { + "artifactLocation": { + "uri": "@@INPUT@@" + }, + "region": { + "endColumn": 27, + "endLine": 11, + "snippet": { + "text": " permissions: write-all" + }, + "sourceLanguage": "yaml", + "startColumn": 5, + "startLine": 11 + } + } + } + }, + { + "importance": "important", + "location": { + "logicalLocations": [ + { + "properties": { + "symbolic": { + "annotation": "this job", + "feature_kind": "Normal", + "key": { + "Local": { + "given_path": "@@INPUT@@", + "prefix": null + } + }, + "kind": "Related", + "route": { + "route": [ + { + "Key": "jobs" + }, + { + "Key": "hackme" + } + ] + } + } + } + } + ], + "message": { + "text": "this job" + }, + "physicalLocation": { + "artifactLocation": { + "uri": "@@INPUT@@" + }, + "region": { + "endColumn": 1, + "endLine": 17, + "snippet": { + "text": " hackme:\n name: hackme\n runs-on: ubuntu-latest\n permissions: write-all\n\n steps:\n - name: hackme\n run: |\n echo \"${{ github.event.pull_request.title }}\"\n" + }, + "sourceLanguage": "yaml", + "startColumn": 3, + "startLine": 8 + } + } + } + } + ] + } + ] + } + ], "kind": "fail", "level": "error", "locations": [ @@ -68,7 +181,7 @@ expression: "zizmor().offline(true).input(input_under_test(\"several-vulnerabili } ], "message": { - "text": "overly broad permissions" + "text": "overly broad permissions [this job](1)" }, "properties": { "zizmor/confidence": "High", @@ -77,6 +190,7 @@ expression: "zizmor().offline(true).input(input_under_test(\"several-vulnerabili }, "relatedLocations": [ { + "id": 1, "logicalLocations": [ { "properties": { @@ -127,6 +241,7 @@ expression: "zizmor().offline(true).input(input_under_test(\"several-vulnerabili "ruleId": "zizmor/excessive-permissions" }, { + "codeFlows": [], "kind": "fail", "level": "error", "locations": [ @@ -187,6 +302,141 @@ expression: "zizmor().offline(true).input(input_under_test(\"several-vulnerabili "ruleId": "zizmor/dangerous-triggers" }, { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "importance": "essential", + "location": { + "logicalLocations": [ + { + "properties": { + "symbolic": { + "annotation": "may expand into attacker-controllable code", + "feature_kind": { + "Subfeature": { + "after": 7, + "fragment": { + "Raw": "github.event.pull_request.title" + } + } + }, + "key": { + "Local": { + "given_path": "@@INPUT@@", + "prefix": null + } + }, + "kind": "Primary", + "route": { + "route": [ + { + "Key": "jobs" + }, + { + "Key": "hackme" + }, + { + "Key": "steps" + }, + { + "Index": 0 + }, + { + "Key": "run" + } + ] + } + } + } + } + ], + "message": { + "text": "may expand into attacker-controllable code" + }, + "physicalLocation": { + "artifactLocation": { + "uri": "@@INPUT@@" + }, + "region": { + "endColumn": 52, + "endLine": 16, + "snippet": { + "text": "|\n echo \"${{ github.event.pull_request.title }}\"\n" + }, + "sourceLanguage": "yaml", + "startColumn": 21, + "startLine": 16 + } + } + } + }, + { + "importance": "important", + "location": { + "logicalLocations": [ + { + "properties": { + "symbolic": { + "annotation": "this run block", + "feature_kind": "KeyOnly", + "key": { + "Local": { + "given_path": "@@INPUT@@", + "prefix": null + } + }, + "kind": "Related", + "route": { + "route": [ + { + "Key": "jobs" + }, + { + "Key": "hackme" + }, + { + "Key": "steps" + }, + { + "Index": 0 + }, + { + "Key": "run" + } + ] + } + } + } + } + ], + "message": { + "text": "this run block" + }, + "physicalLocation": { + "artifactLocation": { + "uri": "@@INPUT@@" + }, + "region": { + "endColumn": 12, + "endLine": 15, + "snippet": { + "text": "run" + }, + "sourceLanguage": "yaml", + "startColumn": 9, + "startLine": 15 + } + } + } + } + ] + } + ] + } + ], "kind": "fail", "level": "error", "locations": [ @@ -255,7 +505,7 @@ expression: "zizmor().offline(true).input(input_under_test(\"several-vulnerabili } ], "message": { - "text": "code injection via template expansion" + "text": "code injection via template expansion [this run block](1)" }, "properties": { "zizmor/confidence": "High", @@ -264,6 +514,7 @@ expression: "zizmor().offline(true).input(input_under_test(\"several-vulnerabili }, "relatedLocations": [ { + "id": 1, "logicalLocations": [ { "properties": { From c4a92013b98852508cb085820e6cef15e75e7f80 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Tue, 31 Mar 2026 21:35:26 -0400 Subject: [PATCH 02/18] Fmt Signed-off-by: William Woodruff --- crates/zizmor/src/output/sarif.rs | 48 +++++++++++++++++-------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/crates/zizmor/src/output/sarif.rs b/crates/zizmor/src/output/sarif.rs index 78809e94e..1ff010061 100644 --- a/crates/zizmor/src/output/sarif.rs +++ b/crates/zizmor/src/output/sarif.rs @@ -5,8 +5,8 @@ use std::collections::HashSet; use serde_sarif::sarif::{ ArtifactContent, ArtifactLocation, CodeFlow, Invocation, Location as SarifLocation, LogicalLocation, Message, MultiformatMessageString, PhysicalLocation, PropertyBag, Region, - ReportingDescriptor, Result as SarifResult, ResultKind, ResultLevel, Run, Sarif, - ThreadFlow, ThreadFlowLocation, Tool, ToolComponent, + ReportingDescriptor, Result as SarifResult, ResultKind, ResultLevel, Run, Sarif, ThreadFlow, + ThreadFlowLocation, Tool, ToolComponent, }; use crate::finding::{Finding, Severity, location::Location}; @@ -132,7 +132,9 @@ fn build_result(finding: &Finding<'_>) -> SarifResult { // Build code flows for better visualization of location chains. // GitHub renders these as step-by-step traces in security alerts. - let all_locations: Vec<_> = std::iter::once(primary).chain(related.iter().copied()).collect(); + let all_locations: Vec<_> = std::iter::once(primary) + .chain(related.iter().copied()) + .collect(); let code_flows = if all_locations.len() > 1 { let thread_flow_locations: Vec = all_locations .iter() @@ -148,13 +150,15 @@ fn build_result(finding: &Finding<'_>) -> SarifResult { .build() }) .collect(); - vec![CodeFlow::builder() - .thread_flows(vec![ - ThreadFlow::builder() - .locations(thread_flow_locations) - .build(), - ]) - .build()] + vec![ + CodeFlow::builder() + .thread_flows(vec![ + ThreadFlow::builder() + .locations(thread_flow_locations) + .build(), + ]) + .build(), + ] } else { vec![] }; @@ -223,17 +227,19 @@ fn build_physical_location(location: &Location<'_>) -> PhysicalLocation { } fn build_logical_locations(location: &Location<'_>) -> Vec { - vec![LogicalLocation::builder() - .properties( - PropertyBag::builder() - .additional_properties([( - "symbolic".into(), - serde_json::value::to_value(location.symbolic.clone()) - .expect("failed to serialize symbolic location"), - )]) - .build(), - ) - .build()] + vec![ + LogicalLocation::builder() + .properties( + PropertyBag::builder() + .additional_properties([( + "symbolic".into(), + serde_json::value::to_value(location.symbolic.clone()) + .expect("failed to serialize symbolic location"), + )]) + .build(), + ) + .build(), + ] } fn build_location(location: &Location<'_>, id: Option) -> SarifLocation { From bfbf79bbfef07684a88a995c85317b39beb6d537 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Tue, 31 Mar 2026 21:40:36 -0400 Subject: [PATCH 03/18] Experimenting Signed-off-by: William Woodruff --- crates/zizmor/src/output/sarif.rs | 33 ++++++++++++++++--------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/crates/zizmor/src/output/sarif.rs b/crates/zizmor/src/output/sarif.rs index 1ff010061..8d58186a0 100644 --- a/crates/zizmor/src/output/sarif.rs +++ b/crates/zizmor/src/output/sarif.rs @@ -106,22 +106,23 @@ fn build_result(finding: &Finding<'_>) -> SarifResult { // SARIF embedded links use [text](id) syntax, where id references // a related location's integer ID. GitHub renders these as clickable // modals that users can click through to see more context. - let message = if related.is_empty() { - finding.desc.to_string() - } else { - use std::fmt::Write; - let mut msg = finding.desc.to_string(); - for (i, loc) in related.iter().enumerate() { - write!( - &mut msg, - " [{annotation}]({id})", - annotation = loc.symbolic.annotation.as_ref(), - id = i + 1 - ) - .unwrap(); - } - msg - }; + let message = finding.desc; + // let message = if related.is_empty() { + // finding.desc.to_string() + // } else { + // use std::fmt::Write; + // let mut msg = finding.desc.to_string(); + // for (i, loc) in related.iter().enumerate() { + // write!( + // &mut msg, + // " [{annotation}]({id})", + // annotation = loc.symbolic.annotation.as_ref(), + // id = i + 1 + // ) + // .expect("oops"); + // } + // msg + // }; // Build related locations with sequential IDs for back-linking. let related_locations: Vec = related From 874f279b5ff3f23f92295c0eb56c87a4ef78e98f Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Tue, 31 Mar 2026 21:47:32 -0400 Subject: [PATCH 04/18] Make the rendering nicer Signed-off-by: William Woodruff --- crates/zizmor/src/output/sarif.rs | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/crates/zizmor/src/output/sarif.rs b/crates/zizmor/src/output/sarif.rs index 8d58186a0..c7962e088 100644 --- a/crates/zizmor/src/output/sarif.rs +++ b/crates/zizmor/src/output/sarif.rs @@ -106,23 +106,19 @@ fn build_result(finding: &Finding<'_>) -> SarifResult { // SARIF embedded links use [text](id) syntax, where id references // a related location's integer ID. GitHub renders these as clickable // modals that users can click through to see more context. - let message = finding.desc; - // let message = if related.is_empty() { - // finding.desc.to_string() - // } else { - // use std::fmt::Write; - // let mut msg = finding.desc.to_string(); - // for (i, loc) in related.iter().enumerate() { - // write!( - // &mut msg, - // " [{annotation}]({id})", - // annotation = loc.symbolic.annotation.as_ref(), - // id = i + 1 - // ) - // .expect("oops"); - // } - // msg - // }; + let message = if related.is_empty() { + finding.desc.to_string() + } else { + let mut msg = format!("{msg}\n\n via:", msg = finding.desc); + for (i, loc) in related.iter().enumerate() { + msg.push_str(&format!( + "- [{annotation}]({id})", + annotation = loc.symbolic.annotation.as_ref(), + id = i + 1 + )); + } + msg + }; // Build related locations with sequential IDs for back-linking. let related_locations: Vec = related From bc40d6c75e0d36dbaba2d93b29c693d7db4ccc67 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Tue, 31 Mar 2026 21:57:42 -0400 Subject: [PATCH 05/18] Disable code flows temporarily Signed-off-by: William Woodruff --- crates/zizmor/src/output/sarif.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/zizmor/src/output/sarif.rs b/crates/zizmor/src/output/sarif.rs index c7962e088..4104a6bef 100644 --- a/crates/zizmor/src/output/sarif.rs +++ b/crates/zizmor/src/output/sarif.rs @@ -171,7 +171,7 @@ fn build_result(finding: &Finding<'_>) -> SarifResult { .message(Message::builder().text(message).build()) .locations(vec![build_location(primary, None)]) .related_locations(related_locations) - .code_flows(code_flows) + // .code_flows(code_flows) .level(ResultLevel::from(finding.determinations.severity)) .kind(ResultKind::from(finding.determinations.severity)) .properties( From 272d2a626d4809143adda7bc06de9622274ea253 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Tue, 31 Mar 2026 22:00:44 -0400 Subject: [PATCH 06/18] Revert Signed-off-by: William Woodruff --- crates/zizmor/src/output/sarif.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/zizmor/src/output/sarif.rs b/crates/zizmor/src/output/sarif.rs index 4104a6bef..c7962e088 100644 --- a/crates/zizmor/src/output/sarif.rs +++ b/crates/zizmor/src/output/sarif.rs @@ -171,7 +171,7 @@ fn build_result(finding: &Finding<'_>) -> SarifResult { .message(Message::builder().text(message).build()) .locations(vec![build_location(primary, None)]) .related_locations(related_locations) - // .code_flows(code_flows) + .code_flows(code_flows) .level(ResultLevel::from(finding.determinations.severity)) .kind(ResultKind::from(finding.determinations.severity)) .properties( From 139a083ce87f94ad34993116529d0a2fb56f7ea7 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Tue, 31 Mar 2026 22:34:51 -0400 Subject: [PATCH 07/18] Avoid link shenanigans Signed-off-by: William Woodruff --- crates/zizmor/src/output/sarif.rs | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/crates/zizmor/src/output/sarif.rs b/crates/zizmor/src/output/sarif.rs index c7962e088..eb994bb76 100644 --- a/crates/zizmor/src/output/sarif.rs +++ b/crates/zizmor/src/output/sarif.rs @@ -102,24 +102,6 @@ fn build_result(finding: &Finding<'_>) -> SarifResult { .filter(|l| !l.symbolic.is_primary()) .collect(); - // Build the message with back-links to related locations. - // SARIF embedded links use [text](id) syntax, where id references - // a related location's integer ID. GitHub renders these as clickable - // modals that users can click through to see more context. - let message = if related.is_empty() { - finding.desc.to_string() - } else { - let mut msg = format!("{msg}\n\n via:", msg = finding.desc); - for (i, loc) in related.iter().enumerate() { - msg.push_str(&format!( - "- [{annotation}]({id})", - annotation = loc.symbolic.annotation.as_ref(), - id = i + 1 - )); - } - msg - }; - // Build related locations with sequential IDs for back-linking. let related_locations: Vec = related .iter() @@ -168,7 +150,7 @@ fn build_result(finding: &Finding<'_>) -> SarifResult { // code security alert titles when the primary annotation was // terse. So now we use the finding's description again, like // we did before 1.4.0. - .message(Message::builder().text(message).build()) + .message(Message::builder().text(finding.desc).build()) .locations(vec![build_location(primary, None)]) .related_locations(related_locations) .code_flows(code_flows) From 7076e855e8ab8aeda560fc9dd5cdb3a9fee86808 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Tue, 31 Mar 2026 22:41:03 -0400 Subject: [PATCH 08/18] Remove related locations Signed-off-by: William Woodruff --- crates/zizmor/src/output/sarif.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/zizmor/src/output/sarif.rs b/crates/zizmor/src/output/sarif.rs index eb994bb76..da6d9c8b2 100644 --- a/crates/zizmor/src/output/sarif.rs +++ b/crates/zizmor/src/output/sarif.rs @@ -102,12 +102,12 @@ fn build_result(finding: &Finding<'_>) -> SarifResult { .filter(|l| !l.symbolic.is_primary()) .collect(); - // Build related locations with sequential IDs for back-linking. - let related_locations: Vec = related - .iter() - .enumerate() - .map(|(i, loc)| build_location(loc, Some((i + 1) as i64))) - .collect(); + // // Build related locations with sequential IDs for back-linking. + // let related_locations: Vec = related + // .iter() + // .enumerate() + // .map(|(i, loc)| build_location(loc, Some((i + 1) as i64))) + // .collect(); // Build code flows for better visualization of location chains. // GitHub renders these as step-by-step traces in security alerts. @@ -115,8 +115,8 @@ fn build_result(finding: &Finding<'_>) -> SarifResult { .chain(related.iter().copied()) .collect(); let code_flows = if all_locations.len() > 1 { - let thread_flow_locations: Vec = all_locations - .iter() + let thread_flow_locations: Vec = finding + .visible_locations() .map(|loc| { let importance = if loc.symbolic.is_primary() { "essential" @@ -152,7 +152,7 @@ fn build_result(finding: &Finding<'_>) -> SarifResult { // we did before 1.4.0. .message(Message::builder().text(finding.desc).build()) .locations(vec![build_location(primary, None)]) - .related_locations(related_locations) + // .related_locations(related_locations) .code_flows(code_flows) .level(ResultLevel::from(finding.determinations.severity)) .kind(ResultKind::from(finding.determinations.severity)) From 73cce4d3edde43f65d12d5b67bceafa3f7af5fed Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Tue, 31 Mar 2026 22:46:23 -0400 Subject: [PATCH 09/18] Remove related locations entirely Signed-off-by: William Woodruff --- crates/zizmor/src/output/sarif.rs | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/crates/zizmor/src/output/sarif.rs b/crates/zizmor/src/output/sarif.rs index da6d9c8b2..3ab3e396b 100644 --- a/crates/zizmor/src/output/sarif.rs +++ b/crates/zizmor/src/output/sarif.rs @@ -97,24 +97,10 @@ fn build_results(findings: &[Finding]) -> Vec { fn build_result(finding: &Finding<'_>) -> SarifResult { let primary = finding.primary_location(); - let related: Vec<_> = finding - .visible_locations() - .filter(|l| !l.symbolic.is_primary()) - .collect(); - - // // Build related locations with sequential IDs for back-linking. - // let related_locations: Vec = related - // .iter() - // .enumerate() - // .map(|(i, loc)| build_location(loc, Some((i + 1) as i64))) - // .collect(); // Build code flows for better visualization of location chains. // GitHub renders these as step-by-step traces in security alerts. - let all_locations: Vec<_> = std::iter::once(primary) - .chain(related.iter().copied()) - .collect(); - let code_flows = if all_locations.len() > 1 { + let code_flows = { let thread_flow_locations: Vec = finding .visible_locations() .map(|loc| { @@ -138,8 +124,6 @@ fn build_result(finding: &Finding<'_>) -> SarifResult { ]) .build(), ] - } else { - vec![] }; SarifResult::builder() @@ -152,7 +136,6 @@ fn build_result(finding: &Finding<'_>) -> SarifResult { // we did before 1.4.0. .message(Message::builder().text(finding.desc).build()) .locations(vec![build_location(primary, None)]) - // .related_locations(related_locations) .code_flows(code_flows) .level(ResultLevel::from(finding.determinations.severity)) .kind(ResultKind::from(finding.determinations.severity)) From a57931031a3ae849a7cc74da6fe49a0333b0de2e Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Tue, 31 Mar 2026 22:54:46 -0400 Subject: [PATCH 10/18] Bump snapshots Signed-off-by: William Woodruff --- crates/zizmor/tests/integration/cli.rs | 191 ++++++++++++----- ...gration__e2e__sarif_zizmor_properties.snap | 200 +++++++----------- 2 files changed, 211 insertions(+), 180 deletions(-) diff --git a/crates/zizmor/tests/integration/cli.rs b/crates/zizmor/tests/integration/cli.rs index e8b0174b7..9a67fa7eb 100644 --- a/crates/zizmor/tests/integration/cli.rs +++ b/crates/zizmor/tests/integration/cli.rs @@ -277,7 +277,69 @@ jobs: ], "results": [ { - "codeFlows": [], + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "importance": "essential", + "location": { + "logicalLocations": [ + { + "properties": { + "symbolic": { + "annotation": "does not set persist-credentials: false", + "feature_kind": "Normal", + "key": { + "Stdin": {} + }, + "kind": "Primary", + "route": { + "route": [ + { + "Key": "jobs" + }, + { + "Key": "test" + }, + { + "Key": "steps" + }, + { + "Index": 0 + } + ] + } + } + } + } + ], + "message": { + "text": "does not set persist-credentials: false" + }, + "physicalLocation": { + "artifactLocation": { + "uri": "" + }, + "region": { + "endColumn": 1, + "endLine": 7, + "snippet": { + "text": "uses: actions/checkout@v3\n" + }, + "sourceLanguage": "yaml", + "startColumn": 9, + "startLine": 6 + } + } + } + } + ] + } + ] + } + ], "kind": "fail", "level": "warning", "locations": [ @@ -340,7 +402,6 @@ jobs: "zizmor/persona": "Regular", "zizmor/severity": "Medium" }, - "relatedLocations": [], "ruleId": "zizmor/artipacked" }, { @@ -350,18 +411,18 @@ jobs: { "locations": [ { - "importance": "essential", + "importance": "important", "location": { "logicalLocations": [ { "properties": { "symbolic": { - "annotation": "default permissions used due to no permissions: block", + "annotation": "this job", "feature_kind": "Normal", "key": { "Stdin": {} }, - "kind": "Primary", + "kind": "Related", "route": { "route": [ { @@ -377,7 +438,7 @@ jobs: } ], "message": { - "text": "default permissions used due to no permissions: block" + "text": "this job" }, "physicalLocation": { "artifactLocation": { @@ -397,18 +458,18 @@ jobs: } }, { - "importance": "important", + "importance": "essential", "location": { "logicalLocations": [ { "properties": { "symbolic": { - "annotation": "this job", + "annotation": "default permissions used due to no permissions: block", "feature_kind": "Normal", "key": { "Stdin": {} }, - "kind": "Related", + "kind": "Primary", "route": { "route": [ { @@ -424,7 +485,7 @@ jobs: } ], "message": { - "text": "this job" + "text": "default permissions used due to no permissions: block" }, "physicalLocation": { "artifactLocation": { @@ -497,64 +558,89 @@ jobs: } ], "message": { - "text": "overly broad permissions [this job](1)" + "text": "overly broad permissions" }, "properties": { "zizmor/confidence": "Medium", "zizmor/persona": "Regular", "zizmor/severity": "Medium" }, - "relatedLocations": [ + "ruleId": "zizmor/excessive-permissions" + }, + { + "codeFlows": [ { - "id": 1, - "logicalLocations": [ + "threadFlows": [ { - "properties": { - "symbolic": { - "annotation": "this job", - "feature_kind": "Normal", - "key": { - "Stdin": {} - }, - "kind": "Related", - "route": { - "route": [ + "locations": [ + { + "importance": "essential", + "location": { + "logicalLocations": [ { - "Key": "jobs" + "properties": { + "symbolic": { + "annotation": "action is not pinned to a hash (required by blanket policy)", + "feature_kind": { + "Subfeature": { + "after": 0, + "fragment": { + "Raw": "actions/checkout@v3" + } + } + }, + "key": { + "Stdin": {} + }, + "kind": "Primary", + "route": { + "route": [ + { + "Key": "jobs" + }, + { + "Key": "test" + }, + { + "Key": "steps" + }, + { + "Index": 0 + }, + { + "Key": "uses" + } + ] + } + } + } + } + ], + "message": { + "text": "action is not pinned to a hash (required by blanket policy)" + }, + "physicalLocation": { + "artifactLocation": { + "uri": "" }, - { - "Key": "test" + "region": { + "endColumn": 34, + "endLine": 6, + "snippet": { + "text": "actions/checkout@v3" + }, + "sourceLanguage": "yaml", + "startColumn": 15, + "startLine": 6 } - ] + } } } - } - } - ], - "message": { - "text": "this job" - }, - "physicalLocation": { - "artifactLocation": { - "uri": "" - }, - "region": { - "endColumn": 1, - "endLine": 7, - "snippet": { - "text": " test:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v3\n" - }, - "sourceLanguage": "yaml", - "startColumn": 3, - "startLine": 3 + ] } - } + ] } ], - "ruleId": "zizmor/excessive-permissions" - }, - { - "codeFlows": [], "kind": "fail", "level": "error", "locations": [ @@ -627,7 +713,6 @@ jobs: "zizmor/persona": "Regular", "zizmor/severity": "High" }, - "relatedLocations": [], "ruleId": "zizmor/unpinned-uses" } ], diff --git a/crates/zizmor/tests/integration/snapshots/integration__e2e__sarif_zizmor_properties.snap b/crates/zizmor/tests/integration/snapshots/integration__e2e__sarif_zizmor_properties.snap index 816040f0b..8cb253956 100644 --- a/crates/zizmor/tests/integration/snapshots/integration__e2e__sarif_zizmor_properties.snap +++ b/crates/zizmor/tests/integration/snapshots/integration__e2e__sarif_zizmor_properties.snap @@ -19,13 +19,13 @@ expression: "zizmor().offline(true).input(input_under_test(\"several-vulnerabili { "locations": [ { - "importance": "essential", + "importance": "important", "location": { "logicalLocations": [ { "properties": { "symbolic": { - "annotation": "uses write-all permissions", + "annotation": "this job", "feature_kind": "Normal", "key": { "Local": { @@ -33,7 +33,7 @@ expression: "zizmor().offline(true).input(input_under_test(\"several-vulnerabili "prefix": null } }, - "kind": "Primary", + "kind": "Related", "route": { "route": [ { @@ -41,9 +41,6 @@ expression: "zizmor().offline(true).input(input_under_test(\"several-vulnerabili }, { "Key": "hackme" - }, - { - "Key": "permissions" } ] } @@ -52,33 +49,33 @@ expression: "zizmor().offline(true).input(input_under_test(\"several-vulnerabili } ], "message": { - "text": "uses write-all permissions" + "text": "this job" }, "physicalLocation": { "artifactLocation": { "uri": "@@INPUT@@" }, "region": { - "endColumn": 27, - "endLine": 11, + "endColumn": 1, + "endLine": 17, "snippet": { - "text": " permissions: write-all" + "text": " hackme:\n name: hackme\n runs-on: ubuntu-latest\n permissions: write-all\n\n steps:\n - name: hackme\n run: |\n echo \"${{ github.event.pull_request.title }}\"\n" }, "sourceLanguage": "yaml", - "startColumn": 5, - "startLine": 11 + "startColumn": 3, + "startLine": 8 } } } }, { - "importance": "important", + "importance": "essential", "location": { "logicalLocations": [ { "properties": { "symbolic": { - "annotation": "this job", + "annotation": "uses write-all permissions", "feature_kind": "Normal", "key": { "Local": { @@ -86,7 +83,7 @@ expression: "zizmor().offline(true).input(input_under_test(\"several-vulnerabili "prefix": null } }, - "kind": "Related", + "kind": "Primary", "route": { "route": [ { @@ -94,6 +91,9 @@ expression: "zizmor().offline(true).input(input_under_test(\"several-vulnerabili }, { "Key": "hackme" + }, + { + "Key": "permissions" } ] } @@ -102,21 +102,21 @@ expression: "zizmor().offline(true).input(input_under_test(\"several-vulnerabili } ], "message": { - "text": "this job" + "text": "uses write-all permissions" }, "physicalLocation": { "artifactLocation": { "uri": "@@INPUT@@" }, "region": { - "endColumn": 1, - "endLine": 17, + "endColumn": 27, + "endLine": 11, "snippet": { - "text": " hackme:\n name: hackme\n runs-on: ubuntu-latest\n permissions: write-all\n\n steps:\n - name: hackme\n run: |\n echo \"${{ github.event.pull_request.title }}\"\n" + "text": " permissions: write-all" }, "sourceLanguage": "yaml", - "startColumn": 3, - "startLine": 8 + "startColumn": 5, + "startLine": 11 } } } @@ -181,67 +181,73 @@ expression: "zizmor().offline(true).input(input_under_test(\"several-vulnerabili } ], "message": { - "text": "overly broad permissions [this job](1)" + "text": "overly broad permissions" }, "properties": { "zizmor/confidence": "High", "zizmor/persona": "Regular", "zizmor/severity": "High" }, - "relatedLocations": [ + "ruleId": "zizmor/excessive-permissions" + }, + { + "codeFlows": [ { - "id": 1, - "logicalLocations": [ + "threadFlows": [ { - "properties": { - "symbolic": { - "annotation": "this job", - "feature_kind": "Normal", - "key": { - "Local": { - "given_path": "@@INPUT@@", - "prefix": null - } - }, - "kind": "Related", - "route": { - "route": [ + "locations": [ + { + "importance": "essential", + "location": { + "logicalLocations": [ { - "Key": "jobs" + "properties": { + "symbolic": { + "annotation": "pull_request_target is almost always used insecurely", + "feature_kind": "Normal", + "key": { + "Local": { + "given_path": "@@INPUT@@", + "prefix": null + } + }, + "kind": "Primary", + "route": { + "route": [ + { + "Key": "on" + } + ] + } + } + } + } + ], + "message": { + "text": "pull_request_target is almost always used insecurely" + }, + "physicalLocation": { + "artifactLocation": { + "uri": "@@INPUT@@" }, - { - "Key": "hackme" + "region": { + "endColumn": 23, + "endLine": 3, + "snippet": { + "text": "on:\n pull_request_target:" + }, + "sourceLanguage": "yaml", + "startColumn": 1, + "startLine": 2 } - ] + } } } - } - } - ], - "message": { - "text": "this job" - }, - "physicalLocation": { - "artifactLocation": { - "uri": "@@INPUT@@" - }, - "region": { - "endColumn": 1, - "endLine": 17, - "snippet": { - "text": " hackme:\n name: hackme\n runs-on: ubuntu-latest\n permissions: write-all\n\n steps:\n - name: hackme\n run: |\n echo \"${{ github.event.pull_request.title }}\"\n" - }, - "sourceLanguage": "yaml", - "startColumn": 3, - "startLine": 8 + ] } - } + ] } ], - "ruleId": "zizmor/excessive-permissions" - }, - { - "codeFlows": [], "kind": "fail", "level": "error", "locations": [ @@ -298,7 +304,6 @@ expression: "zizmor().offline(true).input(input_under_test(\"several-vulnerabili "zizmor/persona": "Regular", "zizmor/severity": "High" }, - "relatedLocations": [], "ruleId": "zizmor/dangerous-triggers" }, { @@ -505,72 +510,13 @@ expression: "zizmor().offline(true).input(input_under_test(\"several-vulnerabili } ], "message": { - "text": "code injection via template expansion [this run block](1)" + "text": "code injection via template expansion" }, "properties": { "zizmor/confidence": "High", "zizmor/persona": "Regular", "zizmor/severity": "High" }, - "relatedLocations": [ - { - "id": 1, - "logicalLocations": [ - { - "properties": { - "symbolic": { - "annotation": "this run block", - "feature_kind": "KeyOnly", - "key": { - "Local": { - "given_path": "@@INPUT@@", - "prefix": null - } - }, - "kind": "Related", - "route": { - "route": [ - { - "Key": "jobs" - }, - { - "Key": "hackme" - }, - { - "Key": "steps" - }, - { - "Index": 0 - }, - { - "Key": "run" - } - ] - } - } - } - } - ], - "message": { - "text": "this run block" - }, - "physicalLocation": { - "artifactLocation": { - "uri": "@@INPUT@@" - }, - "region": { - "endColumn": 12, - "endLine": 15, - "snippet": { - "text": "run" - }, - "sourceLanguage": "yaml", - "startColumn": 9, - "startLine": 15 - } - } - } - ], "ruleId": "zizmor/template-injection" } ], From 95d361581ec22dc64344a68bd70331952db77597 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Tue, 31 Mar 2026 23:04:48 -0400 Subject: [PATCH 11/18] Add link to upstream PR Signed-off-by: William Woodruff --- crates/zizmor/src/output/sarif.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/zizmor/src/output/sarif.rs b/crates/zizmor/src/output/sarif.rs index 3ab3e396b..b270921b1 100644 --- a/crates/zizmor/src/output/sarif.rs +++ b/crates/zizmor/src/output/sarif.rs @@ -111,6 +111,7 @@ fn build_result(finding: &Finding<'_>) -> SarifResult { }; ThreadFlowLocation::builder() .location(build_location(loc, None)) + // See: .importance(serde_json::Value::String(importance.into())) .build() }) From 203812f926cabb3fa41c7ee9b11554299d991774 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Wed, 1 Apr 2026 17:19:48 -0400 Subject: [PATCH 12/18] Tweak tests temporarily Signed-off-by: William Woodruff --- .github/workflows/test-output.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-output.yml b/.github/workflows/test-output.yml index 0cdab9e6d..e2043db99 100644 --- a/.github/workflows/test-output.yml +++ b/.github/workflows/test-output.yml @@ -27,7 +27,7 @@ jobs: - name: Run zizmor run: | - cargo run -- --format sarif . > results.sarif + cargo run -- --format sarif crates/zizmor/tests/integration/test-data/ref-version-mismatch > results.sarif - name: Upload SARIF file uses: github/codeql-action/upload-sarif@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4.31.10 From 351581cd0def4e0383011fac33f2eab2a5648ecb Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Wed, 1 Apr 2026 17:21:37 -0400 Subject: [PATCH 13/18] Fix Signed-off-by: William Woodruff --- .github/workflows/test-output.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-output.yml b/.github/workflows/test-output.yml index e2043db99..1e6e02267 100644 --- a/.github/workflows/test-output.yml +++ b/.github/workflows/test-output.yml @@ -27,7 +27,7 @@ jobs: - name: Run zizmor run: | - cargo run -- --format sarif crates/zizmor/tests/integration/test-data/ref-version-mismatch > results.sarif + cargo run -- --format sarif crates/zizmor/tests/integration/test-data/ref-version-mismatch.yml > results.sarif - name: Upload SARIF file uses: github/codeql-action/upload-sarif@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4.31.10 From d9098daf947313a53913b481f849b0d34b320cec Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Wed, 1 Apr 2026 17:25:40 -0400 Subject: [PATCH 14/18] Actually run the right audit Signed-off-by: William Woodruff --- .github/workflows/test-output.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test-output.yml b/.github/workflows/test-output.yml index 1e6e02267..419a91936 100644 --- a/.github/workflows/test-output.yml +++ b/.github/workflows/test-output.yml @@ -28,6 +28,8 @@ jobs: - name: Run zizmor run: | cargo run -- --format sarif crates/zizmor/tests/integration/test-data/ref-version-mismatch.yml > results.sarif + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload SARIF file uses: github/codeql-action/upload-sarif@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4.31.10 From 377a952fc5683fa2a3c794b34e6cada0f7877651 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Wed, 1 Apr 2026 17:29:29 -0400 Subject: [PATCH 15/18] Better audit description Signed-off-by: William Woodruff --- crates/zizmor/src/audit/ref_version_mismatch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/zizmor/src/audit/ref_version_mismatch.rs b/crates/zizmor/src/audit/ref_version_mismatch.rs index 523f9d0f5..1e2c166d1 100644 --- a/crates/zizmor/src/audit/ref_version_mismatch.rs +++ b/crates/zizmor/src/audit/ref_version_mismatch.rs @@ -24,7 +24,7 @@ pub(crate) struct RefVersionMismatch { audit_meta!( RefVersionMismatch, "ref-version-mismatch", - "detects commit SHAs that don't match their version comment tags" + "action's hash pin does not match version comment" ); #[allow(clippy::unwrap_used)] From 4ede43846cee4757eab85955ba1adcac12d49231 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Wed, 1 Apr 2026 17:35:33 -0400 Subject: [PATCH 16/18] Bump snapshots Signed-off-by: William Woodruff --- crates/zizmor/tests/integration/audit/ref_version_mismatch.rs | 2 +- .../e2e/snapshots/integration__e2e__crater__curl.snap | 3 +-- .../e2e/snapshots/integration__e2e__crater__libssh2.snap | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/crates/zizmor/tests/integration/audit/ref_version_mismatch.rs b/crates/zizmor/tests/integration/audit/ref_version_mismatch.rs index 924a20063..268f60f4a 100644 --- a/crates/zizmor/tests/integration/audit/ref_version_mismatch.rs +++ b/crates/zizmor/tests/integration/audit/ref_version_mismatch.rs @@ -22,7 +22,7 @@ fn test_ref_version_mismatch() -> Result<()> { = note: audit confidence → High = note: this finding has an auto-fix - warning[ref-version-mismatch]: detects commit SHAs that don't match their version comment tags + warning[ref-version-mismatch]: action's hash pin does not match version comment --> @@INPUT@@:22:77 | 22 | - uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.1 diff --git a/crates/zizmor/tests/integration/e2e/snapshots/integration__e2e__crater__curl.snap b/crates/zizmor/tests/integration/e2e/snapshots/integration__e2e__crater__curl.snap index e7d1275e7..2e42a09dc 100644 --- a/crates/zizmor/tests/integration/e2e/snapshots/integration__e2e__crater__curl.snap +++ b/crates/zizmor/tests/integration/e2e/snapshots/integration__e2e__crater__curl.snap @@ -21,7 +21,7 @@ expression: "zizmor().offline(false).output(OutputMode::Both).args([\"--persona= INFO audit: zizmor: 🌈 completed .github/workflows/macos.yml INFO audit: zizmor: 🌈 completed .github/workflows/non-native.yml INFO audit: zizmor: 🌈 completed .github/workflows/windows.yml -warning[ref-version-mismatch]: detects commit SHAs that don't match their version comment tags +warning[ref-version-mismatch]: action's hash pin does not match version comment --> .github/workflows/windows.yml:57:87 | 57 | - uses: cygwin/cygwin-install-action@f2009323764960f80959895c7bc3bb30210afe4d # v6 @@ -33,4 +33,3 @@ warning[ref-version-mismatch]: detects commit SHAs that don't match their versio = note: this finding has an auto-fix 47 findings (2 ignored, 44 suppressed): 0 informational, 0 low, 1 medium, 0 high - diff --git a/crates/zizmor/tests/integration/e2e/snapshots/integration__e2e__crater__libssh2.snap b/crates/zizmor/tests/integration/e2e/snapshots/integration__e2e__crater__libssh2.snap index f601dc55a..9ccba0896 100644 --- a/crates/zizmor/tests/integration/e2e/snapshots/integration__e2e__crater__libssh2.snap +++ b/crates/zizmor/tests/integration/e2e/snapshots/integration__e2e__crater__libssh2.snap @@ -11,7 +11,7 @@ expression: "zizmor().offline(false).output(OutputMode::Both).args([\"--persona= INFO audit: zizmor: 🌈 completed .github/workflows/cifuzz.yml INFO audit: zizmor: 🌈 completed .github/workflows/codeql.yml INFO audit: zizmor: 🌈 completed .github/workflows/openssh_server.yml -warning[ref-version-mismatch]: detects commit SHAs that don't match their version comment tags +warning[ref-version-mismatch]: action's hash pin does not match version comment --> .github/workflows/ci.yml:658:87 | 658 | - uses: cygwin/cygwin-install-action@f2009323764960f80959895c7bc3bb30210afe4d # v6 @@ -23,4 +23,3 @@ warning[ref-version-mismatch]: detects commit SHAs that don't match their versio = note: this finding has an auto-fix 13 findings (2 ignored, 10 suppressed): 0 informational, 0 low, 1 medium, 0 high - From 736fda21150855a7e2b06a2eace0e1408a7ec0d8 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Wed, 1 Apr 2026 17:35:53 -0400 Subject: [PATCH 17/18] Undo test changes Signed-off-by: William Woodruff --- .github/workflows/test-output.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/test-output.yml b/.github/workflows/test-output.yml index 419a91936..0cdab9e6d 100644 --- a/.github/workflows/test-output.yml +++ b/.github/workflows/test-output.yml @@ -27,9 +27,7 @@ jobs: - name: Run zizmor run: | - cargo run -- --format sarif crates/zizmor/tests/integration/test-data/ref-version-mismatch.yml > results.sarif - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + cargo run -- --format sarif . > results.sarif - name: Upload SARIF file uses: github/codeql-action/upload-sarif@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4.31.10 From 0999dfb39ee061f8d20f5a792a75984d0909c1a4 Mon Sep 17 00:00:00 2001 From: William Woodruff Date: Wed, 1 Apr 2026 17:45:32 -0400 Subject: [PATCH 18/18] Record changes Signed-off-by: William Woodruff --- docs/release-notes.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/release-notes.md b/docs/release-notes.md index 523dc7c7a..3edb016c5 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -43,6 +43,12 @@ of `zizmor`. Many thanks to @deckstose for implementing this improvement! +* zizmor's SARIF output now uses codeflows instead of related locations, + improving its rendering behavior on GitHub Advanced Security (#1843) + +* The [ref-version-mismatch] audit now uses a more useful audit description + for its findings (#1843) + ### Bug Fixes 🐛 * Fixed a bug where the [concurrency-limits] audit reported findings