mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-16 18:34:18 +00:00
test: clear file-transfer policy broad matchers
This commit is contained in:
@@ -50,23 +50,35 @@ function withConfig(fileTransfer: Record<string, unknown> | undefined) {
|
||||
}
|
||||
}
|
||||
|
||||
function expectResultFields(result: unknown, fields: Record<string, unknown>) {
|
||||
expect(typeof result).toBe("object");
|
||||
expect(result).not.toBeNull();
|
||||
if (typeof result !== "object" || result === null) {
|
||||
throw new Error("policy result was not an object");
|
||||
}
|
||||
const record = result as Record<string, unknown>;
|
||||
for (const [key, value] of Object.entries(fields)) {
|
||||
expect(record[key]).toEqual(value);
|
||||
}
|
||||
}
|
||||
|
||||
describe("evaluateFilePolicy — default deny", () => {
|
||||
it("returns NO_POLICY when no plugin config block is present", () => {
|
||||
getRuntimeConfigMock.mockReturnValue({});
|
||||
const r = evaluateFilePolicy({ nodeId: "n1", kind: "read", path: "/tmp/x" });
|
||||
expect(r).toMatchObject({ ok: false, code: "NO_POLICY", askable: false });
|
||||
expectResultFields(r, { ok: false, code: "NO_POLICY", askable: false });
|
||||
});
|
||||
|
||||
it("returns NO_POLICY when plugin policy block is missing", () => {
|
||||
getRuntimeConfigMock.mockReturnValue({ plugins: { entries: { "file-transfer": {} } } });
|
||||
const r = evaluateFilePolicy({ nodeId: "n1", kind: "read", path: "/tmp/x" });
|
||||
expect(r).toMatchObject({ ok: false, code: "NO_POLICY" });
|
||||
expectResultFields(r, { ok: false, code: "NO_POLICY" });
|
||||
});
|
||||
|
||||
it("returns NO_POLICY when no entry exists for the node and no '*' fallback", () => {
|
||||
withConfig({ "other-node": { allowReadPaths: ["/tmp/**"] } });
|
||||
const r = evaluateFilePolicy({ nodeId: "n1", kind: "read", path: "/tmp/x" });
|
||||
expect(r).toMatchObject({ ok: false, code: "NO_POLICY" });
|
||||
expectResultFields(r, { ok: false, code: "NO_POLICY" });
|
||||
});
|
||||
|
||||
it("prefers the current runtime config over a stale passed plugin config", () => {
|
||||
@@ -93,7 +105,7 @@ describe("evaluateFilePolicy — default deny", () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(r).toMatchObject({ ok: true, reason: "matched-allow" });
|
||||
expectResultFields(r, { ok: true, reason: "matched-allow" });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -107,7 +119,7 @@ describe("evaluateFilePolicy — '..' traversal short-circuit", () => {
|
||||
kind: "read",
|
||||
path: "/allowed/../etc/passwd",
|
||||
});
|
||||
expect(r).toMatchObject({ ok: false, code: "POLICY_DENIED", askable: false });
|
||||
expectResultFields(r, { ok: false, code: "POLICY_DENIED", askable: false });
|
||||
expect(r.ok ? "" : r.reason).toMatch(/\.\./);
|
||||
});
|
||||
|
||||
@@ -120,7 +132,7 @@ describe("evaluateFilePolicy — '..' traversal short-circuit", () => {
|
||||
kind: "read",
|
||||
path: "/tmp/foo/..",
|
||||
});
|
||||
expect(r).toMatchObject({ ok: false, code: "POLICY_DENIED" });
|
||||
expectResultFields(r, { ok: false, code: "POLICY_DENIED" });
|
||||
});
|
||||
|
||||
it("rejects bare '..'", () => {
|
||||
@@ -128,7 +140,7 @@ describe("evaluateFilePolicy — '..' traversal short-circuit", () => {
|
||||
n1: { allowReadPaths: ["/**"] },
|
||||
});
|
||||
const r = evaluateFilePolicy({ nodeId: "n1", kind: "read", path: ".." });
|
||||
expect(r).toMatchObject({ ok: false, code: "POLICY_DENIED" });
|
||||
expectResultFields(r, { ok: false, code: "POLICY_DENIED" });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -145,7 +157,7 @@ describe("evaluateFilePolicy — denyPaths always wins", () => {
|
||||
kind: "read",
|
||||
path: "/tmp/.ssh/id_rsa",
|
||||
});
|
||||
expect(r).toMatchObject({ ok: false, code: "POLICY_DENIED", askable: false });
|
||||
expectResultFields(r, { ok: false, code: "POLICY_DENIED", askable: false });
|
||||
expect(r.ok ? "" : r.reason).toMatch(/deny/);
|
||||
});
|
||||
|
||||
@@ -161,7 +173,7 @@ describe("evaluateFilePolicy — denyPaths always wins", () => {
|
||||
kind: "read",
|
||||
path: "/var/secrets/api.key",
|
||||
});
|
||||
expect(r).toMatchObject({ ok: false, code: "POLICY_DENIED", askable: false });
|
||||
expectResultFields(r, { ok: false, code: "POLICY_DENIED", askable: false });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -183,26 +195,27 @@ describe("evaluateFilePolicy — allow matching", () => {
|
||||
n1: { allowReadPaths: ["/tmp/**"], maxBytes: 1024 },
|
||||
});
|
||||
const r = evaluateFilePolicy({ nodeId: "n1", kind: "read", path: "/tmp/x" });
|
||||
expect(r).toMatchObject({ ok: true, maxBytes: 1024 });
|
||||
expectResultFields(r, { ok: true, maxBytes: 1024 });
|
||||
});
|
||||
|
||||
it("uses kind=write to consult allowWritePaths, not allowReadPaths", () => {
|
||||
withConfig({
|
||||
n1: { allowReadPaths: ["/tmp/**"], allowWritePaths: ["/srv/**"] },
|
||||
});
|
||||
expect(evaluateFilePolicy({ nodeId: "n1", kind: "write", path: "/srv/out.txt" })).toMatchObject(
|
||||
{ ok: true },
|
||||
);
|
||||
expect(evaluateFilePolicy({ nodeId: "n1", kind: "write", path: "/tmp/out.txt" })).toMatchObject(
|
||||
{ ok: false, code: "POLICY_DENIED" },
|
||||
);
|
||||
expectResultFields(evaluateFilePolicy({ nodeId: "n1", kind: "write", path: "/srv/out.txt" }), {
|
||||
ok: true,
|
||||
});
|
||||
expectResultFields(evaluateFilePolicy({ nodeId: "n1", kind: "write", path: "/tmp/out.txt" }), {
|
||||
ok: false,
|
||||
code: "POLICY_DENIED",
|
||||
});
|
||||
});
|
||||
|
||||
it("propagates followSymlinks=false by default and =true when configured", () => {
|
||||
withConfig({
|
||||
n1: { allowReadPaths: ["/tmp/**"] },
|
||||
});
|
||||
expect(evaluateFilePolicy({ nodeId: "n1", kind: "read", path: "/tmp/x" })).toMatchObject({
|
||||
expectResultFields(evaluateFilePolicy({ nodeId: "n1", kind: "read", path: "/tmp/x" }), {
|
||||
ok: true,
|
||||
followSymlinks: false,
|
||||
});
|
||||
@@ -210,7 +223,7 @@ describe("evaluateFilePolicy — allow matching", () => {
|
||||
withConfig({
|
||||
n2: { allowReadPaths: ["/tmp/**"], followSymlinks: true },
|
||||
});
|
||||
expect(evaluateFilePolicy({ nodeId: "n2", kind: "read", path: "/tmp/x" })).toMatchObject({
|
||||
expectResultFields(evaluateFilePolicy({ nodeId: "n2", kind: "read", path: "/tmp/x" }), {
|
||||
ok: true,
|
||||
followSymlinks: true,
|
||||
});
|
||||
@@ -221,26 +234,28 @@ describe("evaluateFilePolicy — allow matching", () => {
|
||||
withConfig({
|
||||
n1: { allowReadPaths: ["~/Screenshots/**"] },
|
||||
});
|
||||
expect(
|
||||
expectResultFields(
|
||||
evaluateFilePolicy({
|
||||
nodeId: "n1",
|
||||
kind: "read",
|
||||
path: path.join(home, "Screenshots", "shot.png"),
|
||||
}),
|
||||
).toMatchObject({ ok: true });
|
||||
{ ok: true },
|
||||
);
|
||||
});
|
||||
|
||||
it("matches Windows node paths without gateway-local path semantics", () => {
|
||||
withConfig({
|
||||
n1: { allowReadPaths: ["C:/Users/me/**"] },
|
||||
});
|
||||
expect(
|
||||
expectResultFields(
|
||||
evaluateFilePolicy({
|
||||
nodeId: "n1",
|
||||
kind: "read",
|
||||
path: "C:\\Users\\me\\file.txt",
|
||||
}),
|
||||
).toMatchObject({ ok: true });
|
||||
{ ok: true },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -250,7 +265,7 @@ describe("evaluateFilePolicy — ask modes", () => {
|
||||
n1: { ask: "on-miss", allowReadPaths: ["/var/log/**"] },
|
||||
});
|
||||
const r = evaluateFilePolicy({ nodeId: "n1", kind: "read", path: "/tmp/x" });
|
||||
expect(r).toMatchObject({
|
||||
expectResultFields(r, {
|
||||
ok: false,
|
||||
code: "POLICY_DENIED",
|
||||
askable: true,
|
||||
@@ -268,7 +283,7 @@ describe("evaluateFilePolicy — ask modes", () => {
|
||||
},
|
||||
});
|
||||
const r = evaluateFilePolicy({ nodeId: "n1", kind: "read", path: "/tmp/x" });
|
||||
expect(r).toMatchObject({
|
||||
expectResultFields(r, {
|
||||
ok: false,
|
||||
code: "POLICY_DENIED",
|
||||
askable: true,
|
||||
@@ -283,7 +298,7 @@ describe("evaluateFilePolicy — ask modes", () => {
|
||||
n1: { ask: "on-miss", allowReadPaths: ["/tmp/**"] },
|
||||
});
|
||||
const r = evaluateFilePolicy({ nodeId: "n1", kind: "read", path: "/tmp/x" });
|
||||
expect(r).toMatchObject({ ok: true, reason: "matched-allow" });
|
||||
expectResultFields(r, { ok: true, reason: "matched-allow" });
|
||||
});
|
||||
|
||||
it("ask=always always returns ask-always (prompt on every call)", () => {
|
||||
@@ -291,7 +306,7 @@ describe("evaluateFilePolicy — ask modes", () => {
|
||||
n1: { ask: "always", allowReadPaths: ["/tmp/**"] },
|
||||
});
|
||||
const r = evaluateFilePolicy({ nodeId: "n1", kind: "read", path: "/tmp/x" });
|
||||
expect(r).toMatchObject({ ok: true, reason: "ask-always", askMode: "always" });
|
||||
expectResultFields(r, { ok: true, reason: "ask-always", askMode: "always" });
|
||||
});
|
||||
|
||||
it("ask=off returns non-askable POLICY_DENIED on miss", () => {
|
||||
@@ -299,7 +314,7 @@ describe("evaluateFilePolicy — ask modes", () => {
|
||||
n1: { ask: "off", allowReadPaths: ["/var/log/**"] },
|
||||
});
|
||||
const r = evaluateFilePolicy({ nodeId: "n1", kind: "read", path: "/tmp/x" });
|
||||
expect(r).toMatchObject({ ok: false, code: "POLICY_DENIED", askable: false });
|
||||
expectResultFields(r, { ok: false, code: "POLICY_DENIED", askable: false });
|
||||
});
|
||||
|
||||
it("invalid ask values normalize to off", () => {
|
||||
@@ -307,7 +322,7 @@ describe("evaluateFilePolicy — ask modes", () => {
|
||||
n1: { ask: "sometimes", allowReadPaths: ["/var/log/**"] },
|
||||
});
|
||||
const r = evaluateFilePolicy({ nodeId: "n1", kind: "read", path: "/tmp/x" });
|
||||
expect(r).toMatchObject({ ok: false, askable: false });
|
||||
expectResultFields(r, { ok: false, askable: false });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -316,28 +331,30 @@ describe("evaluateFilePolicy — node-id resolution", () => {
|
||||
withConfig({
|
||||
"Lobster MacBook": { allowReadPaths: ["/tmp/**"] },
|
||||
});
|
||||
expect(
|
||||
expectResultFields(
|
||||
evaluateFilePolicy({
|
||||
nodeId: "node-abc-123",
|
||||
nodeDisplayName: "Lobster MacBook",
|
||||
kind: "read",
|
||||
path: "/tmp/x",
|
||||
}),
|
||||
).toMatchObject({ ok: true });
|
||||
{ ok: true },
|
||||
);
|
||||
});
|
||||
|
||||
it("falls back to '*' wildcard when neither id nor displayName matches", () => {
|
||||
withConfig({
|
||||
"*": { allowReadPaths: ["/tmp/**"] },
|
||||
});
|
||||
expect(
|
||||
expectResultFields(
|
||||
evaluateFilePolicy({
|
||||
nodeId: "n1",
|
||||
nodeDisplayName: "anything",
|
||||
kind: "read",
|
||||
path: "/tmp/x",
|
||||
}),
|
||||
).toMatchObject({ ok: true });
|
||||
{ ok: true },
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user