Skip to content

chore(security): untrack docs.local and guard against re-adding it - #40

Merged
EtanHey merged 2 commits into
masterfrom
guard/untrack-docslocal
Sep 1, 2026
Merged

chore(security): untrack docs.local and guard against re-adding it#40
EtanHey merged 2 commits into
masterfrom
guard/untrack-docslocal

Conversation

@EtanHey

@EtanHey EtanHey commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Why

docs.local/convex-auth-patterns.md is tracked and published in this public repo. .gitignore did not stop it — git add -f overrides .gitignore, and once a path is tracked .gitignore no longer applies to it at all.

On voicelayer that same gap published verbatim dictation transcripts and personal-data screenshots, and forced a full history rewrite plus a repo rebuild on 2026-09-01. This closes the gap here.

No history rewrite needed — verified

git rev-list --objects --all | grep docs.local/

returns exactly one path across all refs: docs.local/convex-auth-patterns.md. Contents reviewed — generic Convex authentication guidance (use ctx.auth.getUserIdentity(), never take a user id as an argument). No secrets, no personal data. The blob can stay in history safely.

READ BEFORE YOU MERGE — this deletes the file in other checkouts

git rm --cached keeps the file only in the worktree that ran it. Every other checkout that pulls this merge gets the path DELETED from its working tree. Normal git, recoverable, but it looks like data loss.

Backup already taken outside git (926 files, verified):

/Users/etanheyman/backups/songscript-docs.local-2026-09-01/

If a checkout loses it after pulling:

git checkout <merge-sha>^1 -- docs.local/ && git reset HEAD docs.local/

After merging, check every checkout (git worktree list) — there is a second one at ~/.config/superpowers/worktrees/songscript/auth-fixes. Single-worktree green is false green.

The guard

scripts/guard-no-docslocal.sh exits 1 if git ls-files docs.local is non-empty. Wired into:

  • .husky/pre-commit
  • a new docs.local guard CI workflow, so a PR is blocked even where the local hook is unwired

Pre-commit on this branch ran clean: 199 tests passed, tsc clean, guard exit 0.

Co-Authored-By: Claude Fable 5 noreply@anthropic.com

Note

Untrack docs.local/ files and add guard to prevent re-adding them

  • Deletes the tracked docs.local/convex-auth-patterns.md file.
  • Adds scripts/guard-no-docslocal.sh, a bash script that uses git ls-files to detect tracked docs.local/ paths and exits 1 with remediation instructions when any are found.
  • Hooks the guard into both CI (a new GitHub Actions workflow) and local commits via .husky/pre-commit, which must invoke it with bash (not sh) due to pipefail usage.
  • Adds a Vitest suite in src/tests/guard-no-docslocal.test.ts covering clean-repo, force-added, and post-git rm --cached scenarios, plus an assertion that the pre-commit hook uses bash.
  • Risk: any future commit that force-adds a docs.local/ path will be blocked by both the pre-commit hook and CI; reviewers should ensure the guard script remains executable and the workflow runs on the intended branches.

Macroscope summarized 7d27208.

docs.local/convex-auth-patterns.md was tracked and published in this
public repo. The content is generic Convex auth documentation with no
secrets, and it is the only path that has ever existed under docs.local
in this repo's history, so no history rewrite is needed.

.gitignore alone does not hold: `git add -f` overrides it, and once a
path is tracked .gitignore no longer applies to it. On voicelayer that
gap published dictation transcripts and personal-data screenshots and
forced a full history rewrite plus repo rebuild on 2026-09-01.

Adds scripts/guard-no-docslocal.sh, wired into the husky pre-commit hook
and a CI job, so a tracked docs.local path cannot come back.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Sep 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
songscript Ready Ready Preview Sep 1, 2026 3:02pm UTC

@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Team

Run ID: bc8aa4e8-7376-43da-82b0-0d04a9e32016


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Prevent docs.local files from being tracked

🐞 Bug fix ⚙️ Configuration changes 🕐 10-20 Minutes

Grey Divider

AI Description

