# Risk map — vuejs/core

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

# Risk map — vuejs/core

Scanned commit 4ab865a848a1da3d10fb674f857e5fff13094644 (2026-09-18). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

The security surface is narrow but sharp: three code paths build render functions with `new Function`, two paths assign `innerHTML`, and the SFC compiler reads arbitrary files off disk and `require`s user-supplied preprocessors. Structurally, the biggest blast radius is `renderer.ts` (2670 lines, the single implementation behind every platform) and the `__COMPAT__` layer, which threads conditionals through core runtime code that most contributors never exercise. CI hygiene is unusually strong (pinned action SHAs, `persist-credentials: false`, org-membership gating on the ecosystem-ci trigger), so the residual risks are code-level rather than pipeline-level.
- Runtime template compilation evaluates generated code with `new Function('Vue', code)(runtimeDom)` (or `new Function(code)()` in global builds) in both the full build and the compat build — the intended design, but it means any path that can influence a template string achieves arbitrary JS execution. — [packages/vue/src/index.ts#L93-L100](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/vue/src/index.ts#L93-L100) [packages/vue-compat/src/index.ts#L93-L100](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/vue-compat/src/index.ts#L93-L100)
- Two in-DOM template paths are annotated `__UNSAFE__` by the authors and explicitly push trust onto the user: `compileToFunction` reading `el.innerHTML` for a `#selector` template, and `app.mount` copying `container.innerHTML` into `component.template`. — [packages/vue/src/index.ts#L50-L58](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/vue/src/index.ts#L50-L58) [packages/runtime-dom/src/index.ts#L116-L125](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/runtime-dom/src/index.ts#L116-L125) [packages/runtime-core/src/compat/global.ts#L540-L550](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/runtime-core/src/compat/global.ts#L540-L550)
- Trusted Types are only best-effort: `nodeOps.ts` creates a `'vue'` policy when `window.trustedTypes` exists and otherwise `unsafeToTrustedHTML` degrades to the identity function, so v-html/innerHTML writes pass through unwrapped in browsers or configurations without TT. — [packages/runtime-dom/src/nodeOps.ts#L9-L35](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/runtime-dom/src/nodeOps.ts#L9-L35) [packages/runtime-dom/src/modules/props.ts#L14-L20](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/runtime-dom/src/modules/props.ts#L14-L20)
- `stringifyStatic` evaluates constant expressions at compile time with `new Function(\`return (${exp.content})\`)()`, and `validateExpression` similarly constructs a `new Function` to syntax-check user expressions — both run inside the build process, so a malicious SFC executes code in the developer's toolchain. — [packages/compiler-dom/src/transforms/stringifyStatic.ts#L414](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/compiler-dom/src/transforms/stringifyStatic.ts#L414) [packages/compiler-core/src/validateExpression.ts#L43](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/compiler-core/src/validateExpression.ts#L43)
- `compiler-sfc` performs unconstrained file IO outside the repo root: `resolveFS` reads any path the type resolver walks to (imports, tsconfig `references`, `globalTypeFiles`), bounded only by whatever `options.fs` the host plugin supplies. — [packages/compiler-sfc/src/script/resolveType.ts#L913-L943](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/compiler-sfc/src/script/resolveType.ts#L913-L943) [packages/compiler-sfc/src/compileScript.ts#L90-L140](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/compiler-sfc/src/compileScript.ts#L90-L140)
- Style preprocessors are loaded by dynamic `require` with a caller-injectable `preprocessCustomRequire`, so the module actually loaded for `lang="scss"` depends on the host's resolution context rather than a pinned dependency. — [packages/compiler-sfc/src/style/preprocessors.ts#L24-L60](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/compiler-sfc/src/style/preprocessors.ts#L24-L60) [packages/compiler-sfc/src/compileStyle.ts#L24-L34](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/compiler-sfc/src/compileStyle.ts#L24-L34)
- `packages/runtime-core/src/renderer.ts` is 2670 lines and holds mountComponent, setupRenderEffect, the full patch switch and `patchKeyedChildren` in one closure-scoped factory; every change there affects DOM, SSR hydration, runtime-test and any third-party custom renderer simultaneously. — [packages/runtime-core/src/renderer.ts#L335-L360](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/runtime-core/src/renderer.ts#L335-L360) [packages/runtime-core/src/renderer.ts#L1820](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/runtime-core/src/renderer.ts#L1820)
- The `__COMPAT__` layer is woven through hot core paths — mountComponent's `compatMountInstance`, finishComponentSetup's `inline-template` prop lookup, setupRenderEffect's `hook:beforeMount`/`hook:mounted` emissions — yet `packages/vue-compat/**` is excluded from coverage collection, so these branches are the least-verified code in the runtime. — [packages/runtime-core/src/renderer.ts#L1224-L1230](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/runtime-core/src/renderer.ts#L1224-L1230) [packages/runtime-core/src/component.ts#L1013-L1020](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/runtime-core/src/component.ts#L1013-L1020) [vitest.config.ts#L31-L33](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/vitest.config.ts#L31-L33)
- Suspense + async-setup interaction is a recurring correctness hotspot: the head commit had to change an unmount guard from `hydratedEl && !instance.scope.active` to `!instance.scope.active`, and `effect.ts` carries a long comment about effects orphaned when a component is unmounted while awaiting inside `withAsyncContext`. — [packages/runtime-core/src/components/Suspense.ts#L755-L770](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/runtime-core/src/components/Suspense.ts#L755-L770) [packages/reactivity/src/effect.ts#L118-L131](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/reactivity/src/effect.ts#L118-L131)
- The scheduler's recursion guard is dev-only: `checkRecursiveUpdates` is wired in via `const check = __DEV__ ? ... : NOOP`, so an infinite update loop that only manifests in production silently spins instead of warning. — [packages/runtime-core/src/scheduler.ts#L228-L240](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/runtime-core/src/scheduler.ts#L228-L240) [packages/runtime-core/src/scheduler.ts#L283-L295](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/runtime-core/src/scheduler.ts#L283-L295)
- Module-level mutable singletons create ordering hazards across tests and micro-frontends: `renderer`/`enabledHydration` in runtime-dom, `compile`/`installWithProxy` in component.ts, `compileCache` in vue/src/index.ts, and `globalVersion`/`activeSub` in reactivity are all process-global. — [packages/runtime-dom/src/index.ts#L76-L94](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/runtime-dom/src/index.ts#L76-L94) [packages/runtime-core/src/component.ts#L979-L992](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/runtime-core/src/component.ts#L979-L992) [packages/reactivity/src/dep.ts#L19](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages/reactivity/src/dep.ts#L19)
- The `ecosystem-ci-trigger` workflow accepts `/ecosystem-ci run` from issue comments with `issues: write`/`pull-requests: write` permissions, mitigated by a `checkMembershipForUser({ org: 'vuejs' })` gate that throws for non-members — the gate is the only thing between a comment and a triggered pipeline. — [.github/workflows/ecosystem-ci-trigger.yml#L1-L50](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/.github/workflows/ecosystem-ci-trigger.yml#L1-L50)
- Workflow supply-chain hygiene is deliberately tight: every third-party action is pinned to a full commit SHA, all checkouts use `persist-credentials: false`, and `release.yml` is guarded by both `github.repository == 'vuejs/core'` and a protected `Release` environment. — [.github/workflows/test.yml#L13-L25](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/.github/workflows/test.yml#L13-L25) [.github/workflows/release.yml#L13-L35](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/.github/workflows/release.yml#L13-L35) [.github/workflows/size-report.yml#L20-L45](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/.github/workflows/size-report.yml#L20-L45)
- Because the SFC playground's vite config `spawnSync`s git and copies built bundles out of `../../packages` at bundle time, a playground build silently fails late (throwing 'not built. Run "nr build vue -f esm-browser" first') rather than at config load — a fragile coupling between two build systems. — [packages-private/sfc-playground/vite.config.ts#L29-L60](https://github.com/vuejs/core/blob/4ab865a848a1da3d10fb674f857e5fff13094644/packages-private/sfc-playground/vite.config.ts#L29-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)
