Closes out the fix-auth-bypass convoy (PRs #6–#11, merged through
1629afb) on the docs side. Code already on main; this PR is docs only.
Updates:
AGENTS.md
- §1 auth bullet refreshed (auth-secret SoT, 24h TTL, no synthetic
admin, login/register rate limit)
- §3 conventions point at lib/auth-secret.js + lib/rate-limit.js
- §4 gotchas #2/#3/#5 converted to "Resolved" notes in place
(NOT renumbered, to preserve cross-references)
- new #12 documents the KV_REST_API_* env-var convention
- §5 setup list adds the rate-limit env vars
- §6 testing rewritten for Vitest (16 unit tests, blocking CI gate)
.cursor/rules/auth-and-permissions.mdc
- canonical-surface table gains lib/auth-secret.js + lib/rate-limit.js
- token model now 24h (was 7d) with fail-loud explanation
- server-side authorization patterns lead with null → 401 contract
.cursor/rules/api-routes.mdc
- removes the "CRITICAL — known bug" callout (resolved by Brief 2)
- adds a "Rate limiting" section with verbatim shape + env-var notes
- "Dev/test endpoints" → "Removed" historical note so future agents
searching for test-db understand why it's gone
.convoys/fix-auth-bypass.md (restored — was on convoy branch only)
- frontmatter → status: shipped
- new "Convoy outcome" section: briefs + commits + resolved gotchas,
R1-R12 risk walk, env-var-rename deviation record, queued follow-up
convoys, lessons learned
.convoys/fix-auth-bypass/brief-{1..5}-*.md (restored from convoy branch)
- audit-trail completeness; convoy plan references them by name
- brief 4 additionally updated: UPSTASH_REDIS_REST_* → KV_REST_API_*
across init rules, smoke, pre-deploy checklist
- brief 4 has a new "Post-merge addendum" explaining the rename
.convoys/ship-readiness.md
- P0 #1, #2, #4 → RESOLVED with merge-commit citations
- P0 #5 (CORS), #6 (rate limit) → PARTIAL with deferral pointers
(cors-tighten and add-rate-limiting convoys)
- each item gains an "As-shipped" line for self-containment
README.md
- Next.js 15 → 16, TypeScript claim corrected to JS-with-devDep
- auth + rate-limit + testing bullets updated
- env-var template extended with KV_REST_API_*
- deleted dev-endpoints note added to the API list
- "Default Admin Account" section LEFT ALONE — drop-public-setup territory
Verified: build exit 0 (with JWT_SECRET set), 16/16 vitest tests pass,
lint baseline unchanged (128/81/47).
Convoy: fix-auth-bypass / role-doc-writer (closeout)
Co-authored-by: Cursor <cursoragent@cursor.com>
125 lines
5.7 KiB
Markdown
125 lines
5.7 KiB
Markdown
---
|
|
convoy: fix-auth-bypass
|
|
brief_number: 3
|
|
depends_on: []
|
|
files:
|
|
- .github/workflows/ci.yml
|
|
- README.md
|
|
deletes:
|
|
- pages/api/simple.js
|
|
- pages/api/test-auth.js
|
|
- pages/api/test-db.js
|
|
- pages/api/setup-database.js
|
|
---
|
|
|
|
# Brief 3: Delete dev-only API endpoints + add CI guard
|
|
|
|
## Goal (1 sentence)
|
|
|
|
Delete the four unauthenticated dev endpoints currently shipped to prod (`/api/simple`, `/api/test-auth`, `/api/test-db`, `/api/setup-database`) and add a CI grep step that fails the build if anyone re-introduces them.
|
|
|
|
## Files in scope (do not edit anything else)
|
|
|
|
- `pages/api/simple.js` — **deleted**
|
|
- `pages/api/test-auth.js` — **deleted**
|
|
- `pages/api/test-db.js` — **deleted**
|
|
- `pages/api/setup-database.js` — **deleted**
|
|
- `.github/workflows/ci.yml` — modified (new job)
|
|
- `README.md` — modified (one-line removal)
|
|
|
|
## Conventions to follow
|
|
|
|
- `.cursor/rules/api-routes.mdc` § "Dev/test endpoints" — these files are explicitly called out as dev-only and slated for deletion. This brief executes that.
|
|
- `.cursor/rules/no-go-zones.mdc` — none of these four files appear in the no-go list (they are not in `scripts/add-*` or any "append-only / historical" set). They are explicitly listed in the api-routes rule as "should be deleted."
|
|
- `.github/workflows/ci.yml` formatting: 2-space indent, jobs go under the existing `jobs:` map, match the style of `lint:` and `schema-map-fresh:`.
|
|
|
|
## Acceptance criteria
|
|
|
|
### Deletions
|
|
|
|
- [ ] `pages/api/simple.js` removed via `git rm`.
|
|
- [ ] `pages/api/test-auth.js` removed via `git rm`.
|
|
- [ ] `pages/api/test-db.js` removed via `git rm`.
|
|
- [ ] `pages/api/setup-database.js` removed via `git rm`.
|
|
- [ ] No grep hits for any of these paths anywhere in `pages/`, `components/`, `lib/`, or `scripts/`. Run before the PR:
|
|
|
|
```bash
|
|
rg "/api/(simple|test-auth|test-db|setup-database)" --type js
|
|
rg "(setup-database|test-auth|test-db|api/simple)" pages components lib scripts
|
|
```
|
|
|
|
Expected: zero hits in source. Doc references in `.cursor/rules/api-routes.mdc`, `AGENTS.md`, `.convoys/`, `docs/` are out of scope (doc-writer cleans them up later).
|
|
|
|
### `README.md`
|
|
|
|
- [ ] Remove the line `- \`GET /api/test-db\` - Database connection test` (currently line 79). If the surrounding API list is short and now incomplete, leave it as-is — the doc-writer pass will rewrite that section.
|
|
|
|
### `.github/workflows/ci.yml`
|
|
|
|
- [ ] Add a new job `forbidden-endpoints` after `schema-map-fresh:`. Verbatim shape:
|
|
|
|
```yaml
|
|
forbidden-endpoints:
|
|
name: No dev endpoints in pages/api
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Fail if dev endpoints re-appear under pages/api/
|
|
run: |
|
|
BAD_PATHS=(
|
|
"pages/api/simple.js"
|
|
"pages/api/test-auth.js"
|
|
"pages/api/test-db.js"
|
|
"pages/api/setup-database.js"
|
|
)
|
|
FOUND=()
|
|
for path in "${BAD_PATHS[@]}"; do
|
|
if [ -f "$path" ]; then
|
|
FOUND+=("$path")
|
|
fi
|
|
done
|
|
# Also flag any new pages/api/test-*.js the explicit list missed.
|
|
while IFS= read -r path; do
|
|
FOUND+=("$path")
|
|
done < <(find pages/api -maxdepth 4 -type f -name 'test-*.js' 2>/dev/null || true)
|
|
if [ ${#FOUND[@]} -gt 0 ]; then
|
|
echo "::error::Forbidden dev endpoints present in pages/api/. Delete them or move to scripts/."
|
|
for path in "${FOUND[@]}"; do
|
|
echo "::error file=${path}::Forbidden dev endpoint."
|
|
done
|
|
exit 1
|
|
fi
|
|
echo "OK: no forbidden dev endpoints under pages/api/."
|
|
```
|
|
|
|
- [ ] The job runs on `pull_request` and `push` (it inherits the workflow-level `on:` triggers — no per-job `on:` block needed).
|
|
- [ ] No new `concurrency:` block (the workflow-level `concurrency:` is already set).
|
|
- [ ] No `if:` conditional that lets this job skip on docs-only PRs. The check is fast (a `find` + 4 `[ -f ]` calls) and skipping it would defeat the purpose.
|
|
- [ ] The job is **blocking** — no `|| true` wrapper, no `::warning` fallback. (Lint has the wrapper because of the documented `fix-lint-baseline` debt; this job is not subject to that.)
|
|
|
|
### Smoke
|
|
|
|
- [ ] After deleting the files, `npm run build` succeeds (no broken imports — these endpoints are unreferenced, verified in the architect's audit).
|
|
- [ ] `git grep -l 'api/simple\|test-auth\|test-db\|setup-database' pages components lib` returns no source files (only docs).
|
|
- [ ] Locally, simulate the CI guard:
|
|
|
|
```bash
|
|
bash -c '
|
|
BAD_PATHS=("pages/api/simple.js" "pages/api/test-auth.js" "pages/api/test-db.js" "pages/api/setup-database.js")
|
|
FOUND=(); for p in "${BAD_PATHS[@]}"; do [ -f "$p" ] && FOUND+=("$p"); done
|
|
[ ${#FOUND[@]} -eq 0 ] && echo OK || { echo "FAIL: ${FOUND[@]}"; exit 1; }
|
|
'
|
|
```
|
|
|
|
Expect `OK`. Then create a temporary `pages/api/test-fake.js` (matches `test-*.js` glob) and re-run — expect `FAIL`. Delete the temp file before opening the PR.
|
|
|
|
### Out of scope
|
|
|
|
- [ ] No `pages/api/cards/import-*.js` deletion or gating. Those are admin-imports with rate-limit concerns; `add-rate-limiting` convoy.
|
|
- [ ] No `pages/api/auth/*` changes — Brief 1 + Brief 2 + Brief 4 cover those.
|
|
- [ ] No README rewrite of the API list — doc-writer pass.
|
|
- [ ] No new test files — Brief 5.
|
|
|
|
## Rationale (≤3 sentences)
|
|
|
|
These four files are the highest-impact deletions in the convoy: `pages/api/setup-database.js` is a public unauthenticated POST that triggers DDL, and the other three leak DB / auth internals to anyone who hits them. The CI guard is cheap insurance — without it, a future agent following an outdated tutorial could re-introduce `pages/api/test-db.js` in good faith. Keeping this brief tiny (deletions + one CI job + one README line) means it can ship in parallel with Briefs 1, 2, and 4 with no merge-conflict risk.
|