diff --git a/.github/workflows/guard-no-private-cloud.yml b/.github/workflows/guard-no-private-cloud.yml index 515e959..739074f 100644 --- a/.github/workflows/guard-no-private-cloud.yml +++ b/.github/workflows/guard-no-private-cloud.yml @@ -1,8 +1,11 @@ name: Guard — No Private Cloud Code -# Fails if private Vestige Cloud *service* code (billing, sync-key/namespace -# mapping, Lemon Squeezy webhooks, transactional email) ever lands in this -# public repo. The public cloud *client* is allowed and does not trip this. +# Fails if private content ever lands in this public repo: +# 1. Private Vestige Cloud *service* code (billing, sync-key/namespace +# mapping, Lemon Squeezy webhooks, transactional email). The public cloud +# *client* is allowed and does not trip this. +# 2. Private local paths, usernames, or machine names (e.g. a real +# /home// worktree). Placeholder paths like /Users/you/ are allowed. on: push: branches: [main, feat/cloud-sync-mvp] @@ -23,3 +26,6 @@ jobs: - name: Scan for private cloud service markers run: ./scripts/check-no-private-cloud.sh + + - name: Scan for private paths, usernames, and machine names + run: ./scripts/check-no-private-paths.sh diff --git a/CLAUDE.md b/CLAUDE.md index 7388599..af4f61a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -87,3 +87,9 @@ Do not commit private absolute paths, local agent memory paths, unpublished planning files, real credentials, personal operating notes, or private repo locations. Example environment variables in docs must be empty placeholders or obviously fake examples. + +Two CI guards enforce this on every push and PR (see +`.github/workflows/guard-no-private-cloud.yml`): +`scripts/check-no-private-cloud.sh` blocks private cloud-service code, and +`scripts/check-no-private-paths.sh` blocks real local paths, usernames, and +machine names (placeholder paths like `/Users/you/...` or `~/` are fine). diff --git a/scripts/check-no-private-paths.sh b/scripts/check-no-private-paths.sh new file mode 100755 index 0000000..05e9b93 --- /dev/null +++ b/scripts/check-no-private-paths.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# check-no-private-paths.sh — Fail if a private local filesystem path, personal +# username, or machine name leaks into this public repo. +# +# Background: some planning docs once carried real absolute paths such as +# /home//prppl/vestige-phase2/... +# plus a developer machine name. Those are metadata leaks (username, folder +# layout, host) — not credentials, but exactly the kind of thing the public-repo +# hygiene rules say to keep out. They were removed; this guard stops them (and +# anything like them) from creeping back in. +# +# It scans only tracked files (git grep) for two classes of leak: +# 1. Known private tokens — exact handles / private worktree names. +# 2. Absolute home paths that reveal a REAL username, while allowlisting the +# obvious placeholders the docs legitimately use (e.g. /Users/you/...). +# +# If a match is a genuine false positive, add the placeholder to ALLOW_USERS or +# refine the pattern below — do not weaken it to a no-op. +set -u + +cd "$(git rev-parse --show-toplevel)" || { + echo "check-no-private-paths: not inside a git repository" >&2 + exit 2 +} + +# 1) Exact private tokens that must never appear anywhere in the repo. +BLOCK_TOKENS=( + 'delandtj' + 'prppl' +) + +# 2) Placeholder usernames the docs legitimately use in example home paths. +# A /home// or /Users// whose is NOT one of these trips. +ALLOW_USERS='you|user|username|name|me|example|runner|youruser|YOU|USER|USERNAME|YOURNAME' + +# This guard and its workflow name the tokens/patterns, so exclude them. +EXCLUDES=( + ':(exclude)scripts/check-no-private-paths.sh' + ':(exclude).github/workflows/guard-no-private-cloud.yml' +) + +violations=0 +report="" + +# --- 1. exact private tokens --------------------------------------------------- +for tok in "${BLOCK_TOKENS[@]}"; do + if hits=$(git grep -Ini -F -e "$tok" -- "${EXCLUDES[@]}" 2>/dev/null); then + if [ -n "$hits" ]; then + violations=$((violations + 1)) + report+=$'\n'" ✗ private token: \"$tok\""$'\n' + report+="$(printf '%s\n' "$hits" | sed 's/^/ /')"$'\n' + fi + fi +done + +# --- 2. absolute home paths that reveal a real username ------------------------ +# Candidate lines contain /home// or /Users// or C:\Users\\; then drop +# the lines whose username segment is an allowlisted placeholder. +if cand=$(git grep -nIE '(/home/|/Users/|[Cc]:\\Users\\)[A-Za-z0-9_.-]+[/\\]' \ + -- "${EXCLUDES[@]}" 2>/dev/null); then + leaks=$(printf '%s\n' "$cand" \ + | grep -vE "(/home/|/Users/|[Cc]:\\\\Users\\\\)(${ALLOW_USERS})[/\\]" || true) + if [ -n "$leaks" ]; then + violations=$((violations + 1)) + report+=$'\n'" ✗ private home path (real username?):"$'\n' + report+="$(printf '%s\n' "$leaks" | sed 's/^/ /')"$'\n' + fi +fi + +if [ "$violations" -ne 0 ]; then + echo "════════════════════════════════════════════════════════════════════" + echo " PRIVATE PATH / USERNAME / MACHINE NAME DETECTED IN PUBLIC REPO" + echo "════════════════════════════════════════════════════════════════════" + echo "$report" + echo "════════════════════════════════════════════════════════════════════" + echo " Real local paths, usernames, and host names must not be committed." + echo " Use a placeholder (e.g. /Users/you/... or ~/) instead. If a match is" + echo " a false positive, extend ALLOW_USERS in scripts/check-no-private-paths.sh." + echo "════════════════════════════════════════════════════════════════════" + exit 1 +fi + +echo "check-no-private-paths: OK — no private paths, usernames, or host names in public repo" +exit 0