Skip to content

Commit 384b790

Browse files
Jonathan D.A. Jewellclaude
andcommitted
chore(security): Add missing workflows and security policy
Added standard workflows for OpenSSF Scorecard compliance. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent f6053e6 commit 384b790

7 files changed

Lines changed: 435 additions & 0 deletions

File tree

.github/workflows/codeql.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# SPDX-License-Identifier: AGPL-3.0-or-later
2+
name: CodeQL Security Analysis
3+
4+
on:
5+
push:
6+
branches: [main, master]
7+
pull_request:
8+
branches: [main, master]
9+
schedule:
10+
- cron: '0 6 * * 1'
11+
12+
permissions: read-all
13+
14+
jobs:
15+
analyze:
16+
runs-on: ubuntu-latest
17+
permissions:
18+
contents: read
19+
security-events: write
20+
strategy:
21+
fail-fast: false
22+
matrix:
23+
include:
24+
- language: javascript-typescript
25+
build-mode: none
26+
27+
steps:
28+
- name: Checkout
29+
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
30+
31+
- name: Initialize CodeQL
32+
uses: github/codeql-action/init@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v3.28.1
33+
with:
34+
languages: ${{ matrix.language }}
35+
build-mode: ${{ matrix.build-mode }}
36+
37+
- name: Perform CodeQL Analysis
38+
uses: github/codeql-action/analyze@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v3.28.1
39+
with:
40+
category: "/language:${{ matrix.language }}"

.github/workflows/quality.yml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# SPDX-License-Identifier: AGPL-3.0-or-later
2+
name: Code Quality
3+
on: [push, pull_request]
4+
5+
6+
permissions: read-all
7+
8+
jobs:
9+
lint:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: read
13+
steps:
14+
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
15+
16+
- name: Check file permissions
17+
run: |
18+
find . -type f -perm /111 -name "*.sh" | head -10 || true
19+
20+
- name: Check for secrets
21+
uses: trufflesecurity/trufflehog@ef6e76c3c4023279497fab4721ffa071a722fd05 # v3.92.4
22+
with:
23+
path: ./
24+
base: ${{ github.event.pull_request.base.sha || github.event.before }}
25+
head: ${{ github.sha }}
26+
continue-on-error: true
27+
28+
- name: Check TODO/FIXME
29+
run: |
30+
echo "=== TODOs ==="
31+
grep -rn "TODO\|FIXME\|HACK\|XXX" --include="*.rs" --include="*.res" --include="*.py" --include="*.ex" . | head -20 || echo "None found"
32+
33+
- name: Check for large files
34+
run: |
35+
find . -type f -size +1M -not -path "./.git/*" | head -10 || echo "No large files"
36+
37+
- name: EditorConfig check
38+
uses: editorconfig-checker/action-editorconfig-checker@4b6cd6190d435e7e084fb35e36a096e98506f7b9 # v2.1.0
39+
continue-on-error: true
40+
41+
docs:
42+
runs-on: ubuntu-latest
43+
permissions:
44+
contents: read
45+
steps:
46+
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
47+
- name: Check documentation
48+
run: |
49+
MISSING=""
50+
[ ! -f "README.md" ] && [ ! -f "README.adoc" ] && MISSING="$MISSING README"
51+
[ ! -f "LICENSE" ] && [ ! -f "LICENSE.txt" ] && [ ! -f "LICENSE.md" ] && MISSING="$MISSING LICENSE"
52+
[ ! -f "CONTRIBUTING.md" ] && [ ! -f "CONTRIBUTING.adoc" ] && MISSING="$MISSING CONTRIBUTING"
53+
54+
if [ -n "$MISSING" ]; then
55+
echo "::warning::Missing docs:$MISSING"
56+
else
57+
echo "✅ Core documentation present"
58+
fi

