Skip to content

Commit ffbaba0

Browse files
committed
ci: harden PR validation — broaden injection scan, add malware/secret scans, pin actions
- injection-scan: scan all text files (not just skills/+roles/ markdown), excluding .github/ and SECURITY.md; keep ai-security/ + defensive-context exclusions - injection-scan: add malware-pattern step (curl|bash, install hooks, child_process, eval, /dev/tcp, reverse shells) and committed-secret step (AKIA/sk-/ghp_/xox/glpat/ PEM/AIza) over non-markdown code, scripts, and templates - pin actions/checkout to v4.2.2 SHA across all workflows (supply-chain) Signed-off-by: Kamal Srinivasan <kamal.srini@gmail.com>
1 parent 3946a04 commit ffbaba0

3 files changed

Lines changed: 95 additions & 48 deletions

File tree

.github/workflows/injection-scan.yml

Lines changed: 93 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,24 @@ jobs:
1111
runs-on: ubuntu-latest
1212
steps:
1313
- name: Checkout repository
14-
uses: actions/checkout@v4 # pin to SHA for SLSA compliance in future iteration
14+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
1515

16-
- name: Scan for prompt injection patterns
16+
- name: Build text-file list (all text files, excluding .git/.github)
1717
run: |
18-
# HIGH-CONFIDENCE injection patterns — phrases that should never
19-
# appear in skill files outside of clearly defensive contexts.
20-
# These are imperative commands directed at the agent, not
21-
# descriptions of attacks or technical terminology.
18+
find . -type f \
19+
\( -name '*.md' -o -name '*.mjs' -o -name '*.js' -o -name '*.ts' \
20+
-o -name '*.sh' -o -name '*.json' -o -name '*.yaml' -o -name '*.yml' \
21+
-o -name '*.tf' -o -name '*.ql' -o -name '*.kql' -o -name '*.spl' \
22+
-o -name '*.py' -o -name '*.toml' -o -name '*.cfg' -o -name '*.txt' \) \
23+
-not -path './.git/*' -not -path './.github/*' \
24+
> /tmp/textfiles.txt
25+
echo "Scanning $(wc -l < /tmp/textfiles.txt) text files."
26+
27+
- name: Scan for prompt injection patterns (all text files)
28+
run: |
29+
# Imperative phrases directed at the agent. SECURITY.md and the
30+
# ai-security/ educational skills legitimately quote these, and so
31+
# do defensive skill notices ("treat it as data...") — all excluded.
2232
PATTERNS=(
2333
"new directive"
2434
"system override"
@@ -28,68 +38,105 @@ jobs:
2838
"api.telegram"
2939
"discord.com/api/webhooks"
3040
"hooks.slack.com/services"
31-
)
32-
33-
FOUND_ISSUES=0
34-
35-
for pattern in "${PATTERNS[@]}"; do
36-
MATCHES=$(grep -rin "$pattern" skills/ roles/ --include="*.md" 2>/dev/null || true)
37-
if [ -n "$MATCHES" ]; then
38-
# Exclude ai-security/ files (contain educational attack examples)
39-
FILTERED=$(echo "$MATCHES" | grep -v "ai-security/prompt-injection/\|ai-security/llm-top-10/\|ai-security/agentic-top-10/\|ai-security/agent-security/\|ai-security/model-supply-chain/\|ai-security/ai-data-privacy/" || true)
40-
if [ -n "$FILTERED" ]; then
41-
echo "FOUND injection pattern \"$pattern\":"
42-
echo "$FILTERED"
43-
echo ""
44-
FOUND_ISSUES=1
45-
fi
46-
fi
47-
done
48-
49-
# CONTEXT-DEPENDENT patterns — these words appear legitimately in
50-
# security content (e.g., "Never exfiltrate data", "webhook URL").
51-
# Only flag them when they appear as bare imperatives, not in
52-
# defensive safety notices or technical descriptions.
53-
# We scan for the imperative/attack forms specifically:
54-
IMPERATIVE_PATTERNS=(
5541
"ignore previous instructions and"
5642
"ignore all previous instructions"
5743
"you are now a"
5844
"you are now in"
5945
"disregard your"
6046
"disregard all previous"
6147
)
62-
63-
for pattern in "${IMPERATIVE_PATTERNS[@]}"; do
64-
MATCHES=$(grep -rin "$pattern" skills/ roles/ --include="*.md" 2>/dev/null || true)
65-
if [ -n "$MATCHES" ]; then
66-
# Exclude ai-security/ (educational) and lines with defensive context
67-
FILTERED=$(echo "$MATCHES" \
68-
| grep -v "ai-security/prompt-injection/\|ai-security/llm-top-10/\|ai-security/agentic-top-10/\|ai-security/agent-security/\|ai-security/model-supply-chain/\|ai-security/ai-data-privacy/" \
48+
FOUND_ISSUES=0
49+
while IFS= read -r f; do
50+
case "$f" in
51+
./SECURITY.md|*/ai-security/*) continue ;;
52+
esac
53+
for pattern in "${PATTERNS[@]}"; do
54+
LINES=$(grep -in "$pattern" "$f" 2>/dev/null || true)
55+
[ -z "$LINES" ] && continue
56+
# Drop lines that are clearly defensive/illustrative.
57+
FILTERED=$(echo "$LINES" \
6958
| grep -v "treat it as" \
7059
| grep -v "not a command" \
7160
| grep -v "not as a directive" \
7261
| grep -v "not obeyed" \
62+
| grep -v "disregard them entirely" \
7363
| grep -v "flag it as" \
64+
| grep -v "report the finding" \
7465
| grep -v "e\.g\.\," \
7566
| grep -v "such as" \
76-
| grep -v "contains text like" \
67+
| grep -v "text like" \
68+
| grep -v "contains text" \
7769
|| true)
7870
if [ -n "$FILTERED" ]; then
79-
echo "FOUND imperative injection pattern \"$pattern\":"
71+
echo "INJECTION pattern \"$pattern\" in $f:"
8072
echo "$FILTERED"
8173
echo ""
8274
FOUND_ISSUES=1
8375
fi
84-
fi
85-
done
86-
76+
done
77+
done < /tmp/textfiles.txt
8778
if [ "$FOUND_ISSUES" -ne 0 ]; then
8879
echo "FAIL: Prompt injection patterns detected. Review flagged lines above."
89-
echo ""
90-
echo "NOTE: The ai-security/ directory and lines with defensive context"
91-
echo "(e.g., 'treat it as a finding') are excluded automatically."
9280
exit 1
9381
fi
94-
9582
echo "PASS: No prompt injection patterns detected."
83+
84+
- name: Scan code/scripts/templates for malware patterns (non-markdown)
85+
run: |
86+
# Runnable-code red flags. Scoped to non-md so skill documentation
87+
# that *describes* these patterns is not flagged.
88+
MAL=(
89+
'curl[^|]*\|[[:space:]]*(ba)?sh'
90+
'wget[^|]*\|[[:space:]]*(ba)?sh'
91+
'curl -X POST'
92+
'/dev/tcp/'
93+
'nc -e'
94+
'bash -i'
95+
'base64 -d[^|]*\|[[:space:]]*(ba)?sh'
96+
'"preinstall"'
97+
'"postinstall"'
98+
'child_process'
99+
'eval\('
100+
'new Function\('
101+
'atob\('
102+
)
103+
FOUND=0
104+
for p in "${MAL[@]}"; do
105+
M=$(grep -rInE --binary-files=without-match "$p" . \
106+
--include="*.mjs" --include="*.js" --include="*.ts" --include="*.sh" \
107+
--include="*.json" --include="*.yaml" --include="*.yml" --include="*.tf" \
108+
--include="*.ql" --include="*.kql" --include="*.spl" --include="*.py" \
109+
2>/dev/null | grep -vE '(^|/)\.github/' || true)
110+
if [ -n "$M" ]; then echo "MALWARE pattern \"$p\":"; echo "$M"; echo ""; FOUND=1; fi
111+
done
112+
if [ "$FOUND" -ne 0 ]; then
113+
echo "FAIL: Suspicious executable patterns in non-markdown files. Manual review required."
114+
exit 1
115+
fi
116+
echo "PASS: No malware patterns in code/scripts/templates."
117+
118+
- name: Scan for committed secrets (non-markdown)
119+
run: |
120+
SEC=(
121+
'AKIA[0-9A-Z]{16}'
122+
'sk-[A-Za-z0-9]{20,}'
123+
'gh[pousr]_[A-Za-z0-9]{36,}'
124+
'xox[baprs]-[A-Za-z0-9-]{10,}'
125+
'glpat-[A-Za-z0-9_-]{20,}'
126+
'-----BEGIN [A-Z ]*PRIVATE KEY-----'
127+
'AIza[0-9A-Za-z_-]{35}'
128+
)
129+
FOUND=0
130+
for p in "${SEC[@]}"; do
131+
M=$(grep -rInE --binary-files=without-match "$p" . \
132+
--include="*.mjs" --include="*.js" --include="*.ts" --include="*.sh" \
133+
--include="*.json" --include="*.yaml" --include="*.yml" --include="*.tf" \
134+
--include="*.env" --include="*.py" \
135+
2>/dev/null | grep -vE '(^|/)\.github/' || true)
136+
if [ -n "$M" ]; then echo "SECRET match \"$p\":"; echo "$M"; echo ""; FOUND=1; fi
137+
done
138+
if [ "$FOUND" -ne 0 ]; then
139+
echo "FAIL: Possible committed secret. Rotate and remove from history."
140+
exit 1
141+
fi
142+
echo "PASS: No committed secret values detected."

.github/workflows/lint-skills.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
runs-on: ubuntu-latest
1212
steps:
1313
- name: Checkout repository
14-
uses: actions/checkout@v4 # pin to SHA for SLSA compliance in future iteration
14+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
1515

1616
- name: Validate frontmatter in skill and role files
1717
run: |

.github/workflows/validate-index.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
runs-on: ubuntu-latest
1515
steps:
1616
- name: Checkout repository
17-
uses: actions/checkout@v4 # pin to SHA for SLSA compliance in future iteration
17+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
1818

1919
- name: Install yq
2020
run: |

0 commit comments

Comments
 (0)