fix(pkg-manifest.utils): use Object.defineProperty for runtime-dep write

CodeQL alert 159 (js/prototype-polluting-assignment) stayed open after
#11609 was merged: the barrier I added in convertEnginesRuntimeToDependencies
checked a re-aliased `key` variable, but the actual write went through the
original loop variable `runtimeName`. CodeQL's local data-flow analysis
followed `runtimeName` and didn't see the barrier.

Replace the inline barrier + dynamic assignment with `Object.defineProperty`,
which is the CodeQL-blessed sanitiser for this pattern. The dependency-map
write now produces a regular own data property even if a future entry in
`RUNTIME_NAMES` happens to match an inherited name like `__proto__`, so the
analyser is happy without depending on any pre-write check.
This commit is contained in:
Zoltan Kochan
2026-05-13 00:56:34 +02:00
parent 50b33c1e6b
commit 9cad8274fd
2 changed files with 16 additions and 7 deletions

View File

@@ -0,0 +1,6 @@
---
"@pnpm/pkg-manifest.utils": patch
"pnpm": patch
---
`convertEnginesRuntimeToDependencies`: switch the runtime-dependency write to `Object.defineProperty` so CodeQL's `js/prototype-polluting-assignment` analyser recognises the assignment as safe regardless of the property name (follow-up to [#11609](https://github.com/pnpm/pnpm/pull/11609)).

View File

@@ -29,14 +29,17 @@ export function convertEnginesRuntimeToDependencies (
if ('webcontainer' in process.versions) {
globalWarn(`Installation of ${runtimeName} versions is not supported in WebContainer`)
} else {
// Inline barrier — CodeQL js/prototype-polluting-assignment recognizes
// the literal equality checks but not the equivalent helper call on this
// code path. Unreachable for the current RUNTIME_NAMES, but keeps the
// dynamic assignment safe if a future entry is added.
const key: string = runtimeName
if (key === '__proto__' || key === 'constructor' || key === 'prototype') continue
const deps = (manifest[dependenciesFieldName] ??= {})
deps[runtimeName] = `runtime:${runtime.version}`
// Use Object.defineProperty so a future RUNTIME_NAMES entry that
// happens to match an inherited property name (`__proto__`,
// `constructor`, `prototype`) becomes a regular own data property
// instead of altering Object.prototype.
Object.defineProperty(deps, runtimeName, {
value: `runtime:${runtime.version}`,
enumerable: true,
writable: true,
configurable: true,
})
}
}
}