fix(core): properly quote shell metacharacters in CLI args passed to tasks#34491
fix(core): properly quote shell metacharacters in CLI args passed to tasks#34491baer wants to merge 2 commits intonrwl:masterfrom
Conversation
✅ Deploy Preview for nx-docs canceled.
|
✅ Deploy Preview for nx-dev canceled.
|
c3da8e1 to
05e6b74
Compare
|
View your CI Pipeline Execution ↗ for commit 2991056
☁️ Nx Cloud last updated this comment at |
There was a problem hiding this comment.
Important
At least one additional CI pipeline execution has run since the conclusion below was written and it may no longer be applicable.
Nx Cloud is proposing a fix for your failed CI:
We've updated the shell quoting logic to properly handle values that are already wrapped in quotes. The original implementation only checked if a value started with a quote character, but didn't verify if it was fully wrapped in matching quotes, causing shell parsing errors when literal quoted strings were passed as arguments. This fix detects fully-wrapped single and double quotes, strips double quotes for re-processing, and preserves single quotes as-is, ensuring shell metacharacters are properly escaped while respecting existing quoting conventions.
Warning
❌ We could not verify this fix.
Suggested Fix changes
diff --git a/packages/nx/src/executors/run-commands/run-commands.impl.ts b/packages/nx/src/executors/run-commands/run-commands.impl.ts
index bbbcfa69b9..21f207c674 100644
--- a/packages/nx/src/executors/run-commands/run-commands.impl.ts
+++ b/packages/nx/src/executors/run-commands/run-commands.impl.ts
@@ -398,22 +398,49 @@ function wrapArgIntoQuotesIfNeeded(arg: string): string {
// Split only on first '=' to handle values containing '='
const eqIndex = arg.indexOf('=');
const key = arg.substring(0, eqIndex);
- const value = arg.substring(eqIndex + 1);
- if (
- key.startsWith('--') &&
- needsShellQuoting(value) &&
- !(value[0] === "'" || value[0] === '"')
- ) {
+ let value = arg.substring(eqIndex + 1);
+
+ // Check if value is wrapped in matching quotes
+ if (value.length >= 2) {
+ const firstChar = value[0];
+ const lastChar = value[value.length - 1];
+
+ if (firstChar === "'" && lastChar === "'") {
+ // Preserve single-quoted values as-is
+ return arg;
+ } else if (firstChar === '"' && lastChar === '"') {
+ // Strip outer double quotes and re-process the inner content
+ value = value.substring(1, value.length - 1);
+ }
+ }
+
+ if (key.startsWith('--') && needsShellQuoting(value)) {
// Escape any existing double quotes in the value
const escaped = value.replace(/"/g, '\\"');
return `${key}="${escaped}"`;
}
- return arg;
- } else if (needsShellQuoting(arg) && !(arg[0] === "'" || arg[0] === '"')) {
- // Escape any existing double quotes in the value
- const escaped = arg.replace(/"/g, '\\"');
- return `"${escaped}"`;
+ return `${key}=${value}`;
} else {
+ // Handle positional arguments
+ // Check if already wrapped in matching quotes
+ if (arg.length >= 2) {
+ const firstChar = arg[0];
+ const lastChar = arg[arg.length - 1];
+
+ if (firstChar === "'" && lastChar === "'") {
+ // Preserve single-quoted values as-is
+ return arg;
+ } else if (firstChar === '"' && lastChar === '"') {
+ // Strip outer double quotes and re-process the inner content
+ arg = arg.substring(1, arg.length - 1);
+ }
+ }
+
+ if (needsShellQuoting(arg)) {
+ // Escape any existing double quotes in the value
+ const escaped = arg.replace(/"/g, '\\"');
+ return `"${escaped}"`;
+ }
return arg;
}
}
🔔 Heads up, your workspace has pending recommendations ↗ to auto-apply fixes for similar failures.
Because this branch comes from a fork, it is not possible for us to apply fixes directly, but you can apply the changes locally using the available options below.
Apply changes locally with:
npx nx-cloud apply-locally iKWx-mv3h
Apply fix locally with your editor ↗ View interactive diff ↗
🎓 Learn more about Self-Healing CI on nx.dev
…tasks When CLI arguments containing shell metacharacters (like |, &, $, etc.) were passed through Nx to underlying tasks, they were not properly quoted, causing shell interpretation errors. For example: nx test app --grep="@tag1|@tag2" Would fail because the pipe character was interpreted by the shell as a command pipe, resulting in errors like "/bin/sh: @tag2: command not found". This fix: - Introduces a new `needsShellQuoting()` utility that detects all shell metacharacters: | & ; < > ( ) $ ` \ " ' * ? [ ] { } ~ # ! and whitespace - Updates `wrapArgIntoQuotesIfNeeded()` to use this utility instead of only checking for spaces - Properly handles values containing `=` by splitting on the first `=` only - Escapes existing double quotes within values before wrapping - Adds comprehensive test coverage for the new functionality Fixes nrwl#32305 Fixes nrwl#26682
05e6b74 to
2ed5bb2
Compare
…hell quoting Add isAlreadyQuoted guard to serializeOverridesIntoCommandLine and wrapArgIntoQuotesIfNeeded to skip re-quoting values already wrapped in matching quotes. Add rejoinQuotedFragments to reassemble shell word-split fragments of quoted values in __unparsed__ before applying quoting logic. Without this, fragments like args"}}' get incorrectly re-quoted, breaking commands that pass JSON config via escaped single quotes (e.g., cypress e2e --config).
2ed5bb2 to
2991056
Compare
|
@baer thanks for contributing! I fixed an issue caught by our e2e test suite and added additional tests to prevent regressions. Could you re-verify on your end that everything still works for you? |
Current Behavior
When CLI arguments containing shell metacharacters (like
|,&,$,;,*, etc.) are passed through Nx to underlying tasks, they are not properly quoted, causing shell interpretation errors.For example, running:
Would fail with
/bin/sh: @smoke: command not foundbecause the pipe character|was interpreted by the shell as a pipe operator instead of being passed as a literal string to the underlying command.Users had to use awkward double-quoting workarounds like
--grep='"@tag1|@tag2"'to get the expected behavior.Expected Behavior
CLI arguments containing shell metacharacters should be automatically quoted before being passed to underlying commands, so that:
Works correctly and passes
--grep="@tag1|@tag2"to the underlying test runner without shell interpretation.Related Issue(s)
Fixes #32305
Fixes #26682
Implementation Details
needsShellQuoting()utility inpackages/nx/src/utils/shell-quoting.tsthat detects shell metacharacterswrapArgIntoQuotesIfNeeded()inrun-commands.impl.tsto use the shared utilitystringShouldBeWrappedIntoQuotes()inserialize-overrides-into-command-line.tsto use the shared utilityarg.split('=')would incorrectly split values containing=(e.g.,--define=FOO=bar|baz)Shell metacharacters now handled:
|&;<>()$`\"'*?[]{}~#!and whitespace