Skip to content

Commit 8218f6d

Browse files
authored
Add/actions sniffer (#5)
* feat: add action to check gh action versions * chore(tests): add tests for gh actions script
1 parent a6b4e17 commit 8218f6d

2 files changed

Lines changed: 169 additions & 0 deletions

File tree

bin/gh-actions-sast

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/bin/bash
2+
3+
RED='\033[0;31m'
4+
YELLOW='\033[1;33m'
5+
RESET='\033[0m'
6+
7+
EXCLUDE_LIST=()
8+
9+
MIN_VERSION=6
10+
11+
is_excluded() {
12+
local action="$1"
13+
for ex in "${EXCLUDE_LIST[@]}"; do
14+
[[ "$ex" == "$action" ]] && return 0
15+
done
16+
return 1
17+
}
18+
19+
found_error=0
20+
21+
while IFS= read -r file; do
22+
while IFS= read -r line; do
23+
if [[ "$line" =~ (actions/[a-zA-Z-]+)@v([0-9]+) ]]; then
24+
action="${BASH_REMATCH[1]}"
25+
version="${BASH_REMATCH[2]}"
26+
if (( version < MIN_VERSION )); then
27+
if is_excluded "$action"; then
28+
echo -e "${YELLOW}WARN${RESET} $file: $action@v$version is below v$MIN_VERSION (excluded)"
29+
else
30+
echo -e "${RED}ERROR${RESET} $file: $action@v$version is below v$MIN_VERSION"
31+
found_error=1
32+
fi
33+
fi
34+
fi
35+
done < "$file"
36+
done < <(find .github/workflows -name '*.yml' 2>/dev/null)
37+
38+
exit $found_error

tests/test_gh_actions_sast.bats

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#!/usr/bin/env bats
2+
3+
setup() {
4+
TMPDIR="$(mktemp -d)"
5+
mkdir -p "$TMPDIR/.github/workflows"
6+
cp "$BATS_TEST_DIRNAME/../bin/gh-actions-sast" "$TMPDIR/gh-actions-sast"
7+
chmod +x "$TMPDIR/gh-actions-sast"
8+
GH_ACTIONS_SAST="$TMPDIR/gh-actions-sast"
9+
}
10+
11+
teardown() {
12+
rm -rf "$TMPDIR"
13+
}
14+
15+
write_workflow() {
16+
cat > "$TMPDIR/.github/workflows/ci.yml"
17+
}
18+
19+
@test "no workflows dir: exits 0" {
20+
rm -rf "$TMPDIR/.github"
21+
run bash -c "cd '$TMPDIR' && '$GH_ACTIONS_SAST'"
22+
[ "$status" -eq 0 ]
23+
}
24+
25+
@test "action at MIN_VERSION (v6): exits 0" {
26+
write_workflow <<'EOF'
27+
jobs:
28+
test:
29+
steps:
30+
- uses: actions/checkout@v6
31+
EOF
32+
run bash -c "cd '$TMPDIR' && '$GH_ACTIONS_SAST'"
33+
[ "$status" -eq 0 ]
34+
}
35+
36+
@test "action above MIN_VERSION (v7): exits 0" {
37+
write_workflow <<'EOF'
38+
jobs:
39+
test:
40+
steps:
41+
- uses: actions/checkout@v7
42+
EOF
43+
run bash -c "cd '$TMPDIR' && '$GH_ACTIONS_SAST'"
44+
[ "$status" -eq 0 ]
45+
}
46+
47+
@test "action below MIN_VERSION (v5): exits 1 with ERROR" {
48+
write_workflow <<'EOF'
49+
jobs:
50+
test:
51+
steps:
52+
- uses: actions/checkout@v5
53+
EOF
54+
run bash -c "cd '$TMPDIR' && '$GH_ACTIONS_SAST'"
55+
[ "$status" -eq 1 ]
56+
[[ "$output" == *"ERROR"* ]]
57+
[[ "$output" == *"actions/checkout@v5"* ]]
58+
}
59+
60+
@test "action at v1: exits 1 with ERROR" {
61+
write_workflow <<'EOF'
62+
jobs:
63+
test:
64+
steps:
65+
- uses: actions/setup-node@v1
66+
EOF
67+
run bash -c "cd '$TMPDIR' && '$GH_ACTIONS_SAST'"
68+
[ "$status" -eq 1 ]
69+
[[ "$output" == *"ERROR"* ]]
70+
[[ "$output" == *"actions/setup-node@v1"* ]]
71+
}
72+
73+
@test "multiple old actions: all reported" {
74+
write_workflow <<'EOF'
75+
jobs:
76+
test:
77+
steps:
78+
- uses: actions/checkout@v4
79+
- uses: actions/setup-node@v3
80+
EOF
81+
run bash -c "cd '$TMPDIR' && '$GH_ACTIONS_SAST'"
82+
[ "$status" -eq 1 ]
83+
[[ "$output" == *"actions/checkout@v4"* ]]
84+
[[ "$output" == *"actions/setup-node@v3"* ]]
85+
}
86+
87+
@test "mixed old and new actions: exits 1" {
88+
write_workflow <<'EOF'
89+
jobs:
90+
test:
91+
steps:
92+
- uses: actions/checkout@v6
93+
- uses: actions/setup-node@v3
94+
EOF
95+
run bash -c "cd '$TMPDIR' && '$GH_ACTIONS_SAST'"
96+
[ "$status" -eq 1 ]
97+
[[ "$output" == *"actions/setup-node@v3"* ]]
98+
}
99+
100+
@test "non-yml file ignored" {
101+
cp "$TMPDIR/.github/workflows/ci.yml" "$TMPDIR/.github/workflows/notes.txt" 2>/dev/null || true
102+
cat > "$TMPDIR/.github/workflows/notes.txt" <<'EOF'
103+
uses: actions/checkout@v1
104+
EOF
105+
write_workflow <<'EOF'
106+
jobs:
107+
test:
108+
steps:
109+
- uses: actions/checkout@v6
110+
EOF
111+
run bash -c "cd '$TMPDIR' && '$GH_ACTIONS_SAST'"
112+
[ "$status" -eq 0 ]
113+
}
114+
115+
@test "multiple workflow files: all scanned" {
116+
write_workflow <<'EOF'
117+
jobs:
118+
test:
119+
steps:
120+
- uses: actions/checkout@v6
121+
EOF
122+
cat > "$TMPDIR/.github/workflows/release.yml" <<'EOF'
123+
jobs:
124+
release:
125+
steps:
126+
- uses: actions/upload-artifact@v4
127+
EOF
128+
run bash -c "cd '$TMPDIR' && '$GH_ACTIONS_SAST'"
129+
[ "$status" -eq 1 ]
130+
[[ "$output" == *"actions/upload-artifact@v4"* ]]
131+
}

0 commit comments

Comments
 (0)