CodeVetter · Repo Unpack · Risk map
prettier/prettier
The largest security surface is intentional code execution: config files and plugins are imported from user-controlled paths, and --write overwrites arbitrary matched files. Plugin last-wins lets a later plugin replace builtin parsers/printers. Cache writes with restrictAccessToCwd:false. Generated plugin registries and the dist/ publish boundary are easy to edit in the wrong place. Option policy and the removed custom-parser API are social/API traps, not memory-safety bugs.
- loadPlugin/importPlugin will import() a URL, an absolute path, a cwd-relative file, or a node_modules specifier via importFromDirectory (import-meta-resolve from a fake noop.js parent). This is how --plugin and config plugins run third-party code in-process.
- JS/TS Prettier configs are executed, not parsed: loaders['.js'|'.mjs'|'.cjs'|'.ts'|'.mts'|'.cts'] = importModuleDefault via import(pathToFileURL(file)). External string configs try requireFromFile first then importFromFile.
- --write calls mockable.writeFormattedFile(filename, output) which is fs.writeFile; expandPatterns can match any non-ignored file under cwd. docs/cli.md warns this overwrites files. process.exitCode is set per-file rather than aborting the whole run on a single IO error.
- FormatResultsCache constructs file-entry-cache with restrictAccessToCwd: false. --cache-location can point anywhere that is not a directory; default falls back to os.tmpdir() if find-cache-directory fails.
- Parser/printer lookup uses plugins.findLast, so a user or config plugin can silently replace babel/estree/html printers. Integration tests exist for plugin-override-builtin-printers and plugin-override-buitin-plugins (filename typo).
- scripts/release/release.js uses child_process.exec(command) for git fetch --tags and yarn before importing run.js. The comment says it cannot use dependencies because they may not be installed yet. This is maintainer-only, not the formatter runtime.
- development-plugins.js and production-plugins.js are generated ('do NOT edit'); editing them by hand will be overwritten by yarn generate-builtin-plugins. Production plugin graph (src/plugins/*.js) is a different split than language-* modules.
- src/index.cjs in non-production throws if prettier.util or prettier.doc is accessed (ESM-only in dev CJS). Production CJS requires those files directly. Mixing NODE_ENV and CJS consumers is a support trap.
- Custom Parser API was removed in v3.0.0 (docs/api.md); plugins must use the Plugin API. printer.embed with >2 parameters throws, telling authors the v3 plugin API changed.
- No-new-options policy is enforced socially: CONTRIBUTING.md and docs/option-philosophy.md say option-request PRs are closed without discussion. Agents that add a formatting flag will be rejected.
- handleError maps UndefinedParserError to exit 2 (or ignore with --ignore-unknown); ConfigError/validation calls process.exit(1) immediately (fails the whole CLI); parse errors set exitCode 2 and continue. Check mode uses exit 1 for unformatted files.
- Website playground Worker loads /worker.mjs?version= and production Vite aliases npm deps to esm.sh CDN URLs (website/vite.config.mjs buildCdnAlias). That is a supply-chain/runtime dependency of prettier.io, not of the formatter library. (inferred)