Web: malformed share-token encoding throws during routing
Maintainers usually reply within 1 day
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 88/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- react, typescript
Research direction
Start in web/src/App.tsx at the share-token route resolution and review the supplied web/src/tests/AppRouting.test.tsx reproduction. Run npm test -- src/tests/AppRouting.test.tsx from web/ first. Done means malformed /s/% and /s/%FF paths show an invalid-link message without mounting RecipientPage, while ordinary and valid percent-encoded tokens retain existing behavior and the web tests pass.
Written by the indexing model from the issue text.
Description
What happens
The client router throws URIError: URI malformed when the share-token segment contains invalid percent encoding, for example /s/% or /s/%FF. The exception occurs while rendering App, before the recipient page can display a helpful error.
Expected: a malformed share URL should produce an explicit invalid-link message instead of an uncaught render error. An ordinary share token should still reach the recipient page unchanged.
Reproduction
Verified on a fresh checkout of main at 912b751f4ca72c562346fbc4c26eef2b42ed6a9f, using the real App router with child pages stubbed to isolate routing. After npm ci in web/, save the following as web/src/__tests__/AppRouting.test.tsx:
import { cleanup, render, screen } from "@testing-library/react";
import { afterEach, expect, it, vi } from "vitest";
import { App } from "../App";
vi.mock("../Landing", () => ({ Landing: () => <div>landing</div> }));
vi.mock("../VaultApp", () => ({ VaultApp: () => <div>vault</div> }));
vi.mock("../AuthCallback", () => ({ AuthCallback: () => <div>callback</div> }));
vi.mock("../seo/SeoPage", () => ({ SeoPage: () => <div>guide</div> }));
vi.mock("../RecipientPage", () => ({
RecipientPage: ({ token }: { token: string }) => <div>recipient:{token}</div>,
}));
afterEach(cleanup);
it.each(["/s/%", "/s/%FF"])("handles malformed share path %s", (path) => {
window.history.replaceState(null, "", path);
expect(() => render(<App />)).not.toThrow();
});
it("routes an ordinary token", () => {
window.history.replaceState(null, "", "/s/example-token");
render(<App />);
expect(screen.getByText("recipient:example-token")).toBeTruthy();
});
Run npm test -- src/__tests__/AppRouting.test.tsx from web/.
Observed: the two malformed-path cases fail with URIError: URI malformed; the ordinary-token control passes.
Starting point
web/src/App.tsx calls decodeURIComponent(share[1]) without handling a decoding failure. Keep the change local to route resolution and its error presentation; no router dependency is needed.
Acceptance criteria
- Malformed encoding in the share-token segment renders a clear invalid-link message without throwing from
App. - The invalid path does not mount the recipient page or fetch a share.
- Ordinary tokens and valid percent-encoded tokens retain their existing routing/decoding behavior.
- Add component tests for both malformed examples, the visible error, and valid-token controls. Existing web tests pass.
This is separate from #223 (passphrase retries after a share is fetched) and #274 (guide URL normalization). Do not change share-view consumption or decryption behavior.
Environment: macOS 26.5.2 arm64, Node 24.15.0, Vitest 5.0.0 / happy-dom. Reproduced at the client component boundary; handling of these URLs by a deployed reverse proxy was not tested, and some proxies may reject them before the app loads.
- Dominant language
- Rust
- Stars
- 31
- Forks
- 42
- Avg merge
- 3d 21m
- Merged PRs (30d)
- 147
Getting set up
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from getsotto/sotto
-
Backups are staleOpenbackup-alert
Difficulty 1/5 Under an hour Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
enhancement good first issue help wanted
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
Maintainers usually reply within 1 day
-
Web: show when a pending email verification request expiresPossibly taken @manosm19-netizen claimed this today. Openenhancement good first issue help wanted
Difficulty 2/5 Half a day Newbie friendliness 72/100
getsotto/sotto#827 · 1 comment ·
Maintainers usually reply within 1 day
-
TUI tests: verify binary secret edit rejection preserves stored bytesPossibly taken @dbdefault claimed this today. Opengood first issue help wanted test-coverage
Difficulty 2/5 3-5 days Newbie friendliness 62/100
getsotto/sotto#825 · 2 comments ·
Maintainers usually reply within 1 day
-
Docs: explain listing secret names and the ls JSON outputPossibly taken @Mohamedajab claimed this today. Opendocumentation good first issue help wanted
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
Similar issues
-
Difficulty 1/5 Under an hour Newbie friendliness 94/100
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
Maintainers usually reply within 2 days
-
enhancement good first issue help wanted
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
area:ui
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
openfootmanager/openfootmanager#1365 ·
Maintainers usually reply within 1 day
-
documentation
Difficulty 1/5 1-3 hours Newbie friendliness 90/100
JackMechem/marbble#305 ·
Maintainers usually reply within 1 day