mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(audit): validate raw invoker evidence
This commit is contained in:
@@ -191,6 +191,38 @@ describe("execution identity admission envelope", () => {
|
||||
).toThrow("execution identity admission envelope is not canonical");
|
||||
});
|
||||
|
||||
it.each([
|
||||
["malformed", { state: "invalid" }],
|
||||
["mixed", { state: "unknown", rawPrincipalRef: "raw-substitute-secret" }],
|
||||
["untagged", { kind: "local-account", rawPrincipalRef: "legacy-untagged" }],
|
||||
[
|
||||
"extra-field",
|
||||
{
|
||||
state: "present",
|
||||
kind: "local-account",
|
||||
rawPrincipalRef: "raw-principal",
|
||||
extra: true,
|
||||
},
|
||||
],
|
||||
])("rejects %s raw invoker facts before enqueue projection", (_variant, invoker) => {
|
||||
const sink = vi.fn(() => true);
|
||||
const clear = configureExecutionIdentityAdmissionSink(sink);
|
||||
try {
|
||||
expect(
|
||||
enqueueExecutionIdentityContextAtAdmission(facts({ invoker: invoker as never }), {
|
||||
enabled: true,
|
||||
contextId: "context-invalid",
|
||||
executionId: "execution-invalid",
|
||||
now: 1,
|
||||
runtimeInstanceId: "runtime-1",
|
||||
}),
|
||||
).toBeUndefined();
|
||||
expect(sink).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
clear();
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects non-plain or lossy clone data without invoking accessors", () => {
|
||||
const envelope = captureEnvelope(facts({ invoker: { state: "unknown" } }), {
|
||||
contextId: "context-unknown",
|
||||
|
||||
@@ -25,6 +25,25 @@ const evidenceState = () =>
|
||||
const closedObject = <T extends Parameters<typeof Type.Object>[0]>(properties: T) =>
|
||||
Type.Object(properties, { additionalProperties: false });
|
||||
|
||||
const ExecutionIdentityAdmissionInvokerSchema = Type.Union([
|
||||
closedObject({
|
||||
state: Type.Literal("present"),
|
||||
kind: Type.Union([
|
||||
Type.Literal("person"),
|
||||
Type.Literal("agent"),
|
||||
Type.Literal("service"),
|
||||
Type.Literal("schedule"),
|
||||
Type.Literal("webhook"),
|
||||
Type.Literal("system"),
|
||||
Type.Literal("local-account"),
|
||||
Type.Literal("runtime"),
|
||||
]),
|
||||
rawPrincipalRef: rawRef(),
|
||||
displayLabel: Type.Optional(Type.String({ maxLength: 128 })),
|
||||
}),
|
||||
closedObject({ state: Type.Literal("unknown") }),
|
||||
]);
|
||||
|
||||
const ExecutionIdentityAdmissionEnvelopeSchema = closedObject({
|
||||
envelopeVersion: Type.Literal(1),
|
||||
contextId: boundedRef(),
|
||||
@@ -62,26 +81,7 @@ const ExecutionIdentityAdmissionEnvelopeSchema = closedObject({
|
||||
Type.Literal("acp"),
|
||||
]),
|
||||
}),
|
||||
invoker: Type.Optional(
|
||||
Type.Union([
|
||||
closedObject({
|
||||
state: Type.Literal("present"),
|
||||
kind: Type.Union([
|
||||
Type.Literal("person"),
|
||||
Type.Literal("agent"),
|
||||
Type.Literal("service"),
|
||||
Type.Literal("schedule"),
|
||||
Type.Literal("webhook"),
|
||||
Type.Literal("system"),
|
||||
Type.Literal("local-account"),
|
||||
Type.Literal("runtime"),
|
||||
]),
|
||||
rawPrincipalRef: rawRef(),
|
||||
displayLabel: Type.Optional(Type.String({ maxLength: 128 })),
|
||||
}),
|
||||
closedObject({ state: Type.Literal("unknown") }),
|
||||
]),
|
||||
),
|
||||
invoker: Type.Optional(ExecutionIdentityAdmissionInvokerSchema),
|
||||
applicableGrants: Type.Array(closedObject({ rawGrantRef: rawRef(), state: evidenceState() }), {
|
||||
maxItems: EXECUTION_IDENTITY_ADMISSION_MAX_ITEMS,
|
||||
}),
|
||||
@@ -223,6 +223,12 @@ function validateEnvelope(value: unknown): asserts value is ExecutionIdentityAdm
|
||||
}
|
||||
}
|
||||
|
||||
function validateRawInvoker(value: unknown): void {
|
||||
if (value !== undefined && !Value.Check(ExecutionIdentityAdmissionInvokerSchema, value)) {
|
||||
throw new Error("execution identity admission invoker violates its bounded contract");
|
||||
}
|
||||
}
|
||||
|
||||
function validateToken(value: unknown): asserts value is ExecutionIdentityAdmissionToken {
|
||||
if (
|
||||
!Value.Check(ExecutionIdentityAdmissionTokenSchema, value) ||
|
||||
@@ -419,6 +425,7 @@ export function enqueueExecutionIdentityContextAtAdmission(
|
||||
}
|
||||
try {
|
||||
assertPlainCloneData(facts);
|
||||
validateRawInvoker(facts.invoker);
|
||||
const token =
|
||||
options.token ??
|
||||
createExecutionIdentityAdmissionToken(facts.runId, {
|
||||
|
||||
Reference in New Issue
Block a user