feat(mirrors): reject prefixes that overlap another product - #444
feat(mirrors): reject prefixes that overlap another product#444alukach wants to merge 2 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Claude finished @alukach's task in 2m 44s —— View job ✅ No blocking issues — safe to merge. Reviewed Two non-blocking notes for follow-up:
Both are narrow edge cases; nothing here blocks merging. |
On a shared data connection (= same bucket), storage keys are built by
literal concatenation, so if one product's slash-normalized prefix is a
string-prefix of another's their keyspaces overlap. Add findPrefixConflict
(reusing productsTable.listProductsByConnectionId) and enforce it in both
updateMirrorPrefix (free-form input) and addProductMirror (guards a
prefix_template missing {{repository_id}}). Overlap is nested-either-
direction; root prefix overlaps everything. Replaces the earlier deferral.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The overlap check normalized both sides for comparison, but the raw resolveMirrorPrefix() value was still stored and later concatenated into keys. A prefix_template without a trailing slash (optional in the schema) therefore stored "acct/prod", which literally matches keys under a sibling "acct/prod2/" — the exact overlap the check exists to prevent, slipping past it silently. Normalize once, before both checking and storing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
d8444bc to
87f934b
Compare
What
Prevents a product mirror's prefix from overlapping another product's prefix on the same data connection (= same bucket). Follow-up to the deferred
ponytail:note in #440.Because storage keys are built by literal concatenation (
${prefix}${key}), if one product's slash-normalized prefix is a string-prefix of another's, their keyspaces overlap and one product could list/read the other's objects (e.g.alice/foo/vsalice/foo/bar/, or a root""prefix vs anything).How
findPrefixConflict(connectionId, prefix, self)reusing the existingproductsTable.listProductsByConnectionId(full scan, app-side filter — already robust to legacy region-keyed mirror map keys). Excludes the product being edited.startsWithon slash-normalized prefixes; equal counts; root""overlaps everything since it owns the whole bucket).updateMirrorPrefix(free-form user input) andaddProductMirror(guards aprefix_templatethat omits{{repository.repository_id}}, or a second product attached at a shared root prefix).Tests
src/lib/actions/product-mirrors.test.ts: addslistProductsByConnectionIdto the mock (defaults to[]so existing tests are unaffected) and covers nested-under, contains, self-exclusion (allowed), non-overlapping (allowed), plus anaddProductMirrorcollision case.npx jest ... --forceExit→ 25 passed.tsc --noEmitclean.Note on base branch
Stacked on
worktree-fix-mirror-links-prefix-edit(#440) becauseupdateMirrorPrefix/addProductMirrorprefix handling only exists there, not onmain. Retarget tomainonce #440 merges.Trade-off
listProductsByConnectionIdis a full table scan (no GSI on the mirror-mapconnection_id). Fine for this low-traffic admin path; marked with aponytail:note pointing to a denormalized connection index if product volume grows.🤖 Generated with Claude Code