Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
745 changes: 662 additions & 83 deletions crates/zizmor/src/models/uses.rs

Large diffs are not rendered by default.

44 changes: 44 additions & 0 deletions crates/zizmor/tests/integration/audit/forbidden_uses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,50 @@ fn test_config_invalid_pattern() -> Result<()> {
Ok(())
}

#[test]
fn test_allow_glob() -> Result<()> {
// Test glob patterns in allow list: "actions/*" and "pypa/gh-action-*"
// should allow all three actions in the menagerie
insta::assert_snapshot!(
zizmor()
.config(input_under_test("forbidden-uses/configs/allow-glob.yml"))
.input(input_under_test(
"forbidden-uses/forbidden-uses-menagerie.yml"
))
.run()?,
@"No findings to report. Good job! (1 suppressed)"
);

Ok(())
}

#[test]
fn test_deny_glob() -> Result<()> {
// Test glob pattern in deny list: "pypa/*-pypi-publish"
// should deny pypa/gh-action-pypi-publish but allow actions/*
insta::assert_snapshot!(
zizmor()
.config(input_under_test("forbidden-uses/configs/deny-glob.yml"))
.input(input_under_test(
"forbidden-uses/forbidden-uses-menagerie.yml"
))
.run()?,
@r"
error[forbidden-uses]: forbidden action used
--> @@INPUT@@:14:15
|
14 | - uses: pypa/gh-action-pypi-publish@release/v1
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ use of this action is forbidden
|
= note: audit confidence → High

2 findings (1 suppressed): 0 informational, 0 low, 0 medium, 1 high
"
);

Ok(())
}

#[test]
fn test_config_invalid_variant() -> Result<()> {
insta::assert_snapshot!(
Expand Down
2 changes: 1 addition & 1 deletion crates/zizmor/tests/integration/audit/unpinned_uses.rs
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ fn test_invalid_policy_syntax_4() -> Result<()> {
Caused by:
0: configuration error in @@CONFIG@@
1: invalid syntax for audit `unpinned-uses`
2: invalid pattern: foo/b*r
2: invalid pattern: foo/b*r*
"
);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
rules:
unpinned-uses:
config:
policies:
# just to make unrelated findings go away
"*": ref-pin

forbidden-uses:
config:
allow:
# Allow all actions/* repos using glob
- "actions/*"
# Allow pypa repos starting with "gh-action-"
- "pypa/gh-action-*"
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
rules:
unpinned-uses:
config:
policies:
# just to make unrelated findings go away
"*": ref-pin

forbidden-uses:
config:
deny:
# Deny all repos ending with "-pypi-publish"
- "pypa/*-pypi-publish"
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ rules:
unpinned-uses:
config:
policies:
# Invalid: * must be alone in repo position
"foo/b*r": "any"
# Invalid: multiple wildcards in a single segment
"foo/b*r*": "any"
54 changes: 53 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,9 @@ common syntaxes, and are described here.

Repository patterns are used to match `#!yaml uses:` clauses.

The following patterns are supported, in order of specificity:
The following patterns are supported, in order of specificity (most specific first).
When multiple patterns match a `#!yaml uses:` clause, the most specific pattern
takes precedence.

* `owner/repo/subpath@ref`: matches the exact repository, including
subpath (if given) and ref. The subpath is optional.
Expand Down Expand Up @@ -212,6 +214,51 @@ The following patterns are supported, in order of specificity:
but **not** `#!yaml uses: actions/cache/save@v3` or
`#!yaml uses: actions/cache/restore@v3`.

* `owner/prefix-*`, `owner/*-suffix`, `owner/prefix-*-suffix`: match repos where
the repo name matches the given prefix and/or suffix. A single `*` acts as a
wildcard matching any characters. No subpath is matched.

!!! example

`myorg/action-*` matches `#!yaml uses: myorg/action-checkout@v1`
and `#!yaml uses: myorg/action-setup@v2`, but **not**
`#!yaml uses: myorg/action-checkout/subdir@v1` (which has a subpath).

`myorg/*-action` matches `#!yaml uses: myorg/checkout-action@v1`
and `#!yaml uses: myorg/setup-action@v2`.

* `owner/prefix-*/*`: match repos where the repo name matches the prefix,
including any subpath.

!!! example

`myorg/action-*/*` matches `#!yaml uses: myorg/action-checkout@v1`,
`#!yaml uses: myorg/action-checkout/subdir@v1`, and
`#!yaml uses: myorg/action-setup/init@v2`.

* `owner/prefix-*/subpath`, `owner/repo/subpath-*`, `owner/prefix-*/subpath-*`:
match using wildcards in the repo name, subpath, or both.

!!! example

`github/codeql-*/init` matches `#!yaml uses: github/codeql-action/init@v3`
and `#!yaml uses: github/codeql-bundle/init@v2`.

`github/codeql-action/init-*` matches
`#!yaml uses: github/codeql-action/init-db@v3` but **not**
`#!yaml uses: github/codeql-action/upload@v3`.

`myorg/action-*/init-*` matches `#!yaml uses: myorg/action-db/init-schema@v1`
and `#!yaml uses: myorg/action-cache/init-store@v2`.

* `owner/prefix-*@ref`: match repos where the repo name matches the prefix,
with an exact ref.

!!! example

`myorg/action-*@v1` matches `#!yaml uses: myorg/action-checkout@v1`
but **not** `#!yaml uses: myorg/action-checkout@v2`.

* `owner/repo/*`: match all `#!yaml uses:` clauses that come from the given
`owner/repo`. Any subpath or ref is matched.

Expand All @@ -237,3 +284,8 @@ The following patterns are supported, in order of specificity:

`*` matches `#!yaml uses: actions/checkout` and
`#!yaml uses: pypa/gh-action-pypi-publish@release/v1`.

!!! warning

Multiple wildcards within a single segment are **not** supported.
For example, `owner/foo-*-*` is invalid (two `*` characters in the repo segment).
2 changes: 1 addition & 1 deletion support/zizmor.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
},
"RepositoryUsesPattern": {
"title": "Represents a pattern for matching repository `uses` references.",
"description": "These patterns are ordered by specificity; more specific patterns\nshould be listed first.",
"description": "These patterns are ordered by specificity; more specific patterns\nshould be listed first. The ordering is:\n\n1. `ExactWithRef` - most specific (matches owner/repo/subpath@ref exactly)\n2. `ExactPath` - matches owner/repo/subpath (any ref)\n3. `ExactRepo` - matches owner/repo with no subpath (any ref)\n4. `InRepo` - matches owner/repo/* (any subpath, any ref)\n5. `InOwner` - matches owner/* (any repo, any subpath, any ref)\n6. `Any` - matches * (everything)\n\nWithin variants that have `Segment` fields, patterns with exact segments\nare more specific than patterns with wildcard segments.",
"type": "string"
},
"RulesConfig": {
Expand Down