Skip to content

Commit 34d4a1c

Browse files
committed
fix: convert kubectl port-forward execution to shell:false
Signed-off-by: Fantomasa <ivailoinfo@gmail.com>
1 parent 5f326bc commit 34d4a1c

1 file changed

Lines changed: 15 additions & 5 deletions

File tree

src/integration/kube/k8-client/resources/pod/k8-client-pod.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ export class K8ClientPod implements Pod {
254254
// WSL2 has issues with kubectl port-forward when binding to localhost, binding to all interfaces will trigger
255255
// a permission prompt which if hidden behind the terminal can cause the port-forward command to fail.
256256
if (!isWindows) {
257-
cmdArguments.push(localBindAddress, '&');
257+
cmdArguments.push(localBindAddress);
258258
}
259259
} else {
260260
cmd = constants.KUBECTL;
@@ -280,16 +280,14 @@ export class K8ClientPod implements Pod {
280280
}
281281
}
282282

283-
// Don't use shell on Windows when doing persist mode to avoid argument parsing issues
284-
const useShell: boolean = isWindows && persist ? false : true;
285-
283+
// shell:false to eliminate shell-injection; ShellRunner's detached spawn handles backgrounding.
286284
await new ShellRunner().run(cmd, cmdArguments, {
287285
verbose: true,
288286
detached: true,
289287
environmentVariablesToAppend: {
290288
PATH: `${this.kubectlInstallationDirectory}${path.delimiter}${process.env.PATH}`,
291289
},
292-
useShell,
290+
useShell: false,
293291
});
294292

295293
return availablePort;
@@ -338,6 +336,15 @@ export class K8ClientPod implements Pod {
338336
return matchedProcesses;
339337
}
340338

339+
// Best-effort: signal the process group led by pid (detached forwards are group leaders); ignore if not a leader.
340+
private static killProcessGroup(pid: number, signal: NodeJS.Signals): void {
341+
try {
342+
process.kill(-pid, signal);
343+
} catch {
344+
// not a group leader (or already exited); the direct process.kill(pid) call handles it
345+
}
346+
}
347+
341348
public async stopPortForward(port: number): Promise<void> {
342349
if (!port) {
343350
return;
@@ -364,6 +371,8 @@ export class K8ClientPod implements Pod {
364371
// Extract PIDs and kill the processes
365372
for (const pid of matchedProcesses.map((p: ProcessInfo): number => p.pid)) {
366373
try {
374+
// Signal the whole detached process group so no orphaned kubectl child keeps the local port. #4953
375+
K8ClientPod.killProcessGroup(pid, 'SIGTERM');
367376
process.kill(pid, 'SIGTERM');
368377
this.logger.debug(`Successfully sent SIGTERM to PID: ${pid}`);
369378

@@ -376,6 +385,7 @@ export class K8ClientPod implements Pod {
376385
this.logger.debug(
377386
`Process with PID ${pid} is still running after SIGTERM, attempting to kill with SIGKILL`,
378387
);
388+
K8ClientPod.killProcessGroup(pid, 'SIGKILL');
379389
process.kill(pid, 'SIGKILL');
380390
}
381391
} catch (killError) {

0 commit comments

Comments
 (0)