| 1 | #!/usr/bin/env bash |
| 2 | # SPDX-License-Identifier: AGPL-3.0-or-later |
| 3 | # |
| 4 | # Fail when source code emits log lines containing token-prefix |
| 5 | # patterns that would leak credentials into operator logs. The |
| 6 | # canonical bad shape: |
| 7 | # |
| 8 | # logger.Info("got authorization header", "auth", r.Header.Get("Authorization")) |
| 9 | # |
| 10 | # Even when the value happens to be empty, the format string itself |
| 11 | # is a smell — anyone who edits the line to format the value back in |
| 12 | # will silently start logging the secret. |
| 13 | # |
| 14 | # Patterns we reject (case-insensitive substring match in *.go): |
| 15 | # - "shithub_pat_" — PAT token prefix |
| 16 | # - "otpauth://" — TOTP URI containing the secret |
| 17 | # - "Authorization:" — header literal that suggests dumping headers |
| 18 | # |
| 19 | # Carve-outs (whitelisted paths) live in EXEMPTS below. The S08 PAT |
| 20 | # redactor for git-clone URLs and the auth-flow tests are the legit |
| 21 | # call sites; everything else is suspicious. |
| 22 | # |
| 23 | # Run as part of `make ci`. |
| 24 | |
| 25 | set -euo pipefail |
| 26 | |
| 27 | cd "$(git rev-parse --show-toplevel)" |
| 28 | |
| 29 | EXEMPTS=( |
| 30 | # S08 redactor explicitly handles the prefix to strip it from URLs. |
| 31 | "internal/auth/pat/" |
| 32 | # Log redactor IS the canonical handler for the secret patterns. |
| 33 | "internal/infra/log/log.go" |
| 34 | # TOTP package builds the otpauth:// URI for the QR provisioning |
| 35 | # code — never logs it, just constructs. |
| 36 | "internal/auth/totp/totp.go" |
| 37 | # PAT middleware parses (not logs) the Authorization header. |
| 38 | "internal/web/middleware/pat.go" |
| 39 | # Git-HTTP auth handler mentions the prefix in doc comments. |
| 40 | "internal/web/handlers/githttp/auth.go" |
| 41 | # Tests legitimately mention these strings in fixtures. |
| 42 | "_test.go" |
| 43 | # The lint script itself documents the patterns. |
| 44 | "scripts/lint-secret-logs.sh" |
| 45 | ) |
| 46 | |
| 47 | RE='shithub_pat_|otpauth://|Authorization:' |
| 48 | |
| 49 | # grep -EIn: extended regex, ignore binary, show line numbers. |
| 50 | # Search only shithub-owned trees — .refs/ vendored repos are docs. |
| 51 | matches=$(grep -RIEn --include='*.go' "$RE" cmd internal scripts 2>/dev/null || true) |
| 52 | |
| 53 | filtered="" |
| 54 | while IFS= read -r line; do |
| 55 | [ -z "$line" ] && continue |
| 56 | skip=false |
| 57 | for pat in "${EXEMPTS[@]}"; do |
| 58 | if [[ "$line" == *"$pat"* ]]; then |
| 59 | skip=true |
| 60 | break |
| 61 | fi |
| 62 | done |
| 63 | $skip || filtered="${filtered}${line}"$'\n' |
| 64 | done <<< "$matches" |
| 65 | |
| 66 | if [ -n "${filtered// /}" ]; then |
| 67 | echo "lint-secret-logs: token-prefix patterns found in non-exempt source:" >&2 |
| 68 | echo "$filtered" >&2 |
| 69 | echo >&2 |
| 70 | echo "If this is a legitimate use, add the file to EXEMPTS in scripts/lint-secret-logs.sh." >&2 |
| 71 | exit 1 |
| 72 | fi |
| 73 | |
| 74 | echo "lint-secret-logs: ok" |