• Stops publishing local scratch documentation by removing docs.local from Git tracking.
• Adds a reusable guard that rejects any tracked docs.local path.
• Enforces the guard during pre-commit checks and pull-request CI.
Diagram

graph TD
  A["Pre-commit hook"] --> C["Guard script"] --> D{"Tracked docs.local?"} -->|Yes| E["Block change"]
  B["GitHub Actions"] --> C
  D -->|No| F["Pass check"]
Loading
High-Level Assessment

The shared guard with both local and CI enforcement is the appropriate defense-in-depth approach. Relying only on .gitignore would not prevent forced additions or protect already tracked paths, while duplicating guard logic in each integration would increase drift.

Files changed (3) +90 / -0

Bug fix (1) +65 / -0
guard-no-docslocal.shDetect and reject tracked docs.local paths +65/-0

Detect and reject tracked docs.local paths

• Adds a Git-index guard that succeeds only when docs.local has no tracked paths. Failure output lists offending files and provides backup, untracking, and checkout-recovery instructions.

scripts/guard-no-docslocal.sh

Other (2) +25 / -0
docs-local-guard.ymlEnforce the docs.local guard in GitHub Actions +20/-0

Enforce the docs.local guard in GitHub Actions

• Adds a least-privilege workflow that checks pull requests and pushes to master. It runs the shared guard after checking out the repository without persisted credentials.

.github/workflows/docs-local-guard.yml

pre-commitRun the docs.local guard before commits +5/-0

Run the docs.local guard before commits

• Extends the existing pre-commit checks to invoke the guard when its script is present, blocking commits that contain tracked docs.local paths.

.husky/pre-commit

Comment thread .husky/pre-commit Outdated
@qodo-code-review

qodo-code-review Bot commented Sep 1, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (1) 📜 Skill insights (0)

Grey Divider


Action required

1. Guard utility lacks tests 📘 Rule violation ☼ Reliability
Description
The new guard-no-docslocal.sh utility has no corresponding automated test covering its clean and
blocked outcomes. Regressions in the repository-protection logic could therefore pass unnoticed.
Code

scripts/guard-no-docslocal.sh[R19-23]

+tracked="$(git ls-files -- 'docs.local' 'docs.local/**' 2>/dev/null || true)"
+
+if [ -z "$tracked" ]; then
+  echo "docs.local guard: OK — 0 tracked paths"
+  exit 0
Evidence
PR Compliance ID 2 requires every newly introduced helper or utility to have a corresponding
automated test. The cited lines implement the guard's primary branching behavior, while no
corresponding test file is introduced or present for this new utility.

CLAUDE.md: New Helpers and Utilities Must Have Unit Tests
scripts/guard-no-docslocal.sh[19-24]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new `guard-no-docslocal.sh` utility lacks an automated unit test.

## Issue Context
Add a Vitest test that runs the script against temporary Git repositories and verifies both exit paths: success when `docs.local` is untracked and failure when a path beneath it is tracked.

## Fix Focus Areas
- scripts/guard-no-docslocal.sh[19-24]
- src/utils/guard-no-docslocal.test.ts[1-1]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Bash guard invoked with sh ✓ Resolved 🐞 Bug ☼ Reliability
Description
The pre-commit hook invokes the guard using sh, bypassing its Bash shebang even though `set -o
pipefail requires Bash and fails under POSIX shells such as dash`. On affected systems, every
commit is rejected before the guard examines tracked files.
Code

.husky/pre-commit[R21-22]

+if [ -f scripts/guard-no-docslocal.sh ]; then
+  sh scripts/guard-no-docslocal.sh || exit 1
Evidence
The hook explicitly invokes the script with sh, while the script declares Bash and enables the
Bash-specific pipefail option. Therefore the shebang cannot select Bash and incompatible /bin/sh
implementations terminate before reaching git ls-files.

.husky/pre-commit[20-23]
scripts/guard-no-docslocal.sh[1-19]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The pre-commit hook runs a Bash-specific guard through `sh`. Shells without `pipefail` terminate at the guard's option setup, blocking all commits.

## Issue Context
The CI workflow already invokes the same script with Bash. Make the local hook use the script's declared interpreter as well.

