[codex] Add password-protected profile sharing#499
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR adds password-protected profile sharing (client-side AES-256-GCM + PBKDF2) with a Vercel Blob storage backend, surfaces share controls from the header, client list, and Settings → Data, and completes a report modal overhaul with AI practitioner summaries. The cryptographic design is sound — password never touches the server, min-iteration enforcement is consistent between production and dev, and the decompression bomb guard is implemented correctly.
Confidence Score: 3/5Safe to merge after removing the unnecessary window exposures; the core share, encrypt, and rate-limit logic is correct.
js/profile-share.js — the
|
| Filename | Overview |
|---|---|
| js/profile-share.js | New module implementing client-side AES-256-GCM encryption, share link creation/deletion, and import modal; decompression bomb guard is present; createProfileShare and several other private functions are unnecessarily exposed on window, creating a silent exfiltration path. |
| api/share.js | New Vercel edge function for encrypted envelope storage; enforces minimum KDF iterations, TTL cap, and a slot-based IP rate limit; handleDelete's del call is unguarded (previously flagged). |
| dev-server.js | Adds in-memory /api/share mirror for local development with equivalent validation; streaming size limit and envelope checks match the production handler. |
| js/export.js | Refactors exportClientJSON into a reusable buildClientExportObject helper for JSON export and sharing; buildClientExportObject is correctly NOT exposed on window; adds AI practitioner summary generation for reports. |
| js/client-list.js | Adds "Share Profile" menu item per-client and _clShare handler that opens the share modal; correctly delegates via window.openProfileShareModal. |
| js/settings.js | Adds "Share Profile" button in Settings → Data that opens the share modal; correct pattern. |
| tests/test-profile-share.js | New test file covering crypto round-trip, ID generation, URL building/parsing, and structural checks; does not use window.createProfileShare from tests. |
Sequence Diagram
sequenceDiagram
participant Sharer as Sharer Browser
participant PSM as profile-share.js
participant EXP as export.js
participant API as /api/share (Vercel Blob)
participant Recipient as Recipient Browser
Note over Sharer: User clicks Share Profile
Sharer->>PSM: openProfileShareModal()
PSM-->>Sharer: Render create form (password pre-filled)
Note over Sharer: User submits form
Sharer->>PSM: handleCreateSubmit(form)
PSM->>EXP: buildClientExportObject(profileId)
EXP-->>PSM: exportObj (no wearable credentials)
PSM->>PSM: encryptProfileShareEnvelope(exportObj, password)
Note over PSM: PBKDF2 (600k iter) → AES-256-GCM
PSM->>API: "POST /api/share {id, envelope, manageTokenHash}"
API->>API: enforcePostRateLimit (slot-based, IP-hashed)
API->>API: normalizeEnvelope (validates min iterations, TTL, size)
API->>API: Blob PUT (allowOverwrite:false, private)
API-->>PSM: "201 {id, expiresAt}"
PSM-->>Sharer: Show link + password (send separately!)
PSM->>PSM: saveShareRecord(localStorage) — stores manageToken
Note over Recipient: Opens share URL #share/{id}
Recipient->>PSM: parseProfileShareIdFromLocation()
PSM-->>Recipient: openSharedProfileImportModal(id)
Recipient->>API: "GET /api/share?id={id}"
API-->>Recipient: "{envelope}"
Recipient->>PSM: decryptProfileShareEnvelope(envelope, password)
Note over Recipient: PBKDF2 + AES-GCM decrypt + gzip decompress (capped)
PSM->>EXP: importDataJSON(file)
EXP-->>Recipient: Profile imported as new profile
Note over Sharer: User clicks Stop sharing
Sharer->>PSM: handleDeleteShare()
PSM->>API: "DELETE /api/share?id={id} {manageToken}"
API->>API: "sha256(manageToken) == record.manageTokenHash"
API->>API: Blob del (currently unguarded)
API-->>PSM: "200 {ok:true}"
PSM->>PSM: removeShareRecord(localStorage)
Reviews (6): Last reviewed commit: "Simplify profile sharing changelog" | Re-trigger Greptile
Summary
/api/sharestorage, local dev-server support, deep-link import, active-link listing, and Stop sharing.Why
Users needed a simple way to share one profile without accounts or manual file handling. The implementation keeps the password out of the link and off the server, stores only encrypted envelopes, and lets the creating browser stop active links.
Validation
node --check js/profile-share.jsnode --check api/share.jsnode --check dev-server.jsnode tests/test-profile-share.jsnpm testgit diff --check