Skip to content

Commit 02fe3f2

Browse files
committed
ci: add profile metadata validator + secret scanner workflows
1 parent 65e4a07 commit 02fe3f2

2 files changed

Lines changed: 135 additions & 0 deletions

File tree

.github/workflows/scan-secrets.yml

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
name: Scan for leaked secrets
2+
3+
on:
4+
push:
5+
branches: [main, master]
6+
workflow_dispatch:
7+
schedule:
8+
# Weekly Sunday 03:00 UTC scan (your repos are quiet on weekends)
9+
- cron: "0 3 * * 0"
10+
11+
permissions:
12+
contents: read
13+
issues: write # for weekly audit issue creation
14+
actions: read
15+
16+
jobs:
17+
gitleaks:
18+
name: gitleaks (gate)
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout
22+
uses: actions/checkout@v4
23+
with:
24+
fetch-depth: 0
25+
26+
- name: gitleaks
27+
uses: gitleaks/gitleaks-action@v2
28+
env:
29+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
30+
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
31+
# Tuned to your signal shape: prose READMEs + small CLI/Python repos
32+
GITLEAKS_CONFIG: |
33+
[extend]
34+
useDefault = true
35+
[allowlist]
36+
paths = [
37+
'''**/*.lock''',
38+
'''**/*.png''',
39+
'''**/*.jpg''',
40+
'''**/*.svg''',
41+
'''LICENSE''',
42+
'''LICENSE.md''',
43+
'''LICENSE.txt'''
44+
]
45+
description = "Personal noise allowlist: lockfiles + binaries + license files only"
46+
47+
trufflehog-audit:
48+
name: TruffleHog (weekly deep audit)
49+
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
50+
runs-on: ubuntu-latest
51+
steps:
52+
- name: Checkout
53+
uses: actions/checkout@v4
54+
with:
55+
fetch-depth: 0
56+
57+
- name: TruffleHog (verified only)
58+
uses: trufflesecurity/trufflehog@main
59+
with:
60+
extra_args: --only-verified --no-banner --print-estimates=False
61+
62+
- name: Open audit issue if anything verified
63+
if: steps.trufflehog.outputs.verified_secrets > 0
64+
uses: actions/github-script@v7
65+
with:
66+
script: |
67+
await github.rest.issues.create({
68+
owner: context.repo.owner,
69+
repo: context.repo.repo,
70+
title: `🔐 TruffleHog weekly audit: ${context.runId}`,
71+
body: `Verified secret(s) detected by TruffleHog.\n\nRun: ${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}\n\nTriage immediately.`
72+
});
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: Validate profile metadata
2+
3+
on:
4+
push:
5+
branches: [main, master]
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: read
10+
11+
jobs:
12+
metadata-check:
13+
name: Description + topics + leak guard
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Fetch repo metadata via API
17+
env:
18+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
19+
REPO: ${{ github.repository }}
20+
run: |
21+
set -euo pipefail
22+
META=$(gh api "repos/${REPO}" --jq '{description: .description, topics: [.topics[]]}')
23+
echo "::group::Fetched metadata"
24+
echo "${META}"
25+
echo "::endgroup::"
26+
27+
DESC=$(echo "${META}" | jq -r '.description // ""')
28+
TOPIC_COUNT=$(echo "${META}" | jq -r '.topics | length')
29+
30+
# Description checks
31+
if [[ -z "${DESC}" || "${DESC}" == "null" ]]; then
32+
echo "::error::Description is empty. Set one via: gh repo edit <owner>/<repo> --description '...'"
33+
exit 1
34+
fi
35+
if [[ ${#DESC} -lt 20 ]]; then
36+
echo "::error::Description too short (${#DESC} chars, min 20): '${DESC}'"
37+
exit 1
38+
fi
39+
if [[ ! "${DESC}" =~ ^[A-Z] ]]; then
40+
echo "::error::Description must start with a capital letter: '${DESC}'"
41+
exit 1
42+
fi
43+
44+
# Topic checks
45+
if (( TOPIC_COUNT < 3 )); then
46+
echo "::error::Need at least 3 topics (have ${TOPIC_COUNT}). Add via: gh repo edit <owner>/<repo> --add-topic foo"
47+
exit 1
48+
fi
49+
50+
# Internal-leak guard: block accidental exposure of internal/corporate language
51+
LEAKS="company|bedrock|corporate|corp\\.|sanitized fork of a private|internal tool|not for distribution|do not share"
52+
if [[ "${DESC,,}" =~ ${LEAKS} ]]; then
53+
echo "::error::Description contains internal-leak phrase: '${DESC}'"
54+
exit 1
55+
fi
56+
for t in $(echo "${META}" | jq -r '.topics[]'); do
57+
if [[ "${t,,}" =~ ${LEAKS} ]]; then
58+
echo "::error::Topic '${t}' matches internal-leak pattern"
59+
exit 1
60+
fi
61+
done
62+
63+
echo "OK: description + topics passed all checks."

0 commit comments

Comments
 (0)