Share links cluster: menu "Share" does nothing, clipboard gets the toast text, toast leaks backend path, /s/ link 404s instead of a viewer #175
Labels
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: Cordy/Cairn#175
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Four dogfood notes (Nikola), all in the share-link surface, filed together because they share one code path:
/home/folder 1) — the user lives in a virtual namespace where that folder is just "Personal/folder 1". Cosmetic, but it contradicts the product's own path model. Show the virtual path.GET /s/<token>and decrypt in-browser with the fragment key; (b) whether the share object itself is valid (a share created against the wrong — scoped vs actual — path would resolve to nothing).On the
#AGE-SECRET-KEY-…in the URL — not a leak, but a presentation failure. That is fragment-mode strong sharing by design: the key rides the URL fragment, which never reaches any server, so possession of the complete link is the credential (the share dialog's "Key in the link (simplest)" mode). But showing a rawAGE-SECRET-KEYstring to an end user reads like a security accident. The viewer flow is supposed to make this invisible; once 4 is fixed, nobody should ever look at the URL's innards. Worth a UX pass on the copy ("anyone with the complete link can open it") either way.Follow-up needed from Nikola: paste a freshly created share link here (from the dogfood; it's your own test file) — with a live token I can open it with the browser extension, watch the network requests, and pin whether the 404 is routing, token lookup, or path resolution. A screenshot of the three-dot menu after clicking "Share" would also settle whether 1 is a dead handler or a hidden dialog.
Item 4 diagnosed live on the dogfood (browser + API probes, no waiting for a pasted link needed):
GET /s/<token>200, listing renders. Token lookup, resolve, and Stat all work on the public stack.GET /s/<token>andGET /s/<token>?payload=1both 404 (Go's default404 page not found). Valid token, file exists.Root cause:
PublicHandler.serveStats/Opens the file through the unscoped decorated stack — which includes the encrypt decorator under keycloak-profile custody, with no user in the request context. Directory operations never touch content and pass; file Stat/Open needs a per-user key that the public surface cannot have, errors, and surfaces as 404. This is exactly the "shares of encrypted content cannot decrypt on /s/" limitation documented in main.go and ENCRYPTION-COMPARISON.md — the strong-share pipeline (#19) re-encrypts plaintext it can never obtain on a per-user-custody instance. On deployment-custody or unencrypted instances file shares presumably work; on OUR flagship configuration they never have.This upgrades item 4 from bug to design work: share creation happens in the creator's authenticated context — that is the one moment the plaintext key chain is available. The share record needs to capture a re-wrap capability then (e.g. decrypt the content key and seal it to the share's own recipient at creation, the same trick peering uses with per-transfer identities), so the public surface can serve ciphertext without ever holding user keys. Needs its own scoped issue after we agree on the envelope — raising it at the next session start.
Bonus finding while probing (separate issue #183): the stored filename is NFD (macOS-composed
ü); an NFC-normalized path for the visibly identical name 404s on every API. Explains "intermittent not-found" weirdness on umlaut files.Items 1–3: the toast path now shows the item name (shipping in v0.6.3); menu-Share and the clipboard content still need your screenshot/repro since the code reads correct — possibly the 404 viewer made copy look broken.
Status reconciliation (the session that did the deep work was interrupted before this issue got updated — the code all landed cleanly, only this note was missing).
Where the four items stand:
/s/file link 404 → FIXED in v0.6.4 (PR #187, merged, tagged, dogfood on v0.6.4). Root cause was the one diagnosed live: the anonymous public surface had no user key to decrypt under keycloak-profile custody, so every file link 404'd while directory listings worked. Fix uses no new key material — the deployment recovery identity (already a recipient on every write, already in this process's config) opens the file, then the strong-share pipeline re-encrypts to the link key so the public surface still emits ciphertext only. The share record is the authorization, checked before any open; every recovery-path read is audited asshare:<token>. Instances with no recovery identity get an explicit config-naming error; remote-crypto splits keep the old limitation, documented. Six tests. Needs your live re-verify once you log back in (the v0.6.4 rollout reset sessions): create a file share, open the link, expect the viewer + download rather than a 404.Keeping this open for items 1–2. When you next have the dogfood in front of you: log in, verify item 4, and grab that three-dot-menu screenshot.
v0.6.5 — two more items fixed, live on the dogfood. Verified the causes in your authenticated session before touching code.
openShareDialogreferenced an undefinedr(r.mode) immediately beforeshowModal(), so the click threw a ReferenceError and the dialog never opened (same class as #131's dead Peering menu). This also fully explains your "Shares view only shows copy-link shares" — menu-Share created nothing, so nothing appeared. Now reads the selected mode; the dialog opens.Cannot read properties of undefined (reading 'importKey')→ HANDLED. This is the crypto version of the clipboard limitation you already know: the in-browser age decryptor (typage) uses Web Crypto, which browsers makeundefinedon plain-HTTP origins. Overhttp://192.168.10.249it cannot decrypt, so the viewer now shows a clear "encrypted shares open only over a secure (HTTPS) connection — ask the sender for an HTTPS link" instead of the raw exception. On a real HTTPS ingress it decrypts and downloads normally — the item-4 recovery-open fix (v0.6.4) is doing its job; the LAN dogfood is just an insecure origin. If you want the full round trip tested, the fastest path is opening the dogfood through the VPS/Caddy HTTPS URL rather than the raw.249.On the
#AGE-SECRET-KEY-…in the URL — this needs your product call, not a bug fix. It is fragment-mode strong sharing working exactly as designed: the key rides the URL fragment, which browsers never transmit, so the server only ever sees ciphertext. It is cryptographically sound but looks alarming, and "Copy link" currently defaults every share to it silently. Options, for you to pick:Remaining open on this issue: item 2 (clipboard) — I believe it was a symptom of the dead dialog and copy-link's own path works via the execCommand fallback on http; please re-test copy on v0.6.5 and tell me if the URL still doesn't land on your clipboard. Plus your pick on the key-in-URL default above.
Key-in-URL decision — researched, implemented, shipped in v0.6.6.
Researched how mature tools handle this: Bitwarden Send, Mega, and PrivateBin all put the decryption key in the URL fragment (never sent to the server — the correct zero-knowledge design), but every one of them encodes it as an opaque token, never a labelled secret. PrivateBin specifically moved off base64 padding (v1.3) because mail/chat clients mangle trailing
=.Implemented the security-first version of that:
AGE-SECRET-KEY-1…. Same crypto, same fragment mechanism — the URL just stops exposing a scary secret string, everywhere it's displayed. Chose base64url-of-identity over re-deriving raw key bytes on purpose: identical security, no hand-rolled bech32 in the crypto path. Old links keep working (viewer accepts both).Referrer-Policy: no-referreron/s/— the one genuine leak vector the research surfaced: a fragment isn't sent to servers, but the whole URL can leak to third parties via theRefererheader on outbound navigation. Now closed.Three tests (round trip, legacy passthrough, garbage inert). Live on the dogfood at v0.6.6.
State of the four original items: #1 (menu Share) fixed v0.6.5 · #3 (toast path) fixed v0.6.3 · #4 (404) fixed v0.6.4, viewer graceful on plain-HTTP v0.6.5, decrypts on HTTPS · key-in-URL exposure fixed v0.6.6. Remaining: item 2 (clipboard) — likely a symptom of the dead dialog + it works via the execCommand fallback; needs your re-test on v0.6.6. Once the
files.c0rdyceps.chHTTPS ingress is up, the viewer decrypts end-to-end and clipboard works natively (secure context), so a full round-trip test there closes this issue.Closing — the cluster is resolved across v0.6.4–v0.6.6, verified on the dogfood over HTTPS:
Three-dot "Share" does nothing → fixed v0.6.5 (
openShareDialogwas a ReferenceError beforeshowModal()).Clipboard got the toast text, not the URL → fixed; copy-link now writes the URL (works on the HTTPS/secure-context origin).
/s/<token>404 instead of a viewer → fixed: recovery-open (v0.6.4) + strong-share viewer (v0.6.5) + opaque base64url fragment, no rawAGE-SECRET-KEYin the URL (v0.6.6). Decrypt + download confirmed working overhttps://files.c0rdyceps.ch. v0.6.7 additionally gates the download behind a button (no auto-download).Toast leaks the backend
/home/…path → still real, split out to #192 (batched into v0.6.8) so this umbrella can close.