fix: bound cli dump previews from yaml
This commit is contained in:
+67
-3
@@ -1,20 +1,23 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import type { UniDeskConfig } from "./config";
|
||||
import {
|
||||
createPosixApplyPatchFileSystem,
|
||||
createSshStderrForwarder,
|
||||
createSshStdoutForwarder,
|
||||
formatSshStdoutTruncationHint,
|
||||
parseSshInvocation,
|
||||
sshCaptureBackendPlan,
|
||||
sshStderrStreamMaxBytes,
|
||||
sshStdoutStreamMaxBytes,
|
||||
sshStdoutTruncationHint,
|
||||
windowsFsReadOnlyScript,
|
||||
windowsPowerShellScriptPrelude,
|
||||
} from "./ssh";
|
||||
import { readCliOutputPolicy } from "./output";
|
||||
|
||||
function sha256Hex(text: string): string {
|
||||
return createHash("sha256").update(text, "utf8").digest("hex");
|
||||
@@ -150,14 +153,18 @@ describe("ssh host apply-patch fs backend", () => {
|
||||
});
|
||||
|
||||
describe("ssh stdout bounded streaming", () => {
|
||||
test("uses bounded defaults and clamps env override", () => {
|
||||
expect(sshStdoutStreamMaxBytes({} as NodeJS.ProcessEnv)).toBe(256 * 1024);
|
||||
test("uses YAML output policy defaults and clamps env override", () => {
|
||||
const policy = readCliOutputPolicy();
|
||||
expect(sshStdoutStreamMaxBytes({} as NodeJS.ProcessEnv)).toBe(policy.maxStdoutBytes);
|
||||
expect(sshStderrStreamMaxBytes({} as NodeJS.ProcessEnv)).toBe(policy.maxStdoutBytes);
|
||||
expect(sshStdoutStreamMaxBytes({ UNIDESK_SSH_STDOUT_STREAM_MAX_BYTES: "8192" } as NodeJS.ProcessEnv)).toBe(8192);
|
||||
expect(sshStderrStreamMaxBytes({ UNIDESK_SSH_STDERR_STREAM_MAX_BYTES: "8192" } as NodeJS.ProcessEnv)).toBe(8192);
|
||||
expect(sshStdoutStreamMaxBytes({ UNIDESK_SSH_STDOUT_STREAM_MAX_BYTES: "1" } as NodeJS.ProcessEnv)).toBe(4096);
|
||||
expect(sshStdoutStreamMaxBytes({ UNIDESK_SSH_STDOUT_STREAM_MAX_BYTES: String(64 * 1024 * 1024) } as NodeJS.ProcessEnv)).toBe(16 * 1024 * 1024);
|
||||
});
|
||||
|
||||
test("formats truncation hint without echoing remote command", () => {
|
||||
const policy = readCliOutputPolicy();
|
||||
const invocation = parseSshInvocation("D601:win", ["ps"]);
|
||||
expect(invocation.parsed.remoteCommand).not.toBeNull();
|
||||
const hint = sshStdoutTruncationHint({
|
||||
@@ -175,6 +182,11 @@ describe("ssh stdout bounded streaming", () => {
|
||||
expect(payload.providerId).toBe("D601");
|
||||
expect(payload.route).toBe("D601:win");
|
||||
expect(payload.thresholdBytes).toBe(4096);
|
||||
expect(payload.disclosurePolicy).toMatchObject({
|
||||
name: "unified-cli-dump-preview",
|
||||
configPath: "config/unidesk-cli.yaml",
|
||||
});
|
||||
expect(payload.thresholdLines).toBe(policy.maxPreviewLines);
|
||||
expect(formatted).not.toContain("Get-Content");
|
||||
});
|
||||
|
||||
@@ -201,6 +213,58 @@ describe("ssh stdout bounded streaming", () => {
|
||||
expect(Buffer.concat(forwarded).toString("utf8")).toBe("abcde");
|
||||
expect(hint).toContain("UNIDESK_SSH_STDOUT_TRUNCATED");
|
||||
expect(hint).toContain("\"transport\":\"frontend-websocket\"");
|
||||
expect(hint).toContain("\"forwardedBytes\":5");
|
||||
const payload = JSON.parse(hint!.slice("UNIDESK_SSH_STDOUT_TRUNCATED ".length)) as { dumpPath: string };
|
||||
expect(readFileSync(payload.dumpPath, "utf8")).toBe("abcdefghijkl");
|
||||
rmSync(payload.dumpPath, { force: true });
|
||||
});
|
||||
|
||||
test("forwarder bounds very short lines by YAML-style line budget", () => {
|
||||
const invocation = parseSshInvocation("D601:win", ["ps"]);
|
||||
const forwarded: Buffer[] = [];
|
||||
const stdout = {
|
||||
write(chunk: string | Buffer): boolean {
|
||||
forwarded.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
||||
return true;
|
||||
},
|
||||
} as NodeJS.WritableStream;
|
||||
const forwarder = createSshStdoutForwarder({
|
||||
invocation,
|
||||
transport: "frontend-websocket",
|
||||
maxBytes: 1000,
|
||||
maxLines: 2,
|
||||
stdout,
|
||||
});
|
||||
|
||||
const hint = forwarder.write(Buffer.from("a\nb\nc\n"));
|
||||
expect(Buffer.concat(forwarded).toString("utf8")).toBe("a\nb\n");
|
||||
expect(hint).toContain("\"trigger\":\"lines\"");
|
||||
const payload = JSON.parse(hint!.slice("UNIDESK_SSH_STDOUT_TRUNCATED ".length)) as { dumpPath: string; forwardedLines: number };
|
||||
expect(payload.forwardedLines).toBe(2);
|
||||
expect(readFileSync(payload.dumpPath, "utf8")).toBe("a\nb\nc\n");
|
||||
rmSync(payload.dumpPath, { force: true });
|
||||
});
|
||||
|
||||
test("stderr forwarder uses the same dump guard and marker", () => {
|
||||
const invocation = parseSshInvocation("D601:win", ["ps"]);
|
||||
const forwarded: Buffer[] = [];
|
||||
const stderr = {
|
||||
write(chunk: string | Buffer): boolean {
|
||||
forwarded.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
||||
return true;
|
||||
},
|
||||
} as NodeJS.WritableStream;
|
||||
const forwarder = createSshStderrForwarder({
|
||||
invocation,
|
||||
transport: "backend-core-broker",
|
||||
maxBytes: 4,
|
||||
stderr,
|
||||
});
|
||||
|
||||
const hint = forwarder.write(Buffer.from("abcdef"));
|
||||
expect(Buffer.concat(forwarded).toString("utf8")).toBe("abcd");
|
||||
expect(hint).toContain("UNIDESK_SSH_STDERR_TRUNCATED");
|
||||
expect(hint).toContain("\"stream\":\"stderr\"");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user