Add inbox-check line; align .gitignore with canonical .claude policy

Cross-repo git-hygiene audit remediation: surface ~/Projects/standards/INBOX.md items at session start, and switch .gitignore to the deny-by-default .claude/* block (shared wiring allow-listed) plus the canonical secrets/env lines — per standards/portability.md.
This commit is contained in:
Keysat
2026-06-14 12:17:16 -05:00
parent d4c742d6e7
commit 6bb7e69141
2 changed files with 34 additions and 2 deletions
+8 -2
View File
@@ -26,8 +26,14 @@ ytdlp-cache/
# Local dev secrets # Local dev secrets
.env .env
.env.*
!.env.example
# Claude Code state (worktrees, plans, etc.) — but DO track the portable # Claude Code — deny by default (worktrees, plans, local settings stay out),
# rules symlinks so scoped guides lazy-load in any clone. # allow-list shared wiring (see standards/portability.md).
.claude/* .claude/*
!.claude/rules/ !.claude/rules/
!.claude/agents/
!.claude/commands/
!.claude/skills/
!.claude/settings.json
+26
View File
@@ -2,6 +2,9 @@
YouTube + podcast summarizer + library, served as a single-page app from a Node.js backend. Ships as a StartOS `.s9pk` (single-mode self-host) and as the public `recaps.cc` cloud (multi-mode tenants). YouTube + podcast summarizer + library, served as a single-page app from a Node.js backend. Ships as a StartOS `.s9pk` (single-mode self-host) and as the public `recaps.cc` cloud (multi-mode tenants).
> **Inbox check:** At session start, if `~/Projects/standards/INBOX.md` exists, scan it for
> items tagged `(recap)` and surface them before proposing next steps; triage with `/triage`.
## Stack ## Stack
- **Server**: Node.js (`type: module`, ES modules). The dev box currently runs `v25.6.1`; container runtime is whatever the `Dockerfile` pins — check before assuming. - **Server**: Node.js (`type: module`, ES modules). The dev box currently runs `v25.6.1`; container runtime is whatever the `Dockerfile` pins — check before assuming.
@@ -132,6 +135,29 @@ unsure whether a change is contract-affecting, assume it is and check.
2. Enable cards — run the relay's "Set Zaprite Connection" action (API key) + register the Zaprite webhook at `https://<relay-host>/relay/zaprite/webhook`. 2. Enable cards — run the relay's "Set Zaprite Connection" action (API key) + register the Zaprite webhook at `https://<relay-host>/relay/zaprite/webhook`.
3. Eyeball a reminder email via the test trigger above. 3. Eyeball a reminder email via the test trigger above.
### Evaluation work queue (P0/P1) — from the 2026-06-14 full-eval (`EVALUATION.md`)
Fix before exposing the cloud to untrusted users. The P0s are reproduced/verified, not theoretical.
- **[P0] Arbitrary file write — `POST /api/library/import`.** A `../../` session key escapes the scope dir (reproduced writing `/tmp/rce_test.json`). `server/library.js:131-139` uses the key as a filename without `safeFilename()`. Fix: export `safeFilename()` from `history.js` (it's currently module-private — see `:656`) and validate the key here plus in the array-import and `PUT /api/history/move` paths.
- **[P0] SSRF with read-back — podcast download.** An anonymous trial can POST `{type:"podcast", url:"http://169.254.169.254/..."}`; `downloadPodcastAudio` (`server/audio.js:78-97`, reached from `index.js:3455`) does an unguarded `http.get`, follows redirects to any host, and the body is transcribed back to the attacker. Fix: reject private/link-local/loopback/reserved IPs, https-only, re-validate on each redirect, add a size/time cap.
- **[P0] Live Gemini key in git history, still the active key** — `git show d5046a0:.env`, pushed to `origin/master`. Rotate the key in Google AI Studio now; then purge from history (BFG/filter-repo) and force-push.
- **[P1] ESM `require("crypto")` throws** `ReferenceError` on the anon license-purchase settle path — `server/license-purchase.js:423` (called from `:353-354`). Import `randomBytes`/`randomUUID` at the top of the file.
- **[P1] Global `currentFreeJob` lock serializes the entire multi-tenant cloud.** `isFreeUser()` returns true for every tenant in multi-mode, so a 2nd concurrent `/api/process` from any user gets `409`. `server/index.js:2621`, `license-middleware.js:143`. Scope the slot per-identity, or skip it in multi-mode.
- **[P1] Trial IP-cap + magic-link rate-limit bypass via spoofed `X-Forwarded-For`** (no `trust proxy` / XFF normalization) — `server/anon-trial.js:50-57`, `auth-routes.js:209`. Deployment-dependent: confirm the recaps.cc edge proxy *overwrites* XFF and trust only the last hop. Self-hosted StartOS is unaffected.
- **[P1] StartOS registry submission BLOCKED** (3 blockers) — missing root `instructions.md`; `packageRepo`/`upstreamRepo` point to `https://ten31.xyz` (a homepage, not a source repo); `license: 'Proprietary'` fails the "source available" gate (`startos/manifest/index.ts`). **Only blocks a community-registry submission — does NOT affect `make install`.**
### Known debt (P2) — track; not release-blocking for self-host
- **Operator-internal strings leak to cloud users at the SSE error boundary** (Parakeet/Gemma/CUDA/LAN IPs) — no scrub exists, violating the scrub contract above. `server/index.js:3432,3003,4246` + `providers/relay.js:135-144`. (Sharp edge: `index.js:3419` *detects* these strings, then forwards them anyway.)
- **Credit over-spend TOCTOU on licensed installs** — N parallel requests pass the `total>0` check before any blind `debitOne` lands. Make check+debit atomic (reserve up front, refund on failure). `index.js:2497-2550` vs `:3158,:4197`.
- **Multi-mode tenant can spend the operator's server Gemini key** via `transcriptionProvider:"gemini"` + empty key (bypasses relay metering) — `providers/index.js:104-114`. Refuse the operator-key fallback for non-admin tenants.
- **`GET /api/history` parses every full session file** (transcript+summary, MB each) just to list ~8 metadata fields — cache them into `_meta.json` on save. `history.js:418-437`.
- **Dependency CVEs** — nodemailer 6.10.1 (high; low practical reach here), ws/qs/express/protobufjs (moderate). `npm audit fix` (nodemailer is a major bump).
- **No tests on the riskiest files** (`/api/process` gating, `relay.js`, `tenant-auth.js`, billing) — every code P0/P1 above lives here, and no agent could run the real summarize→save→debit path end-to-end (no key/credits). Add an integration test as the regression net before/with the fixes.
- **Smaller hardening:** unsanitized IDs persisted to `_meta.json` (array-form import + `history/move`); `PUT /api/history/meta` accepts arbitrary JSON shapes with no schema; `index.js` is 4351 lines mixing routing/pipeline/yt-dlp/SSE.
- **Doc drift (high-value):** AGENTS.md credit-gate order omits the "paid cloud user" bypass state (`:77` vs `index.js:2464-2472`); operator-facing `startos-registry/.../INSTRUCTIONS.md` + `assets/ABOUT.md` are stale Gemini-first (relay is the default provider). Lower-severity doc nits are deferred in `ROADMAP.md`.
**Known issues / open decisions** (details + next actions in `ROADMAP.md`): **Known issues / open decisions** (details + next actions in `ROADMAP.md`):
- **Zaprite recurring isn't built** — Zaprite's API only does one-time orders. Card = prepaid until that's confirmed with Zaprite. - **Zaprite recurring isn't built** — Zaprite's API only does one-time orders. Card = prepaid until that's confirmed with Zaprite.
- **"Take Recaps home" is likely broken** for relay-tier users (no `keysat_license` after decoupling). - **"Take Recaps home" is likely broken** for relay-tier users (no `keysat_license` after decoupling).