Repository: woodruffw/zizmor
Pull request #352 (2024-12-23 20:57:38)

feat: detects push to release branches in cache-poisoning
One more follow-up for #334

We now detect at least one common pattern for Workflows triggered with `push/branches`. Deploying when pushing to a convention release branch is quite common, and this change sets the stage to detect other similar branch filters in the future.

There is at least one additional PR to before finishing #334, raising it asap!


Patch:
diff --git a/src/audit/cache_poisoning.rs b/src/audit/cache_poisoning.rs
index c8cb0ed..f34518d 100644
--- a/src/audit/cache_poisoning.rs
+++ b/src/audit/cache_poisoning.rs
@@ -4,7 +4,7 @@ use crate::models::{Job, Step, Steps, Uses};
 use crate::state::AuditState;
 use github_actions_models::common::expr::ExplicitExpr;
 use github_actions_models::common::Env;
-use github_actions_models::workflow::event::{BareEvent, OptionalBody};
+use github_actions_models::workflow::event::{BareEvent, BranchFilters, OptionalBody};
 use github_actions_models::workflow::job::StepBody;
 use github_actions_models::workflow::Trigger;
 use std::ops::Deref;
@@ -317,7 +317,19 @@ impl CachePoisoning {
             Trigger::BareEvent(event) => *event == BareEvent::Release,
             Trigger::BareEvents(events) => events.contains(&BareEvent::Release),
             Trigger::Events(events) => match &events.push {
-                OptionalBody::Body(body) => body.tag_filters.is_some(),
+                OptionalBody::Body(body) => {
+                    let pushing_new_tag = &body.tag_filters.is_some();
+                    let pushing_to_release_branch =
+                        if let Some(BranchFilters::Branches(branches)) = &body.branch_filters {
+                            branches
+                                .iter()
+                                .any(|branch| branch.to_lowercase().contains("release"))
+                        } else {
+                            false
+                        };
+
+                    *pushing_new_tag || pushing_to_release_branch
+                }
                 _ => false,
             },
         }


Test patch:
diff --git a/tests/snapshot.rs b/tests/snapshot.rs
index 291ca88..5e835db 100644
--- a/tests/snapshot.rs
+++ b/tests/snapshot.rs
@@ -334,6 +334,12 @@ fn cache_poisoning() -> Result<()> {
         ))
         .run()?);
 
+    insta::assert_snapshot!(zizmor()
+        .workflow(workflow_under_test(
+            "cache-poisoning/workflow-release-branch-trigger.yml"
+        ))
+        .run()?);
+
     Ok(())
 }
 
diff --git a/tests/snapshots/snapshot__cache_poisoning-13.snap b/tests/snapshots/snapshot__cache_poisoning-13.snap
new file mode 100644
index 0000000..9b8a048
--- /dev/null
+++ b/tests/snapshots/snapshot__cache_poisoning-13.snap
@@ -0,0 +1,22 @@
+---
+source: tests/snapshot.rs
+expression: "zizmor().workflow(workflow_under_test(\"cache-poisoning/workflow-release-branch-trigger.yml\")).run()?"
+snapshot_kind: text
+---
+error[cache-poisoning]: runtime artifacts potentially vulnerable to a cache poisoning attack
+  --> @@INPUT@@:1:1
+   |
+ 1 | / on:
+ 2 | |   push:
+ 3 | |     branches:
+ 4 | |       - 'release-v2.0.0'
+   | |________________________^ generally used when publishing artifacts generated at runtime
+ 5 |
+...
+15 |         - name: Setup CI caching
+16 |           uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cache enabled by default here
+   |
+   = note: audit confidence → Low
+
+1 finding: 0 unknown, 0 informational, 0 low, 0 medium, 1 high
diff --git a/tests/test-data/cache-poisoning/workflow-release-branch-trigger.yml b/tests/test-data/cache-poisoning/workflow-release-branch-trigger.yml
new file mode 100644
index 0000000..6a374b7
--- /dev/null
+++ b/tests/test-data/cache-poisoning/workflow-release-branch-trigger.yml
@@ -0,0 +1,19 @@
+on:
+  push:
+    branches:
+      - 'release-v2.0.0'
+
+jobs:
+  publish:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Project Checkout
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
+        with:
+          persist-credentials: false
+
+      - name: Setup CI caching
+        uses: Swatinem/rust-cache@82a92a6e8fbeee089604da2575dc567ae9ddeaab
+
+      - name: Publish on crates.io
+        run: cargo publish --token ${{ secrets.CRATESIO_PUBLISH_TOKEN }}