Publish curated ForgeFlow source from 2ed1787c0b52
This commit is contained in:
commit
f60b269686
254 files changed
+46204
No files matched your search
@@ -0,0 +1,98 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { EventEmitter } from "node:events";
|
||||
import { createRequire } from "node:module";
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
// SshService resolves ssh2 lazily, so replacing the cached module is enough to
|
||||
// drive a real connection lifecycle without a server.
|
||||
const ssh2Path = require.resolve("ssh2");
|
||||
const realSsh2 = require("ssh2");
|
||||
|
||||
function withFakeSsh2(Client, run) {
|
||||
require.cache[ssh2Path] = { id: ssh2Path, filename: ssh2Path, loaded: true, exports: { ...realSsh2, Client } };
|
||||
try {
|
||||
return run();
|
||||
} finally {
|
||||
require.cache[ssh2Path] = { id: ssh2Path, filename: ssh2Path, loaded: true, exports: realSsh2 };
|
||||
}
|
||||
}
|
||||
|
||||
const { SshService } = require("../src/main/ssh-service.cjs");
|
||||
|
||||
function store(server = {}) {
|
||||
return {
|
||||
getServer: () => ({ id: "unraid", host: "tower", port: 22, username: "root", authType: "password", basePath: "/mnt/user/appdata", hostFingerprint: "SHA256:trusted", ...server }),
|
||||
getServerCredentials: () => ({ password: "secret", passphrase: "" }),
|
||||
};
|
||||
}
|
||||
|
||||
test("a connection that fails twice rejects once and never terminates the process", async () => {
|
||||
class DoubleFailingClient extends EventEmitter {
|
||||
connect() {
|
||||
setImmediate(() => this.emit("error", Object.assign(new Error("connect ECONNREFUSED"), { code: "ECONNREFUSED" })));
|
||||
}
|
||||
end() {
|
||||
// The socket resets shortly after teardown. An unhandled 'error' event on
|
||||
// an EventEmitter takes the whole main process down.
|
||||
setImmediate(() => this.emit("error", new Error("read ECONNRESET")));
|
||||
}
|
||||
}
|
||||
|
||||
const service = withFakeSsh2(DoubleFailingClient, () => new SshService({ store: store(), diagnostics: null }));
|
||||
await assert.rejects(
|
||||
() => withFakeSsh2(DoubleFailingClient, () => service.exec("unraid", "true")),
|
||||
(error) => {
|
||||
assert.equal(error.code, "ECONNREFUSED");
|
||||
assert.match(error.message, /SSH connection failed/);
|
||||
return true;
|
||||
},
|
||||
);
|
||||
|
||||
// Give the delayed teardown error time to land while the test is still running.
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
});
|
||||
|
||||
test("a host key that does not match the trusted fingerprint is reported as an identity change", async () => {
|
||||
class MismatchingClient extends EventEmitter {
|
||||
connect(options) {
|
||||
options.hostVerifier(Buffer.from("a different host key"));
|
||||
setImmediate(() => this.emit("error", new Error("handshake failed")));
|
||||
}
|
||||
end() {}
|
||||
}
|
||||
|
||||
const service = withFakeSsh2(MismatchingClient, () => new SshService({ store: store(), diagnostics: null }));
|
||||
await assert.rejects(
|
||||
() => withFakeSsh2(MismatchingClient, () => service.exec("unraid", "true")),
|
||||
(error) => {
|
||||
assert.equal(error.code, "SSH_HOST_KEY_MISMATCH");
|
||||
assert.match(error.message, /SSH host identity changed/);
|
||||
assert.equal(error.expectedFingerprint, "SHA256:trusted");
|
||||
assert.ok(error.observedFingerprint.startsWith("SHA256:"));
|
||||
return true;
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test("running a command requires a trusted host fingerprint", async () => {
|
||||
const service = new SshService({ store: store({ hostFingerprint: "" }), diagnostics: null });
|
||||
await assert.rejects(() => service.exec("unraid", "true"), (error) => {
|
||||
assert.equal(error.code, "SSH_HOST_NOT_TRUSTED");
|
||||
return true;
|
||||
});
|
||||
await assert.rejects(() => service.uploadBuffer("unraid", "/mnt/user/appdata/x", "data"), (error) => {
|
||||
assert.equal(error.code, "SSH_HOST_NOT_TRUSTED");
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
test("a remote upload path may not escape into an arbitrary location", () => {
|
||||
const service = new SshService({ store: store(), diagnostics: null });
|
||||
assert.equal(service.ensureUploadTarget("/mnt/user/appdata/app/file.tar"), "/mnt/user/appdata/app/file.tar");
|
||||
assert.equal(service.ensureUploadTarget("\\mnt\\user\\appdata\\app"), "/mnt/user/appdata/app");
|
||||
for (const value of ["relative/path", "/mnt/../etc/passwd", "/mnt/user/../../etc", "", null]) {
|
||||
assert.throws(() => service.ensureUploadTarget(value), /absolute safe Unix path/);
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user