#51

fix(harden-react-markdown): preserve explicitly allowed custom protocol links

OpenimMamdouhaboammarwants to merge intofrom

Summary

Fixes an issue where harden-react-markdown strips explicitly allowed custom protocol URLs during rendering, even after rehype-harden has validated them

This is a follow-up to #36, which introduced custom protocol support in rehype-harden but left an inconsistency in the React wrapper

Problem

When developers explicitly allow protocols such as vscode: or postman:, the underlying rehype-harden plugin accepts those URLs correctly

However, react-markdown subsequently applies its default urlTransform, which removes unsupported schemes and leaves the rendered anchor with an empty href

For example:

<HardenedReactMarkdown allowedProtocols={["vscode:"]}>
  {"[Open editor](vscode://file/workspace/app.ts)"}
</HardenedReactMarkdown>

Before: The link renders with href=""

After: The link retains href="vscode://file/workspace/app.ts"

Changes

  • Preserve explicitly allowed custom protocol links after validation by rehype-harden

  • Keep React Markdown's default URL filtering for ordinary URLs and other attributes

  • Continue blocking dangerous protocols, including javascript:, data:, file:, and vbscript:

  • Keep blob: URLs subject to the existing default filtering behavior

  • Respect user-provided urlTransform callbacks

  • Add regression tests covering Markdown links, raw HTML, wildcard protocol configuration, unsafe URLs, and custom transforms

  • Add a patch Changeset for harden-react-markdown

Security considerations

The implementation only preserves custom schemes on anchor URLs that have already passed through rehype-harden

Malformed blob: URLs remain subject to React Markdown's default filtering because broader acceptance exposed cases flagged by the existing security regression suite

No changes were made to the underlying rehype-harden URL validation rules

Verification

The regression tests reproduced the missing behavior before the implementation was changed

Check | Result -- | -- harden-react-markdown | 358 passed rehype-harden | 2,119 passed markdown-to-markdown-sanitizer | 1,343 passed TypeScript checks | Passed Wrapper build | Passed git diff --check | Passed

Total: 3,820 passing tests

The local package installation reported an unapproved esbuild build script, so validation was performed through the installed test and TypeScript binaries without changing the package manager's build approval policy

Commits

  1. test: reproduce wrapper stripping hardened protocol URLs

  2. fix: preserve opt-in protocols in hardened markdown links

  3. chore: add patch changeset for custom protocol rendering

Related to #36