Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/missionmatch / tools/verify/secret-sweep/remote-scan.sh |
| Date | Not specified in source |
| Repository | Janady13/missionmatch |
| Revision | 720d47fae476b0d38477ae4401697d372b1ea2bc |
| Branch | feature/opportunity-to-outcome |
| Path or archive member | tools/verify/secret-sweep/remote-scan.sh |
| Original SHA-256 | b78c477bf61889a1a8ac32c10349c1db4e2c34e1a6d9a1773fe757a45f5cc3f6 |
| Git blob SHA | 229c6cafe4a4785bfee8913c28477a49fb63cdf0 |
| Reading-copy SHA-256 | b78c477bf61889a1a8ac32c10349c1db4e2c34e1a6d9a1773fe757a45f5cc3f6 |
Recovered source transcript
Download complete source text ↓81 lines · 3.6 KB
#!/usr/bin/env bash
#
# THE REMOTE HALF OF THE OUT OF REPO SWEEP. Staged onto a host and run there.
#
# IT NEVER PRINTS A MATCHED VALUE. Every finding is reported as KIND<tab>PATH<tab>LINE<tab>PATTERN_NAME and
# nothing else. grep is invoked with -c and -n only, never in a mode that echoes the line, because a sweep that
# prints what it finds converts a stored secret into a logged one. That is not a hypothetical: this build has
# already disclosed a live credential into a log this week through flag usage output, finding D14.
#
# Usage: remote-scan.sh <kind> <glob>
# kind script the file must REFERENCE credentials, never embed them
# kind unit same, plus a systemd specific check for inline Environment= secrets
# kind mode MODE ONLY. These files are the credential store and are supposed to contain values.
set -uo pipefail
KIND="${1:-}"
GLOB="${2:-}"
[ -z "$KIND" ] && { echo "usage: remote-scan.sh <kind> <glob>" >&2; exit 2; }
# The same shapes the repository sweep looks for, kept deliberately in step with it. A DSN carrying a password,
# an assignment whose right hand side looks like a secret, an inline sshpass, and a bearer or api key literal.
PATTERNS=(
"dsn_with_password|://[A-Za-z0-9_]+:[^@\"'[:space:]]{8,}@"
"password_assignment|(PASS|PASSWD|PASSWORD|SECRET|TOKEN|PEPPER)[A-Za-z_]*[=:][[:space:]]*[\"']?[A-Za-z0-9!@#%^&*_+.-]{10,}"
"inline_sshpass|sshpass[[:space:]]+-p[[:space:]]+[^[:space:]$]{6,}"
"api_key_literal|(sk-[A-Za-z0-9_-]{16,}|AKIA[0-9A-Z]{12,}|ghp_[A-Za-z0-9]{20,})"
)
# Placeholders are not secrets, and the list is short on purpose: every entry is a hole.
ALLOW='REDACTED|EXAMPLE|example\.(com|invalid|test)|PLACEHOLDER|CHANGE_?ME|your-|\$\{|\$\(|xxxxx|://user:password@|GOMEMLIMIT|MM_[A-Z_]*URL"?$|=\$'
findings=0
expand() { compgen -G "$GLOB" 2>/dev/null || true; }
case "$KIND" in
mode)
for f in $(expand); do
[ -f "$f" ] || continue
m=$(stat -c '%a' "$f" 2>/dev/null || stat -f '%Lp' "$f" 2>/dev/null)
o=$(stat -c '%U' "$f" 2>/dev/null || stat -f '%Su' "$f" 2>/dev/null)
# World or group readable is a finding on a file that holds credentials, whatever it holds.
case "$m" in
600|400|0600|0400) : ;;
*) echo -e "mode\t$f\t0\tcredential_file_mode_$m"; findings=$((findings+1)) ;;
esac
[ "$o" = "root" ] || { echo -e "mode\t$f\t0\tcredential_file_owner_$o"; findings=$((findings+1)); }
done
;;
script|unit)
for f in $(expand); do
[ -f "$f" ] || continue
for entry in "${PATTERNS[@]}"; do
name="${entry%%|*}"; rx="${entry#*|}"
# -n gives line numbers. The matched text is DISCARDED by cut before anything is printed.
while IFS= read -r ln; do
[ -z "$ln" ] && continue
num="${ln%%:*}"
body="${ln#*:}"
printf '%s' "$body" | grep -qE "$ALLOW" && continue
echo -e "$KIND\t$f\t$num\t$name"
findings=$((findings+1))
done < <(grep -nIE "$rx" "$f" 2>/dev/null | head -20)
done
if [ "$KIND" = "unit" ]; then
# A systemd unit should point at an EnvironmentFile, never inline a secret with Environment=.
while IFS= read -r ln; do
[ -z "$ln" ] && continue
num="${ln%%:*}"
echo -e "unit\t$f\t$num\tinline_environment_secret"
findings=$((findings+1))
done < <(grep -nIE '^[[:space:]]*Environment=[A-Z_]*(PASS|SECRET|TOKEN|KEY|PEPPER|DSN|URL)[A-Z_]*=.+' "$f" 2>/dev/null | grep -viE 'GOMEMLIMIT|GODEBUG' | head -10)
fi
done
;;
*)
echo "unknown kind $KIND" >&2; exit 2 ;;
esac
echo -e "SUMMARY\t$KIND\t$GLOB\t$findings"
exit 0