fix(server): reject imported port offsets

This commit is contained in:
Mohamed Boudra
2026-07-17 18:28:10 +02:00
parent d6bc201f46
commit 4d2acfbeb5
2 changed files with 58 additions and 22 deletions

View File

@@ -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", () => {

View File

@@ -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;