Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 02f4052

Browse files
authored
ref-version-mismatch: generalize version comment pattern (#2073)
1 parent 84c0c94 commit 02f4052

3 files changed

Lines changed: 65 additions & 30 deletions

File tree

crates/zizmor/src/audit/ref_version_mismatch.rs

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
use std::sync::LazyLock;
2-
31
use anyhow::anyhow;
42
use github_actions_models::common::Uses;
5-
use regex::Regex;
63
use subfeature::Subfeature;
74
use yamlpatch::{Op, Patch};
85

@@ -15,6 +12,7 @@ use crate::{
1512
},
1613
github,
1714
models::{StepCommon, action::CompositeStep, uses::RepositoryUsesExt, workflow::Step},
15+
utils::once::static_regex,
1816
};
1917

2018
pub(crate) struct RefVersionMismatch {
@@ -27,17 +25,21 @@ audit_meta!(
2725
"action's hash pin has mismatched or missing version comment"
2826
);
2927

30-
#[allow(clippy::unwrap_used)]
31-
static VERSION_COMMENT_PATTERNS: LazyLock<Vec<Regex>> = LazyLock::new(|| {
32-
vec![
33-
// Matches "# tag=v2.8.0", "# tag=v6-beta", or any non-whitespace tag token.
34-
Regex::new(r"#\s*tag\s*=\s*(\S+)").unwrap(),
35-
// Matches "# v2.8.0" and prerelease forms like "# v1.2.3-rc.1", with or without the `v` suffix.
36-
Regex::new(r"#\s*(v?\d+(?:\.\d+)*(?:-?[\w.-]+)?)").unwrap(),
37-
// More flexible: "# version: 2.8.0"
38-
Regex::new(r"#\s*(?:version|ver)\s*[:=]\s*(v?\d+(?:\.\d+)*(?:-?[\w.-]+)?)").unwrap(),
39-
]
40-
});
28+
static_regex!(
29+
VERSION_COMMENT_PATTERN,
30+
r#"(?x) # verbose mode
31+
^ # start of string
32+
\# # start of comment
33+
\s* # optional whitespace
34+
(?: # start non-capturing group for version prefix
35+
(?:tag|version|ver)\s*[:=]\s* # version prefix + `:` or `=`
36+
)? # end optional non-capturing group
37+
( # start capturing group for version
38+
\S+ # one or more non-whitespace characters
39+
) # end capturing group for version
40+
$ # end of string
41+
"#
42+
);
4143

4244
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
4345
enum CommentVersionState<'doc> {
@@ -49,12 +51,10 @@ enum CommentVersionState<'doc> {
4951
impl RefVersionMismatch {
5052
fn extract_version_from_comments<'doc>(comments: &'doc [Comment<'doc>]) -> Option<&'doc str> {
5153
for comment in comments {
52-
for pattern in VERSION_COMMENT_PATTERNS.iter() {
53-
if let Some(captures) = pattern.captures(comment.as_ref())
54-
&& let Some(version_match) = captures.get(1)
55-
{
56-
return Some(version_match.as_str());
57-
}
54+
if let Some(captures) = VERSION_COMMENT_PATTERN.captures(comment.as_ref())
55+
&& let Some(version_match) = captures.get(1)
56+
{
57+
return Some(version_match.as_str());
5858
}
5959
}
6060
None
@@ -292,7 +292,7 @@ mod tests {
292292
}
293293

294294
#[test]
295-
fn test_version_comment_patterns() {
295+
fn test_version_comment_pattern() {
296296
let test_cases = vec![
297297
("# tag=v2.8.0", Some("v2.8.0")),
298298
("# tag=v6-beta", Some("v6-beta")),
@@ -316,21 +316,24 @@ mod tests {
316316
("# ver=1.0.0", Some("1.0.0")),
317317
("# visit the docs", None),
318318
("# some other comment", None),
319+
("# zizmor: ignore[ref-version-mismatch]", None),
319320
];
320321

321322
for (comment, expected) in test_cases {
322323
// Test the pattern matching directly
323-
let comment_text = comment;
324-
let mut found_version = None;
325-
for pattern in VERSION_COMMENT_PATTERNS.iter() {
326-
if let Some(captures) = pattern.captures(comment_text) {
327-
if let Some(version_match) = captures.get(1) {
328-
found_version = Some(version_match.as_str());
329-
break;
330-
}
324+
match (VERSION_COMMENT_PATTERN.captures(comment), expected) {
325+
(None, None) => (),
326+
(None, Some(expected)) => {
327+
assert!(
328+
false,
329+
"Got no match in '{comment}', but expected {expected}"
330+
)
331+
}
332+
(Some(caps), None) => {
333+
assert!(false, "Got unexpected match: {caps:?}")
331334
}
335+
(Some(_), Some(_)) => (),
332336
}
333-
assert_eq!(found_version, expected, "Failed for comment: {}", comment);
334337
}
335338
}
336339

crates/zizmor/tests/integration/audit/ref_version_mismatch.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,20 @@ fn test_issue_1899() -> Result<()> {
203203
Ok(())
204204
}
205205

206+
/// Bug #1938: version comments like `# create-github-app-token/v0.2.2` should be detected correctly.
207+
#[cfg_attr(not(feature = "gh-token-tests"), ignore)]
208+
#[test]
209+
fn test_issue_1938() -> Result<()> {
210+
insta::assert_snapshot!(
211+
zizmor()
212+
.offline(false)
213+
.args(["--persona=pedantic"])
214+
.input(input_under_test("ref-version-mismatch/issue-1938-repro.yml"))
215+
.run()?, @"No findings to report. Good job!");
216+
217+
Ok(())
218+
}
219+
206220
/// Bug #2039: version comments like `# 1.2.3rc1` should be detected correctly.
207221
#[cfg_attr(not(feature = "gh-token-tests"), ignore)]
208222
#[test]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: ISSUE-1938-REPRO
2+
3+
on:
4+
push:
5+
6+
permissions: {}
7+
8+
concurrency:
9+
group: ISSUE-1938-REPRO
10+
cancel-in-progress: true
11+
12+
jobs:
13+
test:
14+
name: test
15+
runs-on: ubuntu-latest
16+
steps:
17+
- name: Namespaced tag comment should match pinned SHA
18+
uses: grafana/shared-workflows/actions/create-github-app-token@ae92934a14a48b94494dbc06d74a81d47fe08a40 # create-github-app-token/v0.2.2

0 commit comments

Comments
 (0)