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-hardenKeep React Markdown's default URL filtering for ordinary URLs and other attributes
Continue blocking dangerous protocols, including
javascript:,data:,file:, andvbscript:Keep
blob:URLs subject to the existing default filtering behaviorRespect user-provided
urlTransformcallbacksAdd 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 | PassedTotal: 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
test: reproduce wrapper stripping hardened protocol URLsfix: preserve opt-in protocols in hardened markdown linkschore: add patch changeset for custom protocol rendering
Related to #36