# Risk map — preactjs/preact

> Canonical page: https://codevetter.com/unpack/preact/risk-map

# Risk map — preactjs/preact

Scanned commit 45728eb4315bcb22c5d50b8fec0bbac419eaa799 (2026-10-02). Generated by codevetter codevetter 1.15.0 on 2026-10-02.

Source inspection; upstream code and tests were not executed.

The security-sensitive runtime boundaries inspected are raw HTML assignment, DOM attributes/events, VNode identity checks, compiler serialization overrides, and the devtools attachment hook. Renderer correctness also depends on cross-package property mappings and careful coordination of suspension, hydration, portals, refs, and effects. Tests contain explicit skips, and workflow source exposes concrete wiring and artifact-handling concerns. These are source observations or labeled hypotheses, not confirmed vulnerabilities or reproduced runtime failures; no whole-repository absence of auth, IPC, secrets, or other integrations is claimed.
- dangerouslySetInnerHTML assigns newHtml.__html directly to dom.innerHTML without sanitization in this path; applications must control the supplied HTML trust boundary. — [src/diff/index.js#L655-666](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L655-L666) [src/index.d.ts#L66-70](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/index.d.ts#L66-L70)
- setProperty writes DOM properties or attributes directly, including href through the attribute route; no URL-scheme validation appears in this function. — [src/diff/props.js#L101-143](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/props.js#L101-L143)
- The renderer rejects VNodes whose constructor is not undefined, and both factories establish that marker; changing the marker or coercion logic touches the documented JSON-injection defense. — [src/diff/index.js#L74-76](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L74-L76) [src/create-element.js#L86-103](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/create-element.js#L86-L103) [jsx-runtime/src/index.js#L49-62](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/jsx-runtime/src/index.js#L49-L62) [test/browser/render.test.jsx#L161-179](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/test/browser/render.test.jsx#L161-L179)
- jsxAttr accepts a string returned by options.attr before default entity encoding, so custom serializers control a raw serialized-HTML output boundary. — [jsx-runtime/src/index.js#L113-148](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/jsx-runtime/src/index.js#L113-L148) [jsx-runtime/test/browser/jsx-runtime.test.js#L154-159](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/jsx-runtime/test/browser/jsx-runtime.test.js#L154-L159)
- Devtools attachment invokes a global __PREACT_DEVTOOLS__.attachPreact with shared options and component primitives; that bridge assumes the installed global hook is trusted. — [devtools/src/devtools.js#L3-20](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/devtools/src/devtools.js#L3-L20)
- Private properties shared by independently built bundles require stable mangle mappings; the build warns about newly mangled properties lacking cache entries, making mapping changes a cross-addon compatibility hotspot. — [mangle.json#L2-4](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/mangle.json#L2-L4) [mangle.json#L27-77](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/mangle.json#L27-L77) [scripts/build.mjs#L108-117](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/scripts/build.mjs#L108-L117) [CONTRIBUTING.md#L77-79](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/CONTRIBUTING.md#L77-L79)
- Hooks, debug, compat normalization, and Suspense all replace callbacks on the same options object while retaining previous callbacks; integration behavior depends on preserving those chains. — [hooks/src/index.js#L32-48](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L32-L48) [debug/src/debug.js#L70-76](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/debug/src/debug.js#L70-L76) [compat/src/render.js#L284-320](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/render.js#L284-L320) [compat/src/suspense.js#L11-41](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/suspense.js#L11-L41)
- Hypothesis: suspension/hydration/portal edits have a broad correctness impact because marker rescanning, DOM cursors, alternate containers, retained hook state, and cleanup routing interact across core and addons. (inferred) — [src/diff/index.js#L78-108](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L78-L108) [src/diff/index.js#L309-358](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/src/diff/index.js#L309-L358) [compat/src/suspense.js#L45-82](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/suspense.js#L45-L82) [hooks/src/index.js#L127-160](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L127-L160)
- Three inspected suspended-child update cases and a throwing-ref unmount case are skipped; this is an explicit regression-coverage gap, not proof of current failure. — [compat/test/browser/suspense.test.jsx#L2054-2057](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/test/browser/suspense.test.jsx#L2054-L2057) [compat/test/browser/suspense.test.jsx#L2120-2123](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/test/browser/suspense.test.jsx#L2120-L2123) [compat/test/browser/suspense.test.jsx#L2187-2190](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/test/browser/suspense.test.jsx#L2187-L2190) [test/browser/lifecycles/componentDidCatch.test.jsx#L915-931](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/test/browser/lifecycles/componentDidCatch.test.jsx#L915-L931)
- Compat transition APIs do not provide concurrent scheduling: startTransition calls immediately, useDeferredValue returns its input, useTransition reports false, and useInsertionEffect aliases useLayoutEffect. — [compat/src/hooks.js#L3-17](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/compat/src/hooks.js#L3-L17)
- The manual single-benchmark workflow calls ci.yml as a reusable workflow with ref/artifact_name inputs, but ci.yml declares no workflow_call trigger; it also renames the local tarball before a subsequent rm -rf * step. — [.github/workflows/single-bench.yml#L39-49](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/single-bench.yml#L39-L49) [.github/workflows/single-bench.yml#L59-81](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/single-bench.yml#L59-L81) [.github/workflows/ci.yml#L3-12](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/ci.yml#L3-L12)
- People Browser consumes external Random User JSON without an explicit HTTP status check before parsing/model insertion; malformed responses or model incompatibility can interrupt this demo flow. — [demo/people/store.ts#L36-58](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/people/store.ts#L36-L58) [demo/people/index.tsx#L11-12](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/demo/people/index.tsx#L11-L12)
- The browser configuration selects Chromium only and contains an isolation-leak TODO; browser portability and global addon-state isolation therefore need validation beyond the inspected configuration. (inferred) — [vitest.config.mjs#L214-221](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/vitest.config.mjs#L214-L221) [hooks/src/index.js#L32-37](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/hooks/src/index.js#L32-L37)
- The PR reporter crosses into authenticated GitHub API/comment operations and downloaded benchmark artifacts; release automation has contents-write and id-token-write jobs, making these CI integration boundaries sensitive to workflow edits. — [.github/workflows/pr-reporter.yml#L32-44](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/pr-reporter.yml#L32-L44) [.github/workflows/pr-reporter.yml#L89-106](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/pr-reporter.yml#L89-L106) [.github/workflows/release.yml#L18-30](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/release.yml#L18-L30) [.github/workflows/release.yml#L52-60](https://github.com/preactjs/preact/blob/45728eb4315bcb22c5d50b8fec0bbac419eaa799/.github/workflows/release.yml#L52-L60)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
