fix(deps): Upgrade js-yaml to 4.3.1 (GHSA-5p4m-2wfm-xmqj) (#13704)
## Summary
Remediates **GHSA-5p4m-2wfm-xmqj** / CVE-2026-59870 (High, CWE-407) —
quadratic CPU consumption in js-yaml's `!!omap` resolution.
`resolveYamlOmap()` enforces `!!omap` key uniqueness with a linear
`objectKeys.indexOf()` scan inside the per-element loop, making
resolution **O(n²)** in the number of entries. `!!omap` is registered in
the **default schema**, so a plain `yaml.load(untrustedInput)` with no
options is affected — the work happens synchronously and blocks the
event loop for its whole duration.
## Scope: three packages, not one
The report flagged `@turbo/workspaces` (4.3.0), but the advisory range
is `>=4.0.0 <4.3.1`, so the two `4.2.0` pins are vulnerable to the same
issue. All three call `yaml.load()` with the default schema:
| Package | Was | Now | Call site |
|---|---|---|---|
| `@turbo/workspaces` | 4.3.0 | 4.3.1 | `src/utils.ts:363` — parses
`pnpm-workspace.yaml` |
| `@turbo/utils` | 4.2.0 | 4.3.1 | `src/get-turbo-configs.ts:124` |
| `@turbo/test-utils` | 4.2.0 | 4.3.1 | `src/use-fixtures.ts:102` |
`@turbo/utils` matters in particular: it is consumed by
`@turbo/workspaces` and several other packages, so fixing only the
reported pin would have left the same vulnerable code reachable.
4.3.1 replaces the linear scan with an O(1) key lookup. Staying on the
4.x line keeps this a patch-level bump with no API change.
## Verification
**Advisory PoC** — load time now scales linearly instead of
quadratically:
| n | 4.3.0 (per advisory) | 4.3.1 (this branch) |
|---|---|---|
| 10,000 | 54ms | 62ms |
| 20,000 | 169ms | 77ms |
| 40,000 | 646ms | 135ms |
| 80,000 | (worse still) | 253ms |
Correctness preserved: duplicate `!!omap` keys are still rejected
(`YAMLException`), and valid omaps parse as before.
**Tests** — captured a baseline on unmodified `main`, then re-ran after
the bump. Suite and test counts are byte-identical across every changed
package and every consumer of `@turbo/test-utils`:
- `@turbo/workspaces` — 986 passed
- `@turbo/utils` — 89 passed
- `turbo-ignore` — 74 passed
- `turbo-codemod`, `turbo-gen`, `eslint-plugin-turbo`, `create-turbo` —
unchanged from baseline
The pre-existing failures in those last four are sandbox artifacts
(`Cannot find module '@turbo/workspaces'` — the `test` task `dependsOn:
["^build"]` and `dist/` was not built here). They fail identically
before and after this change.
`pnpm install --frozen-lockfile` succeeds, confirming the lockfile
matches the manifests. The lockfile also deduped
`@fumari/json-schema-to-typescript`, `@textlint/*`, and
`rc-config-loader` onto 4.3.1, since those carry `^4` ranges.
## Left out, deliberately
The docs site still resolves js-yaml 4.2.0 transitively through
`fumadocs-mdx` and `fumadocs-openapi`, which pin the version exactly.
Forcing those would require a `pnpm.overrides` entry pushing a version
upstream has not tested against, and those packages parse only
repo-owned MDX at build time — there is no untrusted-input path. Happy
to add the override if you would rather have a clean `pnpm audit`.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>