From 4d2acfbeb552e8730eff94bb7e5aec8344592965 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Fri, 17 Jul 2026 18:28:10 +0200 Subject: [PATCH] fix(server): reject imported port offsets --- .../import/sources/conductor/importer.test.ts | 60 ++++++++++++------- .../import/sources/conductor/importer.ts | 20 +++++++ 2 files changed, 58 insertions(+), 22 deletions(-) diff --git a/packages/server/src/server/session/project-config/import/sources/conductor/importer.test.ts b/packages/server/src/server/session/project-config/import/sources/conductor/importer.test.ts index cad949294..17ad6e9b3 100644 --- a/packages/server/src/server/session/project-config/import/sources/conductor/importer.test.ts +++ b/packages/server/src/server/session/project-config/import/sources/conductor/importer.test.ts @@ -445,45 +445,61 @@ command = "npm run dev -- --port \${CONDUCTOR_PORT:-3000}" }); }); - test("rewrites bare Conductor ports inside shell arithmetic", () => { + test("reports Conductor port arithmetic as unsupported instead of importing it", () => { const repo = makeRepo(); writeSharedToml( repo, ` [scripts.run.dev] command = "npm run dev -- --hmr-port $((CONDUCTOR_PORT + 1))" -`, - ); - expect(inspect(repo).preview).toMatchObject({ - scripts: { - dev: { - type: "service", - command: "npm run dev -- --hmr-port $((PASEO_PORT + 1))", - }, - }, - }); - }); - - test("preserves shell arithmetic expansion in script arguments", () => { - const repo = makeRepo(); - writeSharedToml( - repo, - ` -[scripts.run.dev] +[scripts.run.args] command = "npm run dev" args = ["--hmr-port=$((CONDUCTOR_PORT + 1))"] + +[scripts.run.plain] +command = "npm run dev -- --port $((CONDUCTOR_PORT))" `, ); - expect(inspect(repo).preview).toMatchObject({ + const preview = inspect(repo); + + expect(preview.preview).toEqual({ scripts: { - dev: { + plain: { + command: "npm run dev -- --port $((PASEO_PORT))", type: "service", - command: `npm run dev '--hmr-port='"$((PASEO_PORT + 1))"`, }, }, }); + expect(preview.items).toEqual([ + { + key: "scripts.args.port_arithmetic", + label: "Script args port arithmetic", + outcome: "unsupported", + detail: + "Conductor port arithmetic is not imported because Paseo reserves one service port.", + }, + { + key: "scripts.dev.port_arithmetic", + label: "Script dev port arithmetic", + outcome: "unsupported", + detail: + "Conductor port arithmetic is not imported because Paseo reserves one service port.", + }, + { + key: "variables.CONDUCTOR_PORT", + label: "CONDUCTOR_PORT", + outcome: "rewrite", + detail: "CONDUCTOR_PORT -> PASEO_PORT", + }, + { + key: "scripts.plain", + label: "Script plain", + outcome: "import", + detail: "npm run dev -- --port $((PASEO_PORT))", + }, + ]); }); test("does not import scripts available only in Conductor cloud", () => { diff --git a/packages/server/src/server/session/project-config/import/sources/conductor/importer.ts b/packages/server/src/server/session/project-config/import/sources/conductor/importer.ts index dbb7193e5..cbf90963c 100644 --- a/packages/server/src/server/session/project-config/import/sources/conductor/importer.ts +++ b/packages/server/src/server/session/project-config/import/sources/conductor/importer.ts @@ -336,6 +336,15 @@ function mapRunScript( } let command = appendArgs(script.command, script.args ?? []); + if (containsArithmeticVariableOperation(command, "CONDUCTOR_PORT")) { + items.push({ + key: `scripts.${scriptId}.port_arithmetic`, + label: `Script ${scriptId} port arithmetic`, + outcome: "unsupported", + detail: "Conductor port arithmetic is not imported because Paseo reserves one service port.", + }); + return; + } if (script.options?.cwd) { const cwdPrefix = safeCwdPrefix(script.options.cwd); if (!cwdPrefix) { @@ -546,6 +555,17 @@ function containsArithmeticVariable(command: string, name: string): boolean { return false; } +function containsArithmeticVariableOperation(command: string, name: string): boolean { + const identifier = new RegExp(`(^|[^A-Za-z0-9_])${name}(?![A-Za-z0-9_])`); + for (const match of command.matchAll(/\$\(\(([\s\S]*?)\)\)/g)) { + const body = match[1].trim(); + if (identifier.test(body) && body !== name) { + return true; + } + } + return false; +} + function appendArgs(command: string, args: string[]): string { if (args.length === 0) { return command;