Repository navigation
Before going public: an AI tool gets read and write unless it names delete, uploads have limits, and the review's other fixes - #107
Merged
Conversation
…elete, and the review's other security fixes
- An AI tool that names no scope now gets read and write: Better Auth stored every registered client with every
scope, delete and the account included, and gave an authorize with no scope all of them. The guard narrows it,
refuses the account scope for anything not ours, and the connector advertises read and write only.
- Sign-in's next path refuses anything a browser reads as another site ("/\evil.com", control characters).
- A public production server refuses ALLOW_PRIVATE_FETCH and AUTH_RATE_LIMIT=off, and page fetches also refuse
NAT64, 6to4, 192.0.0.0/24 and 240.0.0.0/4.
- "Send me one now" sends at most five digests a day.
- Covers and images are kept only as JPEG, PNG, WebP, GIF or AVIF: an SVG can carry script.
- The stdio bridge builds before it is published, and has a README, homepage and issues link.
- .gitignore: Claude Code worktrees and personal settings, tsbuildinfo, coverage.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZorAwPFkDvfWHxJNqzu9B
Signed-off-by: NiallJoeMaher <nialljoemaher@gmail.com>
…efused, a cover stops at 2 MB
- @pluck/blob: getObject(key, { maxBytes }) refuses an object over the limit
from its Content-Length, before the body is read (TooLarge).
- A shared file over 50 MB marks the item failed and removes the blob; an
import over 500 MB fails with that reason. The limits now live next to the
schemas that declare them (MAX_FILE_BYTES, MAX_IMPORT_BYTES).
- Email-in refuses a message over 10 MB before reading it.
- Omnivore: entries over 20 MB, or metadata over 100 MB in all, are refused
before fflate allocates them; a page too big to keep is fetched instead.
- Cover PUT checks content-length first and still stops reading at 2 MB.
- vitest resolves the web app's "@/" path so route handlers can be tested.
The signed PUT URL still does not bind a length: the shipped iPhone app
sends size 1 when it cannot read a file's size, and that upload would fail.
Signed-off-by: NiallJoeMaher <nialljoemaher@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZorAwPFkDvfWHxJNqzu9B
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The second readiness review, before the repo goes public: one scope bug that broke a house rule, and the hardening an open repo invites.
An AI tool got delete and the account by default (fixed). Better Auth stores every dynamically registered or CIMD client with every scope Pluck has, whatever it asked for (
resolveClientRegistrationScopes), and an authorize with noscopeis given all of them. The connector also advertised every scope, and the MCP spec tells clients to requestscopes_supported. So a spec-following AI tool was offeredlibrary:deleteandaccount, against CLAUDE.md. Now (apps/web/lib/auth.ts):accountscope outright for such a client;library:deletewhen a tool names it, which the consent screen then shows;library:readandlibrary:writeonly (advertisedMetadata).Checked on a local server: no scope →
offline_access library:read library:write;account→ 400; named delete → kept. A test inauth-guard.test.tspins all three. AI tools connected before this may still hold the wider grant.Smaller fixes
nextrefuses anything a browser reads as another site, like/\evil.comor control characters (lib/same-site.ts, tested).ALLOW_PRIVATE_FETCH=1andAUTH_RATE_LIMIT=offnow stop a public production server from starting (instrumentation.ts). Production sets neither.@pluck/blobgetObject(key, { maxBytes })):.gitignore: Claude Code worktrees, personal settings, tsbuildinfo, coverage.Test, locally on this branch:
pnpm typecheck,pnpm lint,pnpm test(441),pnpm e2e(56) andpnpm designpass. Merged on local checks, as Niall asked, while Actions minutes are out.🤖 Generated with Claude Code
https://claude.ai/code/session_01GZorAwPFkDvfWHxJNqzu9B