Repository: woodruffw/zizmor
Pull request #358 (2024-12-25 23:05:59)

feat: github-env: support composite actions
Per #350.

Signed-off-by: William Woodruff <william@yossarian.net>


Patch:
diff --git a/src/audit/github_env.rs b/src/audit/github_env.rs
index aaca269..dcfee7d 100644
--- a/src/audit/github_env.rs
+++ b/src/audit/github_env.rs
@@ -3,6 +3,7 @@ use crate::finding::{Confidence, Finding, Severity};
 use crate::models::Step;
 use crate::state::AuditState;
 use anyhow::{Context, Result};
+use github_actions_models::action;
 use github_actions_models::workflow::job::StepBody;
 use regex::Regex;
 use std::cell::RefCell;
@@ -343,6 +344,34 @@ impl Audit for GitHubEnv {
 
         Ok(findings)
     }
+
+    fn audit_composite_step<'a>(
+        &self,
+        step: &super::CompositeStep<'a>,
+    ) -> Result<Vec<Finding<'a>>> {
+        let mut findings = vec![];
+
+        let action::StepBody::Run { run, shell, .. } = &step.body else {
+            return Ok(findings);
+        };
+
+        if self.uses_github_env(run, shell)? {
+            findings.push(
+                Self::finding()
+                    .severity(Severity::High)
+                    .confidence(Confidence::Low)
+                    .add_location(
+                        step.location()
+                            .primary()
+                            .with_keys(&["run".into()])
+                            .annotated("GITHUB_ENV write may allow code execution"),
+                    )
+                    .build(step.action())?,
+            )
+        }
+
+        Ok(findings)
+    }
 }
 
 #[cfg(test)]


Test patch:
diff --git a/tests/snapshot.rs b/tests/snapshot.rs
index 5e835db..c2639b5 100644
--- a/tests/snapshot.rs
+++ b/tests/snapshot.rs
@@ -360,3 +360,12 @@ fn excessive_permissions() -> Result<()> {
 
     Ok(())
 }
+
+#[test]
+fn github_env() -> Result<()> {
+    insta::assert_snapshot!(zizmor()
+        .workflow(workflow_under_test("github-env/action.yml"))
+        .run()?);
+
+    Ok(())
+}
diff --git a/tests/snapshots/snapshot__github_env.snap b/tests/snapshots/snapshot__github_env.snap
new file mode 100644
index 0000000..0b0e810
--- /dev/null
+++ b/tests/snapshots/snapshot__github_env.snap
@@ -0,0 +1,33 @@
+---
+source: tests/snapshot.rs
+expression: "zizmor().workflow(workflow_under_test(\"github-env/action.yml\")).run()?"
+snapshot_kind: text
+---
+error[github-env]: dangerous use of GITHUB_ENV
+  --> @@INPUT@@:10:7
+   |
+10 | /       run: |
+11 | |         echo "foo=$(bar)" >> $GITHUB_ENV
+   | |________________________________________^ GITHUB_ENV write may allow code execution
+   |
+   = note: audit confidence → Low
+
+error[github-env]: dangerous use of GITHUB_ENV
+  --> @@INPUT@@:15:7
+   |
+15 | /       run: |
+16 | |         echo "foo=$env:BAR" >> $env:GITHUB_ENV
+   | |______________________________________________^ GITHUB_ENV write may allow code execution
+   |
+   = note: audit confidence → Low
+
+error[github-env]: dangerous use of GITHUB_ENV
+  --> @@INPUT@@:20:7
+   |
+20 | /       run: |
+21 | |         echo LIBRARY=%LIBRARY% >> %GITHUB_ENV%
+   | |______________________________________________^ GITHUB_ENV write may allow code execution
+   |
+   = note: audit confidence → Low
+
+3 findings: 0 unknown, 0 informational, 0 low, 0 medium, 3 high
diff --git a/tests/test-data/github-env/action.yml b/tests/test-data/github-env/action.yml
new file mode 100644
index 0000000..6d1f67b
--- /dev/null
+++ b/tests/test-data/github-env/action.yml
@@ -0,0 +1,28 @@
+# demo of a composite action being flagged by github-env
+
+name: github-env-composite-action
+description: github-env-composite-action
+
+runs:
+  using: composite
+  steps:
+    - name: true-positive-1
+      run: |
+        echo "foo=$(bar)" >> $GITHUB_ENV
+      shell: bash
+
+    - name: true-positive-2
+      run: |
+        echo "foo=$env:BAR" >> $env:GITHUB_ENV
+      shell: pwsh
+
+    - name: true-positive-3
+      run: |
+        echo LIBRARY=%LIBRARY% >> %GITHUB_ENV%
+      shell: cmd
+
+    - name: true-negative-4
+      # No finding because foo=bar is wholly static.
+      run: |
+        echo foo=bar >> $GITHUB_ENV
+      shell: bash