fix: bound cli dump previews from yaml

This commit is contained in:
Codex
2026-07-01 03:02:16 +00:00
parent a2ed11575d
commit 2210b66eee
7 changed files with 327 additions and 45 deletions
+67 -3
View File
@@ -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\"");
});
});