Skip to content

Commit 8b10f82

Browse files
eloymgclaudeCopilot
authored
Add Semgrep static analysis workflow for create-github-app-token action detection (#101)
* Add Semgrep static analysis workflow and custom rules Introduces a reusable Semgrep scanning workflow that runs on pull requests, along with an initial custom rule to deny usage of actions/create-github-app-token and a script to format findings as GitHub-flavored markdown PR comments. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: pin actions/checkout to v6.0.2 in semgrep workflow Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * testing error on running * fail semgrep on error * fix semgrep rule * Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * no redirect the error message --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent a680ecd commit 8b10f82

3 files changed

Lines changed: 119 additions & 0 deletions

File tree

.github/workflows/semgrep.yaml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: Semgrep static analysis
2+
on:
3+
pull_request:
4+
jobs:
5+
semgrep:
6+
permissions:
7+
contents: read
8+
pull-requests: write
9+
# User definable name of this GitHub Actions job.
10+
name: semgrep-oss/scan
11+
# If you are self-hosting, change the following `runs-on` value:
12+
runs-on: ubuntu-latest
13+
container:
14+
# A Docker image with Semgrep installed. Do not change this.
15+
image: semgrep/semgrep:1.152.0
16+
steps:
17+
# Fetch project source with GitHub Actions Checkout.
18+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
19+
20+
# Fetch org-wide custom Semgrep rules from the central repository.
21+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
22+
with:
23+
repository: grafana/security-github-actions
24+
ref: ${{ github.repository == 'grafana/security-github-actions' && (github.head_ref || github.ref_name) || '' }}
25+
sparse-checkout: |
26+
semgrep/custom-rules.yaml
27+
semgrep/format-results.sh
28+
path: security-github-actions
29+
- id: semgrep
30+
env:
31+
GITHUB_REPOSITORY: ${{github.repository}}
32+
GITHUB_BRANCH: ${{github.head_ref || github.ref_name}}
33+
run: |
34+
set +e
35+
semgrep scan --error --json --config security-github-actions/semgrep/custom-rules.yaml > /tmp/semgrep-results.json
36+
EXIT_CODE=$?
37+
set -e
38+
if [ $EXIT_CODE -eq 1 ]; then
39+
echo "has_findings=true" >> "$GITHUB_OUTPUT"
40+
{
41+
echo 'SEMGREP_OUTPUT<<SEMGREP_EOF'
42+
bash security-github-actions/semgrep/format-results.sh /tmp/semgrep-results.json
43+
echo 'SEMGREP_EOF'
44+
} >> "$GITHUB_ENV"
45+
fi
46+
47+
if [ $EXIT_CODE -gt 1 ]; then
48+
echo "::error::Semgrep run encounters an error"
49+
cat /tmp/semgrep-results.json
50+
exit 1
51+
fi
52+
53+
HIGH_CRITICAL=$(jq '[.results[] | select(.extra.severity == "HIGH" or .extra.severity == "CRITICAL")] | length' /tmp/semgrep-results.json)
54+
if [ "$HIGH_CRITICAL" -gt 0 ]; then
55+
echo "has_high_critical=true" >> "$GITHUB_OUTPUT"
56+
fi
57+
58+
- if: steps.semgrep.outputs.has_findings == 'true'
59+
uses: int128/comment-action@66317511bc86c47bd51e03059040e8a460a167b8
60+
with:
61+
update-if-exists: recreate
62+
post: |
63+
${{ env.SEMGREP_OUTPUT }}
64+
65+
- if: steps.semgrep.outputs.has_high_critical == 'true'
66+
run: |
67+
echo "::error::Semgrep found HIGH or CRITICAL severity findings."
68+
exit 1

semgrep/custom-rules.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
rules:
2+
- id: deny-actions-create-github-app-token
3+
patterns:
4+
- pattern-regex: "uses:\\s*actions/create-github-app-token"
5+
paths:
6+
include:
7+
- "*.yaml"
8+
- "*.yml"
9+
message: >
10+
Do not use actions/create-github-app-token. Use the organization's
11+
approved [alternative for generating GitHub App tokens](https://enghub.grafana-ops.net/docs/default/component/deployment-tools/platform/vault/github-app-token-broker-migration-guide/).
12+
languages: [generic]
13+
severity: LOW

semgrep/format-results.sh

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/usr/bin/env bash
2+
# Format semgrep JSON results into a GitHub-flavored markdown comment.
3+
set -euo pipefail
4+
5+
INPUT_FILE="$1"
6+
7+
RESULTS_COUNT=$(jq '.results | length' "$INPUT_FILE")
8+
9+
if [ "$RESULTS_COUNT" -eq 0 ]; then
10+
exit 0
11+
fi
12+
13+
echo "## Semgrep Findings"
14+
echo ""
15+
echo "**${RESULTS_COUNT}** finding(s) detected."
16+
echo ""
17+
echo "| Severity | Rule | File | Message |"
18+
echo "|----------|------|------|---------|"
19+
20+
jq -r --arg repo "$GITHUB_REPOSITORY" --arg sha "$GITHUB_SHA" '.results[] | {
21+
sev: .extra.severity,
22+
rule: (.check_id | split(".")[-1]),
23+
path: .path,
24+
line: .start.line,
25+
msg: (.extra.message | gsub("\n"; " ") | ltrimstr(" ") | rtrimstr(" ") | gsub("\\|"; "\\|") | gsub("`"; "\\`"))
26+
} | {
27+
icon: (if .sev == "CRITICAL" then "🔴"
28+
elif .sev == "HIGH" then "🟠"
29+
elif .sev == "MEDIUM" then "🟡"
30+
elif .sev == "LOW" then "🔵"
31+
elif .sev == "INFO" then "⚪"
32+
else "⚪" end),
33+
sev: .sev,
34+
rule: .rule,
35+
path: .path,
36+
line: .line,
37+
msg: .msg
38+
} | "| \(.icon) \(.sev) | `\(.rule)` | [`\(.path):\(.line)`](https://github.com/\($repo)/blob/\($sha)/\(.path)#L\(.line)) | \(.msg) |"' "$INPUT_FILE"

0 commit comments

Comments
 (0)