fix: 收敛 trans 远端文本短路径
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { runSshFileTransferOperation, type SshFileTransferCommandBuilders, type SshRemoteCommandExecutor } from "./ssh-file-transfer";
|
||||
import { parseSshInvocation } from "./ssh";
|
||||
|
||||
function fakeTransfer(content: Buffer): { executor: SshRemoteCommandExecutor; builders: SshFileTransferCommandBuilders; calls: string[] } {
|
||||
const calls: string[] = [];
|
||||
const sha256 = createHash("sha256").update(content).digest("hex");
|
||||
return {
|
||||
calls,
|
||||
builders: {
|
||||
buildRouteCommand(_route, command) {
|
||||
return JSON.stringify(command);
|
||||
},
|
||||
buildWindowsPowerShellCommand(script) {
|
||||
return script;
|
||||
},
|
||||
},
|
||||
executor: {
|
||||
async runRemoteCommand(remoteCommand) {
|
||||
const command = JSON.parse(remoteCommand) as string[];
|
||||
const marker = command.indexOf("unidesk-file-transfer");
|
||||
const operation = command[marker + 1] ?? "unknown";
|
||||
calls.push(operation);
|
||||
return operation === "stat"
|
||||
? { exitCode: 0, stdout: `${content.length} ${sha256}\n`, stderr: "" }
|
||||
: { exitCode: 0, stdout: "", stderr: "" };
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe("ssh text transfer preflight", () => {
|
||||
test("rejects a known text download before remote work or local target creation", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "unidesk-text-transfer-"));
|
||||
const target = join(dir, "result.md");
|
||||
const fake = fakeTransfer(Buffer.from("text\n"));
|
||||
const invocation = parseSshInvocation("D601:/tmp", ["download", "/tmp/result.md", target]);
|
||||
try {
|
||||
await runSshFileTransferOperation(invocation, ["download", "/tmp/result.md", target], fake.executor, fake.builders);
|
||||
throw new Error("expected text download preflight to fail");
|
||||
} catch (error) {
|
||||
const typed = error as Error & { details?: Record<string, unknown> };
|
||||
expect(typed.name).toBe("SshFileTransferError");
|
||||
expect(typed.details?.code).toBe("text-transfer-discouraged");
|
||||
expect(typed.details?.targetCreated).toBe(false);
|
||||
expect(typed.details?.remoteOperationStarted).toBe(false);
|
||||
expect(JSON.stringify(typed.details)).toContain("trans D601:/tmp cat");
|
||||
expect(fake.calls).toEqual([]);
|
||||
expect(existsSync(target)).toBe(false);
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("rejects a known text upload before reading the local source or starting remote work", async () => {
|
||||
const fake = fakeTransfer(Buffer.from("text\n"));
|
||||
const missingSource = "/tmp/unidesk-text-transfer-source-does-not-exist.md";
|
||||
const invocation = parseSshInvocation("D601:/tmp", ["upload", missingSource, "/tmp/result.md"]);
|
||||
try {
|
||||
await runSshFileTransferOperation(invocation, ["upload", missingSource, "/tmp/result.md"], fake.executor, fake.builders);
|
||||
throw new Error("expected text upload preflight to fail");
|
||||
} catch (error) {
|
||||
const typed = error as Error & { details?: Record<string, unknown> };
|
||||
expect(typed.name).toBe("SshFileTransferError");
|
||||
expect(typed.details?.code).toBe("text-transfer-discouraged");
|
||||
expect(JSON.stringify(typed.details)).toContain("apply-patch <<'PATCH'");
|
||||
expect(fake.calls).toEqual([]);
|
||||
}
|
||||
});
|
||||
|
||||
test("keeps verified binary upload as the default transfer workflow", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "unidesk-binary-transfer-"));
|
||||
const source = join(dir, "firmware.bin");
|
||||
const content = Buffer.from([0, 1, 2, 3, 255]);
|
||||
writeFileSync(source, content);
|
||||
const fake = fakeTransfer(content);
|
||||
const invocation = parseSshInvocation("D601:/tmp", ["upload", source, "/tmp/firmware.bin"]);
|
||||
try {
|
||||
expect(await runSshFileTransferOperation(invocation, ["upload", source, "/tmp/firmware.bin"], fake.executor, fake.builders)).toBe(0);
|
||||
expect(fake.calls).toEqual(["write-b64-argv", "stat"]);
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("allows an explicit generated-text override without bypassing verification", async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "unidesk-generated-transfer-"));
|
||||
const source = join(dir, "result.json");
|
||||
const content = Buffer.from("{\"ok\":true}\n", "utf8");
|
||||
writeFileSync(source, content);
|
||||
const fake = fakeTransfer(content);
|
||||
const invocation = parseSshInvocation("D601:/tmp", ["upload", "--allow-text-transfer", source, "/tmp/result.json"]);
|
||||
try {
|
||||
expect(await runSshFileTransferOperation(
|
||||
invocation,
|
||||
["upload", "--allow-text-transfer", source, "/tmp/result.json"],
|
||||
fake.executor,
|
||||
fake.builders,
|
||||
)).toBe(0);
|
||||
expect(fake.calls).toEqual(["write-b64-argv", "stat"]);
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user