Skip to content

Security (low): password-protected public link reveals the item's name before the password #402

Closed

Description

@BeSovereign

Where

server/src/routes/links.ts:101-104 (info): when unlocked() is false the answer is { name: <basename of the owner's path>, type, locked: true }. web/src/views/PublicLinkView.vue:115 renders info.name in the heading on the locked page as well.

What happens

Anyone who obtains the URL (a forwarded mail, browser history, a proxy log) learns the file or folder name without the password, for example Kuendigung_2026.pdf. The owner set a password to keep the item private, and the name is part of what it names. File requests already do this right: routes/fileRequests.ts:146 answers only the owner-written label while locked.

Steps

  1. Create a public link with a password on any file.
  2. GET /s/<token>/info with no cookie.
  3. The JSON carries name and type; the page /s/<token> shows the file name above the password field.

Expected behaviour

While locked, info carries neither name nor type, and the page shows a generic heading (the app name plus a 'This link is protected' line) with the password field. After unlock the real name appears as today.

Fix

In info() return { locked: true } only; make name and type optional in PublicLinkInfo (shared/src/links.ts); in PublicLinkView.vue show the generic heading from the locale files (all three locales) while info.locked is true. Note the decision in DECISIONS.md.

Test that must fail without the fix

  • server/test/publicLinks.test.ts: create a link with a password; GET /s/<token>/info without the unlock cookie: expect(body).not.toHaveProperty('name') and not.toHaveProperty('type'); after POST unlock with the right password, name is present.
  • Playwright e2e/public-link.spec.ts (or the existing link spec): open a password link; before unlocking expect(page.getByRole('heading', { level: 1 })).not.toContainText(fileName); after unlocking it contains the name.

Found by the automatic review round (claude-fable-5-1, b739414); per CLAUDE.md the fix starts with a failing behaviour test.

Activity

  1. added
    rebuildPart of the ground-up rebuild (spec 2026-10-14)
    securitySecurity finding
    on Oct 20, 2026
  2. added a commit that references this issue on Oct 20, 2026
  3. BeSovereign commented on Oct 20, 2026

    @BeSovereign
    ContributorAuthor

    Fixed: PR #411 squash-merged into main after green CI (one commit; git revert it to undo).

    • Suites: typecheck: 1 passed, 0 failed | lint: 1 passed, 0 failed | unit: 1187 passed, 0 failed | e2e: 603 passed, 0 failed
    • Declared tests fail without the production change and pass with it (output in the PR).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    rebuildPart of the ground-up rebuild (spec 2026-10-14)securitySecurity finding

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions