feat: add --show-audit-urls=... for controlling URL rendering (#1391)

This commit is contained in:
William Woodruff 2025-11-29 20:05:00 -05:00 committed by GitHub
parent f203b457f6
commit 3b1951b71e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 140 additions and 10 deletions

View file

@ -2,6 +2,7 @@
use std::{
collections::HashSet,
env,
io::{Write, stdout},
process::ExitCode,
};
@ -116,6 +117,13 @@ struct App {
#[arg(long, value_enum, default_value_t)]
format: OutputFormat,
/// Whether to render audit URLs in the output, separately from any URLs
/// embedded in OSC 8 links.
///
/// Only affects `--format=plain` (the default).
#[arg(long, value_enum, default_value_t, env = "ZIZMOR_SHOW_AUDIT_URLS")]
show_audit_urls: CliShowAuditUrls,
/// Control the use of color in output.
#[arg(long, value_enum, value_name = "MODE")]
color: Option<ColorMode>,
@ -316,6 +324,41 @@ pub(crate) enum OutputFormat {
Github,
}
#[derive(Debug, Default, Copy, Clone, ValueEnum)]
pub(crate) enum CliShowAuditUrls {
/// Render audit URLs in output automatically based on output format and runtime context.
///
/// For example, URLs will be shown if a CI runtime is detected.
#[default]
Auto,
/// Always render audit URLs in output.
Always,
/// Never render audit URLs in output.
Never,
}
#[derive(Debug, Copy, Clone)]
pub(crate) enum ShowAuditUrls {
Always,
Never,
}
impl From<CliShowAuditUrls> for ShowAuditUrls {
fn from(value: CliShowAuditUrls) -> Self {
match value {
CliShowAuditUrls::Auto => {
if utils::is_ci() || !stdout().is_terminal() {
ShowAuditUrls::Always
} else {
ShowAuditUrls::Never
}
}
CliShowAuditUrls::Always => ShowAuditUrls::Always,
CliShowAuditUrls::Never => ShowAuditUrls::Never,
}
}
}
#[derive(Debug, Copy, Clone, ValueEnum)]
pub(crate) enum ColorMode {
/// Use color output if the output supports it.
@ -768,7 +811,12 @@ async fn run(app: &mut App) -> Result<ExitCode, Error> {
}
match app.format {
OutputFormat::Plain => output::plain::render_findings(&registry, &results, app.naches),
OutputFormat::Plain => output::plain::render_findings(
&registry,
&results,
&app.show_audit_urls.into(),
app.naches,
),
OutputFormat::Json | OutputFormat::JsonV1 => {
output::json::v1::output(stdout(), results.findings()).map_err(Error::Output)?
}
@ -811,7 +859,7 @@ async fn main() -> ExitCode {
// which is then typically inaccessible from an already failed
// CI job. In those cases, it's better to dump directly to stderr,
// since that'll typically be captured by console logging.
if std::env::var_os("CI").is_some() {
if utils::is_ci() {
std::panic::set_hook(Box::new(|info| {
let trace = std::backtrace::Backtrace::force_capture();
eprintln!("FATAL: zizmor crashed. This is a bug that should be reported.");

View file

@ -7,6 +7,7 @@ use anstream::{eprintln, print, println};
use owo_colors::OwoColorize;
use crate::{
ShowAuditUrls,
finding::{
Finding, Severity,
location::{Location, LocationKind},
@ -94,10 +95,11 @@ pub(crate) fn finding_snippets<'doc>(
pub(crate) fn render_findings(
registry: &InputRegistry,
findings: &FindingRegistry,
show_urls_mode: &ShowAuditUrls,
naches_mode: bool,
) {
for finding in findings.findings() {
render_finding(registry, finding);
render_finding(registry, finding, show_urls_mode);
println!();
}
@ -190,7 +192,7 @@ pub(crate) fn render_findings(
}
}
fn render_finding(registry: &InputRegistry, finding: &Finding) {
fn render_finding(registry: &InputRegistry, finding: &Finding, show_urls_mode: &ShowAuditUrls) {
let title = Level::from(&finding.determinations.severity)
.primary_title(finding.desc)
.id(finding.ident)
@ -213,6 +215,13 @@ fn render_finding(registry: &InputRegistry, finding: &Finding) {
group = group.element(Level::NOTE.message("this finding has an auto-fix"));
}
if matches!(show_urls_mode, ShowAuditUrls::Always) {
group = group.element(Level::HELP.message(format!(
"audit documentation → {url}",
url = finding.url.green()
)))
}
// TODO: Evaluate alternative decor styles.
let renderer = Renderer::styled();
println!("{}", renderer.render(&[group]));

View file

@ -703,6 +703,13 @@ pub(crate) mod once {
pub(crate) use warn_once;
}
/// Returns whether we are running in a CI environment.
pub(crate) fn is_ci() -> bool {
static IS_CI: LazyLock<bool> = LazyLock::new(|| std::env::var_os("CI").is_some());
*IS_CI
}
#[cfg(test)]
mod tests {
use anyhow::Result;

View file

@ -47,6 +47,7 @@ pub struct Zizmor {
no_config: bool,
output: OutputMode,
expects_failure: bool,
show_audit_urls: bool,
}
impl Zizmor {
@ -64,6 +65,7 @@ impl Zizmor {
no_config: false,
output: OutputMode::Stdout,
expects_failure: false,
show_audit_urls: false,
}
}
@ -125,6 +127,11 @@ impl Zizmor {
self
}
pub fn show_audit_urls(mut self, flag: bool) -> Self {
self.show_audit_urls = flag;
self
}
pub fn working_dir(mut self, dir: impl Into<String>) -> Self {
self.cmd.current_dir(dir.into());
self
@ -165,6 +172,12 @@ impl Zizmor {
self.cmd.arg("--no-progress");
}
if self.show_audit_urls {
self.cmd.arg("--show-audit-urls=always");
} else {
self.cmd.arg("--show-audit-urls=never");
}
for input in &self.inputs {
self.cmd.arg(input);
}

View file

@ -595,3 +595,25 @@ fn test_github_output() -> Result<()> {
Ok(())
}
/// Ensures that the `--show-audit-urls` flag works as expected.
#[test]
fn test_show_urls() -> Result<()> {
let with_urls = zizmor()
.offline(true)
.show_audit_urls(true)
.input(input_under_test("several-vulnerabilities.yml"))
.run()?;
assert!(with_urls.contains("audit documentation → "));
let without_urls = zizmor()
.offline(true)
.show_audit_urls(false)
.input(input_under_test("several-vulnerabilities.yml"))
.run()?;
assert!(!without_urls.contains("audit documentation → "));
Ok(())
}

View file

@ -28,6 +28,8 @@ Options:
Don't show progress bars, even if the terminal supports them
--format <FORMAT>
The output format to emit. By default, cargo-style diagnostics will be emitted [default: plain] [possible values: plain, json, json-v1, sarif, github]
--show-audit-urls <SHOW_AUDIT_URLS>
Whether to render audit URLs in the output, separately from any URLs embedded in OSC 8 links [env: ZIZMOR_SHOW_AUDIT_URLS=] [default: auto] [possible values: auto, always, never]
--color <MODE>
Control the use of color in output [possible values: auto, always, never]
-c, --config <CONFIG>

View file

@ -206,16 +206,15 @@ By default, `zizmor` produces `cargo`-style diagnostic output.
```console
error[template-injection]: code injection via template expansion
--> ./tests/integration/test-data/template-injection/pr-425-backstop/action.yml:28:7
--> ./tests/integration/test-data/template-injection/pr-425-backstop/action.yml:31:56
|
28 | - name: case4
| ^^^^^^^^^^^ this step
29 | uses: azure/powershell
29 | uses: azure/powershell@whatever
| ------------------------------- action accepts arbitrary code
30 | with:
31 | inlineScript: Get-AzVM -ResourceGroupName "${{ inputs.expandme }}"
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ inputs.expandme may expand into attacker-controllable code
| ------------ via this input ^^^^^^^^^^^^^^^ may expand into attacker-controllable code
|
= note: audit confidence → Low
= note: audit confidence → High
```
This output will be colorized by default when sent to a supporting terminal and
@ -224,6 +223,36 @@ disable output colorization by setting `NO_COLOR=1` in their environment.
This format can also be explicitly selected with `--format=plain`.
#### Audit documentation links
By default, `zizmor` includes links to relevant documentation pages
for each finding in its plain output format. These links are provided via
[OSC 8](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda)
hyperlinks, which are supported by many modern terminal emulators.
For example, in the output above, `template-injection` within
`error[template-injection]: ...` is a clickable link that takes you to
the [template-injection](./audits#template-injection) audit documentation.
In addition to these OSC 8 links, `zizmor` also includes the full URL
as part of each finding _if_ it detects a non-terminal output _or_
a CI environment (e.g. GitHub Actions).
To make this behavior explicir, users can supply the `--show-audit-urls`
option:
```bash
# always show audit documentation URLs, even if output is to a terminal
zizmor --show-audit-urls=always ...
# never show audit documentation URLs
zizmor --show-audit-urls=never ...
```
!!! note
`--show-audit-urls=...` is available in `v1.19.0` and later.
#### Color customization
When invoked from a terminal, `zizmor` will attempt to enrich its output