Skip to content

Audit fixes - #1

Merged
shimentosh merged 8 commits into
mainfrom
audit-fixes
Sep 3, 2026
Merged

shimentosh merged 8 commits into
mainfrom
audit-fixes

Conversation

@shimentosh

Copy link
Copy Markdown
Owner

No description provided.

shimentosh and others added 8 commits September 3, 2026 20:44
…r, researcher

Security
- requireUser() on all 15 data routes that only checked for a cookie's
  presence (packs, runs, series, topics, workspaces, tools). A forged
  contentos_session read and wrote the whole workspace; now 401.
- proxy.ts comment and docs corrected to say so (middleware.ts -> proxy.ts).

Correctness
- Failed status derived from sections; a dead run no longer reads Writing
  forever, a never-started run is an Idea again; Retry requeues failures.
- Run pages render the polled copy, not the store's snapshot (live updates
  without a refresh); WRITING NOW strip links to /runs/<id>, not a 404.
- deleteSource removes the download directory with the row.
- One slug rule (lib/slug.ts) replaces five copies.

Features
- Template import/export as a file (lib/packs-transfer.ts).
- Tools as rows with per-workspace assignment; /tools/manage.
- Content Researcher: upload or link, pick frames, read them with Claude,
  save ideas as topics. Vision on the API path; opt-in Read for the CLI.
- Per-template builder URLs (/builder/<slug>); status in builder step 1.
- Holding shelf renamed Misc.

Structure
- lib/use-run-document.ts, lib/use-run-watch.ts, lib/use-content-list.ts
  and components/tools/FramePicker.tsx lifted out of the two largest views.
- projectsLoaded guards writes until the server's workspace list arrives.

Tooling
- vitest with 34 unit tests; npm run check; GitHub Actions workflow.
- sourcedata volume in docker-compose.prod.yml; env template un-ignored.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- ingestFile takes the request's stream and pipes it into the source's
  directory through a byte counter; past MAX_UPLOAD_BYTES the pipeline is
  torn down and the partial file removed. The route rejects the declared
  size first. Previously the whole upload was buffered twice in memory.
- Dead orphanSourceDirs helper removed.
- ARCHITECTURE "Where things live" and README layout/routes list the hooks,
  tools/, slug.ts, tests/, the workflow, proxy.ts, and the new API routes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s-origin

- api/: NestJS 12, one module, ten controllers over the shared lib/server
  (repos, services, run driver, ingest, researcher). A global SessionGuard
  closes every route by default; @public() opens the four sign-in routes.
  ErrorsFilter answers exactly as caught() did ({error}, 503 offline,
  409 + existing on DuplicateName). Streamed multipart upload via busboy.
- app/api/** removed (35 handlers). proxy.ts guards pages only.
- lib/server/auth.ts no longer reads Next's cookie jar; currentUser and
  requireUser take the session id. The only Next-bound line is gone.
- lib/api-base.ts: NEXT_PUBLIC_API_URL + apiFetch (credentials: include);
  all 64 browser calls and the frame <img> URL go through it.
- One data root for both processes (CONTENTOS_DATA_DIR).
- Deploy: api/Dockerfile (ffmpeg, yt-dlp move here), compose runs api + app
  on API_HOST / APP_HOST behind Traefik, cookie shared via COOKIE_DOMAIN,
  NEXT_PUBLIC_API_URL as a build arg. start.sh runs both. CI builds the API.
- Docs: ARCHITECTURE, DEVELOPING, DEPLOYING, README, DECISIONS.

Verified: guard/CORS probes, upload → frames → delete on the API, compiled
dist run the way the image runs it, web app calling only :4000, production
build of the web app, npm run check green.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- scripts/api-smoke.mjs (npm run api:smoke): six requests at the API — public
  probe, refused without and with a forged cookie, answering for a session,
  CORS preflight from the web origin. API_URL / WEB_ORIGIN / SESSION for a
  deployed one. Exits non-zero if any is wrong.
- lib/server/http.ts keeps only ready(); ok/fail/caught went with app/api.
- BROWSER.md, DEVELOPING.md, DEPLOYING.md say where the API is and how to
  check it after a deploy.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
claude/codex/gemini read the project they are started in. From the repo root
that quietly put CLAUDE.md and AGENTS.md into every generation; from api/
the CLI walked up to the repo's CLAUDE.md, was refused its @AGENTS.md include,
and wrote a paragraph about it at the top of a script. cliHome() under the
data root is now the cwd for all three spawns. Verified through
/api/integrations/test: "BRAIN OK", no mention of the repo.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Even from an empty working directory the CLI decided there must be a
"project's template file" it could not open, and said so at the top of an
English script. Four things together:
- --exclude-dynamic-system-prompt-sections (replacing the whole system
  prompt made the CLI never finish; the working-directory framing is what
  had to go)
- --disallowedTools for the filesystem unless frames are handed over
- CLI_GUARD appended on the CLI paths: nothing to open, never mention files
- scrubPreamble(): a leading paragraph about the tool is cut before saving

Verified through the API: test brain "BRAIN OK", script-en regenerated
clean in 44s.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
With the backend on its own port the console can be up while the API is not.
The store swallowed that and rendered the design's sample workspaces with
"0 templates", which reads as "my work is gone". Now: apiDown is set when
the first request cannot connect, ApiDownBanner names the port and how to
start the service, and the store polls every 5s and reloads when it answers.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…gh the builder

The pack's system prompt (6-8K chars) was passed to `claude -p` as a
command-line argument, which Windows silently truncates — so the fixed
script format never reached the model. Scripts had come out right only
because the CLI, started in the repo, read lib/packs/enbn-website.ts itself;
moving it to an empty directory removed that crutch and every section came
back as a bold-headed report. Now --append-system-prompt-file, in the CLI's
own directory, deleted after the call. Verified: fixed opening and series
line back in 23s, no narration. The dynamic-sections flag is gone (it was
not the cause); the scrub is narrowed to first-person tool talk.

fromDraft wrote every section as tier standard with no dependsOn, so one
Save from the builder — or an export — cut every chain a template had.
DraftSection carries both now; round-trip test added.

Runs left "writing" by a dead process are requeued: per run when the driver
starts, and for every run at API boot, which then drives them again.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@shimentosh
shimentosh merged commit 0341701 into main Sep 3, 2026
0 of 2 checks passed
shimentosh pushed a commit that referenced this pull request Sep 5, 2026
Merge pull request #1 from shimentosh/audit-fixes
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