Repository: woodruffw/zizmor
Pull request #298 (2024-12-14 16:51:22)

fix: uses context when evaluating static values from matrix expansions
A tentative fix for the issue describe [here](https://github.com/woodruffw/zizmor/issues/22#issuecomment-2543128489)

We change the existing `models::Matrix` API for checking static expansions (and used know only by the `template-injection` audit) to actually take a given context (i.e, an expansion path) when evaluating.



woodruffw: Thanks @ubiratansoares! I'll review this tonight.
ubiratansoares: @woodruffw Done [here](https://github.com/woodruffw/zizmor/pull/298/commits/114a4ab63826cfc1af2117a25ab3d936aed9d926). Also moved the reproducer under `test-data/template-injection` (I missed that somehow 🙈)

Patch:
diff --git a/src/audit/template_injection.rs b/src/audit/template_injection.rs
index 8f8c61b..404caf4 100644
--- a/src/audit/template_injection.rs
+++ b/src/audit/template_injection.rs
@@ -187,8 +187,10 @@ impl TemplateInjection {
                             // The matrix is generated by an expression, meaning
                             // that it's trivially not static.
                             Some(LoE::Expr(_)) => false,
-
-                            Some(inner) => models::Matrix::new(inner).is_static(),
+                            // The matrix may expand to static values according to the context
+                            Some(inner) => {
+                                models::Matrix::new(inner).expands_to_static_values(context)
+                            }
                             // Context specifies a matrix, but there is no matrix defined.
                             // This is an invalid workflow so there's no point in flagging it.
                             None => continue,
diff --git a/src/models.rs b/src/models.rs
index b5660ba..bb866cd 100644
--- a/src/models.rs
+++ b/src/models.rs
@@ -273,11 +273,11 @@ impl<'w> Matrix<'w> {
     }
 
     /// Checks whether some expanded path leads to an expression
-    pub(crate) fn is_static(&self) -> bool {
-        let expands_to_expression = self
-            .expanded_values
-            .iter()
-            .any(|(_, expansion)| expansion.starts_with("${{") && expansion.ends_with("}}"));
+    pub(crate) fn expands_to_static_values(&self, context: &str) -> bool {
+        let expands_to_expression = self.expanded_values.iter().any(|(path, expansion)| {
+            let expanded_to_expression = expansion.starts_with("${{") && expansion.ends_with("}}");
+            context == path && expanded_to_expression
+        });
 
         !expands_to_expression
     }


Test patch:
diff --git a/tests/snapshot.rs b/tests/snapshot.rs
index 16d22f3..746ac9a 100644
--- a/tests/snapshot.rs
+++ b/tests/snapshot.rs
@@ -197,6 +197,7 @@ fn self_hosted() -> Result<()> {
         .workflow(workflow_under_test("self-hosted/issue-283-repro.yml"))
         .args(["--persona=auditor"])
         .run()?);
+
     Ok(())
 }
 
@@ -244,5 +245,11 @@ fn template_injection() -> Result<()> {
         .args(["--persona=auditor"])
         .run()?);
 
+    // Fixed regressions
+
+    insta::assert_snapshot!(zizmor()
+        .workflow(workflow_under_test("template-injection/issue-22-repro.yml"))
+        .run()?);
+
     Ok(())
 }
diff --git a/tests/snapshots/snapshot__template_injection-3.snap b/tests/snapshots/snapshot__template_injection-3.snap
new file mode 100644
index 0000000..2ef3d57
--- /dev/null
+++ b/tests/snapshots/snapshot__template_injection-3.snap
@@ -0,0 +1,6 @@
+---
+source: tests/snapshot.rs
+expression: "zizmor().workflow(workflow_under_test(\"template-injection/issue-22-repro.yml\")).run()?"
+snapshot_kind: text
+---
+No findings to report. Good job! (4 suppressed)
diff --git a/tests/test-data/template-injection/issue-22-repro.yml b/tests/test-data/template-injection/issue-22-repro.yml
new file mode 100644
index 0000000..2ec27f1
--- /dev/null
+++ b/tests/test-data/template-injection/issue-22-repro.yml
@@ -0,0 +1,64 @@
+# Adapted from :
+# https://github.com/python/cpython/blob/e2325c9db0650fc06d909eb2b5930c0573f24f71/.github/workflows/jit.yml
+# See also https://github.com/woodruffw/zizmor/issues/22#issuecomment-2543128489
+
+name: JIT
+on:
+  pull_request:
+
+jobs:
+  jit:
+    name: ${{ matrix.target }} (${{ matrix.debug && 'Debug' || 'Release' }})
+    runs-on: ${{ matrix.runner }}
+    strategy:
+      fail-fast: false
+      matrix:
+        target:
+          - x86_64-apple-darwin/clang
+          - aarch64-apple-darwin/clang
+          - x86_64-unknown-linux-gnu/gcc
+          - aarch64-unknown-linux-gnu/gcc
+        debug:
+          - true
+          - false
+        llvm:
+          - 19
+        include:
+          - target: aarch64-apple-darwin/clang
+            architecture: aarch64
+            runner: macos-14
+          - target: x86_64-unknown-linux-gnu/gcc
+            architecture: x86_64
+            runner: ubuntu-24.04
+          - target: aarch64-unknown-linux-gnu/gcc
+            architecture: aarch64
+            # Should not be flagged in the template injection audit
+            runner: ${{ github.repository_owner == 'python' && 'ubuntu-24.04-aarch64' || 'ubuntu-24.04' }}
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          persist-credentials: false
+
+      - uses: actions/setup-python@v5
+        with:
+          python-version: '3.11'
+
+      - name: Native macOS
+        if: runner.os == 'macOS'
+        run: |
+          brew update
+          find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete
+          brew install llvm@${{ matrix.llvm }}
+          export SDKROOT="$(xcrun --show-sdk-path)"
+          ./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '' }}
+          make all --jobs 4
+          ./python.exe -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
+
+      - name: Native Linux
+        if: runner.os == 'Linux' && (matrix.architecture == 'x86_64' || github.repository_owner == 'python')
+        run: |
+          sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ matrix.llvm }}
+          export PATH="$(llvm-config-${{ matrix.llvm }} --bindir):$PATH"
+          ./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '' }}
+          make all --jobs 4
+          ./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3