Repository: zizmorcore/zizmor (Rust)

Issue:
[BUG]: Booleans written as strings are not handled correctly
### Pre-submission checks

- [X] I am **not** filing a feature request. These should be filed via the feature request form instead.
- [X] I have looked through the [open issues](https://github.com/woodruffw/zizmor/issues?q=is%3Aissue+is%3Aopen+) for a duplicate report.

### Expected behavior

When I write a boolean value between quotes (e.g. `'false'`) I expect it to be treated as a boolean equal to false, and not a string containing `false`. Which is what yaml and GHA do.

I want to be able to write the following snippet in my workflow and have `zizmor` not issue any warning saying that  `persist-credentials` should be set to `false`.

```yaml
    - name: Check out code
      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
      with:
        persist-credentials: 'false'
```

### Actual behavior

Right now `zizmor` emits a warning when using the snippet pasted above:
```
2025-01-14T23:01:19.684524Z  WARN zizmor: skipping impostor-commit: can't run without a GitHub API token
2025-01-14T23:01:19.684741Z  WARN zizmor: skipping ref-confusion: can't run without a GitHub API token
2025-01-14T23:01:19.684752Z  WARN zizmor: skipping known-vulnerable-actions: can't run without a GitHub API token
2025-01-14T23:01:19.714096Z  INFO audit: zizmor: 🌈 completed /aaaa/.github/workflows/create_release.yml
warning[artipacked]: credential persistence through GitHub Actions artifacts
  --> /aaaa/.github/workflows/create_release.yml:19:7
   |
19 |       - name: Check out code
   |  _______-
20 | |       uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
21 | |       with:
22 | |         persist-credentials: 'false'
   | |____________________________________- does not set persist-credentials: false
   |
   = note: audit confidence → Low

2 findings (1 suppressed): 0 unknown, 0 informational, 0 low, 1 medium, 0 high
```

Despite the warning, the value is correctly set to `false` on GitHub Actions:
![image](https://github.com/user-attachments/assets/6121ac98-38eb-4c0a-b664-192dbeb497dd)


### Reproduction steps

1. Use the following step:
	```yaml
    steps:
    - name: Check out code
      uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
      with:
        persist-credentials: 'false'
	```

2. Run `zizmor` on the yaml file and observe the warning.

### Logs

_No response_

### Additional context

_No response_


Discussion:
Thanks for the report @frouioui! 

FWIW this is a GHA-ism, not a YAML-ism: `true` and `'true'` are distinct types in YAML, but GHA considers them the same (this is a side effect of expression expansion always returning strings, AFAICT).

Regardless, this is indeed a bug in `zizmor` itself, since we should be doing what GHA does and not just what YAML does 🙂. I'll look into a fix now!

That's a good point, thanks for the clarification 🙇🏻 
#448 should fix this!
Awesome, thanks for the quick turnaround!



Patch:
diff --git a/docs/release-notes.md b/docs/release-notes.md
index e8b32a94b..5e3ef6cb6 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -20,6 +20,8 @@ of `zizmor`.
 
 * The [template-injection] audit no longer considers `github.event.pull_request.base.sha`
   dangerous (#445)
+* The [artipacked] audit now correctly handles the strings `'true'` and `'false'`
+  as their boolean counterparts (#448)
 
 ## v1.1.1
 
@@ -434,6 +436,7 @@ This is one of `zizmor`'s bigger recent releases! Key enhancements include:
 
 <!-- useful shortlinks -->
 
+[artipacked]: ./audits.md#artipacked
 [excessive-permissions]: ./audits.md#excessive-permissions
 [cache-poisoning]: ./audits.md#cache-poisoning
 [github-env]: ./audits.md#github-env
diff --git a/src/audit/artipacked.rs b/src/audit/artipacked.rs
index e9291a8b7..7f98e9b33 100644
--- a/src/audit/artipacked.rs
+++ b/src/audit/artipacked.rs
@@ -74,14 +74,18 @@ impl Audit for Artipacked {
                 };
 
                 if uses.matches("actions/checkout") {
-                    match with.get("persist-credentials") {
-                        Some(EnvValue::Boolean(false)) => continue,
-                        Some(EnvValue::Boolean(true)) => {
+                    match with
+                        .get("persist-credentials")
+                        .map(|v| v.to_string())
+                        .as_deref()
+                    {
+                        Some("false") => continue,
+                        Some("true") => {
                             // If a user explicitly sets `persist-credentials: true`,
                             // they probably mean it. Only report if in auditor mode.
                             vulnerable_checkouts.push((step, Persona::Auditor))
                         }
-                        // TODO: handle expressions and literal strings here.
+                        // TODO: handle expressions here.
                         // persist-credentials is true by default.
                         _ => vulnerable_checkouts.push((step, Persona::default())),
                     }


Test patch:
diff --git a/tests/snapshot.rs b/tests/snapshot.rs
index da7cbf51b..845d671dc 100644
--- a/tests/snapshot.rs
+++ b/tests/snapshot.rs
@@ -143,6 +143,11 @@ fn artipacked() -> Result<()> {
         .args(["--persona=auditor"])
         .run()?);
 
+    insta::assert_snapshot!(zizmor()
+        .workflow(workflow_under_test("artipacked/issue-447-repro.yml"))
+        .args(["--persona=auditor"])
+        .run()?);
+
     Ok(())
 }
 
diff --git a/tests/snapshots/snapshot__artipacked-4.snap b/tests/snapshots/snapshot__artipacked-4.snap
new file mode 100644
index 000000000..ce483f0fe
--- /dev/null
+++ b/tests/snapshots/snapshot__artipacked-4.snap
@@ -0,0 +1,18 @@
+---
+source: tests/snapshot.rs
+expression: "zizmor().workflow(workflow_under_test(\"artipacked/issue-447-repro.yml\")).args([\"--persona=auditor\"]).run()?"
+---
+warning[artipacked]: credential persistence through GitHub Actions artifacts
+  --> @@INPUT@@:19:9
+   |
+19 |         - name: true-positive
+   |  _________-
+20 | |         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+21 | |         with:
+22 | |           # finding in auditor mode only
+23 | |           persist-credentials: "true"
+   | |______________________________________- does not set persist-credentials: false
+   |
+   = note: audit confidence → Low
+
+1 finding: 0 unknown, 0 informational, 0 low, 1 medium, 0 high
diff --git a/tests/test-data/artipacked/issue-447-repro.yml b/tests/test-data/artipacked/issue-447-repro.yml
new file mode 100644
index 000000000..028793d13
--- /dev/null
+++ b/tests/test-data/artipacked/issue-447-repro.yml
@@ -0,0 +1,23 @@
+# see https://github.com/woodruffw/zizmor/issues/447
+
+name: ISSUE-447-REPRO
+on: push
+
+permissions: {}
+
+jobs:
+  issue-557-repro:
+    runs-on: ubuntu-latest
+
+    steps:
+      - name: true-negative
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        with:
+          # no finding since GHA permits stringy bools
+          persist-credentials: "false"
+
+      - name: true-positive
+        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        with:
+          # finding in auditor mode only
+          persist-credentials: "true"