|
| 1 | +# SPDX-License-Identifier: PMPL-1.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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 |
| 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/{{OWNER}}/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/{{OWNER}}/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