fix(plugin): harden kubernetes exec upload parsing

This commit is contained in:
Dotta
2026-05-13 14:28:37 -05:00
committed by Chris Farhood
parent 3e998bda97
commit f8b8303089
4 changed files with 30 additions and 1 deletions
@@ -59,6 +59,8 @@ export async function execInPod(
stderrStream.on("data", (chunk: Buffer) => {
stderrData += chunk.toString("utf-8");
});
stdoutStream.on("error", () => {});
stderrStream.on("error", () => {});
return await new Promise<ExecInPodResult>(
(resolve, reject) => {
@@ -17,7 +17,7 @@ import { posix as pathPosix } from "node:path";
const INIT_RE =
/^mkdir -p '([^']+)' && rm -f '([^']+)\.paperclip-upload\.b64' && : > '\2\.paperclip-upload\.b64'$/;
const CHUNK_RE =
/^printf '%s' '([A-Za-z0-9+/=]+)' >> '([^']+)\.paperclip-upload\.b64'$/;
/^printf '%s' '([A-Za-z0-9+/]+={0,2})' >> '([^']+)\.paperclip-upload\.b64'$/;
const FINALIZE_RE =
/^base64 -d < '([^']+)\.paperclip-upload\.b64' > '\1' && rm -f '\1\.paperclip-upload\.b64'$/;
@@ -38,6 +38,17 @@ describe("execInPod", () => {
expect(result).toEqual({ exitCode: 0, timedOut: false, stdout: "ok\n", stderr: "" });
});
it("handles output stream errors after status completion", async () => {
execMock.mockImplementation((_namespace, _pod, _container, _command, stdout, _stderr, _stdin, _tty, statusCallback) => {
statusCallback({ status: "Success" });
stdout.emit("error", new Error("write after end"));
return Promise.resolve(new EventEmitter());
});
const result = await execInPod({} as never, "ns", "pod-1", "agent", ["echo", "ok"]);
expect(result).toEqual({ exitCode: 0, timedOut: false, stdout: "", stderr: "" });
});
it("returns an execution failure if the websocket closes before a status frame", async () => {
const ws = new EventEmitter();
execMock.mockResolvedValue(ws);
@@ -46,6 +46,22 @@ describe("FastUploadInterceptor", () => {
).toMatchObject({ action: "passthrough", reason: "finalize without buffered state" });
});
it("does not intercept chunks with padding before the end", () => {
const interceptor = new FastUploadInterceptor();
const target = "/workspace/file.bin";
expect(
interceptor.decide(
`mkdir -p '/workspace' && rm -f '${target}.paperclip-upload.b64' && : > '${target}.paperclip-upload.b64'`,
),
).toMatchObject({ action: "ack" });
expect(
interceptor.decide(`printf '%s' 'aGVs=bG8=' >> '${target}.paperclip-upload.b64'`),
).toMatchObject({ action: "passthrough", reason: "no upload pattern" });
expect(interceptor.pendingCount).toBe(1);
});
it("falls through when the init command does not match the target parent directory", () => {
const interceptor = new FastUploadInterceptor();