# This workflow is managed by gh actions-lock. # SPDX-License-Identifier: MPL-2.0 name: Well-Known Standards (RFC 9116 + RSR) on: push: branches: [main, master] paths: - 'www/.well-known/**' - 'www/tests/**' - 'www/schemas/**' - '.well-known/**' # legacy location — migration window (issue #53) - 'security.txt' pull_request: paths: - 'www/.well-known/**' - 'www/tests/**' - 'www/schemas/**' - '.well-known/**' # legacy location — migration window (issue #53) schedule: # Weekly expiry check (Mondays 09:00 UTC) - cron: '0 9 * * 1' workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true permissions: contents: read jobs: validate: runs-on: ubuntu-24.04 timeout-minutes: 15 permissions: contents: read steps: - uses: actions/checkout@v7.0.1 - name: RFC 9116 security.txt validation run: | SECTXT="" if [ -f "www/.well-known/security.txt" ]; then SECTXT="www/.well-known/security.txt" elif [ -f ".well-known/security.txt" ]; then SECTXT=".well-known/security.txt" echo "::warning::security.txt at legacy root .well-known/ — canonical location is www/.well-known/ (run scripts/migrate-wellknown-to-www.sh)" elif [ -f "security.txt" ]; then SECTXT="security.txt" echo "::warning::security.txt at repository root — canonical location is www/.well-known/ (run scripts/migrate-wellknown-to-www.sh)" fi if [ -z "$SECTXT" ]; then echo "::error::No security.txt found — required for OpenSSF Best Practices. See https://github.com/hyperpolymath/well-known-ecosystem" exit 1 fi # Required: Contact grep -q "^Contact:" "$SECTXT" || { echo "::error::Missing Contact field"; exit 1; } # Required: Expires if ! grep -q "^Expires:" "$SECTXT"; then echo "::error::Missing Expires field" exit 1 fi # Check expiry EXPIRES=$(grep "^Expires:" "$SECTXT" | cut -d: -f2- | tr -d ' ' | head -1) if date -d "$EXPIRES" > /dev/null 2>&1; then DAYS=$(( ($(date -d "$EXPIRES" +%s) - $(date +%s)) / 86400 )) if [ $DAYS -lt 0 ]; then echo "::error::security.txt EXPIRED" exit 1 elif [ $DAYS -lt 30 ]; then echo "::warning::security.txt expires in $DAYS days" else echo "✅ security.txt valid ($DAYS days)" fi fi - name: RSR well-known compliance run: | MISSING="" LEGACY="" for f in security.txt ai.txt humans.txt; do if [ -f "www/.well-known/$f" ]; then : elif [ -f ".well-known/$f" ] || { [ "$f" = security.txt ] && [ -f security.txt ]; }; then LEGACY="$LEGACY $f" else MISSING="$MISSING $f" fi done if [ -n "$LEGACY" ]; then echo "::warning::legacy .well-known location (canonical is www/.well-known/):$LEGACY — run scripts/migrate-wellknown-to-www.sh" fi if [ -n "$MISSING" ]; then echo "::warning::Missing RSR recommended files:$MISSING" echo "Reference: https://github.com/hyperpolymath/well-known-ecosystem/.well-known/" else echo "✅ RSR well-known compliant" fi # Stage 5 (#119) sweeps ~270 repositories. #106 claimed CI "drives the # migrator"; until now it only printed advice, so nothing verified that a # repo would actually survive the migration. This step runs the real # migrator and turns every conflict into an annotation. # # --dry-run because this job holds contents: read and cannot push: the # point is to surface divergence early in the migration window, not to # mutate a checkout that will be thrown away. The sweep driver # (scripts/sweep-wellknown.sh) is what performs the real migration. - name: Legacy root .well-known/ migration report run: | if [ ! -d .well-known ]; then echo "No root .well-known/ — nothing to migrate." exit 0 fi if [ ! -f scripts/migrate-wellknown-to-www.sh ]; then echo "::warning::root .well-known/ present but no migrator — template is behind #53 stage 5." exit 0 fi bash scripts/migrate-wellknown-to-www.sh --dry-run --report wellknown-migration.tsv echo "--- planned migration ---" cat wellknown-migration.tsv # Conflicts are warnings, not errors, for the duration of the # migration window (#106): a repo must not go red for a condition # the sweep exists to fix. It must not be silent either. while IFS=$'\t' read -r action path detail; do case "$action" in quarantined|conflict|left) echo "::warning title=well-known migration::$action $path — $detail" ;; esac done < <(tail -n +2 wellknown-migration.tsv) - name: www bundle self-test run: | if [ -d www/tests ]; then bash www/tests/run-all.sh else echo "www/tests absent — site-operations bundle not present; skipping." fi - name: Mixed content check run: | 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) if [ -n "$MIXED" ]; then echo "::error::Mixed content (HTTP in HTML)" echo "$MIXED" exit 1 fi echo "✅ No mixed content" - name: DNS security records check if: hashFiles('CNAME') != '' run: | DOMAIN=$(cat CNAME 2>/dev/null | tr -d '\n') if [ -n "$DOMAIN" ]; then echo "Checking DNS for $DOMAIN..." # CAA record dig +short CAA "$DOMAIN" | grep -q "issue" && echo "✅ CAA record" || echo "::warning::No CAA record" # DNSSEC dig +dnssec +short "$DOMAIN" | grep -q "RRSIG" && echo "✅ DNSSEC" || echo "::warning::No DNSSEC" fi