Skip to content

Conversation

@BioPhoton
Copy link
Collaborator

@BioPhoton BioPhoton commented Sep 8, 2025

This PR includes:

  • move all command helper into 1 file - command.ts
  • adjust executeProgress - removed hard dependencies to other functions so it is easier to use in nx-plugin
  • copied command.ts and executeProcess.ts into nx-plugin project and marked them

@BioPhoton BioPhoton marked this pull request as ready for review October 4, 2025 11:10
@BioPhoton BioPhoton requested a review from matejchalk as a code owner October 4, 2025 11:10
return new Promise((resolve, reject) => {
// shell:true tells Windows to use shell command for spawning a child process
const process = spawn(command, args, { cwd, shell: true });
const spawnedProcess = spawn(command, args ?? [], {

Check warning

Code scanning / CodeQL

Unsafe shell command constructed from library input Medium

This shell argument which depends on
library input
is later used in a
shell command
.

Copilot Autofix

AI about 1 month ago

To fix this, we should not use { shell: true } with arguments provided directly by untrusted/library input. Instead, we should use { shell: false } (the default), allowing spawn to pass the command and arguments directly to the process without interpretation by the shell. If there are legitimate use cases requiring shell features (such as pipes, I/O redirection), then inputs embedded in the shell command line must be safely escaped using a library like shell-quote. But for generic process execution, switching to non-shell mode is safest.

Thus:

  • In packages/nx-plugin/src/internal/execute-process.ts, at line 175, remove or change shell: true to either omit it or set shell: false.
  • No other code needs changing, unless shell features are known to be required for all usage (which is not demonstrated here).

Alternatively, if some processes require shell interpretation and others do not, expose { shell: true/false } as an explicit configurable option, and document the dangers, but set default to false.

Suggested changeset 1
packages/nx-plugin/src/internal/execute-process.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/packages/nx-plugin/src/internal/execute-process.ts b/packages/nx-plugin/src/internal/execute-process.ts
--- a/packages/nx-plugin/src/internal/execute-process.ts
+++ b/packages/nx-plugin/src/internal/execute-process.ts
@@ -173,7 +173,6 @@
   return new Promise((resolve, reject) => {
     // shell:true tells Windows to use shell command for spawning a child process
     const spawnedProcess = spawn(command, args ?? [], {
-      shell: true,
       windowsHide: true,
       ...options,
     }) as ChildProcessByStdio<Writable, Readable, Readable>;
EOF
@@ -173,7 +173,6 @@
return new Promise((resolve, reject) => {
// shell:true tells Windows to use shell command for spawning a child process
const spawnedProcess = spawn(command, args ?? [], {
shell: true,
windowsHide: true,
...options,
}) as ChildProcessByStdio<Writable, Readable, Readable>;
Copilot is powered by AI and may make mistakes. Always verify output.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a comment that we need it for windows (I'm not sure what exactly the reason was :D)

command: string;
args?: string[];
observer?: ProcessObserver;
verbose?: boolean;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding verbose: isVerbose() to each usage is quite error-prone. We lose the advantage of setting CP_VERBOSE once and then having all process executions automatically logged or not consistently. 😬

However, I understand you're trying to make the implementation as isolated as possible, to make it easier to maintain a copy.

I think there may be a better solution. CJS can't statically import ESM with require 1, but CJS can dynamically import ESM. For many synchronous utility functions, this would be a little inconvenient - code would need to become async because of await import('@code-pushup/utils') - but executeProcess is already asynchronous, so there isn't really any downside.

const { executeProcess } = await import('@code-pushup/utils');
await executeProcess(...);

For convenience, you could implement a simple wrapper to deduplicate usage across @code-pushup/nx-plugin:

// somewhere in nx-plugin's internal utils

// same function signature => exact same usage
export async function executeProcess(cfg: ProcessConfig): Promise<ProcessResult> {
  // CJS-compatible import
  const utils = await import('@code-pushup/utils');
  // no duplicated logic, call original code
  return utils.executeProcess(cfg);
}
Experiments with importing ESM into CJS
// utils.mjs

// top-level await to break require(esm) in modern Node
await new Promise(resolve => {
  setTimeout(resolve, 500);
});

// ESM syntax
export function greet(name = 'world') {
  console.log(`Hello, ${name}!`);
}
// nx-plugin.cjs

// ❌ THIS DOESN'T WORK (ERR_REQUIRE_ESM or ERR_REQUIRE_ASYNC_MODULE)
const { greet } = require('./utils.mjs');
greet('Nx plugin');
// nx-plugin.cjs

// ✅ THIS WORKS
import('./utils.mjs').then(({ greet }) => {
  greet('Nx plugin');
});

Footnotes

  1. Technically, CJS can statically require ESM if running a newer Node version (22+, I believe) and there's no top-level await anywhere in the imported module. Unfortunately, because we want to maintain compatibility for users running older Node versions, the minimum version is probably a deal-breaker for us at the moment.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered importing executeProcess from @code-pushup/utils instead of duplication?

@github-actions github-actions bot removed 🧩 core 🧩 js-packages-plugin Plugin for audit and outdated dependencies labels Oct 23, 2025
@BioPhoton BioPhoton requested a review from matejchalk October 23, 2025 13:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants