Repository: zizmorcore/zizmor (Rust)

Issue:
[BUG]: zizmor rejects workflows named `dependabot.yml`
### Pre-submission checks

- [x] I am **not** filing a feature request. These should be filed via the feature request form instead.
- [x] I have checked the [Troubleshooting Guide](https://docs.zizmor.sh/troubleshooting) for my problem.
- [x] I have looked through both the [open](https://github.com/zizmorcore/zizmor/issues?q=is%3Aissue+is%3Aopen+) and [closed](https://github.com/zizmorcore/zizmor/issues?q=is%3Aissue%20state%3Aclosed) issues for a duplicate report.

### zizmor version

1.16.3

### Expected behavior

I expected `zizmor` to lint a workflow at path `.github/workflows/dependabot.yml` just like any other workflow.

### Actual behavior

Instead of that, `zizmor` attempted to parse it as a Dependabot configuration file (see logs below).

### Reproduction steps

```sh
cargo install zizmor --locked --git https://github.com/zizmorcore/zizmor --rev 6b1422361d485ebe63282ffebb202b21cc87165d
mkdir tmp && cd tmp
mkdir -p .github/workflows/
cat > .github/workflows/dependabot.yml <<EOF
name: a valid workflow
on: [push]
jobs:
  something:
    runs-on: ubuntu-latest
    steps:
      - run: echo ok
EOF
zizmor --verbose .github/workflows/dependabot.yml
```

### Logs

```plain text
🌈 zizmor v1.16.3
DEBUG collect_inputs: zizmor::config: discovering config for local input `.github/workflows/dependabot.yml`
DEBUG collect_inputs: zizmor::config: attempting config discovery in `.github/workflows`
DEBUG collect_inputs: zizmor::config: reached filesystem root without finding a config
DEBUG collect_inputs: zizmor::registry::input: registering dependabot config input as with key file://.github/workflows/dependabot.yml
 WARN collect_inputs: zizmor::registry::input: failed to validate input as dependabot config: input does not match expected validation schema
fatal: no audit was performed
no inputs collected

Stack backtrace:
   0: std::backtrace::Backtrace::create
   1: anyhow::error::<impl core::convert::From<E> for anyhow::Error>::from
   2: zizmor::main
   3: std::sys::backtrace::__rust_begin_short_backtrace
   4: _main
```

### Additional context

Renaming the workflow to `ci.yml` results in a normal audit.


Discussion:
Thanks @langston-barrett! Yeah, this is definitely a bug -- we should detect when a file named `dependabot.yml` is in the workflows directory and handle it correctly.
While reproducing this, I noticed that it _only_ happens if the user passes the full path to their `dependabot.yml` workflow, not if zizmor performs any kind of directory or remote repo collection. That's probably why I didn't notice this before.



Patch:
diff --git a/crates/zizmor/src/registry/input.rs b/crates/zizmor/src/registry/input.rs
index 7fe215107..47b49fcea 100644
--- a/crates/zizmor/src/registry/input.rs
+++ b/crates/zizmor/src/registry/input.rs
@@ -384,16 +384,29 @@ impl InputGroup {
     ) -> Result<Self, CollectionError> {
         let config = Config::discover(options, || Config::discover_local(path)).await?;
 
+        // Workflows can be named anything, including `dependabot.yml`
+        // (overlapping with Dependabot configs) and `action.yml` (overlapping
+        // with action definitions). Consequently, we make a best effort
+        // disambiguate them by looking at their parent path.
+        // See: https://github.com/zizmorcore/zizmor/issues/1341
+        let is_workflow_path = {
+            let resolved = path.canonicalize_utf8()?;
+
+            resolved
+                .parent()
+                .is_some_and(|parent| parent.ends_with(".github/workflows"))
+        };
+
         let mut group = Self::new(config);
 
         // When collecting individual files, we don't know which part
         // of the input path is the prefix.
         let (key, kind) = match (path.file_stem(), path.extension()) {
-            (Some("dependabot"), Some("yml" | "yaml")) => (
+            (Some("dependabot"), Some("yml" | "yaml")) if !is_workflow_path => (
                 InputKey::local(Group(path.as_str().into()), path, None),
                 InputKind::Dependabot,
             ),
-            (Some("action"), Some("yml" | "yaml")) => (
+            (Some("action"), Some("yml" | "yaml")) if !is_workflow_path => (
                 InputKey::local(Group(path.as_str().into()), path, None),
                 InputKind::Action,
             ),
diff --git a/docs/release-notes.md b/docs/release-notes.md
index e2a31567f..f9332a2b9 100644
--- a/docs/release-notes.md
+++ b/docs/release-notes.md
@@ -35,6 +35,10 @@ of `zizmor`.
 * The [concurrency-limits] audit now correctly detects job-level `concurrency`
   settings, in addition to workflow-level settings (#1338)
 
+* Fixed a bug where `zizmor` would fail to collect workflows with names that
+  overlapped with other input types (e.g. `action.yml` and `dependabot.yml`)
+  when passed explicitly by path (#1345)
+
 ## 1.16.3
 
 ### Bug Fixes 🐛


Test patch:
diff --git a/crates/zizmor/tests/integration/e2e.rs b/crates/zizmor/tests/integration/e2e.rs
index bf2148469..afc6aeee3 100644
--- a/crates/zizmor/tests/integration/e2e.rs
+++ b/crates/zizmor/tests/integration/e2e.rs
@@ -430,3 +430,21 @@ fn issue_1300() -> Result<()> {
 
     Ok(())
 }
+
+/// Regression test for #1341.
+///
+/// Ensures that we successfully collect a *workflow* named `dependabot.yml`, rather
+/// than failing to parse it as a Dependabot config.
+#[test]
+fn issue_1341() -> Result<()> {
+    insta::assert_snapshot!(
+        zizmor()
+            .offline(true)
+            .input(input_under_test(
+                "issue-1341-repro/.github/workflows/dependabot.yml"
+            ))
+            .run()?,
+    );
+
+    Ok(())
+}
diff --git a/crates/zizmor/tests/integration/snapshots/integration__e2e__issue_1341.snap b/crates/zizmor/tests/integration/snapshots/integration__e2e__issue_1341.snap
new file mode 100644
index 000000000..fbb9a8677
--- /dev/null
+++ b/crates/zizmor/tests/integration/snapshots/integration__e2e__issue_1341.snap
@@ -0,0 +1,5 @@
+---
+source: crates/zizmor/tests/integration/e2e.rs
+expression: "zizmor().offline(true).input(input_under_test(\"issue-1341-repro/.github/workflows/dependabot.yml\")).run()?"
+---
+No findings to report. Good job! (2 suppressed)
diff --git a/crates/zizmor/tests/integration/test-data/issue-1341-repro/.github/workflows/dependabot.yml b/crates/zizmor/tests/integration/test-data/issue-1341-repro/.github/workflows/dependabot.yml
new file mode 100644
index 000000000..003ba8aef
--- /dev/null
+++ b/crates/zizmor/tests/integration/test-data/issue-1341-repro/.github/workflows/dependabot.yml
@@ -0,0 +1,8 @@
+name: a valid workflow
+on: [push]
+permissions: {}
+jobs:
+  something:
+    runs-on: ubuntu-latest
+    steps:
+      - run: echo ok