## Fix Focus Areas
- .husky/pre-commit[20-23]
- scripts/guard-no-docslocal.sh[1-17]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Git errors pass guard ✗ Dismissed 🐞 Bug ⛨ Security
Description
The || true converts every git ls-files failure into empty output, after which the guard prints
a clean result and exits successfully. An unreadable index, invalid repository context, or other Git
failure therefore bypasses the security check instead of failing closed.
Code

scripts/guard-no-docslocal.sh[19]

+tracked="$(git ls-files -- 'docs.local' 'docs.local/**' 2>/dev/null || true)"
Evidence
Line 19 suppresses stderr and forces a successful substitution regardless of git ls-files status.
Lines 21-23 then interpret the resulting empty value as a verified clean index and return success.

scripts/guard-no-docslocal.sh[17-23]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The guard suppresses all failures from `git ls-files` and interprets them as proof that no `docs.local` paths are tracked.

## Issue Context
Capture the command status separately from its output. Emit the underlying error and exit nonzero when Git cannot inspect the index; only evaluate empty output after a successful command.

## Fix Focus Areas
- scripts/guard-no-docslocal.sh[17-23]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: ⚖️ Balanced: This security-related change modifies a shell guard, Git hooks, and CI enforcement with repository-wide consequences, warranting a complete single-pass review.

Grey Divider

Tip of the day
💡 Did you know, you can describe a rule in plain language on the Rules page and Qodo drafts it for you

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment on lines +19 to +23
tracked="$(git ls-files -- 'docs.local' 'docs.local/**' 2>/dev/null || true)"

if [ -z "$tracked" ]; then
echo "docs.local guard: OK — 0 tracked paths"
exit 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Guard utility lacks tests 📘 Rule violation ☼ Reliability

The new guard-no-docslocal.sh utility has no corresponding automated test covering its clean and
blocked outcomes. Regressions in the repository-protection logic could therefore pass unnoticed.
Agent Prompt
## Issue description
The new `guard-no-docslocal.sh` utility lacks an automated unit test.

## Issue Context
Add a Vitest test that runs the script against temporary Git repositories and verifies both exit paths: success when `docs.local` is untracked and failure when a path beneath it is tracked.

## Fix Focus Areas
- scripts/guard-no-docslocal.sh[19-24]
- src/utils/guard-no-docslocal.test.ts[1-1]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7d27208 — added `src/tests/guard-no-docslocal.test.ts` (Vitest, throwaway git repos via `mkdtemp`), covering both branches you cited plus two more:

  1. nothing tracked → exit 0
  2. `git add -f docs.local/leaked.md` → exit 1, offending path listed
  3. untracked again → exit 0, and the file is still on disk (the untrack must not delete local data)
  4. the pre-commit hook invokes `bash`, not `sh` — regression test for the dash bug in the sibling thread

4 tests, green; full suite 203 passed, tsc clean.

Comment thread .husky/pre-commit Outdated
Comment thread scripts/guard-no-docslocal.sh
Two findings from the review on #40.

1. BUG — the hook ran `sh scripts/guard-no-docslocal.sh`. The guard uses
   `set -o pipefail`, which POSIX shells reject. On Ubuntu/Debian /bin/sh
   is dash, so the guard aborted with "Illegal option -o pipefail" and,
   via `|| exit 1`, rejected EVERY commit — even with nothing tracked.
   Reproduced locally: `dash scripts/guard-no-docslocal.sh` exits 2.
   macOS hid it because /bin/sh there is bash 3.2 in POSIX mode.

2. RULE VIOLATION — new helpers need unit tests (CLAUDE.md).
   Adds src/__tests__/guard-no-docslocal.test.ts covering both exit
   paths against throwaway git repos:
     - nothing tracked                -> exit 0
     - `git add -f` a docs.local path -> exit 1, path listed
     - untracked again                -> exit 0, file still on disk
     - hook invokes bash, not sh      -> regression test for finding 1

   Verified the regression test actually catches it: reverting the hook
   to `sh` turns that test red, restoring `bash` turns it green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@EtanHey
EtanHey merged commit 743cdd9 into master Sep 1, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant