Web: malformed share-token encoding throws during routing

Open Beginner friendly
#302 0 comments 0 reactions 0 assignees View on GitHub

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
Domain
frontend, testing

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

bug good first issue help wanted

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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from getsotto/sotto

All issues in getsotto/sotto

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.