Skip to content

fix(security): untrusted kind lookups, root error boundary, opaque default handle, seed skips owned rows - #19

Merged
jphein merged 2 commits into
mainfrom
fix/security-scan-2026-10-22
Oct 22, 2026
Merged

jphein merged 2 commits into
mainfrom
fix/security-scan-2026-10-22

Conversation

@jphein

@jphein jphein commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator

Client- and script-side fixes from the 2026-10-22 security scan (private advisory GHSA-72r8-ccwp-xcwq). Server-side rules come in a separate PR after a staging A/B run.

  • F3–F6: every glyph/source lookup keyed by listing data now goes through ownKey()/glyphFor() in src/lib/kinds.ts (own-property check, string-only). One shared KIND_GLYPH replaces five copies (MapPin, ListingCard, MapScreen.web ×2, add, calendar); getSourceColor and ListingCard's source layer use the same guard.
  • Error boundary: the root layout exports expo-router's ErrorBoundary.
  • F2: new profiles get forager-<10 hex> (from a v4 id) and "New forager" instead of the email local part.
  • F7: seed-listings skips any derived listing id whose row already has a createdBy owner (logs the ids).

Checks

  • npm run typecheck ✓, npm run lint 0 errors (the 22 warnings are the same exhaustive-deps ones as on main).
  • Runtime check (tsx): glyphFor returns the fallback for __proto__, constructor, toString, non-strings; ownKey(SOURCE_BY_KEY,"__proto__") is undefined. Perturbation: the old table[k] ?? "🌱" pattern returns a non-string for __proto__ (red).
  • Read-only checks on the live self-hosted app: where: { id: { $in } } works and createdBy returns as an array (the shape ownedListingIds reads); 0 listings carry an unknown kind; 2 profiles exist, both with email-derived handles (not rewritten here).
  • gitleaks clean.

🤖 Generated with Claude Code

…fault handle, seed skips owned rows

From the 2026-10-22 security scan (advisory GHSA-72r8-ccwp-xcwq):
- F3–F6: every glyph/source lookup keyed by listing data goes through ownKey()/glyphFor()
  (src/lib/kinds.ts), so inherited keys fall back instead of rendering Object.prototype.
  One shared KIND_GLYPH table replaces five copies.
- Root layout exports expo-router's ErrorBoundary: one bad row can't unmount the app.
- F2: new profiles get a random handle (forager-<10 hex>) and "New forager", never the
  login email's local part.
- F7: the listings seed skips any derived id whose row already has a createdBy owner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copilot AI balanced review requested due to automatic review settings October 22, 2026 21:19

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 8715cfc3-fadd-4982-b550-563333597385
📥 Commits

Reviewing files that changed from the base of the PR and between 1697ff1 and dab4552.

📒 Files selected for processing: 10
  • app/(tabs)/calendar.tsx
  • app/_layout.tsx
  • app/add.tsx
  • scripts/seed-listings.ts
  • src/components/ListingCard.tsx
  • src/components/MapPin.tsx
  • src/config/sourceLayers.ts
  • src/hooks/useAuthedProfile.ts
  • src/lib/kinds.ts
  • src/screens/MapScreen.web.tsx
+--------------------------------------------------+
| Veni, Vidi, Validavi. I came, I saw, I reviewed. |
+--------------------------------------------------+
  \
   \     /)/)
        ( '')
        /  |
      o(___)
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…rite

The pre-write ownership check could race a create at a derived id. After writing,
re-query the written ids and unlink createdBy from any that gained an owner; a
non-owner can't link createdBy to an existing row, so the window closes there.
Cross-model review finding (gpt-6.1-sol).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jphein
jphein merged commit a04352e into main Oct 22, 2026
1 of 2 checks passed
@jphein

jphein commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator Author

Self-merged by vale (techempower lane) under JP's standing yes (2026-09-28).

  • CI green on dab4552; npm run typecheck ✓; lint 0 errors (warnings match main).
  • Perturbation: the old table[k] ?? fallback pattern returns a non-string for __proto__; glyphFor returns the fallback.
  • gitleaks clean.
  • Oracle (spyglass-ba) @ 8635715: MERGE. Five claims PASS. It noted the seed TOCTOU as LOW, with the real closure in the perms PR.
  • Cross-model (gpt-6.1-sol) @ 8635715: MERGE-AFTER-FIXES (the seed ownership check races). dab4552 adds a post-write owner unlink. Re-review: DO-NOT-MERGE on the remaining seconds-long window, in which an owner could edit the imported fields before the unlink lands.
  • Why merge anyway: this PR is strictly safer than main. The residual window is closed server-side by the follow-up perms PR: owners can't edit or delete rows whose source is an import, and provenance is frozen. Tracked in the private advisory.
  • Existing profiles keep their email-derived handles; there are 2 on prod. A one-off backfill comes with the perms deploy.

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.

2 participants