# Risk map — d3/d3

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

# Risk map — d3/d3

Scanned commit ca958d45217b4c15332d971b935451a6d4c978f4 (2026-05-28). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

The hottest risks are supply-chain and publish footguns, not application auth. Docs build executes a remote Observable module, the site loads a third-party script, and map examples fetch atlases from jsDelivr at view time. prebuild.sh does not fail fast, the test workflow never checks out d3.github.com, and postpublish force-pushes tags if someone publishes. The barrel, sidebar, API index, and prebuild copy list can drift, and this repo has no tests for the actual visualization code.
- gallery.data.js executes remote code at docs build time: import("https://api.observablehq.com/@d3/gallery.js?v=4") and then runtime.module on its default export. package.json docs scripts enable this with node --experimental-network-imports. A compromised gallery notebook becomes code running inside CI and the deploy job. — [docs/.vitepress/theme/gallery.data.js#L1-8](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/docs/.vitepress/theme/gallery.data.js#L1-L8) [package.json#L85-86](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/package.json#L85-L86)
- config.ts injects https://static.observablehq.com/assets/components/observable-made-by.js into every page via head script async defer, and Vue is told to treat observable-* tags as custom elements. That is a third-party script on the documentation origin. — [docs/.vitepress/config.ts#L14-18](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/docs/.vitepress/config.ts#L14-L18) [docs/.vitepress/config.ts#L35-39](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/docs/.vitepress/config.ts#L35-L39)
- stargazers.data.ts reads process.env.GITHUB_TOKEN and sends it as Authorization: token <value> to api.github.com. The token is not committed. Failure is fatal only when process.env.CI is set, so a rate-limit or auth error fails CI and deploy builds and is swallowed locally as NaN. — [docs/.vitepress/theme/stargazers.data.ts#L4-27](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/docs/.vitepress/theme/stargazers.data.ts#L4-L27)
- postpublish is git push && git push --tags. It runs after a successful npm publish. There is no GitHub Action that publishes to npm; the script is local. Running npm publish from a checkout with a dirty or unintended tag will push the branch and all tags to the remote named by the local git config. — [package.json#L84-85](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/package.json#L84-L85)
- prebuild.sh never calls set -e. The first hundreds of commands cp historical files from ./build/d3.github.com, which only deploy.yml checks out. test.yml still runs yarn docs:build. Because the script's exit status is the last command, and the last commands copy from node_modules after a normal install, missing historical CDN files do not by themselves fail CI. (inferred) — [prebuild.sh#L1-4](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/prebuild.sh#L1-L4) [prebuild.sh#L628-690](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/prebuild.sh#L628-L690) [.github/workflows/test.yml#L19-25](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/.github/workflows/test.yml#L19-L25) [.github/workflows/deploy.yml#L18-22](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/.github/workflows/deploy.yml#L18-L22)
- Docs cannot import d3 until dist/d3.mjs exists, because the Vite alias replaces "d3" with ./dist/d3.mjs. docs:dev does not run Rollup. A clean checkout that runs yarn docs:dev without a prior yarn prepublishOnly fails module resolution for every example that imports d3. — [docs/.vitepress/config.ts#L24-29](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/docs/.vitepress/config.ts#L24-L29) [package.json#L84-86](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/package.json#L84-L86) [docs/components/ExampleBlankChart.vue#L3](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/docs/components/ExampleBlankChart.vue#L3)
- dist/d3.mjs is produced and aliased by the docs site but omitted from package.json files, while exports.default points at src/index.js and exports.umd points at the minified UMD file. Shipping a docs-only ESM bundle that is not the package consumers import is a packaging trap when debugging "works on d3js.org, differs in npm". — [package.json#L24-36](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/package.json#L24-L36) [rollup.config.js#L36-43](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/rollup.config.js#L36-L43)
- WorldMap and UsMap fetch topojson from cdn.jsdelivr.net at browser runtime (world-atlas@2.0.2 and us-atlas@3.0.1) and parse it with topojson-client. Visitors of those doc pages depend on jsDelivr availability and on those package versions staying compatible. deferRender only delays the work; it does not vendor the data. — [docs/components/WorldMap.vue#L55-58](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/docs/components/WorldMap.vue#L55-L58) [docs/components/UsMap.vue#L27-34](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/docs/components/UsMap.vue#L27-L34) [docs/components/deferRender.js#L1-4](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/docs/components/deferRender.js#L1-L4)
- The documented d3.html, d3.svg, and d3.xml helpers fetch a URL and parse it with DOMParser, and d3.image forwards a crossOrigin init. Implementations are not in this repo, but the public API is specified here and is re-exported by the barrel, so any app that passes untrusted URLs into those helpers inherits that behavior. — [docs/d3-fetch.md#L62-96](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/docs/d3-fetch.md#L62-L96) [docs/d3-fetch.md#L114-120](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/docs/d3-fetch.md#L114-L120) [src/index.js#L12](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/src/index.js#L12)
- PlotRender calls Plot[method] where method is a component prop defaulting to "plot". A markdown page can therefore select any export of @observablehq/plot. The fake DOM used for SSR ignores addEventListener, so interactive plots are not faithful until the client replace hook runs. — [docs/components/PlotRender.js#L63-70](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/docs/components/PlotRender.js#L63-L70) [docs/components/PlotRender.js#L132-218](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/docs/components/PlotRender.js#L132-L218)
- terser reserves only InternMap and InternSet. CHANGES.md says ordinal scales key domains with InternMap and that those names must survive minification. Dropping the reserved list, or minifying a future build that needs additional constructor names, would break the UMD build that jsDelivr and unpkg serve. Nothing in test/ constructs the minified bundle and reads those names back. — [rollup.config.js#L50-62](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/rollup.config.js#L50-L62) [CHANGES.md#L11](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/CHANGES.md#L11) [package.json#L31-35](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/package.json#L31-L35)
- Adding or renaming a module is a multi-file contract with partial enforcement. test/d3-test.js fails if a dependency export is missing from src/index.js, and docs-test.js fails if a new page is not linked with a real anchor, but nothing checks that prebuild.sh, .gitignore, docs/api.md, and themeConfig.sidebar were all updated together. .gitignore already duplicates several generated paths (for example d3-array.v3.js is listed twice). — [test/d3-test.js#L10-18](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/test/d3-test.js#L10-L18) [test/docs-test.js#L19-28](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/test/docs-test.js#L19-L28) [.gitignore#L20-24](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/.gitignore#L20-L24) [docs/.vitepress/config.ts#L46-54](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/docs/.vitepress/config.ts#L46-L54)
- Rollup file IO is local and expected: readFileSync("./LICENSE") and an import assertion of ./package.json. volcano.data.js readFileSyncs the watched JSON path VitePress passes in. Neither path is user-controlled. There is no shell-out, no child_process, and no auth middleware anywhere in src/ or docs/. — [rollup.config.js#L1-8](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/rollup.config.js#L1-L8) [docs/data/volcano.data.js#L1-7](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/docs/data/volcano.data.js#L1-L7) [src/index.js#L1-30](https://github.com/d3/d3/blob/ca958d45217b4c15332d971b935451a6d4c978f4/src/index.js#L1-L30)

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)
