refactor(pnpm): rename managerData.pnpmShrinkwrap to pnpmLockFile
The field holds the path to pnpm-lock.yaml, and pnpm has not called that file
a shrinkwrap since v5, so the old name reads as a different artifact than the
one it points at.
Co-authored-by: Jamie Tanna <jamie.tanna@mend.io>
As noted in #43761, we had recently set `CI=true` for the Renovate and
Containerbase orgs on Mend-hosted apps due to a pnpm v11 requirement:
ERR_PNPM_ABORTED_REMOVE_MODULES_DIR_NO_TTY Aborted removal of
modules directory due to no TTY
If you are running pnpm in CI, set the CI environment variable to
"true", or set "confirmModulesPurge" to "false".
Similarly in #43606, it was noted that we should do this for `mise lock`
to mark the workspace as "trusted".
Although we went with a different situation, there will be cases where
this is useful for tools being executed, for instance when we invoke `yarn`.
We also make sure to keep our documentation up-to-date as we add to the
specific overrides.
With help from Claude Sonnet and Opus to:
- perform some of the mechanical updates as part of this change
- ensure that the Docker sidecar always has `hardcodedProcessEnv` passed
into it
- catch a needed change to `exposeAllEnv` handling
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
When executing commands for `binarySource=docker`, we have two areas
that a command could have shell expansion/injection:
- on the host (when constructing the `bash -l -c`)
- in the container (when running a manager-specific command)
When constructing the host's command, we previously would escape the
`"`, which allows Bash syntax for `${VAR}` and `$(...)` syntax inside
it.
While managers use `quote()` to make the inputs safer, the use of `"`
around that would not provide any protection, and lead to cases where
the `quote()` could be expanded on the host, before `docker run`
executes.
After recent changes to each manager - and removal of `shell: true` -
this doesn't seem to be exploitable, but this hardens it further.
Additionally, we never escaped `\`, so a value containing `\"` produces
a command that `shlex.split` rejects, and the update fails rather than
running.
Instead, we can quote the whole `-c` argument with `quote()`, which
round-trips through both `shlex.split` - the default, no-shell path - and
a real shell.
This removes the `lgtm [js/incomplete-sanitization]` suppression as
we're now correctly sanitising this.
Note this doesn't remove the need for the managers to quote their own
values, as the second area above is still a real shell.
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
To prevent the possibility of a npm package name including shell
metacharacters (which is unlikely, but potentially possible through a
well-crafted payload), which could lead to command execution, we can
make sure they are quoted appropriately.
Co-authored-by: Claude Opus 5 <jamie.tanna+claude-code@mend.io>
* refactor(types): document `localDir` isn't a `globalOnly` option
To make sure that it's clear when reviewing `GlobalConfig`, as well as
working with our types, we can move the `localDir` definition into a
separate type.
* fixup! refactor(types): document `localDir` isn't a `globalOnly` option
* fixup! refactor(types): document `localDir` isn't a `globalOnly` option
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
* fixup! refactor(types): document `localDir` isn't a `globalOnly` option
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
* fixup! refactor(types): document `localDir` isn't a `globalOnly` option
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
---------
Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
* fix(npm): use packageName for pnpm overrides with range selectors in minimumReleaseAgeExclude
For pnpm.overrides entries with version range selectors (e.g. "pkg@<=1.0.0"),
depName contains the full override key including the selector, which is needed
to correctly update package.json. However this caused invalid entries in
minimumReleaseAgeExclude like "fast-xml-parser@<=5.3.5@5.5.7" instead of
"fast-xml-parser@5.5.7", breaking pnpm install with ERR_PNPM_INVALID_MINIMUM_RELEASE_AGE_EXCLUDE.
Fix by using packageName (bare npm package name parsed by @pnpm/parse-overrides)
instead of depName when constructing minimumReleaseAgeExclude entries. Falls back
to depName for dependency types where packageName is not set.
Made-with: Cursor
* test(npm): format pnpm override minimumReleaseAgeExclude test
Use codeBlock helper for expected pnpm-workspace.yaml contents per review.
* test(npm): avoid string concat in shrinkwrap exclude test
Use template literal instead of + to satisfy restrict-plus-operands.
* fix(npm): replace malformed minimumReleaseAgeExclude entries
Skip invalid package@version lines (extra @ after package prefix) when
matching excludes, and replace them instead of appending versions.
* test(npm): cover valid vs malformed exclude matching
* Update lib/modules/manager/npm/artifacts.spec.ts
Co-authored-by: Michael Kriese <michael.kriese@gmx.de>
* Update lib/modules/manager/npm/artifacts.spec.ts
Co-authored-by: Michael Kriese <michael.kriese@gmx.de>
* test(npm): use codeBlock for expected workspace contents
* fix(npm): remove stale malformed minimumReleaseAgeExclude entries
When a valid exclude entry exists for the same package, drop leftover
malformed lines from the prior double-@ bug instead of leaving them.
* test(npm): avoid string concat in codeBlock expectations
Use template literal instead of + '\n' for prefer-template.
---------
Co-authored-by: Tapio Oikarinen <tapio.oikarinen@hoxhunt.com>
Co-authored-by: Martijn van der Meij <Squixx@users.noreply.github.com>
Co-authored-by: Michael Kriese <michael.kriese@gmx.de>
* test: ensure we set `binarySource=global`
As part of future changes, we'll correctly return the default value from
`GlobalConfig.get`, instead of `undefined` if no default is provided in
the `get` call.
As `binarySource=install` is the default, this then leads to these tests
failing, as they're intended to run with `binarySource=global`.
Co-authored-by: Claude Sonnet 4.6 <jamie.tanna+claude-code@mend.io>
* fixup! test: ensure we set `binarySource=global`
* fixup! fixup! test: ensure we set `binarySource=global`
---------
Co-authored-by: Claude Sonnet 4.6 <jamie.tanna+claude-code@mend.io>
As noted in #40774, if a `pnpm-lock.yaml` isn't found, the
`pnpmShrinkwrap` is undefined, which then results in `upath.dirname`
throwing an error.
We can make sure that we have a value, and exit out early if not.
This is a short-term fix, and we should follow-up to make it so we can
find the `pnpm-workspace.yaml` if there's not a lockfile.
As noted in #40611, we shouldn't reference the same version multiple
times in the same exclude directive.
This also handles the case where we could end up with multiple comments
for a given package.
This is slightly more complex logic, and slightly simplifes by
extracting out to a new helper method.
We also need to make sure that we use `getIn` so we get the raw node, so
we can look at the comment inside the `minimumReleaseAgeExclude`'s list,
but before the first element.
As noted in #40610, when `newVersion` contains a caret (or other
npm-specific versioning constraints) a generated
`minimumReleaseAgeExclude` directive will result in
ERR_PNPM_INVALID_MINIMUM_RELEASE_AGE_EXCLUDE Invalid value in
minimumReleaseAgeExclude: Invalid versions union. Found: "...". Use
exact versions only.
To handle this better, we can make sure we use `newVersion` over
`newValue`, as it'll provide the raw version number.
We also need to update an unrelated to test to make sure it's using
`newVersion`.