.github/workflows/scorecard.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# SPDX-License-Identifier: AGPL-3.0-or-later
2+
name: OSSF Scorecard
3+
on:
4+
push:
5+
branches: [main, master]
6+
schedule:
7+
- cron: '0 4 * * *'
8+
workflow_dispatch:
9+
10+
permissions: read-all
11+
12+
jobs:
13+
analysis:
14+
runs-on: ubuntu-latest
15+
permissions:
16+
security-events: write
17+
id-token: write
18+
steps:
19+
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
20+
with:
21+
persist-credentials: false
22+
23+
- name: Run Scorecard
24+
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.3.1
25+
with:
26+
results_file: results.sarif
27+
results_format: sarif
28+
29+
- name: Upload results
30+
uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # v3.31.8
31+
with:
32+
sarif_file: results.sarif
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# SPDX-License-Identifier: AGPL-3.0-or-later
2+
name: Security Policy
3+
on: [push, pull_request]
4+
5+
permissions: read-all
6+
7+
jobs:
8+
check:
9+
runs-on: ubuntu-latest
10+
permissions:
11+
contents: read
12+
steps:
13+
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
14+
- name: Security checks
15+
run: |
16+
FAILED=false
17+
18+
# Block MD5/SHA1 for security (allow for checksums/caching)
19+
WEAK_CRYPTO=$(grep -rE 'md5\(|sha1\(' --include="*.py" --include="*.rb" --include="*.js" --include="*.ts" --include="*.go" --include="*.rs" . 2>/dev/null | grep -v 'checksum\|cache\|test\|spec' | head -5 || true)
20+
if [ -n "$WEAK_CRYPTO" ]; then
21+
echo "⚠️ Weak crypto (MD5/SHA1) detected. Use SHA256+ for security:"
22+
echo "$WEAK_CRYPTO"
23+
fi
24+
25+
# Block HTTP URLs (except localhost)
26+
HTTP_URLS=$(grep -rE 'http://[^l][^o][^c]' --include="*.py" --include="*.js" --include="*.ts" --include="*.go" --include="*.rs" --include="*.yaml" --include="*.yml" . 2>/dev/null | grep -v 'localhost\|127.0.0.1\|example\|test\|spec' | head -5 || true)
27+
if [ -n "$HTTP_URLS" ]; then
28+
echo "⚠️ HTTP URLs found. Use HTTPS:"
29+
echo "$HTTP_URLS"
30+
fi
31+
32+
# Block hardcoded secrets patterns
33+
SECRETS=$(grep -rEi '(api_key|apikey|secret_key|password)\s*[=:]\s*["\x27][A-Za-z0-9+/=]{20,}' --include="*.py" --include="*.js" --include="*.ts" --include="*.go" --include="*.rs" --include="*.env" . 2>/dev/null | grep -v 'example\|sample\|test\|mock\|placeholder' | head -3 || true)
34+
if [ -n "$SECRETS" ]; then
35+
echo "❌ Potential hardcoded secrets detected!"
36+
FAILED=true
37+
fi
38+
39+
if [ "$FAILED" = true ]; then
40+
exit 1
41+
fi
42+
43+
echo "✅ Security policy check passed"
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# SPDX-License-Identifier: AGPL-3.0-or-later
2+
name: Well-Known Standards (RFC 9116 + RSR)
3+
on:
4+
push:
5+
branches: [main, master]
6+
paths:
7+
- '.well-known/**'
8+
- 'security.txt'
9+
pull_request:
10+
paths:
11+
- '.well-known/**'
12+
schedule:
13+
# Weekly expiry check
14+
- cron: '0 9 * * *'
15+
workflow_dispatch:
16+
17+
18+
permissions: read-all
19+
20+
jobs:
21+
validate:
22+
runs-on: ubuntu-latest
23+
permissions:
24+
contents: read
25+
steps:
26+
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
27+
28+
- name: RFC 9116 security.txt validation
29+
run: |
30+
SECTXT=""
31+
[ -f ".well-known/security.txt" ] && SECTXT=".well-known/security.txt"
32+
[ -f "security.txt" ] && SECTXT="security.txt"
33+
34+
if [ -z "$SECTXT" ]; then
35+
echo "::warning::No security.txt found. See https://github.com/hyperpolymath/well-known-ecosystem"
36+
exit 0
37+
fi
38+
39+
# Required: Contact
40+
grep -q "^Contact:" "$SECTXT" || { echo "::error::Missing Contact field"; exit 1; }
41+
42+
# Required: Expires
43+
if ! grep -q "^Expires:" "$SECTXT"; then
44+
echo "::error::Missing Expires field"
45+
exit 1
46+
fi
47+
48+
# Check expiry
49+
EXPIRES=$(grep "^Expires:" "$SECTXT" | cut -d: -f2- | tr -d ' ' | head -1)
50+
if date -d "$EXPIRES" > /dev/null 2>&1; then
51+
DAYS=$(( ($(date -d "$EXPIRES" +%s) - $(date +%s)) / 86400 ))
52+
if [ $DAYS -lt 0 ]; then
53+
echo "::error::security.txt EXPIRED"
54+
exit 1
55+
elif [ $DAYS -lt 30 ]; then
56+
echo "::warning::security.txt expires in $DAYS days"
57+
else
58+
echo "✅ security.txt valid ($DAYS days)"
59+
fi
60+
fi
61+
62+
- name: RSR well-known compliance
63+
run: |
64+
MISSING=""
65+
[ ! -f ".well-known/security.txt" ] && [ ! -f "security.txt" ] && MISSING="$MISSING security.txt"
66+
[ ! -f ".well-known/ai.txt" ] && MISSING="$MISSING ai.txt"
67+
[ ! -f ".well-known/humans.txt" ] && MISSING="$MISSING humans.txt"
68+
69+
if [ -n "$MISSING" ]; then
70+
echo "::warning::Missing RSR recommended files:$MISSING"
71+
echo "Reference: https://github.com/hyperpolymath/well-known-ecosystem/.well-known/"
72+
else
73+
echo "✅ RSR well-known compliant"
74+
fi
75+
76+
- name: Mixed content check
77+
run: |
78+
MIXED=$(grep -rE 'src="http://|href="http://' --include="*.html" --include="*.htm" . 2>/dev/null | grep -vE 'localhost|127\.0\.0\.1|example\.com' | head -5 || true)
79+
if [ -n "$MIXED" ]; then
80+
echo "::error::Mixed content (HTTP in HTML)"
81+
echo "$MIXED"
82+
exit 1
83+
fi
84+
echo "✅ No mixed content"
85+
86+
- name: DNS security records check
87+
if: hashFiles('CNAME') != ''
88+
run: |
89+
DOMAIN=$(cat CNAME 2>/dev/null | tr -d '\n')
90+
if [ -n "$DOMAIN" ]; then
91+
echo "Checking DNS for $DOMAIN..."
92+
# CAA record
93+
dig +short CAA "$DOMAIN" | grep -q "issue" && echo "✅ CAA record" || echo "::warning::No CAA record"
94+
# DNSSEC
95+
dig +dnssec +short "$DOMAIN" | grep -q "RRSIG" && echo "✅ DNSSEC" || echo "::warning::No DNSSEC"
96+
fi

0 commit comments

Comments
 (0)