@@ -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."
0 commit comments