chore: generate
This commit is contained in:
@@ -556,372 +556,398 @@ test("disabled - specific allow overrides wildcard deny", () => {
|
|||||||
|
|
||||||
// ask tests
|
// ask tests
|
||||||
|
|
||||||
it.instance("ask - resolves immediately when action is allow", () =>
|
it.instance(
|
||||||
Effect.gen(function* () {
|
"ask - resolves immediately when action is allow",
|
||||||
const result = yield* ask({
|
() =>
|
||||||
sessionID: SessionID.make("session_test"),
|
Effect.gen(function* () {
|
||||||
permission: "bash",
|
const result = yield* ask({
|
||||||
patterns: ["ls"],
|
|
||||||
metadata: {},
|
|
||||||
always: [],
|
|
||||||
ruleset: [{ permission: "bash", pattern: "*", action: "allow" }],
|
|
||||||
})
|
|
||||||
expect(result).toBeUndefined()
|
|
||||||
}),
|
|
||||||
{ git: true },
|
|
||||||
)
|
|
||||||
|
|
||||||
it.instance("ask - throws DeniedError when action is deny", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const err = yield* fail(
|
|
||||||
ask({
|
|
||||||
sessionID: SessionID.make("session_test"),
|
sessionID: SessionID.make("session_test"),
|
||||||
permission: "bash",
|
permission: "bash",
|
||||||
patterns: ["rm -rf /"],
|
patterns: ["ls"],
|
||||||
metadata: {},
|
metadata: {},
|
||||||
always: [],
|
always: [],
|
||||||
ruleset: [{ permission: "bash", pattern: "*", action: "deny" }],
|
ruleset: [{ permission: "bash", pattern: "*", action: "allow" }],
|
||||||
}),
|
})
|
||||||
)
|
expect(result).toBeUndefined()
|
||||||
expect(err).toBeInstanceOf(Permission.DeniedError)
|
}),
|
||||||
}),
|
|
||||||
{ git: true },
|
{ git: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
it.instance("ask - stays pending when action is ask", () =>
|
it.instance(
|
||||||
Effect.gen(function* () {
|
"ask - throws DeniedError when action is deny",
|
||||||
const fiber = yield* ask({
|
() =>
|
||||||
sessionID: SessionID.make("session_test"),
|
Effect.gen(function* () {
|
||||||
permission: "bash",
|
const err = yield* fail(
|
||||||
patterns: ["ls"],
|
ask({
|
||||||
metadata: {},
|
sessionID: SessionID.make("session_test"),
|
||||||
always: [],
|
permission: "bash",
|
||||||
ruleset: [{ permission: "bash", pattern: "*", action: "ask" }],
|
patterns: ["rm -rf /"],
|
||||||
}).pipe(Effect.forkScoped)
|
metadata: {},
|
||||||
|
always: [],
|
||||||
expect(yield* waitForPending(1)).toHaveLength(1)
|
ruleset: [{ permission: "bash", pattern: "*", action: "deny" }],
|
||||||
yield* rejectAll()
|
|
||||||
yield* Fiber.await(fiber)
|
|
||||||
}),
|
|
||||||
{ git: true },
|
|
||||||
)
|
|
||||||
|
|
||||||
it.instance("ask - adds request to pending list", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const fiber = yield* ask({
|
|
||||||
sessionID: SessionID.make("session_test"),
|
|
||||||
permission: "bash",
|
|
||||||
patterns: ["ls"],
|
|
||||||
metadata: { cmd: "ls" },
|
|
||||||
always: ["ls"],
|
|
||||||
tool: {
|
|
||||||
messageID: MessageID.make("msg_test"),
|
|
||||||
callID: "call_test",
|
|
||||||
},
|
|
||||||
ruleset: [],
|
|
||||||
}).pipe(Effect.forkScoped)
|
|
||||||
|
|
||||||
const items = yield* waitForPending(1)
|
|
||||||
expect(items).toHaveLength(1)
|
|
||||||
expect(items[0]).toMatchObject({
|
|
||||||
sessionID: SessionID.make("session_test"),
|
|
||||||
permission: "bash",
|
|
||||||
patterns: ["ls"],
|
|
||||||
metadata: { cmd: "ls" },
|
|
||||||
always: ["ls"],
|
|
||||||
tool: {
|
|
||||||
messageID: MessageID.make("msg_test"),
|
|
||||||
callID: "call_test",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
yield* rejectAll()
|
|
||||||
yield* Fiber.await(fiber)
|
|
||||||
}),
|
|
||||||
{ git: true },
|
|
||||||
)
|
|
||||||
|
|
||||||
it.instance("ask - publishes asked event", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const bus = yield* Bus.Service
|
|
||||||
const seen = yield* Deferred.make<Permission.Request>()
|
|
||||||
const unsub = yield* bus.subscribeCallback(Permission.Event.Asked, (event) => {
|
|
||||||
Deferred.doneUnsafe(seen, Effect.succeed(event.properties))
|
|
||||||
})
|
|
||||||
yield* Effect.addFinalizer(() => Effect.sync(unsub))
|
|
||||||
|
|
||||||
const fiber = yield* ask({
|
|
||||||
sessionID: SessionID.make("session_test"),
|
|
||||||
permission: "bash",
|
|
||||||
patterns: ["ls"],
|
|
||||||
metadata: { cmd: "ls" },
|
|
||||||
always: ["ls"],
|
|
||||||
tool: {
|
|
||||||
messageID: MessageID.make("msg_test"),
|
|
||||||
callID: "call_test",
|
|
||||||
},
|
|
||||||
ruleset: [],
|
|
||||||
}).pipe(Effect.forkScoped)
|
|
||||||
|
|
||||||
expect(yield* waitForPending(1)).toHaveLength(1)
|
|
||||||
expect(
|
|
||||||
yield* Deferred.await(seen).pipe(
|
|
||||||
Effect.timeoutOrElse({
|
|
||||||
duration: "1 second",
|
|
||||||
orElse: () => Effect.fail(new Error("timed out waiting for permission asked event")),
|
|
||||||
}),
|
}),
|
||||||
),
|
)
|
||||||
).toMatchObject({
|
expect(err).toBeInstanceOf(Permission.DeniedError)
|
||||||
sessionID: SessionID.make("session_test"),
|
}),
|
||||||
permission: "bash",
|
{ git: true },
|
||||||
patterns: ["ls"],
|
)
|
||||||
})
|
|
||||||
|
|
||||||
yield* rejectAll()
|
it.instance(
|
||||||
yield* Fiber.await(fiber)
|
"ask - stays pending when action is ask",
|
||||||
}),
|
() =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const fiber = yield* ask({
|
||||||
|
sessionID: SessionID.make("session_test"),
|
||||||
|
permission: "bash",
|
||||||
|
patterns: ["ls"],
|
||||||
|
metadata: {},
|
||||||
|
always: [],
|
||||||
|
ruleset: [{ permission: "bash", pattern: "*", action: "ask" }],
|
||||||
|
}).pipe(Effect.forkScoped)
|
||||||
|
|
||||||
|
expect(yield* waitForPending(1)).toHaveLength(1)
|
||||||
|
yield* rejectAll()
|
||||||
|
yield* Fiber.await(fiber)
|
||||||
|
}),
|
||||||
|
{ git: true },
|
||||||
|
)
|
||||||
|
|
||||||
|
it.instance(
|
||||||
|
"ask - adds request to pending list",
|
||||||
|
() =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const fiber = yield* ask({
|
||||||
|
sessionID: SessionID.make("session_test"),
|
||||||
|
permission: "bash",
|
||||||
|
patterns: ["ls"],
|
||||||
|
metadata: { cmd: "ls" },
|
||||||
|
always: ["ls"],
|
||||||
|
tool: {
|
||||||
|
messageID: MessageID.make("msg_test"),
|
||||||
|
callID: "call_test",
|
||||||
|
},
|
||||||
|
ruleset: [],
|
||||||
|
}).pipe(Effect.forkScoped)
|
||||||
|
|
||||||
|
const items = yield* waitForPending(1)
|
||||||
|
expect(items).toHaveLength(1)
|
||||||
|
expect(items[0]).toMatchObject({
|
||||||
|
sessionID: SessionID.make("session_test"),
|
||||||
|
permission: "bash",
|
||||||
|
patterns: ["ls"],
|
||||||
|
metadata: { cmd: "ls" },
|
||||||
|
always: ["ls"],
|
||||||
|
tool: {
|
||||||
|
messageID: MessageID.make("msg_test"),
|
||||||
|
callID: "call_test",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
yield* rejectAll()
|
||||||
|
yield* Fiber.await(fiber)
|
||||||
|
}),
|
||||||
|
{ git: true },
|
||||||
|
)
|
||||||
|
|
||||||
|
it.instance(
|
||||||
|
"ask - publishes asked event",
|
||||||
|
() =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const bus = yield* Bus.Service
|
||||||
|
const seen = yield* Deferred.make<Permission.Request>()
|
||||||
|
const unsub = yield* bus.subscribeCallback(Permission.Event.Asked, (event) => {
|
||||||
|
Deferred.doneUnsafe(seen, Effect.succeed(event.properties))
|
||||||
|
})
|
||||||
|
yield* Effect.addFinalizer(() => Effect.sync(unsub))
|
||||||
|
|
||||||
|
const fiber = yield* ask({
|
||||||
|
sessionID: SessionID.make("session_test"),
|
||||||
|
permission: "bash",
|
||||||
|
patterns: ["ls"],
|
||||||
|
metadata: { cmd: "ls" },
|
||||||
|
always: ["ls"],
|
||||||
|
tool: {
|
||||||
|
messageID: MessageID.make("msg_test"),
|
||||||
|
callID: "call_test",
|
||||||
|
},
|
||||||
|
ruleset: [],
|
||||||
|
}).pipe(Effect.forkScoped)
|
||||||
|
|
||||||
|
expect(yield* waitForPending(1)).toHaveLength(1)
|
||||||
|
expect(
|
||||||
|
yield* Deferred.await(seen).pipe(
|
||||||
|
Effect.timeoutOrElse({
|
||||||
|
duration: "1 second",
|
||||||
|
orElse: () => Effect.fail(new Error("timed out waiting for permission asked event")),
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
).toMatchObject({
|
||||||
|
sessionID: SessionID.make("session_test"),
|
||||||
|
permission: "bash",
|
||||||
|
patterns: ["ls"],
|
||||||
|
})
|
||||||
|
|
||||||
|
yield* rejectAll()
|
||||||
|
yield* Fiber.await(fiber)
|
||||||
|
}),
|
||||||
{ git: true },
|
{ git: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
// reply tests
|
// reply tests
|
||||||
|
|
||||||
it.instance("reply - once resolves the pending ask", () =>
|
it.instance(
|
||||||
Effect.gen(function* () {
|
"reply - once resolves the pending ask",
|
||||||
const fiber = yield* ask({
|
() =>
|
||||||
id: PermissionID.make("per_test1"),
|
Effect.gen(function* () {
|
||||||
sessionID: SessionID.make("session_test"),
|
const fiber = yield* ask({
|
||||||
permission: "bash",
|
id: PermissionID.make("per_test1"),
|
||||||
patterns: ["ls"],
|
sessionID: SessionID.make("session_test"),
|
||||||
metadata: {},
|
permission: "bash",
|
||||||
always: [],
|
patterns: ["ls"],
|
||||||
ruleset: [],
|
metadata: {},
|
||||||
}).pipe(Effect.forkScoped)
|
always: [],
|
||||||
|
ruleset: [],
|
||||||
|
}).pipe(Effect.forkScoped)
|
||||||
|
|
||||||
yield* waitForPending(1)
|
yield* waitForPending(1)
|
||||||
yield* reply({ requestID: PermissionID.make("per_test1"), reply: "once" })
|
yield* reply({ requestID: PermissionID.make("per_test1"), reply: "once" })
|
||||||
yield* Fiber.join(fiber)
|
yield* Fiber.join(fiber)
|
||||||
}),
|
}),
|
||||||
{ git: true },
|
{ git: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
it.instance("reply - reject throws RejectedError", () =>
|
it.instance(
|
||||||
Effect.gen(function* () {
|
"reply - reject throws RejectedError",
|
||||||
const fiber = yield* ask({
|
() =>
|
||||||
id: PermissionID.make("per_test2"),
|
Effect.gen(function* () {
|
||||||
sessionID: SessionID.make("session_test"),
|
const fiber = yield* ask({
|
||||||
permission: "bash",
|
id: PermissionID.make("per_test2"),
|
||||||
patterns: ["ls"],
|
sessionID: SessionID.make("session_test"),
|
||||||
metadata: {},
|
permission: "bash",
|
||||||
always: [],
|
patterns: ["ls"],
|
||||||
ruleset: [],
|
metadata: {},
|
||||||
}).pipe(Effect.forkScoped)
|
always: [],
|
||||||
|
ruleset: [],
|
||||||
|
}).pipe(Effect.forkScoped)
|
||||||
|
|
||||||
yield* waitForPending(1)
|
yield* waitForPending(1)
|
||||||
yield* reply({ requestID: PermissionID.make("per_test2"), reply: "reject" })
|
yield* reply({ requestID: PermissionID.make("per_test2"), reply: "reject" })
|
||||||
|
|
||||||
const exit = yield* Fiber.await(fiber)
|
const exit = yield* Fiber.await(fiber)
|
||||||
expect(Exit.isFailure(exit)).toBe(true)
|
expect(Exit.isFailure(exit)).toBe(true)
|
||||||
if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Permission.RejectedError)
|
if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Permission.RejectedError)
|
||||||
}),
|
}),
|
||||||
{ git: true },
|
{ git: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
it.instance("reply - reject with message throws CorrectedError", () =>
|
it.instance(
|
||||||
Effect.gen(function* () {
|
"reply - reject with message throws CorrectedError",
|
||||||
const fiber = yield* ask({
|
() =>
|
||||||
id: PermissionID.make("per_test2b"),
|
Effect.gen(function* () {
|
||||||
sessionID: SessionID.make("session_test"),
|
const fiber = yield* ask({
|
||||||
permission: "bash",
|
id: PermissionID.make("per_test2b"),
|
||||||
patterns: ["ls"],
|
sessionID: SessionID.make("session_test"),
|
||||||
metadata: {},
|
permission: "bash",
|
||||||
always: [],
|
patterns: ["ls"],
|
||||||
ruleset: [],
|
metadata: {},
|
||||||
}).pipe(Effect.forkScoped)
|
always: [],
|
||||||
|
ruleset: [],
|
||||||
|
}).pipe(Effect.forkScoped)
|
||||||
|
|
||||||
yield* waitForPending(1)
|
yield* waitForPending(1)
|
||||||
yield* reply({
|
yield* reply({
|
||||||
requestID: PermissionID.make("per_test2b"),
|
requestID: PermissionID.make("per_test2b"),
|
||||||
reply: "reject",
|
reply: "reject",
|
||||||
message: "Use a safer command",
|
message: "Use a safer command",
|
||||||
})
|
})
|
||||||
|
|
||||||
const exit = yield* Fiber.await(fiber)
|
const exit = yield* Fiber.await(fiber)
|
||||||
expect(Exit.isFailure(exit)).toBe(true)
|
expect(Exit.isFailure(exit)).toBe(true)
|
||||||
if (Exit.isFailure(exit)) {
|
if (Exit.isFailure(exit)) {
|
||||||
const err = Cause.squash(exit.cause)
|
const err = Cause.squash(exit.cause)
|
||||||
expect(err).toBeInstanceOf(Permission.CorrectedError)
|
expect(err).toBeInstanceOf(Permission.CorrectedError)
|
||||||
expect(String(err)).toContain("Use a safer command")
|
expect(String(err)).toContain("Use a safer command")
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
{ git: true },
|
{ git: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
it.instance("reply - always persists approval and resolves", () =>
|
it.instance(
|
||||||
Effect.gen(function* () {
|
"reply - always persists approval and resolves",
|
||||||
const fiber = yield* ask({
|
() =>
|
||||||
id: PermissionID.make("per_test3"),
|
Effect.gen(function* () {
|
||||||
sessionID: SessionID.make("session_test"),
|
const fiber = yield* ask({
|
||||||
permission: "bash",
|
id: PermissionID.make("per_test3"),
|
||||||
patterns: ["ls"],
|
sessionID: SessionID.make("session_test"),
|
||||||
metadata: {},
|
permission: "bash",
|
||||||
always: ["ls"],
|
patterns: ["ls"],
|
||||||
ruleset: [],
|
metadata: {},
|
||||||
}).pipe(Effect.forkScoped)
|
always: ["ls"],
|
||||||
|
ruleset: [],
|
||||||
|
}).pipe(Effect.forkScoped)
|
||||||
|
|
||||||
yield* waitForPending(1)
|
yield* waitForPending(1)
|
||||||
yield* reply({ requestID: PermissionID.make("per_test3"), reply: "always" })
|
yield* reply({ requestID: PermissionID.make("per_test3"), reply: "always" })
|
||||||
yield* Fiber.join(fiber)
|
yield* Fiber.join(fiber)
|
||||||
|
|
||||||
const result = yield* ask({
|
const result = yield* ask({
|
||||||
sessionID: SessionID.make("session_test2"),
|
sessionID: SessionID.make("session_test2"),
|
||||||
permission: "bash",
|
permission: "bash",
|
||||||
patterns: ["ls"],
|
patterns: ["ls"],
|
||||||
metadata: {},
|
metadata: {},
|
||||||
always: [],
|
always: [],
|
||||||
ruleset: [],
|
ruleset: [],
|
||||||
})
|
})
|
||||||
expect(result).toBeUndefined()
|
expect(result).toBeUndefined()
|
||||||
}),
|
}),
|
||||||
{ git: true },
|
{ git: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
it.instance("reply - reject cancels all pending for same session", () =>
|
it.instance(
|
||||||
Effect.gen(function* () {
|
"reply - reject cancels all pending for same session",
|
||||||
const a = yield* ask({
|
() =>
|
||||||
id: PermissionID.make("per_test4a"),
|
Effect.gen(function* () {
|
||||||
sessionID: SessionID.make("session_same"),
|
const a = yield* ask({
|
||||||
permission: "bash",
|
id: PermissionID.make("per_test4a"),
|
||||||
patterns: ["ls"],
|
sessionID: SessionID.make("session_same"),
|
||||||
metadata: {},
|
permission: "bash",
|
||||||
always: [],
|
patterns: ["ls"],
|
||||||
ruleset: [],
|
metadata: {},
|
||||||
}).pipe(Effect.forkScoped)
|
always: [],
|
||||||
|
ruleset: [],
|
||||||
|
}).pipe(Effect.forkScoped)
|
||||||
|
|
||||||
const b = yield* ask({
|
const b = yield* ask({
|
||||||
id: PermissionID.make("per_test4b"),
|
id: PermissionID.make("per_test4b"),
|
||||||
sessionID: SessionID.make("session_same"),
|
sessionID: SessionID.make("session_same"),
|
||||||
permission: "edit",
|
permission: "edit",
|
||||||
patterns: ["foo.ts"],
|
patterns: ["foo.ts"],
|
||||||
metadata: {},
|
metadata: {},
|
||||||
always: [],
|
always: [],
|
||||||
ruleset: [],
|
ruleset: [],
|
||||||
}).pipe(Effect.forkScoped)
|
}).pipe(Effect.forkScoped)
|
||||||
|
|
||||||
yield* waitForPending(2)
|
yield* waitForPending(2)
|
||||||
yield* reply({ requestID: PermissionID.make("per_test4a"), reply: "reject" })
|
yield* reply({ requestID: PermissionID.make("per_test4a"), reply: "reject" })
|
||||||
|
|
||||||
const [ea, eb] = yield* Effect.all([Fiber.await(a), Fiber.await(b)])
|
const [ea, eb] = yield* Effect.all([Fiber.await(a), Fiber.await(b)])
|
||||||
expect(Exit.isFailure(ea)).toBe(true)
|
expect(Exit.isFailure(ea)).toBe(true)
|
||||||
expect(Exit.isFailure(eb)).toBe(true)
|
expect(Exit.isFailure(eb)).toBe(true)
|
||||||
if (Exit.isFailure(ea)) expect(Cause.squash(ea.cause)).toBeInstanceOf(Permission.RejectedError)
|
if (Exit.isFailure(ea)) expect(Cause.squash(ea.cause)).toBeInstanceOf(Permission.RejectedError)
|
||||||
if (Exit.isFailure(eb)) expect(Cause.squash(eb.cause)).toBeInstanceOf(Permission.RejectedError)
|
if (Exit.isFailure(eb)) expect(Cause.squash(eb.cause)).toBeInstanceOf(Permission.RejectedError)
|
||||||
}),
|
}),
|
||||||
{ git: true },
|
{ git: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
it.instance("reply - always resolves matching pending requests in same session", () =>
|
it.instance(
|
||||||
Effect.gen(function* () {
|
"reply - always resolves matching pending requests in same session",
|
||||||
const a = yield* ask({
|
() =>
|
||||||
id: PermissionID.make("per_test5a"),
|
Effect.gen(function* () {
|
||||||
sessionID: SessionID.make("session_same"),
|
const a = yield* ask({
|
||||||
permission: "bash",
|
id: PermissionID.make("per_test5a"),
|
||||||
patterns: ["ls"],
|
sessionID: SessionID.make("session_same"),
|
||||||
metadata: {},
|
permission: "bash",
|
||||||
always: ["ls"],
|
patterns: ["ls"],
|
||||||
ruleset: [],
|
metadata: {},
|
||||||
}).pipe(Effect.forkScoped)
|
always: ["ls"],
|
||||||
|
ruleset: [],
|
||||||
|
}).pipe(Effect.forkScoped)
|
||||||
|
|
||||||
const b = yield* ask({
|
const b = yield* ask({
|
||||||
id: PermissionID.make("per_test5b"),
|
id: PermissionID.make("per_test5b"),
|
||||||
sessionID: SessionID.make("session_same"),
|
sessionID: SessionID.make("session_same"),
|
||||||
permission: "bash",
|
permission: "bash",
|
||||||
patterns: ["ls"],
|
patterns: ["ls"],
|
||||||
metadata: {},
|
metadata: {},
|
||||||
always: [],
|
always: [],
|
||||||
ruleset: [],
|
ruleset: [],
|
||||||
}).pipe(Effect.forkScoped)
|
}).pipe(Effect.forkScoped)
|
||||||
|
|
||||||
yield* waitForPending(2)
|
yield* waitForPending(2)
|
||||||
yield* reply({ requestID: PermissionID.make("per_test5a"), reply: "always" })
|
yield* reply({ requestID: PermissionID.make("per_test5a"), reply: "always" })
|
||||||
|
|
||||||
yield* Fiber.join(a)
|
yield* Fiber.join(a)
|
||||||
yield* Fiber.join(b)
|
yield* Fiber.join(b)
|
||||||
expect(yield* list()).toHaveLength(0)
|
expect(yield* list()).toHaveLength(0)
|
||||||
}),
|
}),
|
||||||
{ git: true },
|
{ git: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
it.instance("reply - always keeps other session pending", () =>
|
it.instance(
|
||||||
Effect.gen(function* () {
|
"reply - always keeps other session pending",
|
||||||
const a = yield* ask({
|
() =>
|
||||||
id: PermissionID.make("per_test6a"),
|
Effect.gen(function* () {
|
||||||
sessionID: SessionID.make("session_a"),
|
const a = yield* ask({
|
||||||
permission: "bash",
|
id: PermissionID.make("per_test6a"),
|
||||||
patterns: ["ls"],
|
sessionID: SessionID.make("session_a"),
|
||||||
metadata: {},
|
permission: "bash",
|
||||||
always: ["ls"],
|
patterns: ["ls"],
|
||||||
ruleset: [],
|
metadata: {},
|
||||||
}).pipe(Effect.forkScoped)
|
always: ["ls"],
|
||||||
|
ruleset: [],
|
||||||
|
}).pipe(Effect.forkScoped)
|
||||||
|
|
||||||
const b = yield* ask({
|
const b = yield* ask({
|
||||||
id: PermissionID.make("per_test6b"),
|
id: PermissionID.make("per_test6b"),
|
||||||
sessionID: SessionID.make("session_b"),
|
sessionID: SessionID.make("session_b"),
|
||||||
permission: "bash",
|
permission: "bash",
|
||||||
patterns: ["ls"],
|
patterns: ["ls"],
|
||||||
metadata: {},
|
metadata: {},
|
||||||
always: [],
|
always: [],
|
||||||
ruleset: [],
|
ruleset: [],
|
||||||
}).pipe(Effect.forkScoped)
|
}).pipe(Effect.forkScoped)
|
||||||
|
|
||||||
yield* waitForPending(2)
|
yield* waitForPending(2)
|
||||||
yield* reply({ requestID: PermissionID.make("per_test6a"), reply: "always" })
|
yield* reply({ requestID: PermissionID.make("per_test6a"), reply: "always" })
|
||||||
|
|
||||||
yield* Fiber.join(a)
|
yield* Fiber.join(a)
|
||||||
expect((yield* list()).map((item) => item.id)).toEqual([PermissionID.make("per_test6b")])
|
expect((yield* list()).map((item) => item.id)).toEqual([PermissionID.make("per_test6b")])
|
||||||
|
|
||||||
yield* rejectAll()
|
yield* rejectAll()
|
||||||
yield* Fiber.await(b)
|
yield* Fiber.await(b)
|
||||||
}),
|
}),
|
||||||
{ git: true },
|
{ git: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
it.instance("reply - publishes replied event", () =>
|
it.instance(
|
||||||
Effect.gen(function* () {
|
"reply - publishes replied event",
|
||||||
const bus = yield* Bus.Service
|
() =>
|
||||||
const seen = yield* Deferred.make<{ sessionID: SessionID; requestID: PermissionID; reply: Permission.Reply }>()
|
Effect.gen(function* () {
|
||||||
|
const bus = yield* Bus.Service
|
||||||
|
const seen = yield* Deferred.make<{ sessionID: SessionID; requestID: PermissionID; reply: Permission.Reply }>()
|
||||||
|
|
||||||
const fiber = yield* ask({
|
const fiber = yield* ask({
|
||||||
id: PermissionID.make("per_test7"),
|
id: PermissionID.make("per_test7"),
|
||||||
sessionID: SessionID.make("session_test"),
|
sessionID: SessionID.make("session_test"),
|
||||||
permission: "bash",
|
permission: "bash",
|
||||||
patterns: ["ls"],
|
patterns: ["ls"],
|
||||||
metadata: {},
|
metadata: {},
|
||||||
always: [],
|
always: [],
|
||||||
ruleset: [],
|
ruleset: [],
|
||||||
}).pipe(Effect.forkScoped)
|
}).pipe(Effect.forkScoped)
|
||||||
|
|
||||||
yield* waitForPending(1)
|
yield* waitForPending(1)
|
||||||
|
|
||||||
const unsub = yield* bus.subscribeCallback(Permission.Event.Replied, (event) => {
|
const unsub = yield* bus.subscribeCallback(Permission.Event.Replied, (event) => {
|
||||||
Deferred.doneUnsafe(seen, Effect.succeed(event.properties))
|
Deferred.doneUnsafe(seen, Effect.succeed(event.properties))
|
||||||
})
|
})
|
||||||
yield* Effect.addFinalizer(() => Effect.sync(unsub))
|
yield* Effect.addFinalizer(() => Effect.sync(unsub))
|
||||||
|
|
||||||
yield* reply({ requestID: PermissionID.make("per_test7"), reply: "once" })
|
yield* reply({ requestID: PermissionID.make("per_test7"), reply: "once" })
|
||||||
yield* Fiber.join(fiber)
|
yield* Fiber.join(fiber)
|
||||||
expect(
|
expect(
|
||||||
yield* Deferred.await(seen).pipe(
|
yield* Deferred.await(seen).pipe(
|
||||||
Effect.timeoutOrElse({
|
Effect.timeoutOrElse({
|
||||||
duration: "1 second",
|
duration: "1 second",
|
||||||
orElse: () => Effect.fail(new Error("timed out waiting for permission replied event")),
|
orElse: () => Effect.fail(new Error("timed out waiting for permission replied event")),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
).toEqual({
|
).toEqual({
|
||||||
sessionID: SessionID.make("session_test"),
|
sessionID: SessionID.make("session_test"),
|
||||||
requestID: PermissionID.make("per_test7"),
|
requestID: PermissionID.make("per_test7"),
|
||||||
reply: "once",
|
reply: "once",
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
{ git: true },
|
{ git: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -977,142 +1003,156 @@ it.live("permission requests stay isolated by directory", () =>
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
it.instance("pending permission rejects on instance dispose", () =>
|
it.instance(
|
||||||
Effect.gen(function* () {
|
"pending permission rejects on instance dispose",
|
||||||
const test = yield* TestInstance
|
() =>
|
||||||
const store = yield* InstanceStore.Service
|
Effect.gen(function* () {
|
||||||
const fiber = yield* ask({
|
const test = yield* TestInstance
|
||||||
id: PermissionID.make("per_dispose"),
|
const store = yield* InstanceStore.Service
|
||||||
sessionID: SessionID.make("session_dispose"),
|
const fiber = yield* ask({
|
||||||
permission: "bash",
|
id: PermissionID.make("per_dispose"),
|
||||||
patterns: ["ls"],
|
sessionID: SessionID.make("session_dispose"),
|
||||||
metadata: {},
|
|
||||||
always: [],
|
|
||||||
ruleset: [],
|
|
||||||
}).pipe(Effect.forkScoped)
|
|
||||||
|
|
||||||
expect(yield* waitForPending(1)).toHaveLength(1)
|
|
||||||
const ctx = yield* store.load({ directory: test.directory })
|
|
||||||
yield* store.dispose(ctx)
|
|
||||||
|
|
||||||
const exit = yield* Fiber.await(fiber)
|
|
||||||
expect(Exit.isFailure(exit)).toBe(true)
|
|
||||||
if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Permission.RejectedError)
|
|
||||||
}),
|
|
||||||
{ git: true },
|
|
||||||
)
|
|
||||||
|
|
||||||
it.instance("pending permission rejects on instance reload", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const test = yield* TestInstance
|
|
||||||
const store = yield* InstanceStore.Service
|
|
||||||
const fiber = yield* ask({
|
|
||||||
id: PermissionID.make("per_reload"),
|
|
||||||
sessionID: SessionID.make("session_reload"),
|
|
||||||
permission: "bash",
|
|
||||||
patterns: ["ls"],
|
|
||||||
metadata: {},
|
|
||||||
always: [],
|
|
||||||
ruleset: [],
|
|
||||||
}).pipe(Effect.forkScoped)
|
|
||||||
|
|
||||||
expect(yield* waitForPending(1)).toHaveLength(1)
|
|
||||||
yield* store.reload({ directory: test.directory })
|
|
||||||
|
|
||||||
const exit = yield* Fiber.await(fiber)
|
|
||||||
expect(Exit.isFailure(exit)).toBe(true)
|
|
||||||
if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Permission.RejectedError)
|
|
||||||
}),
|
|
||||||
{ git: true },
|
|
||||||
)
|
|
||||||
|
|
||||||
it.instance("reply - does nothing for unknown requestID", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
yield* reply({ requestID: PermissionID.make("per_unknown"), reply: "once" })
|
|
||||||
expect(yield* list()).toHaveLength(0)
|
|
||||||
}),
|
|
||||||
{ git: true },
|
|
||||||
)
|
|
||||||
|
|
||||||
it.instance("ask - checks all patterns and stops on first deny", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const err = yield* fail(
|
|
||||||
ask({
|
|
||||||
sessionID: SessionID.make("session_test"),
|
|
||||||
permission: "bash",
|
permission: "bash",
|
||||||
patterns: ["echo hello", "rm -rf /"],
|
patterns: ["ls"],
|
||||||
metadata: {},
|
metadata: {},
|
||||||
always: [],
|
always: [],
|
||||||
ruleset: [
|
ruleset: [],
|
||||||
{ permission: "bash", pattern: "*", action: "allow" },
|
}).pipe(Effect.forkScoped)
|
||||||
{ permission: "bash", pattern: "rm *", action: "deny" },
|
|
||||||
],
|
expect(yield* waitForPending(1)).toHaveLength(1)
|
||||||
}),
|
const ctx = yield* store.load({ directory: test.directory })
|
||||||
)
|
yield* store.dispose(ctx)
|
||||||
expect(err).toBeInstanceOf(Permission.DeniedError)
|
|
||||||
}),
|
const exit = yield* Fiber.await(fiber)
|
||||||
|
expect(Exit.isFailure(exit)).toBe(true)
|
||||||
|
if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Permission.RejectedError)
|
||||||
|
}),
|
||||||
{ git: true },
|
{ git: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
it.instance("ask - allows all patterns when all match allow rules", () =>
|
it.instance(
|
||||||
Effect.gen(function* () {
|
"pending permission rejects on instance reload",
|
||||||
const result = yield* ask({
|
() =>
|
||||||
sessionID: SessionID.make("session_test"),
|
Effect.gen(function* () {
|
||||||
permission: "bash",
|
const test = yield* TestInstance
|
||||||
patterns: ["echo hello", "ls -la", "pwd"],
|
const store = yield* InstanceStore.Service
|
||||||
metadata: {},
|
const fiber = yield* ask({
|
||||||
always: [],
|
id: PermissionID.make("per_reload"),
|
||||||
ruleset: [{ permission: "bash", pattern: "*", action: "allow" }],
|
sessionID: SessionID.make("session_reload"),
|
||||||
})
|
|
||||||
expect(result).toBeUndefined()
|
|
||||||
}),
|
|
||||||
{ git: true },
|
|
||||||
)
|
|
||||||
|
|
||||||
it.instance("ask - should deny even when an earlier pattern is ask", () =>
|
|
||||||
Effect.gen(function* () {
|
|
||||||
const err = yield* fail(
|
|
||||||
ask({
|
|
||||||
sessionID: SessionID.make("session_test"),
|
|
||||||
permission: "bash",
|
permission: "bash",
|
||||||
patterns: ["echo hello", "rm -rf /"],
|
patterns: ["ls"],
|
||||||
metadata: {},
|
metadata: {},
|
||||||
always: [],
|
always: [],
|
||||||
ruleset: [
|
ruleset: [],
|
||||||
{ permission: "bash", pattern: "echo *", action: "ask" },
|
}).pipe(Effect.forkScoped)
|
||||||
{ permission: "bash", pattern: "rm *", action: "deny" },
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(err).toBeInstanceOf(Permission.DeniedError)
|
expect(yield* waitForPending(1)).toHaveLength(1)
|
||||||
expect(yield* list()).toHaveLength(0)
|
yield* store.reload({ directory: test.directory })
|
||||||
}),
|
|
||||||
|
const exit = yield* Fiber.await(fiber)
|
||||||
|
expect(Exit.isFailure(exit)).toBe(true)
|
||||||
|
if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Permission.RejectedError)
|
||||||
|
}),
|
||||||
{ git: true },
|
{ git: true },
|
||||||
)
|
)
|
||||||
|
|
||||||
it.instance("ask - abort should clear pending request", () =>
|
it.instance(
|
||||||
Effect.gen(function* () {
|
"reply - does nothing for unknown requestID",
|
||||||
const test = yield* TestInstance
|
() =>
|
||||||
const store = yield* InstanceStore.Service
|
Effect.gen(function* () {
|
||||||
|
yield* reply({ requestID: PermissionID.make("per_unknown"), reply: "once" })
|
||||||
const fiber = yield* ask({
|
expect(yield* list()).toHaveLength(0)
|
||||||
id: PermissionID.make("per_reload"),
|
}),
|
||||||
sessionID: SessionID.make("session_reload"),
|
{ git: true },
|
||||||
permission: "bash",
|
)
|
||||||
patterns: ["ls"],
|
|
||||||
metadata: {},
|
it.instance(
|
||||||
always: [],
|
"ask - checks all patterns and stops on first deny",
|
||||||
ruleset: [{ permission: "bash", pattern: "*", action: "ask" }],
|
() =>
|
||||||
}).pipe(Effect.forkScoped)
|
Effect.gen(function* () {
|
||||||
|
const err = yield* fail(
|
||||||
const pending = yield* waitForPending(1)
|
ask({
|
||||||
expect(pending).toHaveLength(1)
|
sessionID: SessionID.make("session_test"),
|
||||||
yield* store.reload({ directory: test.directory })
|
permission: "bash",
|
||||||
|
patterns: ["echo hello", "rm -rf /"],
|
||||||
const exit = yield* Fiber.await(fiber)
|
metadata: {},
|
||||||
expect(Exit.isFailure(exit)).toBe(true)
|
always: [],
|
||||||
if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Permission.RejectedError)
|
ruleset: [
|
||||||
}),
|
{ permission: "bash", pattern: "*", action: "allow" },
|
||||||
|
{ permission: "bash", pattern: "rm *", action: "deny" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
expect(err).toBeInstanceOf(Permission.DeniedError)
|
||||||
|
}),
|
||||||
|
{ git: true },
|
||||||
|
)
|
||||||
|
|
||||||
|
it.instance(
|
||||||
|
"ask - allows all patterns when all match allow rules",
|
||||||
|
() =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const result = yield* ask({
|
||||||
|
sessionID: SessionID.make("session_test"),
|
||||||
|
permission: "bash",
|
||||||
|
patterns: ["echo hello", "ls -la", "pwd"],
|
||||||
|
metadata: {},
|
||||||
|
always: [],
|
||||||
|
ruleset: [{ permission: "bash", pattern: "*", action: "allow" }],
|
||||||
|
})
|
||||||
|
expect(result).toBeUndefined()
|
||||||
|
}),
|
||||||
|
{ git: true },
|
||||||
|
)
|
||||||
|
|
||||||
|
it.instance(
|
||||||
|
"ask - should deny even when an earlier pattern is ask",
|
||||||
|
() =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const err = yield* fail(
|
||||||
|
ask({
|
||||||
|
sessionID: SessionID.make("session_test"),
|
||||||
|
permission: "bash",
|
||||||
|
patterns: ["echo hello", "rm -rf /"],
|
||||||
|
metadata: {},
|
||||||
|
always: [],
|
||||||
|
ruleset: [
|
||||||
|
{ permission: "bash", pattern: "echo *", action: "ask" },
|
||||||
|
{ permission: "bash", pattern: "rm *", action: "deny" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(err).toBeInstanceOf(Permission.DeniedError)
|
||||||
|
expect(yield* list()).toHaveLength(0)
|
||||||
|
}),
|
||||||
|
{ git: true },
|
||||||
|
)
|
||||||
|
|
||||||
|
it.instance(
|
||||||
|
"ask - abort should clear pending request",
|
||||||
|
() =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const test = yield* TestInstance
|
||||||
|
const store = yield* InstanceStore.Service
|
||||||
|
|
||||||
|
const fiber = yield* ask({
|
||||||
|
id: PermissionID.make("per_reload"),
|
||||||
|
sessionID: SessionID.make("session_reload"),
|
||||||
|
permission: "bash",
|
||||||
|
patterns: ["ls"],
|
||||||
|
metadata: {},
|
||||||
|
always: [],
|
||||||
|
ruleset: [{ permission: "bash", pattern: "*", action: "ask" }],
|
||||||
|
}).pipe(Effect.forkScoped)
|
||||||
|
|
||||||
|
const pending = yield* waitForPending(1)
|
||||||
|
expect(pending).toHaveLength(1)
|
||||||
|
yield* store.reload({ directory: test.directory })
|
||||||
|
|
||||||
|
const exit = yield* Fiber.await(fiber)
|
||||||
|
expect(Exit.isFailure(exit)).toBe(true)
|
||||||
|
if (Exit.isFailure(exit)) expect(Cause.squash(exit.cause)).toBeInstanceOf(Permission.RejectedError)
|
||||||
|
}),
|
||||||
{ git: true },
|
{ git: true },
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -31,7 +31,10 @@ const setBootstrap = (run: Effect.Effect<void>) =>
|
|||||||
)
|
)
|
||||||
|
|
||||||
const registerDisposerScoped = (disposer: (directory: string) => Promise<void>) =>
|
const registerDisposerScoped = (disposer: (directory: string) => Promise<void>) =>
|
||||||
Effect.acquireRelease(Effect.sync(() => registerDisposer(disposer)), (off) => Effect.sync(off))
|
Effect.acquireRelease(
|
||||||
|
Effect.sync(() => registerDisposer(disposer)),
|
||||||
|
(off) => Effect.sync(off),
|
||||||
|
)
|
||||||
|
|
||||||
describe("InstanceStore", () => {
|
describe("InstanceStore", () => {
|
||||||
it.live("loads instance context without installing ALS for the caller", () =>
|
it.live("loads instance context without installing ALS for the caller", () =>
|
||||||
|
|||||||
Reference in New Issue
Block a user