@stigmer/server 3.36.0 → 3.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/domain/agentexecution/constants.d.ts +0 -10
- package/dist/domain/agentexecution/constants.d.ts.map +1 -1
- package/dist/domain/agentexecution/constants.js +0 -12
- package/dist/domain/agentexecution/constants.js.map +1 -1
- package/dist/domain/agentexecution/usage.d.ts +0 -2
- package/dist/domain/agentexecution/usage.d.ts.map +1 -1
- package/dist/domain/agentexecution/usage.js +12 -10
- package/dist/domain/agentexecution/usage.js.map +1 -1
- package/dist/domain/organization/controller.d.ts +4 -1
- package/dist/domain/organization/controller.d.ts.map +1 -1
- package/dist/domain/organization/controller.js +30 -8
- package/dist/domain/organization/controller.js.map +1 -1
- package/dist/domain/workflow/controller.js +2 -1
- package/dist/domain/workflow/controller.js.map +1 -1
- package/dist/domain/workflow/steps.d.ts +7 -4
- package/dist/domain/workflow/steps.d.ts.map +1 -1
- package/dist/domain/workflow/steps.js +29 -17
- package/dist/domain/workflow/steps.js.map +1 -1
- package/dist/extensions/organization-directory.d.ts +29 -6
- package/dist/extensions/organization-directory.d.ts.map +1 -1
- package/dist/extensions/organization-directory.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/pipeline/steps/guard-memory-capture.d.ts +9 -4
- package/dist/pipeline/steps/guard-memory-capture.d.ts.map +1 -1
- package/dist/pipeline/steps/guard-memory-capture.js +10 -12
- package/dist/pipeline/steps/guard-memory-capture.js.map +1 -1
- package/package.json +6 -6
- package/src/domain/agentexecution/__tests__/agentexecution.test.ts +2 -8
- package/src/domain/agentexecution/__tests__/execution-usage-report.test.ts +78 -0
- package/src/domain/agentexecution/constants.ts +0 -15
- package/src/domain/agentexecution/usage.ts +16 -13
- package/src/domain/organization/controller.ts +47 -10
- package/src/domain/workflow/__tests__/version-audit-degradation.test.ts +192 -0
- package/src/domain/workflow/controller.ts +2 -1
- package/src/domain/workflow/steps.ts +30 -18
- package/src/extensions/__tests__/extension-composition.test.ts +90 -18
- package/src/extensions/organization-directory.ts +33 -7
- package/src/index.ts +5 -1
- package/src/pipeline/steps/__tests__/guard-memory-capture.test.ts +67 -5
- package/src/pipeline/steps/guard-memory-capture.ts +24 -19
|
@@ -7,8 +7,13 @@
|
|
|
7
7
|
* the capability, the org-mismatch throw propagating untouched, and the
|
|
8
8
|
* absent-capability posture staying byte-identical to the pre-seam gate
|
|
9
9
|
* (trusted-local and OIDC callers admit; machine and
|
|
10
|
-
* platform_client_id-carrying callers refuse).
|
|
10
|
+
* platform_client_id-carrying callers refuse). The claim counts only on a
|
|
11
|
+
* platform token (stigmer#1312): the refusing cases carry tokens signed by
|
|
12
|
+
* the envelope every Stigmer lane mints through, and another issuer's
|
|
13
|
+
* token carrying the same claim is admitted.
|
|
11
14
|
*/
|
|
15
|
+
import { generateKeyPairSync } from "node:crypto";
|
|
16
|
+
|
|
12
17
|
import { describe, expect, it } from "vitest";
|
|
13
18
|
import { Code, ConnectError } from "@connectrpc/connect";
|
|
14
19
|
import { create } from "@bufbuild/protobuf";
|
|
@@ -17,6 +22,9 @@ import { MemorySchema } from "@stigmer/protos/ai/stigmer/agentic/memory/v1/api_p
|
|
|
17
22
|
import { ApiResourceKind } from "@stigmer/protos/ai/stigmer/commons/apiresource/apiresourcekind/api_resource_kind_pb";
|
|
18
23
|
|
|
19
24
|
import type { CallerIdentity } from "../../../extensions/identity.js";
|
|
25
|
+
import { signPlatformToken } from "../../../platformtoken/envelope.js";
|
|
26
|
+
import { platformTokenKeyRingFromPem } from "../../../platformtoken/key-ring.js";
|
|
27
|
+
import type { SigningPlatformTokenKeyRing } from "../../../platformtoken/key-ring.js";
|
|
20
28
|
import type {
|
|
21
29
|
MemoryCaptureDecision,
|
|
22
30
|
RunnerCredentialProvider,
|
|
@@ -77,11 +85,40 @@ function providerWith(
|
|
|
77
85
|
};
|
|
78
86
|
}
|
|
79
87
|
|
|
88
|
+
const { privateKey, publicKey } = generateKeyPairSync("rsa", {
|
|
89
|
+
modulusLength: 2048,
|
|
90
|
+
});
|
|
91
|
+
const built = platformTokenKeyRingFromPem({
|
|
92
|
+
privateKeyPem: privateKey.export({ format: "pem", type: "pkcs8" }).toString(),
|
|
93
|
+
publicKeyPems: [publicKey.export({ format: "pem", type: "spki" }).toString()],
|
|
94
|
+
});
|
|
95
|
+
if (built.signer === undefined) throw new Error("fixture ring must sign");
|
|
96
|
+
const ring: SigningPlatformTokenKeyRing = { ...built, signer: built.signer };
|
|
97
|
+
|
|
98
|
+
/** A platform token, signed as every Stigmer-minted lane signs it. */
|
|
99
|
+
function mintedToken(claims: Record<string, string>): string {
|
|
100
|
+
return signPlatformToken(ring, { sub: "ida_minted", ...claims }).token;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** Another issuer's JWT: the claims as given, never signed by the ring. */
|
|
80
104
|
function jwtWith(payload: Record<string, unknown>): string {
|
|
81
|
-
const
|
|
82
|
-
|
|
105
|
+
const segment = (value: object) =>
|
|
106
|
+
Buffer.from(JSON.stringify(value)).toString("base64url");
|
|
107
|
+
return `${segment({ alg: "RS256", typ: "JWT" })}.${segment(payload)}.signature`;
|
|
83
108
|
}
|
|
84
109
|
|
|
110
|
+
/** An OIDC login whose issuer happens to stamp a platform_client_id claim. */
|
|
111
|
+
const FOREIGN_CLAIM_CALLER: CallerIdentity = {
|
|
112
|
+
identityId: "ida_oidc",
|
|
113
|
+
callerClass: "user",
|
|
114
|
+
issuer: "https://issuer.example",
|
|
115
|
+
rawToken: jwtWith({
|
|
116
|
+
iss: "https://issuer.example",
|
|
117
|
+
sub: "ida_oidc",
|
|
118
|
+
platform_client_id: "pc_1",
|
|
119
|
+
}),
|
|
120
|
+
};
|
|
121
|
+
|
|
85
122
|
async function rejectionOf(run: void | Promise<void>): Promise<unknown> {
|
|
86
123
|
return Promise.resolve(run).then(
|
|
87
124
|
() => {
|
|
@@ -172,13 +209,22 @@ describe("GuardMemoryCapture with the capability composed", () => {
|
|
|
172
209
|
identityId: "ida_guest",
|
|
173
210
|
callerClass: "guest",
|
|
174
211
|
issuer: "stigmer",
|
|
175
|
-
rawToken:
|
|
212
|
+
rawToken: mintedToken({ platform_client_id: "pc_1" }),
|
|
176
213
|
};
|
|
177
214
|
await expect(step.execute(memoryCtx(minted))).rejects.toThrowError(
|
|
178
215
|
MEMORY_CAPTURE_CALLER_MESSAGE,
|
|
179
216
|
);
|
|
180
217
|
});
|
|
181
218
|
|
|
219
|
+
it("no-opinion falls through: another issuer's platform_client_id claim is admitted", async () => {
|
|
220
|
+
// Both editions' capabilities answer no-opinion for a person, so this
|
|
221
|
+
// is the path an OIDC login takes in production.
|
|
222
|
+
const step = newGuardMemoryCaptureStep<typeof MemorySchema>(
|
|
223
|
+
providerWith(() => ({ verdict: "no-opinion" })),
|
|
224
|
+
);
|
|
225
|
+
await step.execute(memoryCtx(FOREIGN_CLAIM_CALLER));
|
|
226
|
+
});
|
|
227
|
+
|
|
182
228
|
it("internal and in-process callers never consult the capability", async () => {
|
|
183
229
|
const step = newGuardMemoryCaptureStep<typeof MemorySchema>(
|
|
184
230
|
providerWith(() => {
|
|
@@ -220,12 +266,28 @@ describe("GuardMemoryCapture without the capability (the pre-seam gate)", () =>
|
|
|
220
266
|
step.execute(
|
|
221
267
|
memoryCtx({
|
|
222
268
|
...LOCAL_USER,
|
|
223
|
-
rawToken:
|
|
269
|
+
rawToken: mintedToken({ platform_client_id: "pc_1" }),
|
|
224
270
|
}),
|
|
225
271
|
),
|
|
226
272
|
).rejects.toThrowError(MEMORY_CAPTURE_CALLER_MESSAGE);
|
|
227
273
|
});
|
|
228
274
|
|
|
275
|
+
it("admits a platform_client_id claim on a token Stigmer did not issue", async () => {
|
|
276
|
+
const step = newGuardMemoryCaptureStep<typeof MemorySchema>(providerWith());
|
|
277
|
+
await step.execute(memoryCtx(FOREIGN_CLAIM_CALLER));
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it("admits a platform token whose platform_client_id is empty", async () => {
|
|
281
|
+
const step = newGuardMemoryCaptureStep<typeof MemorySchema>(providerWith());
|
|
282
|
+
await step.execute(
|
|
283
|
+
memoryCtx({
|
|
284
|
+
...LOCAL_USER,
|
|
285
|
+
issuer: "stigmer",
|
|
286
|
+
rawToken: mintedToken({ platform_client_id: "" }),
|
|
287
|
+
}),
|
|
288
|
+
);
|
|
289
|
+
});
|
|
290
|
+
|
|
229
291
|
it("admits a runner-shaped caller — the recorded pre-fix posture", async () => {
|
|
230
292
|
// With no capability composed the gate cannot classify runner
|
|
231
293
|
// credentials — the single-user OSS posture admits them (the #564
|
|
@@ -14,7 +14,12 @@
|
|
|
14
14
|
* every Stigmer-minted non-first-party credential carries the
|
|
15
15
|
* `platform_client_id` claim (StigmerJwtIssuer stamps it on user, guest,
|
|
16
16
|
* channel, and schedule tokens alike), so its presence IS the exclusion
|
|
17
|
-
* the Java RequestCallerIdentity.isFirstPartyHumanOperator computes.
|
|
17
|
+
* the Java RequestCallerIdentity.isFirstPartyHumanOperator computes. The
|
|
18
|
+
* claim counts only on a platform token (`iss: "stigmer"`, read through
|
|
19
|
+
* decodeVerifiedPlatformTokenPayload as the PlatformClient origin guard
|
|
20
|
+
* reads it): a bearer another verifier claimed is some other issuer's,
|
|
21
|
+
* whatever claims it carries, and is never taken for a Stigmer-minted
|
|
22
|
+
* one (stigmer#1312).
|
|
18
23
|
*
|
|
19
24
|
* RUNNER credentials carry neither the machine class nor that claim —
|
|
20
25
|
* their eligibility is EDITION POLICY, so the gate consults the composed
|
|
@@ -30,16 +35,21 @@
|
|
|
30
35
|
* this gate's own logic applies). The capability throws its own
|
|
31
36
|
* byte-pinned refusal for the org-mismatch arm. With no capability
|
|
32
37
|
* composed the gate's logic is byte-identical to before the seam: OSS
|
|
33
|
-
* trusted-local callers carry no token and OIDC console logins
|
|
34
|
-
*
|
|
35
|
-
*
|
|
38
|
+
* trusted-local callers carry no token and OIDC console logins are not
|
|
39
|
+
* platform tokens — the step admits both, so the single-user posture is
|
|
40
|
+
* unchanged (proven by the rosters).
|
|
36
41
|
*/
|
|
37
42
|
import type { DescMessage } from "@bufbuild/protobuf";
|
|
38
43
|
import { Code, ConnectError } from "@connectrpc/connect";
|
|
39
44
|
|
|
45
|
+
import { USER_TOKEN_CLAIMS } from "../../domain/platformclient/constants.js";
|
|
40
46
|
import { isServerComposedRequest } from "../../extensions/identity.js";
|
|
41
47
|
import type { PipelineStep } from "../pipeline.js";
|
|
42
48
|
import type { RequestContext } from "../request-context.js";
|
|
49
|
+
import {
|
|
50
|
+
decodeVerifiedPlatformTokenPayload,
|
|
51
|
+
stringClaim,
|
|
52
|
+
} from "../../platformtoken/envelope.js";
|
|
43
53
|
import type { RunnerCredentialProvider } from "../../runnerauth/runner-credential-provider.js";
|
|
44
54
|
import { metadataOf } from "./shapes.js";
|
|
45
55
|
|
|
@@ -138,20 +148,15 @@ export function memoryCaptureCredentialOf<Desc extends DescMessage>(
|
|
|
138
148
|
return undefined;
|
|
139
149
|
}
|
|
140
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Whether the bearer is a platform token naming a PlatformClient. Anything
|
|
153
|
+
* that is not a platform token (no token, an API key, another issuer's
|
|
154
|
+
* JWT) is not one, whatever claims it carries.
|
|
155
|
+
*/
|
|
141
156
|
function carriesPlatformClientClaim(rawToken: string): boolean {
|
|
142
|
-
const
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const payload = JSON.parse(
|
|
148
|
-
Buffer.from(segments[1], "base64url").toString("utf8"),
|
|
149
|
-
) as { platform_client_id?: unknown };
|
|
150
|
-
return (
|
|
151
|
-
typeof payload.platform_client_id === "string" &&
|
|
152
|
-
payload.platform_client_id !== ""
|
|
153
|
-
);
|
|
154
|
-
} catch {
|
|
155
|
-
return false;
|
|
156
|
-
}
|
|
157
|
+
const payload = decodeVerifiedPlatformTokenPayload(rawToken);
|
|
158
|
+
return (
|
|
159
|
+
payload !== undefined &&
|
|
160
|
+
stringClaim(payload, USER_TOKEN_CLAIMS.platformClientId) !== undefined
|
|
161
|
+
);
|
|
157
162
|
}
|