@safebrowse/daemon 0.1.4 → 0.1.5
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/README.md +64 -11
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +54 -11
- package/dist/cli.js.map +1 -1
- package/dist/loaders.d.ts.map +1 -1
- package/dist/loaders.js +65 -2
- package/dist/loaders.js.map +1 -1
- package/dist/modelGuard.d.ts +28 -0
- package/dist/modelGuard.d.ts.map +1 -0
- package/dist/modelGuard.js +325 -0
- package/dist/modelGuard.js.map +1 -0
- package/dist/parserIsolation.d.ts +7 -9
- package/dist/parserIsolation.d.ts.map +1 -1
- package/dist/parserIsolation.js.map +1 -1
- package/dist/parserWorker.js +11 -12
- package/dist/parserWorker.js.map +1 -1
- package/dist/runtime/config/auditor/{v5_secure_claim_suite.json → v6_secure_claim_suite.json} +9 -9
- package/dist/server.d.ts +5 -3
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +541 -1853
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
- package/dist/runtime/config/auditor/v4_prompt_injection_coverage_suite.json +0 -2789
- package/dist/runtime/config/v2-compromised-fixtures.json +0 -34
package/dist/server.js
CHANGED
|
@@ -3,8 +3,9 @@ import { readFile, stat } from "node:fs/promises";
|
|
|
3
3
|
import { createServer } from "node:http";
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
import {
|
|
7
|
-
import { buildRegistryDefaults, loadKnowledgeBaseContext,
|
|
6
|
+
import { applyModelGuardAssessment, applyV6ObservationMediation, buildReplayBundle, buildModelGuardObservationRequest, compilePolicy, computeToolManifestHash, computeToolSchemaHash, createApprovalIntentPayloadV6, evaluateCapabilityUseV6, extractAttachmentGraphV6, issueApprovalEnvelopeV6, mintCapabilitiesForObservationV6, mintMemoryPromotionCapabilityV6, prepareToolOnboardingV6, promoteMemoryRecordV6, rollbackMemoryRecordV6, stageMemoryRecordV6, tightenAuthoritiesWithModelGuard, verifyApprovalIntentSignatureV6, verifyToolCallbackV6 } from "@safebrowse/core";
|
|
7
|
+
import { buildRegistryDefaults, loadKnowledgeBaseContext, loadPolicyPackFromPaths, loadVerifiedRegistryBundle, resolvePolicyLayerFiles } from "./loaders.js";
|
|
8
|
+
import { createModelGuardClient } from "./modelGuard.js";
|
|
8
9
|
import { createParserIsolationService } from "./parserIsolation.js";
|
|
9
10
|
const PARSER_HEALTH_REFRESH_INTERVAL_MS = 30_000;
|
|
10
11
|
function hashValue(input) {
|
|
@@ -29,17 +30,6 @@ function logServerError(error) {
|
|
|
29
30
|
}
|
|
30
31
|
console.error("SafeBrowse daemon error:", error);
|
|
31
32
|
}
|
|
32
|
-
function legacyResponseMeta(route) {
|
|
33
|
-
return {
|
|
34
|
-
deprecated: true,
|
|
35
|
-
telemetry: {
|
|
36
|
-
deprecated: true,
|
|
37
|
-
claimScope: "legacy_compatibility",
|
|
38
|
-
preventionClaim: false,
|
|
39
|
-
routeVersion: route
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
33
|
async function fileExists(path) {
|
|
44
34
|
try {
|
|
45
35
|
await stat(path);
|
|
@@ -60,10 +50,8 @@ async function resolveDefaultRootDir() {
|
|
|
60
50
|
}
|
|
61
51
|
async function buildRuntimeContext(options) {
|
|
62
52
|
const rootDir = options.rootDir ?? (await resolveDefaultRootDir());
|
|
63
|
-
const deploymentProfile = options.deploymentProfile
|
|
64
|
-
|
|
65
|
-
: (options.deploymentProfile ?? "development");
|
|
66
|
-
const secureDeployment = deploymentProfile === "secure_v5";
|
|
53
|
+
const deploymentProfile = options.deploymentProfile ?? "development";
|
|
54
|
+
const secureDeployment = deploymentProfile === "secure_v6";
|
|
67
55
|
const policyPack = options.policyPack ??
|
|
68
56
|
(await loadPolicyPackFromPaths(resolvePolicyLayerFiles(resolve(rootDir))));
|
|
69
57
|
const knowledgeBase = options.knowledgeBase ?? (await loadKnowledgeBaseContext(resolve(rootDir, "knowledge_base")));
|
|
@@ -87,12 +75,11 @@ async function buildRuntimeContext(options) {
|
|
|
87
75
|
: undefined,
|
|
88
76
|
approvalBrokerConfigured: Boolean(approvalBrokerPublicKeyPem),
|
|
89
77
|
approvalBrokerMode: secureDeployment ? "external_service" : options.approvalBrokerMode ?? "signature_verification",
|
|
90
|
-
|
|
78
|
+
modelGuardBaseUrl: options.modelGuardBaseUrl?.trim() || undefined,
|
|
79
|
+
modelGuardTimeoutMs: options.modelGuardTimeoutMs ?? 2_500,
|
|
80
|
+
modelGuardEnforcementMode: options.modelGuardEnforcementMode ?? "off"
|
|
91
81
|
};
|
|
92
82
|
}
|
|
93
|
-
function plusMinutes(value, minutes) {
|
|
94
|
-
return new Date(new Date(value).getTime() + minutes * 60_000).toISOString();
|
|
95
|
-
}
|
|
96
83
|
function plusSeconds(value, seconds) {
|
|
97
84
|
return new Date(new Date(value).getTime() + seconds * 1_000).toISOString();
|
|
98
85
|
}
|
|
@@ -101,172 +88,75 @@ function createWorkflowHash(payload) {
|
|
|
101
88
|
taskId: payload.taskId,
|
|
102
89
|
userGoal: payload.userGoal,
|
|
103
90
|
phase: payload.phase ?? "",
|
|
91
|
+
taskPurposeClass: payload.taskPurposeClass ?? "",
|
|
92
|
+
taskPhase: payload.taskPhase ?? "",
|
|
104
93
|
allowedOrigins: payload.allowedOrigins ?? [],
|
|
105
94
|
allowedVerbs: payload.allowedVerbs ?? [],
|
|
106
|
-
forbiddenSinks: payload.forbiddenSinks ?? []
|
|
95
|
+
forbiddenSinks: payload.forbiddenSinks ?? [],
|
|
96
|
+
allowedPathClasses: payload.allowedPathClasses ?? [],
|
|
97
|
+
approvalRequiredPathClasses: payload.approvalRequiredPathClasses ?? []
|
|
107
98
|
});
|
|
108
99
|
}
|
|
109
|
-
function
|
|
100
|
+
function secureParserIsolationSatisfied(probe) {
|
|
101
|
+
return (probe.mode === "node_permission_process" &&
|
|
102
|
+
probe.processIsolated &&
|
|
103
|
+
probe.egressDenied &&
|
|
104
|
+
probe.permissionModelEnabled &&
|
|
105
|
+
probe.childProcessDenied &&
|
|
106
|
+
probe.workerThreadsDenied &&
|
|
107
|
+
probe.envKeys.length === 0);
|
|
108
|
+
}
|
|
109
|
+
function claimBearingReady(runtime, probe) {
|
|
110
|
+
return (runtime.deploymentProfile === "secure_v6" &&
|
|
111
|
+
runtime.approvalBrokerConfigured &&
|
|
112
|
+
Boolean(runtime.approvalBrokerPublicKey) &&
|
|
113
|
+
runtime.approvalBrokerMode === "external_service" &&
|
|
114
|
+
runtime.verifiedRegistry?.signatureVerified === true &&
|
|
115
|
+
runtime.parserIsolationMode === "node_permission_process" &&
|
|
116
|
+
secureParserIsolationSatisfied(probe));
|
|
117
|
+
}
|
|
118
|
+
function createSessionState(request, runtime, secureReady) {
|
|
110
119
|
const createdAt = new Date().toISOString();
|
|
111
|
-
const allowedOrigins = request.allowedOrigins ??
|
|
112
|
-
[...runtime.policy.readOnlyOrigins, ...runtime.policy.writableOrigins];
|
|
120
|
+
const allowedOrigins = request.allowedOrigins ?? [...runtime.policy.readOnlyOrigins, ...runtime.policy.writableOrigins];
|
|
113
121
|
const allowedVerbs = request.allowedVerbs ?? [...runtime.policy.allowedActions];
|
|
114
|
-
const forbiddenSinks = request.forbiddenSinks ?? [];
|
|
115
122
|
const session = {
|
|
116
123
|
sessionId: randomUUID(),
|
|
117
124
|
taskId: request.taskId,
|
|
118
125
|
userGoal: request.userGoal,
|
|
119
126
|
phase: request.phase,
|
|
127
|
+
taskPurposeClass: request.taskPurposeClass,
|
|
128
|
+
taskPhase: request.taskPhase,
|
|
120
129
|
allowedOrigins,
|
|
121
130
|
allowedVerbs,
|
|
122
|
-
forbiddenSinks,
|
|
131
|
+
forbiddenSinks: request.forbiddenSinks ?? [],
|
|
132
|
+
allowedPathClasses: request.allowedPathClasses,
|
|
133
|
+
approvalRequiredPathClasses: request.approvalRequiredPathClasses,
|
|
123
134
|
workflowHash: createWorkflowHash({
|
|
124
|
-
|
|
125
|
-
userGoal: request.userGoal,
|
|
126
|
-
phase: request.phase,
|
|
135
|
+
...request,
|
|
127
136
|
allowedOrigins,
|
|
128
|
-
allowedVerbs
|
|
129
|
-
forbiddenSinks
|
|
137
|
+
allowedVerbs
|
|
130
138
|
}),
|
|
131
139
|
currentStep: 0,
|
|
132
140
|
createdAt,
|
|
133
141
|
expiresAt: plusSeconds(createdAt, request.expiresInSeconds ?? 1800),
|
|
134
|
-
claimProfile:
|
|
135
|
-
approvalBrokerRequired:
|
|
136
|
-
legacyRoutesDisabled:
|
|
142
|
+
claimProfile: secureReady ? "secure_v6" : undefined,
|
|
143
|
+
approvalBrokerRequired: runtime.deploymentProfile === "secure_v6",
|
|
144
|
+
legacyRoutesDisabled: true
|
|
137
145
|
};
|
|
138
|
-
const memorySourceClasses = new Map();
|
|
139
146
|
return {
|
|
140
147
|
session,
|
|
141
148
|
observations: new Map(),
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
149
|
+
authorities: new Map(),
|
|
150
|
+
usedAuthorities: new Set(),
|
|
151
|
+
approvalEnvelopes: new Map(),
|
|
152
|
+
onboardingSessions: new Map(),
|
|
153
|
+
connectorHandles: new Map(),
|
|
147
154
|
memoryRecords: new Map(),
|
|
155
|
+
memorySourceClasses: new Map(),
|
|
148
156
|
memorySnapshots: new Map(),
|
|
149
|
-
onboardingSessions: new Map(),
|
|
150
|
-
observationsV5: new Map(),
|
|
151
|
-
capabilitiesV5: new Map(),
|
|
152
|
-
consumedCapabilitiesV5: new Map(),
|
|
153
|
-
usedCapabilitiesV5: new Set(),
|
|
154
|
-
approvalEnvelopesV5: new Map(),
|
|
155
|
-
onboardingSessionsV5: new Map(),
|
|
156
|
-
connectorHandlesV5: new Map(),
|
|
157
|
-
memorySourceClassesV5: memorySourceClasses,
|
|
158
|
-
memorySourceClassesV6: memorySourceClasses,
|
|
159
157
|
replayEvents: []
|
|
160
158
|
};
|
|
161
159
|
}
|
|
162
|
-
function secureParserIsolationSatisfied(probe) {
|
|
163
|
-
return (probe.mode === "node_permission_process" &&
|
|
164
|
-
probe.processIsolated &&
|
|
165
|
-
probe.egressDenied &&
|
|
166
|
-
probe.permissionModelEnabled &&
|
|
167
|
-
probe.childProcessDenied &&
|
|
168
|
-
probe.workerThreadsDenied &&
|
|
169
|
-
probe.envKeys.length === 0);
|
|
170
|
-
}
|
|
171
|
-
function isSecureDeploymentProfile(deploymentProfile) {
|
|
172
|
-
return deploymentProfile === "secure_v5";
|
|
173
|
-
}
|
|
174
|
-
function secureClaimProfile(runtime) {
|
|
175
|
-
return "secure_v5";
|
|
176
|
-
}
|
|
177
|
-
function claimBearingReady(runtime, probe) {
|
|
178
|
-
return (isSecureDeploymentProfile(runtime.deploymentProfile) &&
|
|
179
|
-
runtime.approvalBrokerConfigured &&
|
|
180
|
-
Boolean(runtime.approvalBrokerPublicKey) &&
|
|
181
|
-
runtime.approvalBrokerMode === "external_service" &&
|
|
182
|
-
runtime.verifiedRegistry?.signatureVerified === true &&
|
|
183
|
-
runtime.parserIsolationMode === "node_permission_process" &&
|
|
184
|
-
secureParserIsolationSatisfied(probe));
|
|
185
|
-
}
|
|
186
|
-
function shouldReduceAuthority(sessionState, observation) {
|
|
187
|
-
if (sessionState.authorityReduced) {
|
|
188
|
-
return true;
|
|
189
|
-
}
|
|
190
|
-
const hasPriorSurface = Boolean(sessionState.latestObservation);
|
|
191
|
-
const hasMeaningfulRisk = observation.parseStatus !== "compiled" ||
|
|
192
|
-
observation.riskFindings.length > 0 ||
|
|
193
|
-
observation.secretFindings.length > 0;
|
|
194
|
-
return hasPriorSurface && hasMeaningfulRisk;
|
|
195
|
-
}
|
|
196
|
-
function applyAuthorityReduction(sessionState, observation, plannerInput) {
|
|
197
|
-
const reasons = [
|
|
198
|
-
...sessionState.authorityReductionReasons,
|
|
199
|
-
...(observation.parseStatus !== "compiled"
|
|
200
|
-
? [`parse_status_${observation.parseStatus}`]
|
|
201
|
-
: []),
|
|
202
|
-
...observation.riskFindings,
|
|
203
|
-
...(observation.secretFindings.length ? ["secret_redaction_boundary"] : [])
|
|
204
|
-
];
|
|
205
|
-
sessionState.authorityReduced = true;
|
|
206
|
-
sessionState.authorityReductionReasons = [...new Set(reasons)];
|
|
207
|
-
sessionState.capabilities.clear();
|
|
208
|
-
return {
|
|
209
|
-
...plannerInput,
|
|
210
|
-
candidateCapabilities: [],
|
|
211
|
-
riskMarkers: [
|
|
212
|
-
...new Set([
|
|
213
|
-
...plannerInput.riskMarkers,
|
|
214
|
-
"multimodal_reducer_active",
|
|
215
|
-
...sessionState.authorityReductionReasons.map((reason) => `chain:${reason}`)
|
|
216
|
-
])
|
|
217
|
-
]
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
function createOnboardingSession(request, runtime, approvalGrant, verifiedRegistryEntry) {
|
|
221
|
-
const callbackUri = verifiedRegistryEntry?.allowedRedirectUris[0] ??
|
|
222
|
-
request.oauthContext?.callbackUri ??
|
|
223
|
-
request.callbackUri ??
|
|
224
|
-
request.oauthContext?.redirectUri ??
|
|
225
|
-
request.requestedRedirectUri;
|
|
226
|
-
if (!callbackUri) {
|
|
227
|
-
return undefined;
|
|
228
|
-
}
|
|
229
|
-
const callbackOrigin = verifiedRegistryEntry?.allowedCallbackOrigins[0] ?? new URL(callbackUri).origin;
|
|
230
|
-
const createdAt = (runtime.now?.() ?? new Date()).toISOString();
|
|
231
|
-
return {
|
|
232
|
-
sessionId: randomUUID(),
|
|
233
|
-
approvalBindingId: approvalGrant.approvalGrantId,
|
|
234
|
-
workflowBindingId: request.sourceObservationId,
|
|
235
|
-
toolId: request.toolId,
|
|
236
|
-
registryEntryId: verifiedRegistryEntry?.registryEntryId ?? request.registryEntryId ?? request.toolId,
|
|
237
|
-
registryBundleId: verifiedRegistryEntry?.bundleId ??
|
|
238
|
-
request.registryBundleId ??
|
|
239
|
-
runtime.verifiedRegistry?.bundleId ??
|
|
240
|
-
"unverified-registry",
|
|
241
|
-
callbackUri,
|
|
242
|
-
callbackOrigin,
|
|
243
|
-
requestedScopes: approvalGrant.scopes,
|
|
244
|
-
state: randomUUID(),
|
|
245
|
-
pkceMethod: "S256",
|
|
246
|
-
createdAt,
|
|
247
|
-
expiresAt: plusMinutes(createdAt, 5),
|
|
248
|
-
status: "prepared"
|
|
249
|
-
};
|
|
250
|
-
}
|
|
251
|
-
function issueApprovalGrant(request, sessionState) {
|
|
252
|
-
const issuedAt = new Date().toISOString();
|
|
253
|
-
const grantWithoutHash = {
|
|
254
|
-
approvalGrantId: randomUUID(),
|
|
255
|
-
sessionId: sessionState.session.sessionId,
|
|
256
|
-
workflowHash: sessionState.session.workflowHash,
|
|
257
|
-
connectorId: request.connectorId,
|
|
258
|
-
scopes: request.scopes ?? [],
|
|
259
|
-
sinkClass: request.sinkClass,
|
|
260
|
-
capabilityIds: request.capabilityIds ?? [],
|
|
261
|
-
targetOrigin: request.targetOrigin,
|
|
262
|
-
issuedAt,
|
|
263
|
-
expiresAt: plusSeconds(issuedAt, request.expiresInSeconds ?? 600)
|
|
264
|
-
};
|
|
265
|
-
return {
|
|
266
|
-
...grantWithoutHash,
|
|
267
|
-
grantHash: createApprovalGrantHash(grantWithoutHash)
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
160
|
function findSessionState(sessions, sessionId) {
|
|
271
161
|
const sessionState = sessions.get(sessionId);
|
|
272
162
|
if (!sessionState) {
|
|
@@ -278,1095 +168,224 @@ function findSessionState(sessions, sessionId) {
|
|
|
278
168
|
}
|
|
279
169
|
return sessionState;
|
|
280
170
|
}
|
|
281
|
-
function
|
|
282
|
-
|
|
171
|
+
function appendReplayEvent(sessionState, event) {
|
|
172
|
+
if (!sessionState) {
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
const replayEvent = {
|
|
176
|
+
eventId: randomUUID(),
|
|
177
|
+
timestamp: new Date().toISOString(),
|
|
178
|
+
...event
|
|
179
|
+
};
|
|
180
|
+
sessionState.replayEvents.push(replayEvent);
|
|
181
|
+
return replayEvent.eventId;
|
|
283
182
|
}
|
|
284
|
-
function
|
|
285
|
-
return
|
|
183
|
+
function authorityCandidateFromDescriptor(authority) {
|
|
184
|
+
return {
|
|
185
|
+
authorityId: authority.capabilityId,
|
|
186
|
+
authorityDigest: authority.capabilityDigest,
|
|
187
|
+
semanticDigest: authority.semanticDigest,
|
|
188
|
+
title: authority.title,
|
|
189
|
+
kind: authority.kind,
|
|
190
|
+
operationClass: authority.operationClass,
|
|
191
|
+
targetPathClass: authority.targetPathClass,
|
|
192
|
+
requiresApproval: authority.requiresApproval,
|
|
193
|
+
evidenceSpanIds: authority.evidenceSpanIds,
|
|
194
|
+
providerId: authority.providerId,
|
|
195
|
+
operationId: authority.operationId,
|
|
196
|
+
method: authority.method,
|
|
197
|
+
pathTemplate: authority.pathTemplate,
|
|
198
|
+
mailboxId: authority.mailboxId,
|
|
199
|
+
accountId: authority.accountId,
|
|
200
|
+
messageId: authority.messageId,
|
|
201
|
+
threadId: authority.threadId,
|
|
202
|
+
recipientSetHash: authority.recipientSetHash,
|
|
203
|
+
subjectHash: authority.subjectHash,
|
|
204
|
+
bodyDigest: authority.bodyDigest,
|
|
205
|
+
attachmentDigestSet: authority.attachmentDigestSet,
|
|
206
|
+
parameterSchema: authority.parameterSchema,
|
|
207
|
+
expiresAt: authority.expiresAt
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function consumeAuthority(sessionState, authority) {
|
|
211
|
+
sessionState.authorities.set(authority.capabilityId, {
|
|
212
|
+
...authority,
|
|
213
|
+
consumedAt: new Date().toISOString()
|
|
214
|
+
});
|
|
215
|
+
sessionState.usedAuthorities.add(authority.capabilityId);
|
|
286
216
|
}
|
|
287
217
|
function lookupVerifiedRegistryEntry(runtime, toolId, registryEntryId) {
|
|
288
218
|
return runtime.verifiedRegistry?.entries.find((entry) => entry.registryEntryId === registryEntryId ||
|
|
289
219
|
entry.registryEntryId === toolId ||
|
|
290
220
|
entry.adapterId === toolId);
|
|
291
221
|
}
|
|
292
|
-
function
|
|
293
|
-
return runtime.verifiedRegistry?.
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
return
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
222
|
+
function lookupVerifiedApiProviderEntry(runtime, providerId) {
|
|
223
|
+
return runtime.verifiedRegistry?.apiProviders?.find((entry) => entry.providerId === providerId);
|
|
224
|
+
}
|
|
225
|
+
function buildArtifactRef(capture, observation) {
|
|
226
|
+
const extractionMethod = capture.surfaceType === "html"
|
|
227
|
+
? "dom"
|
|
228
|
+
: capture.surfaceType === "email_message"
|
|
229
|
+
? "mime"
|
|
230
|
+
: capture.surfaceType === "docx" ||
|
|
231
|
+
capture.surfaceType === "xlsx" ||
|
|
232
|
+
capture.surfaceType === "pptx"
|
|
233
|
+
? "ooxml"
|
|
234
|
+
: capture.surfaceType === "attachment_bundle"
|
|
235
|
+
? "extractor"
|
|
236
|
+
: capture.surfaceType === "tool_manifest" || capture.surfaceType === "memory_candidate"
|
|
237
|
+
? "api"
|
|
238
|
+
: capture.surfaceType === "external_api_response"
|
|
239
|
+
? "api"
|
|
240
|
+
: capture.surfaceType === "image"
|
|
241
|
+
? "ocr"
|
|
242
|
+
: "download";
|
|
243
|
+
const surfaceKind = capture.surfaceType === "tool_manifest"
|
|
244
|
+
? "tool_manifest"
|
|
245
|
+
: capture.surfaceType === "memory_candidate"
|
|
246
|
+
? "memory"
|
|
247
|
+
: capture.surfaceType;
|
|
248
|
+
return {
|
|
249
|
+
artifactId: observation.observationId,
|
|
250
|
+
surfaceKind,
|
|
251
|
+
sourceOrigin: observation.sourceOrigin,
|
|
252
|
+
viewerOrigin: observation.frameOrigin,
|
|
253
|
+
mismatchSignals: observation.parseStatus === "compiled" ? [] : [`parse_status_${observation.parseStatus}`],
|
|
254
|
+
metadataSignals: observation.policyFindings.map((finding) => finding.code),
|
|
255
|
+
provenance: {
|
|
256
|
+
extractionMethod,
|
|
257
|
+
lineageChain: [],
|
|
258
|
+
derivedTaintClass: "tainted"
|
|
259
|
+
},
|
|
260
|
+
authorityEligible: observation.authorityEligible
|
|
325
261
|
};
|
|
326
|
-
sessionState.capabilitiesV5.delete(capability.capabilityId);
|
|
327
|
-
sessionState.consumedCapabilitiesV5.set(capability.capabilityId, consumedCapability);
|
|
328
|
-
sessionState.usedCapabilitiesV5.add(capability.capabilityId);
|
|
329
|
-
return consumedCapability;
|
|
330
|
-
}
|
|
331
|
-
function getActiveApprovalEnvelopeForCapability(sessionState, capabilityId) {
|
|
332
|
-
if (!sessionState || !capabilityId) {
|
|
333
|
-
return undefined;
|
|
334
|
-
}
|
|
335
|
-
return [...sessionState.approvalEnvelopesV5.values()].find((envelope) => envelope.capabilityId === capabilityId &&
|
|
336
|
-
!envelope.consumedAt &&
|
|
337
|
-
new Date(envelope.expiresAt).getTime() > Date.now());
|
|
338
262
|
}
|
|
339
263
|
function buildMemorySnapshotState(sessionState, record) {
|
|
340
|
-
const
|
|
341
|
-
.filter((candidate) => candidate.
|
|
264
|
+
const priorTrustedRecord = [...sessionState.memoryRecords.values()]
|
|
265
|
+
.filter((candidate) => candidate.recordId !== record.recordId &&
|
|
342
266
|
candidate.key === record.key &&
|
|
343
|
-
candidate.tier === "trusted_durable"
|
|
344
|
-
candidate.recordId !== record.recordId)
|
|
267
|
+
candidate.tier === "trusted_durable")
|
|
345
268
|
.sort((left, right) => right.createdAt.localeCompare(left.createdAt))[0];
|
|
346
269
|
return {
|
|
347
|
-
snapshotId:
|
|
348
|
-
sessionId:
|
|
270
|
+
snapshotId: "",
|
|
271
|
+
sessionId: sessionState.session.sessionId,
|
|
349
272
|
recordId: record.recordId,
|
|
350
273
|
key: record.key,
|
|
351
274
|
createdAt: new Date().toISOString(),
|
|
352
|
-
baselineAbsent: !
|
|
353
|
-
snapshotRecord:
|
|
354
|
-
? {
|
|
355
|
-
...priorTrustedBaseline
|
|
356
|
-
}
|
|
357
|
-
: undefined
|
|
358
|
-
};
|
|
359
|
-
}
|
|
360
|
-
function retireObservationCapabilitiesV5(sessionState) {
|
|
361
|
-
const retiredCapabilityIds = [];
|
|
362
|
-
for (const [capabilityId, capability] of sessionState.capabilitiesV5.entries()) {
|
|
363
|
-
if (capability.kind !== "memory_promote") {
|
|
364
|
-
sessionState.capabilitiesV5.delete(capabilityId);
|
|
365
|
-
retiredCapabilityIds.push(capabilityId);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
if (!retiredCapabilityIds.length) {
|
|
369
|
-
return;
|
|
370
|
-
}
|
|
371
|
-
for (const [approvalId, envelope] of sessionState.approvalEnvelopesV5.entries()) {
|
|
372
|
-
if (retiredCapabilityIds.includes(envelope.capabilityId) && !envelope.consumedAt) {
|
|
373
|
-
sessionState.approvalEnvelopesV5.delete(approvalId);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
function buildV5ExecutionPlan(capability) {
|
|
378
|
-
if (!capability) {
|
|
379
|
-
return undefined;
|
|
380
|
-
}
|
|
381
|
-
return {
|
|
382
|
-
verb: capability.kind,
|
|
383
|
-
targetUrl: capability.targetUrl,
|
|
384
|
-
targetOrigin: capability.targetOrigin,
|
|
385
|
-
selector: capability.selector,
|
|
386
|
-
connectorId: capability.connectorId,
|
|
387
|
-
callbackUri: capability.callbackUri,
|
|
388
|
-
callbackOrigin: capability.callbackOrigin,
|
|
389
|
-
memoryRecordId: capability.memoryRecordId,
|
|
390
|
-
derivedSinkClass: capability.derivedSinkClass,
|
|
391
|
-
derivedSensitiveSink: capability.derivedSensitiveSink,
|
|
392
|
-
semanticDigest: capability.semanticDigest
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
function summarizeCapabilityV5(capability) {
|
|
396
|
-
return {
|
|
397
|
-
capabilityId: capability.capabilityId,
|
|
398
|
-
capabilityDigest: capability.capabilityDigest,
|
|
399
|
-
semanticDigest: capability.semanticDigest,
|
|
400
|
-
title: capability.title,
|
|
401
|
-
kind: capability.kind,
|
|
402
|
-
parameterSchema: capability.parameterSchema,
|
|
403
|
-
expiresAt: capability.expiresAt
|
|
275
|
+
baselineAbsent: !priorTrustedRecord,
|
|
276
|
+
snapshotRecord: priorTrustedRecord
|
|
404
277
|
};
|
|
405
278
|
}
|
|
406
|
-
function
|
|
279
|
+
function retiredRouteResponse(route) {
|
|
407
280
|
return {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
semanticDigest: capability.semanticDigest,
|
|
413
|
-
title: capability.title,
|
|
414
|
-
kind: capability.kind,
|
|
415
|
-
parameterSchema: capability.parameterSchema,
|
|
416
|
-
expiresAt: capability.expiresAt
|
|
281
|
+
error: "route_retired_use_v6",
|
|
282
|
+
route,
|
|
283
|
+
replacementPrefix: "/v6",
|
|
284
|
+
deprecated: true
|
|
417
285
|
};
|
|
418
286
|
}
|
|
419
|
-
function
|
|
420
|
-
return
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
claim_profile: claimProfile
|
|
425
|
-
}
|
|
426
|
-
};
|
|
287
|
+
function isRetiredRoute(url) {
|
|
288
|
+
return (url.startsWith("/v1/") ||
|
|
289
|
+
url.startsWith("/v2/") ||
|
|
290
|
+
url.startsWith("/v4/") ||
|
|
291
|
+
url.startsWith("/v5/"));
|
|
427
292
|
}
|
|
428
|
-
function appendReplayEvent(sessionState, event) {
|
|
429
|
-
const replayEvent = {
|
|
430
|
-
eventId: randomUUID(),
|
|
431
|
-
timestamp: new Date().toISOString(),
|
|
432
|
-
...event
|
|
433
|
-
};
|
|
434
|
-
sessionState?.replayEvents.push(replayEvent);
|
|
435
|
-
return replayEvent;
|
|
436
|
-
}
|
|
437
|
-
function buildV5ObservationDecision(compiledObservation) {
|
|
438
|
-
if (compiledObservation.parseStatus !== "compiled") {
|
|
439
|
-
return {
|
|
440
|
-
decision: "BLOCK",
|
|
441
|
-
reasonCodes: [
|
|
442
|
-
compiledObservation.parseStatus === "partial"
|
|
443
|
-
? "PARSE_STATUS_PARTIAL"
|
|
444
|
-
: "PARSE_STATUS_UNSUPPORTED"
|
|
445
|
-
],
|
|
446
|
-
riskScore: 0.98,
|
|
447
|
-
safeConstraints: {
|
|
448
|
-
authority_eligible: false
|
|
449
|
-
},
|
|
450
|
-
telemetryTags: ["v5_observation", "block"]
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
return {
|
|
454
|
-
decision: compiledObservation.authorityEligible ? "ALLOW" : "REPLAN_READ_ONLY",
|
|
455
|
-
reasonCodes: compiledObservation.authorityEligible ? [] : ["AUTHORITY_REDUCED_TO_FACTS_ONLY"],
|
|
456
|
-
riskScore: compiledObservation.riskScore,
|
|
457
|
-
safeConstraints: {
|
|
458
|
-
authority_eligible: compiledObservation.authorityEligible
|
|
459
|
-
},
|
|
460
|
-
telemetryTags: [
|
|
461
|
-
"v5_observation",
|
|
462
|
-
compiledObservation.authorityEligible ? "authority_eligible" : "facts_only"
|
|
463
|
-
]
|
|
464
|
-
};
|
|
465
|
-
}
|
|
466
|
-
function buildV6ObservationDecision(compiledObservation) {
|
|
467
|
-
return buildV5ObservationDecision(compiledObservation);
|
|
468
|
-
}
|
|
469
|
-
function buildObservationDecisionForClaimProfile(compiledObservation, claimProfile) {
|
|
470
|
-
if (!compiledObservation) {
|
|
471
|
-
return relabelClaimProfile({
|
|
472
|
-
decision: "BLOCK",
|
|
473
|
-
reasonCodes: ["OBSERVATION_REQUIRED"],
|
|
474
|
-
riskScore: 0.99,
|
|
475
|
-
safeConstraints: {},
|
|
476
|
-
telemetryTags: [`${claimProfile}_observation`, "block"]
|
|
477
|
-
}, claimProfile);
|
|
478
|
-
}
|
|
479
|
-
return relabelClaimProfile(buildV5ObservationDecision(compiledObservation), claimProfile);
|
|
480
|
-
}
|
|
481
|
-
function canonicalReplayRoute(route) {
|
|
482
|
-
return route.replace(/^\/v6\//, "/v5/");
|
|
483
|
-
}
|
|
484
|
-
function buildLegacyObservationCapture(payload) {
|
|
485
|
-
if (payload.surfaceType === "html") {
|
|
486
|
-
return {
|
|
487
|
-
mimeType: "text/html",
|
|
488
|
-
sourceOrigin: payload.url,
|
|
489
|
-
viewerOrigin: payload.frameUrl ?? payload.url,
|
|
490
|
-
renderedText: payload.visibleText,
|
|
491
|
-
extractedText: payload.visibleText,
|
|
492
|
-
metadataText: payload.metadataText,
|
|
493
|
-
annotations: payload.annotations,
|
|
494
|
-
extractionMethod: "dom",
|
|
495
|
-
trustSignals: payload.trustSignals
|
|
496
|
-
};
|
|
497
|
-
}
|
|
498
|
-
if (payload.surfaceType === "pdf") {
|
|
499
|
-
return {
|
|
500
|
-
mimeType: "application/pdf",
|
|
501
|
-
sourceOrigin: payload.url,
|
|
502
|
-
viewerOrigin: payload.frameUrl ?? payload.url,
|
|
503
|
-
renderedText: payload.renderedText,
|
|
504
|
-
extractedText: payload.extractedText,
|
|
505
|
-
ocrText: payload.ocrText,
|
|
506
|
-
annotations: payload.annotations,
|
|
507
|
-
metadataText: payload.metadataText,
|
|
508
|
-
extractionMethod: "download",
|
|
509
|
-
trustSignals: payload.trustSignals
|
|
510
|
-
};
|
|
511
|
-
}
|
|
512
|
-
if (payload.surfaceType === "image") {
|
|
513
|
-
return {
|
|
514
|
-
mimeType: "image/png",
|
|
515
|
-
sourceOrigin: payload.url,
|
|
516
|
-
viewerOrigin: payload.frameUrl ?? payload.url,
|
|
517
|
-
ocrText: payload.ocrText,
|
|
518
|
-
metadataText: payload.metadataText,
|
|
519
|
-
extractionMethod: "ocr",
|
|
520
|
-
trustSignals: payload.trustSignals
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
if (payload.surfaceType === "tool_manifest") {
|
|
524
|
-
return {
|
|
525
|
-
mimeType: "application/json",
|
|
526
|
-
surfaceKind: "tool_manifest",
|
|
527
|
-
sourceOrigin: payload.url,
|
|
528
|
-
viewerOrigin: payload.frameUrl ?? payload.url,
|
|
529
|
-
extractedText: payload.description,
|
|
530
|
-
metadataText: payload.schemaDescriptions,
|
|
531
|
-
extractionMethod: "api",
|
|
532
|
-
trustSignals: payload.trustSignals
|
|
533
|
-
};
|
|
534
|
-
}
|
|
535
|
-
return undefined;
|
|
536
|
-
}
|
|
537
|
-
function buildV5ArtifactRef(artifact, authorityEligible) {
|
|
538
|
-
return {
|
|
539
|
-
artifactId: artifact.artifactId,
|
|
540
|
-
surfaceKind: artifact.surfaceKind,
|
|
541
|
-
sourceOrigin: artifact.sourceOrigin,
|
|
542
|
-
viewerOrigin: artifact.viewerOrigin,
|
|
543
|
-
mismatchSignals: artifact.mismatchSignals,
|
|
544
|
-
metadataSignals: artifact.metadataSignals,
|
|
545
|
-
provenance: {
|
|
546
|
-
extractionMethod: artifact.extractionMethod,
|
|
547
|
-
lineageChain: artifact.lineageChain,
|
|
548
|
-
derivedTaintClass: artifact.derivedTaintClass
|
|
549
|
-
},
|
|
550
|
-
authorityEligible
|
|
551
|
-
};
|
|
552
|
-
}
|
|
553
|
-
const buildV6ArtifactRef = buildV5ArtifactRef;
|
|
554
293
|
export async function createSafeBrowseServer(options = {}) {
|
|
555
294
|
const runtime = await buildRuntimeContext(options);
|
|
556
|
-
const
|
|
295
|
+
const modelGuardClient = createModelGuardClient({
|
|
296
|
+
baseUrl: runtime.modelGuardBaseUrl,
|
|
297
|
+
timeoutMs: runtime.modelGuardTimeoutMs,
|
|
298
|
+
enforcementMode: runtime.modelGuardEnforcementMode
|
|
299
|
+
});
|
|
557
300
|
const parserIsolationService = createParserIsolationService(runtime.parserIsolationMode, {
|
|
558
301
|
allowlistedEgress: runtime.parserAllowlistedEgress,
|
|
559
|
-
runtime
|
|
560
|
-
knowledgeBase: runtime.knowledgeBase
|
|
561
|
-
}
|
|
302
|
+
runtime
|
|
562
303
|
});
|
|
563
304
|
let parserProbeSnapshot = await parserIsolationService.refreshProbe();
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
const secureProfile = runtime.deploymentProfile;
|
|
567
|
-
if (!runtime.verifiedRegistry?.signatureVerified) {
|
|
568
|
-
throw new Error(`${secureProfile} requires a signature-verified registry bundle`);
|
|
569
|
-
}
|
|
570
|
-
if (!runtime.approvalBrokerConfigured || !runtime.approvalBrokerPublicKey) {
|
|
571
|
-
throw new Error(`${secureProfile} requires an approval broker public key`);
|
|
572
|
-
}
|
|
573
|
-
if (runtime.approvalBrokerMode !== "external_service") {
|
|
574
|
-
throw new Error(`${secureProfile} requires approvalBrokerMode=external_service`);
|
|
575
|
-
}
|
|
576
|
-
if (runtime.parserIsolationMode !== "node_permission_process") {
|
|
577
|
-
throw new Error(`${secureProfile} requires parserIsolationMode=node_permission_process`);
|
|
578
|
-
}
|
|
579
|
-
if (!secureParserIsolationSatisfied(parserProbeSnapshot.probe)) {
|
|
580
|
-
throw new Error(`${secureProfile} requires a permission-constrained parser process with denied egress and scrubbed env`);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
const onboardingSessions = new Map();
|
|
305
|
+
let modelGuardHealthSnapshot = await modelGuardClient.refreshHealth();
|
|
306
|
+
const secureReady = claimBearingReady(runtime, parserProbeSnapshot.probe);
|
|
584
307
|
const sessions = new Map();
|
|
585
308
|
const parserHealthRefreshTimer = setInterval(() => {
|
|
586
309
|
void parserIsolationService
|
|
587
310
|
.refreshProbe()
|
|
588
311
|
.then((snapshot) => {
|
|
589
312
|
parserProbeSnapshot = snapshot;
|
|
590
|
-
runtime.claimBearingReady = claimBearingReady(runtime, snapshot.probe);
|
|
591
313
|
})
|
|
592
|
-
.catch(() =>
|
|
593
|
-
|
|
594
|
-
|
|
314
|
+
.catch(() => undefined);
|
|
315
|
+
void modelGuardClient
|
|
316
|
+
.refreshHealth()
|
|
317
|
+
.then((snapshot) => {
|
|
318
|
+
modelGuardHealthSnapshot = snapshot;
|
|
319
|
+
})
|
|
320
|
+
.catch(() => undefined);
|
|
595
321
|
}, PARSER_HEALTH_REFRESH_INTERVAL_MS);
|
|
596
|
-
parserHealthRefreshTimer.unref();
|
|
322
|
+
parserHealthRefreshTimer.unref?.();
|
|
597
323
|
const server = createServer(async (request, response) => {
|
|
598
|
-
if (!request.url) {
|
|
599
|
-
writeJson(response, 400, { error: "missing_url" });
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
602
|
-
if (request.url.startsWith("/v6/")) {
|
|
603
|
-
request.url = canonicalReplayRoute(request.url);
|
|
604
|
-
}
|
|
605
324
|
try {
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
const claimReady = claimBearingReady(runtime, parserProbeSnapshot.probe);
|
|
609
|
-
runtime.claimBearingReady = claimReady;
|
|
325
|
+
const requestUrl = request.url ?? "/";
|
|
326
|
+
if (request.method === "GET" && requestUrl === "/health") {
|
|
610
327
|
writeJson(response, 200, {
|
|
611
328
|
status: "ok",
|
|
612
|
-
profile: runtime.policy.profile,
|
|
613
329
|
deploymentProfile: runtime.deploymentProfile,
|
|
614
|
-
claimBearingReady:
|
|
615
|
-
|
|
616
|
-
policyLayers: runtime.policy.layerProvenance,
|
|
617
|
-
legacyRoutesEnabled: !legacyRoutesDisabled(runtime),
|
|
618
|
-
verifiedRegistry: runtime.verifiedRegistry
|
|
619
|
-
? {
|
|
620
|
-
bundleId: runtime.verifiedRegistry.bundleId,
|
|
621
|
-
version: runtime.verifiedRegistry.version,
|
|
622
|
-
signatureVerified: runtime.verifiedRegistry.signatureVerified,
|
|
623
|
-
entryCount: runtime.verifiedRegistry.entries.length,
|
|
624
|
-
required: isSecureDeploymentProfile(runtime.deploymentProfile)
|
|
625
|
-
}
|
|
626
|
-
: undefined,
|
|
627
|
-
parserIsolation: {
|
|
628
|
-
...parserProbeSnapshot.probe,
|
|
629
|
-
lastCheckedAt: parserProbeSnapshot.lastCheckedAt,
|
|
630
|
-
configuredMode: runtime.parserIsolationMode,
|
|
631
|
-
enforced: isSecureDeploymentProfile(runtime.deploymentProfile)
|
|
632
|
-
},
|
|
330
|
+
claimBearingReady: claimBearingReady(runtime, parserProbeSnapshot.probe),
|
|
331
|
+
legacyRoutesEnabled: false,
|
|
633
332
|
approvalBroker: {
|
|
634
|
-
required: isSecureDeploymentProfile(runtime.deploymentProfile),
|
|
635
333
|
configured: runtime.approvalBrokerConfigured,
|
|
636
334
|
mode: runtime.approvalBrokerMode
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
}
|
|
645
|
-
if (legacyRoutesDisabled(runtime) && isLegacyRoute(request.url)) {
|
|
646
|
-
writeJson(response, 403, {
|
|
647
|
-
error: `route_disabled_in_${runtime.deploymentProfile}`,
|
|
648
|
-
route: request.url,
|
|
649
|
-
claimProfile: secureClaimProfile(runtime)
|
|
650
|
-
});
|
|
651
|
-
return;
|
|
652
|
-
}
|
|
653
|
-
if (request.url === "/v1/observe") {
|
|
654
|
-
const payload = await readJson(request);
|
|
655
|
-
writeJson(response, 200, {
|
|
656
|
-
...sanitizeObservation(payload, runtime),
|
|
657
|
-
...legacyResponseMeta("/v1/observe")
|
|
658
|
-
});
|
|
659
|
-
return;
|
|
660
|
-
}
|
|
661
|
-
if (request.url === "/v1/action") {
|
|
662
|
-
const payload = await readJson(request);
|
|
663
|
-
writeJson(response, 200, {
|
|
664
|
-
...evaluateAction(payload, runtime),
|
|
665
|
-
...legacyResponseMeta("/v1/action")
|
|
666
|
-
});
|
|
667
|
-
return;
|
|
668
|
-
}
|
|
669
|
-
if (request.url === "/v1/artifact") {
|
|
670
|
-
const payload = await readJson(request);
|
|
671
|
-
writeJson(response, 200, {
|
|
672
|
-
...brokerArtifact(payload, runtime),
|
|
673
|
-
...legacyResponseMeta("/v1/artifact")
|
|
674
|
-
});
|
|
675
|
-
return;
|
|
676
|
-
}
|
|
677
|
-
if (request.url === "/v1/tool") {
|
|
678
|
-
const payload = await readJson(request);
|
|
679
|
-
writeJson(response, 200, {
|
|
680
|
-
...evaluateToolRequest(payload, runtime),
|
|
681
|
-
...legacyResponseMeta("/v1/tool")
|
|
682
|
-
});
|
|
683
|
-
return;
|
|
684
|
-
}
|
|
685
|
-
if (request.url === "/v1/memory") {
|
|
686
|
-
const payload = await readJson(request);
|
|
687
|
-
writeJson(response, 200, {
|
|
688
|
-
...evaluateMemoryWrite(payload, runtime),
|
|
689
|
-
...legacyResponseMeta("/v1/memory")
|
|
690
|
-
});
|
|
691
|
-
return;
|
|
692
|
-
}
|
|
693
|
-
if (request.url === "/v1/replay") {
|
|
694
|
-
const payload = await readJson(request);
|
|
695
|
-
writeJson(response, 200, {
|
|
696
|
-
...buildReplayBundle(payload.events, runtime),
|
|
697
|
-
...legacyResponseMeta("/v1/replay")
|
|
698
|
-
});
|
|
699
|
-
return;
|
|
700
|
-
}
|
|
701
|
-
if (request.url === "/v2/tool/prepare") {
|
|
702
|
-
const payload = await readJson(request);
|
|
703
|
-
const prepared = prepareToolOnboarding(payload, runtime);
|
|
704
|
-
const onboardingSession = prepared.verdict.decision === "ALLOW" && payload.authType === "oauth"
|
|
705
|
-
? createOnboardingSession(payload, runtime, {
|
|
706
|
-
approvalGrantId: payload.approvalBindingId ?? randomUUID(),
|
|
707
|
-
sessionId: "legacy-session",
|
|
708
|
-
workflowHash: hashValue(payload.sourceObservationId ?? payload.requestId),
|
|
709
|
-
connectorId: payload.toolId,
|
|
710
|
-
scopes: payload.requestedScopes ?? [],
|
|
711
|
-
sinkClass: "connector_oauth",
|
|
712
|
-
capabilityIds: payload.capabilityId ? [payload.capabilityId] : [],
|
|
713
|
-
targetOrigin: payload.callbackOrigin ??
|
|
714
|
-
payload.oauthContext?.callbackOrigin ??
|
|
715
|
-
payload.callbackUri ??
|
|
716
|
-
payload.requestedRedirectUri ??
|
|
717
|
-
"unknown",
|
|
718
|
-
issuedAt: new Date().toISOString(),
|
|
719
|
-
expiresAt: plusMinutes(new Date().toISOString(), 10),
|
|
720
|
-
grantHash: "legacy"
|
|
721
|
-
})
|
|
722
|
-
: undefined;
|
|
723
|
-
if (onboardingSession) {
|
|
724
|
-
onboardingSessions.set(onboardingSession.sessionId, onboardingSession);
|
|
725
|
-
}
|
|
726
|
-
writeJson(response, 200, {
|
|
727
|
-
verdict: prepared.verdict,
|
|
728
|
-
verifiedRegistryEntry: prepared.verifiedRegistryEntry,
|
|
729
|
-
workflowBinding: prepared.workflowBinding,
|
|
730
|
-
onboardingSession,
|
|
731
|
-
...legacyResponseMeta("/v2/tool/prepare")
|
|
732
|
-
});
|
|
733
|
-
return;
|
|
734
|
-
}
|
|
735
|
-
if (request.url === "/v2/tool/callback/verify") {
|
|
736
|
-
const payload = await readJson(request);
|
|
737
|
-
const session = onboardingSessions.get(payload.sessionId);
|
|
738
|
-
const result = verifyToolCallback(payload, session, runtime);
|
|
739
|
-
if (session) {
|
|
740
|
-
onboardingSessions.set(payload.sessionId, {
|
|
741
|
-
...session,
|
|
742
|
-
status: result.verdict.decision === "ALLOW" ? "used" : session.status
|
|
743
|
-
});
|
|
744
|
-
}
|
|
745
|
-
writeJson(response, 200, {
|
|
746
|
-
...result,
|
|
747
|
-
...legacyResponseMeta("/v2/tool/callback/verify")
|
|
748
|
-
});
|
|
749
|
-
return;
|
|
750
|
-
}
|
|
751
|
-
if (request.url === "/v2/artifact") {
|
|
752
|
-
const payload = await readJson(request);
|
|
753
|
-
writeJson(response, 200, {
|
|
754
|
-
...brokerArtifactV2(payload, runtime),
|
|
755
|
-
...legacyResponseMeta("/v2/artifact")
|
|
756
|
-
});
|
|
757
|
-
return;
|
|
758
|
-
}
|
|
759
|
-
if (request.url === "/v6/session/start" ||
|
|
760
|
-
(unifiedV5InSecureV6 && request.url === "/v5/session/start")) {
|
|
761
|
-
const payload = await readJson(request);
|
|
762
|
-
const sessionState = createSessionState(payload, runtime);
|
|
763
|
-
sessions.set(sessionState.session.sessionId, sessionState);
|
|
764
|
-
appendReplayEvent(sessionState, {
|
|
765
|
-
kind: "verdict",
|
|
766
|
-
actor: "system",
|
|
767
|
-
payload: {
|
|
768
|
-
route: canonicalReplayRoute(request.url),
|
|
769
|
-
sessionId: sessionState.session.sessionId,
|
|
770
|
-
claimProfile: sessionState.session.claimProfile ?? "secure_v6"
|
|
771
|
-
}
|
|
772
|
-
});
|
|
773
|
-
writeJson(response, 200, {
|
|
774
|
-
session: sessionState.session
|
|
775
|
-
});
|
|
776
|
-
return;
|
|
777
|
-
}
|
|
778
|
-
if (request.url === "/v6/observe" || (unifiedV5InSecureV6 && request.url === "/v5/observe")) {
|
|
779
|
-
const payload = await readJson(request);
|
|
780
|
-
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
781
|
-
if (!sessionState) {
|
|
782
|
-
writeJson(response, 404, { error: "unknown_session" });
|
|
783
|
-
return;
|
|
784
|
-
}
|
|
785
|
-
const capture = {
|
|
786
|
-
...payload.capture,
|
|
787
|
-
sessionId: payload.sessionId,
|
|
788
|
-
taskId: sessionState.session.taskId
|
|
789
|
-
};
|
|
790
|
-
const observationResult = await parserIsolationService.compileObservation({
|
|
791
|
-
capture,
|
|
792
|
-
workflowHash: sessionState.session.workflowHash,
|
|
793
|
-
compilerVersion: "v5"
|
|
794
|
-
});
|
|
795
|
-
const compiledObservation = observationResult.compiledObservation;
|
|
796
|
-
const plannerView = observationResult.plannerView;
|
|
797
|
-
const verifiedEntry = capture.surfaceType === "tool_manifest"
|
|
798
|
-
? resolveToolManifestRegistryEntry(runtime, capture)
|
|
799
|
-
: undefined;
|
|
800
|
-
const manifestHash = observationResult.toolManifestDigests?.manifestHash;
|
|
801
|
-
const schemaHash = observationResult.toolManifestDigests?.schemaHash;
|
|
802
|
-
retireObservationCapabilitiesV5(sessionState);
|
|
803
|
-
const capabilities = mintCapabilitiesForObservationV5(sessionState.session, compiledObservation, plannerView, {
|
|
804
|
-
verifiedRegistryEntry: verifiedEntry,
|
|
805
|
-
registryEntryId: capture.surfaceType === "tool_manifest" ? verifiedEntry?.registryEntryId : undefined,
|
|
806
|
-
connectorId: capture.surfaceType === "tool_manifest"
|
|
807
|
-
? verifiedEntry?.adapterId ?? capture.toolId
|
|
808
|
-
: undefined,
|
|
809
|
-
requestedScopes: capture.surfaceType === "tool_manifest" ? capture.requestedScopes : undefined,
|
|
810
|
-
callbackUri: capture.surfaceType === "tool_manifest" ? capture.callbackUri : undefined,
|
|
811
|
-
callbackOrigin: capture.surfaceType === "tool_manifest" ? capture.callbackOrigin : undefined,
|
|
812
|
-
manifestAuthType: capture.surfaceType === "tool_manifest" ? capture.authType : undefined,
|
|
813
|
-
manifestHash,
|
|
814
|
-
schemaHash
|
|
815
|
-
});
|
|
816
|
-
sessionState.latestObservationV5 = compiledObservation;
|
|
817
|
-
sessionState.observationsV5.set(compiledObservation.observationId, compiledObservation);
|
|
818
|
-
for (const capability of capabilities) {
|
|
819
|
-
sessionState.capabilitiesV5.set(capability.capabilityId, capability);
|
|
820
|
-
}
|
|
821
|
-
const observationVerdict = relabelClaimProfile(buildV6ObservationDecision(compiledObservation), "secure_v6");
|
|
822
|
-
const artifactCapture = buildLegacyObservationCapture(capture);
|
|
823
|
-
const artifactRefs = artifactCapture
|
|
824
|
-
? [
|
|
825
|
-
buildV6ArtifactRef(brokerArtifact(artifactCapture, runtime).artifact, compiledObservation.authorityEligible)
|
|
826
|
-
]
|
|
827
|
-
: [];
|
|
828
|
-
const authorityCandidates = capabilities.map((capability) => ({
|
|
829
|
-
authorityId: capability.capabilityId,
|
|
830
|
-
authorityDigest: capability.capabilityDigest,
|
|
831
|
-
semanticDigest: capability.semanticDigest,
|
|
832
|
-
title: capability.title,
|
|
833
|
-
kind: capability.kind,
|
|
834
|
-
parameterSchema: capability.parameterSchema,
|
|
835
|
-
expiresAt: capability.expiresAt
|
|
836
|
-
}));
|
|
837
|
-
const capabilityAliases = authorityCandidates.map((candidate) => ({
|
|
838
|
-
capabilityId: candidate.authorityId,
|
|
839
|
-
capabilityDigest: candidate.authorityDigest,
|
|
840
|
-
semanticDigest: candidate.semanticDigest,
|
|
841
|
-
title: candidate.title,
|
|
842
|
-
kind: candidate.kind,
|
|
843
|
-
parameterSchema: candidate.parameterSchema,
|
|
844
|
-
expiresAt: candidate.expiresAt
|
|
845
|
-
}));
|
|
846
|
-
const replayEvent = appendReplayEvent(sessionState, {
|
|
847
|
-
kind: "observation",
|
|
848
|
-
actor: "sdk",
|
|
849
|
-
payload: {
|
|
850
|
-
route: canonicalReplayRoute(request.url),
|
|
851
|
-
observationId: compiledObservation.observationId,
|
|
852
|
-
surfaceType: compiledObservation.surfaceType,
|
|
853
|
-
authorityEligible: compiledObservation.authorityEligible,
|
|
854
|
-
authorityCandidateCount: capabilities.length,
|
|
855
|
-
verdict: observationVerdict.decision
|
|
856
|
-
}
|
|
857
|
-
});
|
|
858
|
-
writeJson(response, 200, {
|
|
859
|
-
compiledObservation,
|
|
860
|
-
plannerView,
|
|
861
|
-
capabilities: capabilityAliases,
|
|
862
|
-
authorityCandidates,
|
|
863
|
-
artifactRefs,
|
|
864
|
-
observationVerdict,
|
|
865
|
-
replayEventId: replayEvent.eventId
|
|
866
|
-
});
|
|
867
|
-
return;
|
|
868
|
-
}
|
|
869
|
-
if (request.url === "/v6/action/evaluate" ||
|
|
870
|
-
(unifiedV5InSecureV6 && request.url === "/v5/action/evaluate")) {
|
|
871
|
-
const payload = await readJson(request);
|
|
872
|
-
const authorityId = payload.authorityId ?? payload.capabilityId;
|
|
873
|
-
const authorityDigest = payload.authorityDigest ?? payload.capabilityDigest;
|
|
874
|
-
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
875
|
-
const capability = authorityId ? getCapabilityV5(sessionState, authorityId) : undefined;
|
|
876
|
-
const authorityDecision = relabelClaimProfile(evaluateCapabilityUseV5({
|
|
877
|
-
sessionId: payload.sessionId,
|
|
878
|
-
capabilityId: authorityId ?? "",
|
|
879
|
-
capabilityDigest: authorityDigest ?? "",
|
|
880
|
-
parameters: payload.parameters
|
|
881
|
-
}, sessionState?.session, capability, {
|
|
882
|
-
alreadyUsed: authorityId
|
|
883
|
-
? (sessionState?.usedCapabilitiesV5.has(authorityId) ?? false)
|
|
884
|
-
: false
|
|
885
|
-
}), "secure_v6");
|
|
886
|
-
const observationDecision = relabelClaimProfile(sessionState?.latestObservationV5
|
|
887
|
-
? buildV6ObservationDecision(sessionState.latestObservationV5)
|
|
888
|
-
: {
|
|
889
|
-
decision: "BLOCK",
|
|
890
|
-
reasonCodes: ["OBSERVATION_REQUIRED"],
|
|
891
|
-
riskScore: 0.99,
|
|
892
|
-
safeConstraints: {},
|
|
893
|
-
telemetryTags: ["v6_observation", "block"]
|
|
894
|
-
}, "secure_v6");
|
|
895
|
-
const effectDecision = observationDecision.decision === "ALLOW" &&
|
|
896
|
-
authorityDecision.decision === "ALLOW"
|
|
897
|
-
? authorityDecision
|
|
898
|
-
: relabelClaimProfile({
|
|
899
|
-
decision: "BLOCK",
|
|
900
|
-
reasonCodes: [...new Set([
|
|
901
|
-
...(observationDecision.decision === "ALLOW"
|
|
902
|
-
? []
|
|
903
|
-
: ["OBSERVATION_NOT_EFFECT_ELIGIBLE"]),
|
|
904
|
-
...(authorityDecision.decision === "ALLOW"
|
|
905
|
-
? []
|
|
906
|
-
: authorityDecision.reasonCodes)
|
|
907
|
-
])],
|
|
908
|
-
riskScore: Math.max(observationDecision.riskScore, authorityDecision.riskScore),
|
|
909
|
-
safeConstraints: {
|
|
910
|
-
target_class: capability?.targetClass ?? "unknown"
|
|
911
|
-
},
|
|
912
|
-
telemetryTags: ["v6_action", "block"]
|
|
913
|
-
}, "secure_v6");
|
|
914
|
-
if (effectDecision.decision === "ALLOW" && sessionState && capability) {
|
|
915
|
-
consumeCapabilityV5(sessionState, capability);
|
|
916
|
-
sessionState.session = {
|
|
917
|
-
...sessionState.session,
|
|
918
|
-
currentStep: sessionState.session.currentStep + 1
|
|
919
|
-
};
|
|
920
|
-
}
|
|
921
|
-
appendReplayEvent(sessionState, {
|
|
922
|
-
kind: "action",
|
|
923
|
-
actor: "sdk",
|
|
924
|
-
payload: {
|
|
925
|
-
route: canonicalReplayRoute(request.url),
|
|
926
|
-
authorityId: authorityId ?? null,
|
|
927
|
-
observationDecision: observationDecision.decision,
|
|
928
|
-
authorityDecision: authorityDecision.decision,
|
|
929
|
-
effectDecision: effectDecision.decision
|
|
930
|
-
}
|
|
931
|
-
});
|
|
932
|
-
writeJson(response, 200, {
|
|
933
|
-
observationDecision,
|
|
934
|
-
authorityDecision,
|
|
935
|
-
effectDecision,
|
|
936
|
-
verdict: effectDecision,
|
|
937
|
-
executionPlan: buildV5ExecutionPlan(capability)
|
|
938
|
-
});
|
|
939
|
-
return;
|
|
940
|
-
}
|
|
941
|
-
if (request.url === "/v6/approval/issue" ||
|
|
942
|
-
(unifiedV5InSecureV6 && request.url === "/v5/approval/issue")) {
|
|
943
|
-
const payload = await readJson(request);
|
|
944
|
-
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
945
|
-
const capability = sessionState?.capabilitiesV5.get(payload.capabilityId);
|
|
946
|
-
if (sessionState?.usedCapabilitiesV5.has(payload.capabilityId)) {
|
|
947
|
-
writeJson(response, 200, {
|
|
948
|
-
verdict: relabelClaimProfile({
|
|
949
|
-
decision: "BLOCK",
|
|
950
|
-
reasonCodes: ["CAPABILITY_REPLAYED"],
|
|
951
|
-
riskScore: 0.99,
|
|
952
|
-
safeConstraints: {},
|
|
953
|
-
telemetryTags: ["approval_v6_issue", "block"]
|
|
954
|
-
}, "secure_v6")
|
|
955
|
-
});
|
|
956
|
-
return;
|
|
957
|
-
}
|
|
958
|
-
if (capability && payload.capabilityDigest !== capability.capabilityDigest) {
|
|
959
|
-
writeJson(response, 200, {
|
|
960
|
-
verdict: relabelClaimProfile({
|
|
961
|
-
decision: "BLOCK",
|
|
962
|
-
reasonCodes: ["CAPABILITY_DIGEST_MISMATCH"],
|
|
963
|
-
riskScore: 0.99,
|
|
964
|
-
safeConstraints: {},
|
|
965
|
-
telemetryTags: ["approval_v6_issue", "block"]
|
|
966
|
-
}, "secure_v6")
|
|
967
|
-
});
|
|
968
|
-
return;
|
|
969
|
-
}
|
|
970
|
-
const activeApproval = getActiveApprovalEnvelopeForCapability(sessionState, payload.capabilityId);
|
|
971
|
-
if (activeApproval) {
|
|
972
|
-
writeJson(response, 200, {
|
|
973
|
-
verdict: relabelClaimProfile({
|
|
974
|
-
decision: "BLOCK",
|
|
975
|
-
reasonCodes: ["APPROVAL_ALREADY_ISSUED_FOR_CAPABILITY"],
|
|
976
|
-
riskScore: 0.99,
|
|
977
|
-
safeConstraints: {},
|
|
978
|
-
telemetryTags: ["approval_v6_issue", "block"]
|
|
979
|
-
}, "secure_v6"),
|
|
980
|
-
approvalEnvelope: activeApproval
|
|
981
|
-
});
|
|
982
|
-
return;
|
|
983
|
-
}
|
|
984
|
-
const brokerPayload = sessionState && capability
|
|
985
|
-
? createApprovalIntentPayloadV5({
|
|
986
|
-
sessionId: sessionState.session.sessionId,
|
|
987
|
-
workflowHash: sessionState.session.workflowHash,
|
|
988
|
-
capabilityId: capability.capabilityId,
|
|
989
|
-
capabilityDigest: capability.capabilityDigest,
|
|
990
|
-
expiresInSeconds: payload.expiresInSeconds
|
|
991
|
-
})
|
|
992
|
-
: "";
|
|
993
|
-
const brokerSignatureVerified = verifyApprovalIntentSignatureV5(brokerPayload, payload.brokerSignature, runtime.approvalBrokerPublicKey);
|
|
994
|
-
const issued = issueApprovalEnvelopeV5({
|
|
995
|
-
session: sessionState?.session,
|
|
996
|
-
capability,
|
|
997
|
-
brokerSignature: payload.brokerSignature,
|
|
998
|
-
brokerSignatureVerified,
|
|
999
|
-
expiresInSeconds: payload.expiresInSeconds
|
|
1000
|
-
});
|
|
1001
|
-
if (issued.approvalEnvelope && sessionState) {
|
|
1002
|
-
sessionState.approvalEnvelopesV5.set(issued.approvalEnvelope.approvalId, issued.approvalEnvelope);
|
|
1003
|
-
}
|
|
1004
|
-
appendReplayEvent(sessionState, {
|
|
1005
|
-
kind: "tool",
|
|
1006
|
-
actor: "sdk",
|
|
1007
|
-
payload: {
|
|
1008
|
-
route: canonicalReplayRoute(request.url),
|
|
1009
|
-
capabilityId: payload.capabilityId,
|
|
1010
|
-
verdict: issued.verdict.decision
|
|
1011
|
-
}
|
|
1012
|
-
});
|
|
1013
|
-
writeJson(response, 200, {
|
|
1014
|
-
...issued,
|
|
1015
|
-
verdict: relabelClaimProfile(issued.verdict, "secure_v6")
|
|
1016
|
-
});
|
|
1017
|
-
return;
|
|
1018
|
-
}
|
|
1019
|
-
if (request.url === "/v6/tool/prepare" ||
|
|
1020
|
-
(unifiedV5InSecureV6 && request.url === "/v5/tool/prepare")) {
|
|
1021
|
-
const payload = await readJson(request);
|
|
1022
|
-
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
1023
|
-
const approvalEnvelope = sessionState?.approvalEnvelopesV5.get(payload.approvalId);
|
|
1024
|
-
const capability = approvalEnvelope
|
|
1025
|
-
? getCapabilityV5(sessionState, approvalEnvelope.capabilityId)
|
|
1026
|
-
: undefined;
|
|
1027
|
-
const verifiedEntry = approvalEnvelope
|
|
1028
|
-
? lookupVerifiedRegistryEntry(runtime, approvalEnvelope.connectorId, approvalEnvelope.registryEntryId)
|
|
1029
|
-
: undefined;
|
|
1030
|
-
const prepared = prepareToolOnboardingV5({
|
|
1031
|
-
session: sessionState?.session,
|
|
1032
|
-
capability,
|
|
1033
|
-
approvalEnvelope,
|
|
1034
|
-
verifiedRegistryEntry: verifiedEntry
|
|
1035
|
-
});
|
|
1036
|
-
if (sessionState && prepared.onboardingSession) {
|
|
1037
|
-
const consumedAt = new Date().toISOString();
|
|
1038
|
-
if (capability) {
|
|
1039
|
-
consumeCapabilityV5(sessionState, capability, consumedAt);
|
|
1040
|
-
}
|
|
1041
|
-
if (approvalEnvelope) {
|
|
1042
|
-
sessionState.approvalEnvelopesV5.set(payload.approvalId, {
|
|
1043
|
-
...approvalEnvelope,
|
|
1044
|
-
consumedAt,
|
|
1045
|
-
onboardingSessionId: prepared.onboardingSession.onboardingSessionId
|
|
1046
|
-
});
|
|
1047
|
-
}
|
|
1048
|
-
sessionState.onboardingSessionsV5.set(prepared.onboardingSession.onboardingSessionId, prepared.onboardingSession);
|
|
1049
|
-
sessionState.session = {
|
|
1050
|
-
...sessionState.session,
|
|
1051
|
-
currentStep: sessionState.session.currentStep + 1
|
|
1052
|
-
};
|
|
1053
|
-
}
|
|
1054
|
-
appendReplayEvent(sessionState, {
|
|
1055
|
-
kind: "tool",
|
|
1056
|
-
actor: "sdk",
|
|
1057
|
-
payload: {
|
|
1058
|
-
route: canonicalReplayRoute(request.url),
|
|
1059
|
-
approvalId: payload.approvalId,
|
|
1060
|
-
verdict: prepared.verdict.decision
|
|
1061
|
-
}
|
|
1062
|
-
});
|
|
1063
|
-
writeJson(response, 200, {
|
|
1064
|
-
verdict: relabelClaimProfile(prepared.verdict, "secure_v6"),
|
|
1065
|
-
approvalEnvelope: sessionState?.approvalEnvelopesV5.get(payload.approvalId) ?? approvalEnvelope,
|
|
1066
|
-
verifiedRegistryEntry: verifiedEntry,
|
|
1067
|
-
onboardingSession: prepared.onboardingSession
|
|
1068
|
-
});
|
|
1069
|
-
return;
|
|
1070
|
-
}
|
|
1071
|
-
if (request.url === "/v6/tool/callback/verify" ||
|
|
1072
|
-
(unifiedV5InSecureV6 && request.url === "/v5/tool/callback/verify")) {
|
|
1073
|
-
const payload = await readJson(request);
|
|
1074
|
-
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
1075
|
-
const approvalEnvelope = sessionState?.approvalEnvelopesV5.get(payload.approvalId);
|
|
1076
|
-
const capability = approvalEnvelope
|
|
1077
|
-
? getCapabilityV5(sessionState, approvalEnvelope.capabilityId)
|
|
1078
|
-
: undefined;
|
|
1079
|
-
const onboardingSession = sessionState?.onboardingSessionsV5.get(payload.onboardingSessionId);
|
|
1080
|
-
const verifiedEntry = approvalEnvelope
|
|
1081
|
-
? lookupVerifiedRegistryEntry(runtime, approvalEnvelope.connectorId, approvalEnvelope.registryEntryId)
|
|
1082
|
-
: undefined;
|
|
1083
|
-
const verified = verifyToolCallbackV5({
|
|
1084
|
-
session: sessionState?.session,
|
|
1085
|
-
capability,
|
|
1086
|
-
approvalEnvelope,
|
|
1087
|
-
onboardingSession,
|
|
1088
|
-
verifiedRegistryEntry: verifiedEntry,
|
|
1089
|
-
request: payload.request
|
|
1090
|
-
});
|
|
1091
|
-
if (sessionState && onboardingSession && verified.verdict.decision === "ALLOW") {
|
|
1092
|
-
sessionState.onboardingSessionsV5.set(payload.onboardingSessionId, {
|
|
1093
|
-
...onboardingSession,
|
|
1094
|
-
status: "used"
|
|
1095
|
-
});
|
|
1096
|
-
}
|
|
1097
|
-
if (sessionState && verified.connectorHandle) {
|
|
1098
|
-
sessionState.connectorHandlesV5.set(verified.connectorHandle.handleId, verified.connectorHandle);
|
|
1099
|
-
}
|
|
1100
|
-
appendReplayEvent(sessionState, {
|
|
1101
|
-
kind: "tool",
|
|
1102
|
-
actor: "sdk",
|
|
1103
|
-
payload: {
|
|
1104
|
-
route: canonicalReplayRoute(request.url),
|
|
1105
|
-
onboardingSessionId: payload.onboardingSessionId,
|
|
1106
|
-
verdict: verified.verdict.decision
|
|
1107
|
-
}
|
|
1108
|
-
});
|
|
1109
|
-
writeJson(response, 200, {
|
|
1110
|
-
...verified,
|
|
1111
|
-
verdict: relabelClaimProfile(verified.verdict, "secure_v6")
|
|
1112
|
-
});
|
|
1113
|
-
return;
|
|
1114
|
-
}
|
|
1115
|
-
if (request.url === "/v6/artifact/ingest") {
|
|
1116
|
-
const payload = await readJson(request);
|
|
1117
|
-
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
1118
|
-
if (!sessionState) {
|
|
1119
|
-
writeJson(response, 404, { error: "unknown_session" });
|
|
1120
|
-
return;
|
|
1121
|
-
}
|
|
1122
|
-
const capture = {
|
|
1123
|
-
...payload.capture,
|
|
1124
|
-
sessionId: payload.sessionId,
|
|
1125
|
-
taskId: sessionState.session.taskId
|
|
1126
|
-
};
|
|
1127
|
-
const observationResult = await parserIsolationService.compileObservation({
|
|
1128
|
-
capture,
|
|
1129
|
-
workflowHash: sessionState.session.workflowHash,
|
|
1130
|
-
compilerVersion: "v5"
|
|
1131
|
-
});
|
|
1132
|
-
const compiledObservation = observationResult.compiledObservation;
|
|
1133
|
-
const plannerView = observationResult.plannerView;
|
|
1134
|
-
const artifactCapture = buildLegacyObservationCapture(capture);
|
|
1135
|
-
const brokeredArtifact = artifactCapture
|
|
1136
|
-
? brokerArtifact(artifactCapture, runtime)
|
|
1137
|
-
: brokerArtifact({
|
|
1138
|
-
mimeType: "application/octet-stream",
|
|
1139
|
-
surfaceKind: "document",
|
|
1140
|
-
sourceOrigin: capture.url,
|
|
1141
|
-
viewerOrigin: capture.frameUrl ?? capture.url,
|
|
1142
|
-
extractionMethod: "download",
|
|
1143
|
-
trustSignals: capture.trustSignals
|
|
1144
|
-
}, runtime);
|
|
1145
|
-
const authorityEligible = compiledObservation.parseStatus === "compiled" &&
|
|
1146
|
-
compiledObservation.authorityEligible &&
|
|
1147
|
-
!brokeredArtifact.artifact.mismatchSignals.length &&
|
|
1148
|
-
!brokeredArtifact.artifact.metadataSignals.length;
|
|
1149
|
-
const artifactVerdict = relabelClaimProfile(authorityEligible
|
|
1150
|
-
? {
|
|
1151
|
-
decision: "ALLOW",
|
|
1152
|
-
reasonCodes: [],
|
|
1153
|
-
riskScore: Math.max(compiledObservation.riskScore, brokeredArtifact.verdict.riskScore),
|
|
1154
|
-
safeConstraints: {
|
|
1155
|
-
authority_eligible: true,
|
|
1156
|
-
handoff_mode: "artifact_reference"
|
|
1157
|
-
},
|
|
1158
|
-
telemetryTags: ["artifact_v6", "allow"]
|
|
1159
|
-
}
|
|
1160
|
-
: {
|
|
1161
|
-
decision: brokeredArtifact.verdict.decision === "QUARANTINE_ARTIFACT" ||
|
|
1162
|
-
brokeredArtifact.artifact.mismatchSignals.length > 0 ||
|
|
1163
|
-
brokeredArtifact.artifact.metadataSignals.length > 0
|
|
1164
|
-
? "QUARANTINE_ARTIFACT"
|
|
1165
|
-
: "BLOCK",
|
|
1166
|
-
reasonCodes: [...new Set([
|
|
1167
|
-
...(compiledObservation.parseStatus === "compiled"
|
|
1168
|
-
? ["ARTIFACT_NOT_AUTHORITY_ELIGIBLE"]
|
|
1169
|
-
: [
|
|
1170
|
-
compiledObservation.parseStatus === "partial"
|
|
1171
|
-
? "PARSE_STATUS_PARTIAL"
|
|
1172
|
-
: "PARSE_STATUS_UNSUPPORTED"
|
|
1173
|
-
]),
|
|
1174
|
-
...brokeredArtifact.verdict.reasonCodes
|
|
1175
|
-
])],
|
|
1176
|
-
riskScore: Math.max(0.95, brokeredArtifact.verdict.riskScore),
|
|
1177
|
-
safeConstraints: {
|
|
1178
|
-
authority_eligible: false,
|
|
1179
|
-
handoff_mode: "artifact_reference"
|
|
1180
|
-
},
|
|
1181
|
-
telemetryTags: ["artifact_v6", "quarantine"]
|
|
1182
|
-
}, "secure_v6");
|
|
1183
|
-
sessionState.latestObservationV5 = compiledObservation;
|
|
1184
|
-
sessionState.observationsV5.set(compiledObservation.observationId, compiledObservation);
|
|
1185
|
-
const replayEvent = appendReplayEvent(sessionState, {
|
|
1186
|
-
kind: "artifact",
|
|
1187
|
-
actor: "sdk",
|
|
1188
|
-
payload: {
|
|
1189
|
-
route: canonicalReplayRoute(request.url),
|
|
1190
|
-
artifactId: brokeredArtifact.artifact.artifactId,
|
|
1191
|
-
surfaceKind: brokeredArtifact.artifact.surfaceKind,
|
|
1192
|
-
verdict: artifactVerdict.decision
|
|
1193
|
-
}
|
|
1194
|
-
});
|
|
1195
|
-
writeJson(response, 200, {
|
|
1196
|
-
compiledObservation,
|
|
1197
|
-
plannerView,
|
|
1198
|
-
artifactRef: buildV6ArtifactRef(brokeredArtifact.artifact, authorityEligible),
|
|
1199
|
-
mismatchSignals: brokeredArtifact.artifact.mismatchSignals,
|
|
1200
|
-
artifactVerdict,
|
|
1201
|
-
replayEventId: replayEvent.eventId
|
|
1202
|
-
});
|
|
1203
|
-
return;
|
|
1204
|
-
}
|
|
1205
|
-
if (request.url === "/v6/memory/stage" ||
|
|
1206
|
-
(unifiedV5InSecureV6 && request.url === "/v5/memory/stage")) {
|
|
1207
|
-
const payload = await readJson(request);
|
|
1208
|
-
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
1209
|
-
const result = stageMemoryRecordV6(payload, sessionState?.session);
|
|
1210
|
-
let promotionTicket;
|
|
1211
|
-
if (sessionState && result.record) {
|
|
1212
|
-
sessionState.memoryRecords.set(result.record.recordId, result.record);
|
|
1213
|
-
sessionState.memorySourceClassesV6.set(result.record.recordId, payload.sourceClass);
|
|
1214
|
-
if (result.record.tier === "candidate_durable") {
|
|
1215
|
-
const capability = mintMemoryPromotionCapabilityV5(sessionState.session, {
|
|
1216
|
-
recordId: result.record.recordId,
|
|
1217
|
-
sourceDigest: result.record.sourceDigest,
|
|
1218
|
-
sourceObservationId: result.record.sourceObservationId,
|
|
1219
|
-
key: result.record.key,
|
|
1220
|
-
valueDigest: result.record.sourceDigest ?? hashValue(result.record.value)
|
|
1221
|
-
});
|
|
1222
|
-
sessionState.capabilitiesV5.set(capability.capabilityId, capability);
|
|
1223
|
-
promotionTicket = {
|
|
1224
|
-
ticketId: capability.capabilityId,
|
|
1225
|
-
ticketDigest: capability.capabilityDigest,
|
|
1226
|
-
semanticDigest: capability.semanticDigest,
|
|
1227
|
-
recordId: result.record.recordId,
|
|
1228
|
-
sourceClass: payload.sourceClass,
|
|
1229
|
-
expiresAt: capability.expiresAt
|
|
1230
|
-
};
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
appendReplayEvent(sessionState, {
|
|
1234
|
-
kind: "memory",
|
|
1235
|
-
actor: "sdk",
|
|
1236
|
-
payload: {
|
|
1237
|
-
route: canonicalReplayRoute(request.url),
|
|
1238
|
-
recordId: result.record?.recordId ?? null,
|
|
1239
|
-
sourceClass: payload.sourceClass,
|
|
1240
|
-
verdict: result.verdict.decision
|
|
1241
|
-
}
|
|
1242
|
-
});
|
|
1243
|
-
writeJson(response, 200, {
|
|
1244
|
-
...result,
|
|
1245
|
-
verdict: relabelClaimProfile(result.verdict, "secure_v6"),
|
|
1246
|
-
promotionTicket
|
|
1247
|
-
});
|
|
1248
|
-
return;
|
|
1249
|
-
}
|
|
1250
|
-
if (request.url === "/v6/memory/promote" ||
|
|
1251
|
-
(unifiedV5InSecureV6 && request.url === "/v5/memory/promote")) {
|
|
1252
|
-
const payload = await readJson(request);
|
|
1253
|
-
const normalizedPayload = {
|
|
1254
|
-
...payload,
|
|
1255
|
-
ticketId: payload.ticketId ?? payload.capabilityId ?? "",
|
|
1256
|
-
ticketDigest: payload.ticketDigest ?? payload.capabilityDigest ?? ""
|
|
1257
|
-
};
|
|
1258
|
-
const sessionState = findSessionState(sessions, normalizedPayload.sessionId);
|
|
1259
|
-
const record = sessionState?.memoryRecords.get(normalizedPayload.recordId);
|
|
1260
|
-
const capability = sessionState?.capabilitiesV5.get(normalizedPayload.ticketId);
|
|
1261
|
-
const approvalEnvelope = sessionState?.approvalEnvelopesV5.get(normalizedPayload.approvalId);
|
|
1262
|
-
const snapshotState = sessionState && record ? buildMemorySnapshotState(sessionState, record) : undefined;
|
|
1263
|
-
const result = promoteMemoryRecordV6(normalizedPayload, sessionState?.session, record, capability, approvalEnvelope, {
|
|
1264
|
-
sourceClass: record ? sessionState?.memorySourceClassesV6.get(record.recordId) : undefined,
|
|
1265
|
-
priorTrustedRecord: snapshotState?.snapshotRecord
|
|
1266
|
-
});
|
|
1267
|
-
if (sessionState && result.promotedRecord) {
|
|
1268
|
-
if (snapshotState && result.promotedRecord.snapshotId) {
|
|
1269
|
-
sessionState.memorySnapshots.set(result.promotedRecord.snapshotId, {
|
|
1270
|
-
...snapshotState,
|
|
1271
|
-
snapshotId: result.promotedRecord.snapshotId
|
|
1272
|
-
});
|
|
1273
|
-
}
|
|
1274
|
-
sessionState.memoryRecords.set(result.promotedRecord.recordId, result.promotedRecord);
|
|
1275
|
-
if (capability) {
|
|
1276
|
-
consumeCapabilityV5(sessionState, capability);
|
|
1277
|
-
}
|
|
1278
|
-
if (approvalEnvelope) {
|
|
1279
|
-
sessionState.approvalEnvelopesV5.set(payload.approvalId, {
|
|
1280
|
-
...approvalEnvelope,
|
|
1281
|
-
consumedAt: new Date().toISOString()
|
|
1282
|
-
});
|
|
1283
|
-
}
|
|
1284
|
-
sessionState.session = {
|
|
1285
|
-
...sessionState.session,
|
|
1286
|
-
currentStep: sessionState.session.currentStep + 1
|
|
1287
|
-
};
|
|
1288
|
-
}
|
|
1289
|
-
appendReplayEvent(sessionState, {
|
|
1290
|
-
kind: "memory",
|
|
1291
|
-
actor: "sdk",
|
|
1292
|
-
payload: {
|
|
1293
|
-
route: canonicalReplayRoute(request.url),
|
|
1294
|
-
recordId: normalizedPayload.recordId,
|
|
1295
|
-
verdict: result.verdict.decision
|
|
1296
|
-
}
|
|
1297
|
-
});
|
|
1298
|
-
writeJson(response, 200, {
|
|
1299
|
-
...result,
|
|
1300
|
-
verdict: relabelClaimProfile(result.verdict, "secure_v6"),
|
|
1301
|
-
approvalEnvelope: normalizedPayload.approvalId && sessionState
|
|
1302
|
-
? sessionState.approvalEnvelopesV5.get(normalizedPayload.approvalId)
|
|
1303
|
-
: approvalEnvelope
|
|
1304
|
-
});
|
|
1305
|
-
return;
|
|
1306
|
-
}
|
|
1307
|
-
if (request.url === "/v6/memory/rollback" ||
|
|
1308
|
-
(unifiedV5InSecureV6 && request.url === "/v5/memory/rollback")) {
|
|
1309
|
-
const payload = await readJson(request);
|
|
1310
|
-
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
1311
|
-
const record = sessionState?.memoryRecords.get(payload.recordId);
|
|
1312
|
-
const snapshotState = sessionState?.memorySnapshots.get(payload.snapshotId);
|
|
1313
|
-
const result = rollbackMemoryRecordV5(payload, sessionState?.session, record, {
|
|
1314
|
-
snapshotRecord: snapshotState?.snapshotRecord,
|
|
1315
|
-
baselineAbsent: snapshotState?.baselineAbsent
|
|
1316
|
-
});
|
|
1317
|
-
if (sessionState && result.verdict.decision === "ALLOW") {
|
|
1318
|
-
if (result.restoredRecord) {
|
|
1319
|
-
sessionState.memoryRecords.set(result.restoredRecord.recordId, result.restoredRecord);
|
|
1320
|
-
}
|
|
1321
|
-
else {
|
|
1322
|
-
sessionState.memoryRecords.delete(payload.recordId);
|
|
1323
|
-
}
|
|
1324
|
-
}
|
|
1325
|
-
appendReplayEvent(sessionState, {
|
|
1326
|
-
kind: "memory",
|
|
1327
|
-
actor: "sdk",
|
|
1328
|
-
payload: {
|
|
1329
|
-
route: canonicalReplayRoute(request.url),
|
|
1330
|
-
recordId: payload.recordId,
|
|
1331
|
-
snapshotId: payload.snapshotId,
|
|
1332
|
-
verdict: result.verdict.decision
|
|
1333
|
-
}
|
|
1334
|
-
});
|
|
1335
|
-
writeJson(response, 200, {
|
|
1336
|
-
...result,
|
|
1337
|
-
verdict: relabelClaimProfile(result.verdict, "secure_v6"),
|
|
1338
|
-
rollbackEvent: result.verdict.decision === "ALLOW"
|
|
335
|
+
},
|
|
336
|
+
parserIsolation: {
|
|
337
|
+
configuredMode: runtime.parserIsolationMode,
|
|
338
|
+
lastCheckedAt: parserProbeSnapshot.lastCheckedAt,
|
|
339
|
+
...parserProbeSnapshot.probe
|
|
340
|
+
},
|
|
341
|
+
verifiedRegistry: runtime.verifiedRegistry
|
|
1339
342
|
? {
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
343
|
+
bundleId: runtime.verifiedRegistry.bundleId,
|
|
344
|
+
version: runtime.verifiedRegistry.version,
|
|
345
|
+
signer: runtime.verifiedRegistry.signer,
|
|
346
|
+
signatureVerified: runtime.verifiedRegistry.signatureVerified,
|
|
347
|
+
apiProviderCount: runtime.verifiedRegistry.apiProviders?.length ?? 0,
|
|
348
|
+
extractorProfileCount: runtime.verifiedRegistry.extractorProfiles?.length ?? 0
|
|
1343
349
|
}
|
|
1344
|
-
: undefined
|
|
350
|
+
: undefined,
|
|
351
|
+
policyLayers: runtime.policy.layerProvenance,
|
|
352
|
+
captureAttestation: {
|
|
353
|
+
htmlDom: true,
|
|
354
|
+
required: true
|
|
355
|
+
},
|
|
356
|
+
modelGuard: {
|
|
357
|
+
configured: modelGuardHealthSnapshot.configured,
|
|
358
|
+
ready: modelGuardHealthSnapshot.ready,
|
|
359
|
+
runtimeMode: modelGuardHealthSnapshot.runtimeMode,
|
|
360
|
+
enforcementMode: modelGuardHealthSnapshot.enforcementMode,
|
|
361
|
+
bundleVersion: modelGuardHealthSnapshot.bundleVersion,
|
|
362
|
+
featureSchemaVersion: modelGuardHealthSnapshot.featureSchemaVersion,
|
|
363
|
+
bundleDigest: modelGuardHealthSnapshot.bundleDigest,
|
|
364
|
+
componentDigests: modelGuardHealthSnapshot.componentDigests,
|
|
365
|
+
validationError: modelGuardHealthSnapshot.validationError
|
|
366
|
+
}
|
|
1345
367
|
});
|
|
1346
368
|
return;
|
|
1347
369
|
}
|
|
1348
|
-
if (
|
|
1349
|
-
(
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
return;
|
|
1355
|
-
}
|
|
1356
|
-
writeJson(response, 200, buildReplayBundle(sessionState.replayEvents, runtime));
|
|
370
|
+
if (isRetiredRoute(requestUrl)) {
|
|
371
|
+
writeJson(response, 410, retiredRouteResponse(requestUrl));
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
if (request.method !== "POST" || !requestUrl.startsWith("/v6/")) {
|
|
375
|
+
writeJson(response, 404, { error: "not_found" });
|
|
1357
376
|
return;
|
|
1358
377
|
}
|
|
1359
|
-
if (
|
|
378
|
+
if (requestUrl === "/v6/session/start") {
|
|
1360
379
|
const payload = await readJson(request);
|
|
1361
|
-
const sessionState = createSessionState(payload, runtime);
|
|
380
|
+
const sessionState = createSessionState(payload, runtime, secureReady);
|
|
1362
381
|
sessions.set(sessionState.session.sessionId, sessionState);
|
|
1363
382
|
appendReplayEvent(sessionState, {
|
|
1364
|
-
kind: "
|
|
1365
|
-
actor: "
|
|
383
|
+
kind: "observation",
|
|
384
|
+
actor: "sdk",
|
|
1366
385
|
payload: {
|
|
1367
|
-
route:
|
|
386
|
+
route: requestUrl,
|
|
1368
387
|
sessionId: sessionState.session.sessionId,
|
|
1369
|
-
|
|
388
|
+
taskId: sessionState.session.taskId
|
|
1370
389
|
}
|
|
1371
390
|
});
|
|
1372
391
|
writeJson(response, 200, {
|
|
@@ -1374,7 +393,7 @@ export async function createSafeBrowseServer(options = {}) {
|
|
|
1374
393
|
});
|
|
1375
394
|
return;
|
|
1376
395
|
}
|
|
1377
|
-
if (
|
|
396
|
+
if (requestUrl === "/v6/observe") {
|
|
1378
397
|
const payload = await readJson(request);
|
|
1379
398
|
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
1380
399
|
if (!sessionState) {
|
|
@@ -1386,141 +405,172 @@ export async function createSafeBrowseServer(options = {}) {
|
|
|
1386
405
|
sessionId: payload.sessionId,
|
|
1387
406
|
taskId: sessionState.session.taskId
|
|
1388
407
|
};
|
|
1389
|
-
const
|
|
408
|
+
const parsed = await parserIsolationService.compileObservation({
|
|
1390
409
|
capture,
|
|
1391
410
|
workflowHash: sessionState.session.workflowHash,
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
411
|
+
runtime,
|
|
412
|
+
compilerVersion: "v6"
|
|
413
|
+
});
|
|
414
|
+
let compiledObservation = parsed.compiledObservation;
|
|
415
|
+
let plannerView = parsed.plannerView;
|
|
416
|
+
let mediated = applyV6ObservationMediation(compiledObservation, plannerView);
|
|
417
|
+
const verifiedRegistryEntry = capture.surfaceType === "tool_manifest"
|
|
418
|
+
? lookupVerifiedRegistryEntry(runtime, capture.toolId, capture.toolId)
|
|
419
|
+
: undefined;
|
|
420
|
+
const verifiedApiProviderEntry = "providerId" in capture && capture.providerId
|
|
421
|
+
? lookupVerifiedApiProviderEntry(runtime, capture.providerId)
|
|
1399
422
|
: undefined;
|
|
1400
|
-
const manifestHash =
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
423
|
+
const manifestHash = capture.surfaceType === "tool_manifest"
|
|
424
|
+
? parsed.toolManifestDigests?.manifestHash ??
|
|
425
|
+
computeToolManifestHash({
|
|
426
|
+
toolId: capture.toolId,
|
|
427
|
+
description: capture.description,
|
|
428
|
+
authType: capture.authType,
|
|
429
|
+
requestedScopes: capture.requestedScopes,
|
|
430
|
+
callbackUri: capture.callbackUri
|
|
431
|
+
})
|
|
432
|
+
: undefined;
|
|
433
|
+
const schemaHash = capture.surfaceType === "tool_manifest"
|
|
434
|
+
? parsed.toolManifestDigests?.schemaHash ??
|
|
435
|
+
computeToolSchemaHash(capture.schemaDescriptions)
|
|
436
|
+
: undefined;
|
|
437
|
+
if (modelGuardClient.configured &&
|
|
438
|
+
modelGuardClient.enforcementMode !== "off" &&
|
|
439
|
+
mediated.verdict.decision === "ALLOW") {
|
|
440
|
+
try {
|
|
441
|
+
const scored = await modelGuardClient.scoreObservation(buildModelGuardObservationRequest(sessionState.session, compiledObservation, plannerView));
|
|
442
|
+
if (modelGuardClient.enforcementMode === "shadow") {
|
|
443
|
+
compiledObservation = {
|
|
444
|
+
...compiledObservation,
|
|
445
|
+
modelAssessment: scored.assessment
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
const applied = applyModelGuardAssessment(compiledObservation, plannerView, mediated.verdict, scored.assessment);
|
|
450
|
+
compiledObservation = applied.compiledObservation;
|
|
451
|
+
plannerView = applied.plannerView;
|
|
452
|
+
mediated = {
|
|
453
|
+
plannerView,
|
|
454
|
+
verdict: applied.verdict,
|
|
455
|
+
failClosed: false
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
catch {
|
|
460
|
+
if (modelGuardClient.enforcementMode === "tighten") {
|
|
461
|
+
compiledObservation = {
|
|
462
|
+
...compiledObservation,
|
|
463
|
+
authorityEligible: false
|
|
464
|
+
};
|
|
465
|
+
plannerView = {
|
|
466
|
+
...plannerView,
|
|
467
|
+
visibleExcerpt: "",
|
|
468
|
+
riskMarkers: [...new Set([...plannerView.riskMarkers, "model_guard_unavailable"])]
|
|
469
|
+
};
|
|
470
|
+
mediated = {
|
|
471
|
+
plannerView,
|
|
472
|
+
verdict: {
|
|
473
|
+
decision: "REPLAN_READ_ONLY",
|
|
474
|
+
reasonCodes: ["MODEL_GUARD_UNAVAILABLE"],
|
|
475
|
+
riskScore: Math.max(0.6, compiledObservation.riskScore),
|
|
476
|
+
safeConstraints: {
|
|
477
|
+
claim_profile: "secure_v6",
|
|
478
|
+
authority_eligible: false
|
|
479
|
+
},
|
|
480
|
+
telemetryTags: ["v6_observation", "model_guard_unavailable"]
|
|
481
|
+
},
|
|
482
|
+
failClosed: false
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
const authorities = mediated.verdict.decision === "BLOCK"
|
|
1404
488
|
? []
|
|
1405
|
-
:
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
489
|
+
: mintCapabilitiesForObservationV6(sessionState.session, compiledObservation, plannerView, {
|
|
490
|
+
policy: runtime.policy,
|
|
491
|
+
...(capture.surfaceType === "tool_manifest" && verifiedRegistryEntry
|
|
492
|
+
? {
|
|
493
|
+
verifiedRegistryEntry,
|
|
494
|
+
registryEntryId: capture.toolId,
|
|
495
|
+
connectorId: capture.toolId,
|
|
496
|
+
requestedScopes: capture.requestedScopes,
|
|
497
|
+
callbackUri: capture.callbackUri,
|
|
498
|
+
callbackOrigin: capture.callbackOrigin,
|
|
499
|
+
manifestAuthType: capture.authType,
|
|
500
|
+
manifestHash,
|
|
501
|
+
schemaHash
|
|
502
|
+
}
|
|
503
|
+
: verifiedApiProviderEntry
|
|
504
|
+
? {
|
|
505
|
+
verifiedApiProviderEntry
|
|
506
|
+
}
|
|
507
|
+
: {})
|
|
1415
508
|
});
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
: [];
|
|
1427
|
-
const observationVerdict = relabelClaimProfile(buildV5ObservationDecision(compiledObservation), "secure_v5");
|
|
1428
|
-
const replayEvent = appendReplayEvent(sessionState, {
|
|
509
|
+
const tightenedAuthorities = tightenAuthoritiesWithModelGuard(authorities, modelGuardClient.enforcementMode === "tighten"
|
|
510
|
+
? compiledObservation.modelAssessment
|
|
511
|
+
: undefined);
|
|
512
|
+
for (const authority of tightenedAuthorities) {
|
|
513
|
+
sessionState.authorities.set(authority.capabilityId, authority);
|
|
514
|
+
}
|
|
515
|
+
sessionState.latestObservation = compiledObservation;
|
|
516
|
+
sessionState.latestObservationVerdict = mediated.verdict;
|
|
517
|
+
sessionState.observations.set(compiledObservation.observationId, compiledObservation);
|
|
518
|
+
const replayEventId = appendReplayEvent(sessionState, {
|
|
1429
519
|
kind: "observation",
|
|
1430
520
|
actor: "sdk",
|
|
1431
521
|
payload: {
|
|
1432
|
-
route:
|
|
522
|
+
route: requestUrl,
|
|
1433
523
|
observationId: compiledObservation.observationId,
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
524
|
+
parseStatus: compiledObservation.parseStatus,
|
|
525
|
+
authorityCount: tightenedAuthorities.length,
|
|
526
|
+
decision: mediated.verdict.decision,
|
|
527
|
+
surfaceType: capture.surfaceType,
|
|
528
|
+
providerId: "providerId" in capture ? capture.providerId ?? null : null,
|
|
529
|
+
extractorIds: "extractionAttestation" in capture && capture.extractionAttestation
|
|
530
|
+
? [capture.extractionAttestation.extractorId]
|
|
531
|
+
: "extractionAttestations" in capture
|
|
532
|
+
? capture.extractionAttestations?.map((entry) => entry.extractorId) ?? []
|
|
533
|
+
: [],
|
|
534
|
+
modelAssessment: compiledObservation.modelAssessment
|
|
535
|
+
? {
|
|
536
|
+
bundleVersion: compiledObservation.modelAssessment.bundleVersion,
|
|
537
|
+
calibratedDecisionLabel: compiledObservation.modelAssessment.calibratedDecisionLabel,
|
|
538
|
+
coarseReasonCodes: compiledObservation.modelAssessment.coarseReasonCodes,
|
|
539
|
+
evidenceChunkIds: compiledObservation.modelAssessment.evidenceChunkIds
|
|
540
|
+
}
|
|
541
|
+
: null
|
|
1438
542
|
}
|
|
1439
543
|
});
|
|
1440
544
|
writeJson(response, 200, {
|
|
1441
545
|
compiledObservation,
|
|
1442
|
-
plannerView
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
artifactRefs,
|
|
1446
|
-
observationVerdict,
|
|
1447
|
-
replayEventId:
|
|
1448
|
-
});
|
|
1449
|
-
return;
|
|
1450
|
-
}
|
|
1451
|
-
if (request.url === "/v5/action/evaluate") {
|
|
1452
|
-
const payload = await readJson(request);
|
|
1453
|
-
const authorityId = payload.authorityId ?? payload.capabilityId;
|
|
1454
|
-
const authorityDigest = payload.authorityDigest ?? payload.capabilityDigest;
|
|
1455
|
-
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
1456
|
-
const capability = authorityId ? getCapabilityV5(sessionState, authorityId) : undefined;
|
|
1457
|
-
const authorityDecision = relabelClaimProfile(evaluateCapabilityUseV5({
|
|
1458
|
-
sessionId: payload.sessionId,
|
|
1459
|
-
capabilityId: authorityId ?? "",
|
|
1460
|
-
capabilityDigest: authorityDigest ?? "",
|
|
1461
|
-
parameters: payload.parameters
|
|
1462
|
-
}, sessionState?.session, capability, {
|
|
1463
|
-
alreadyUsed: authorityId
|
|
1464
|
-
? (sessionState?.usedCapabilitiesV5.has(authorityId) ?? false)
|
|
1465
|
-
: false
|
|
1466
|
-
}), "secure_v5");
|
|
1467
|
-
const observationDecision = buildObservationDecisionForClaimProfile(sessionState?.latestObservationV5, "secure_v5");
|
|
1468
|
-
const effectDecision = observationDecision.decision === "ALLOW" && authorityDecision.decision === "ALLOW"
|
|
1469
|
-
? authorityDecision
|
|
1470
|
-
: relabelClaimProfile({
|
|
1471
|
-
decision: "BLOCK",
|
|
1472
|
-
reasonCodes: [
|
|
1473
|
-
...new Set([
|
|
1474
|
-
...(observationDecision.decision === "ALLOW"
|
|
1475
|
-
? []
|
|
1476
|
-
: ["OBSERVATION_NOT_EFFECT_ELIGIBLE"]),
|
|
1477
|
-
...(authorityDecision.decision === "ALLOW"
|
|
1478
|
-
? []
|
|
1479
|
-
: authorityDecision.reasonCodes)
|
|
1480
|
-
])
|
|
1481
|
-
],
|
|
1482
|
-
riskScore: Math.max(observationDecision.riskScore, authorityDecision.riskScore),
|
|
1483
|
-
safeConstraints: {
|
|
1484
|
-
target_class: capability?.targetClass ?? "unknown"
|
|
1485
|
-
},
|
|
1486
|
-
telemetryTags: ["v5_action", "block"]
|
|
1487
|
-
}, "secure_v5");
|
|
1488
|
-
if (effectDecision.decision === "ALLOW" && sessionState && capability) {
|
|
1489
|
-
consumeCapabilityV5(sessionState, capability);
|
|
1490
|
-
sessionState.session = {
|
|
1491
|
-
...sessionState.session,
|
|
1492
|
-
currentStep: sessionState.session.currentStep + 1
|
|
1493
|
-
};
|
|
1494
|
-
}
|
|
1495
|
-
appendReplayEvent(sessionState, {
|
|
1496
|
-
kind: "action",
|
|
1497
|
-
actor: "sdk",
|
|
1498
|
-
payload: {
|
|
1499
|
-
route: request.url,
|
|
1500
|
-
authorityId: authorityId ?? null,
|
|
1501
|
-
observationDecision: observationDecision.decision,
|
|
1502
|
-
authorityDecision: authorityDecision.decision,
|
|
1503
|
-
effectDecision: effectDecision.decision
|
|
1504
|
-
}
|
|
1505
|
-
});
|
|
1506
|
-
writeJson(response, 200, {
|
|
1507
|
-
observationDecision,
|
|
1508
|
-
authorityDecision,
|
|
1509
|
-
effectDecision,
|
|
1510
|
-
verdict: effectDecision,
|
|
1511
|
-
executionPlan: buildV5ExecutionPlan(capability)
|
|
546
|
+
plannerView,
|
|
547
|
+
authorityCandidates: tightenedAuthorities.map(authorityCandidateFromDescriptor),
|
|
548
|
+
capabilities: tightenedAuthorities.map(authorityCandidateFromDescriptor),
|
|
549
|
+
artifactRefs: [],
|
|
550
|
+
observationVerdict: mediated.verdict,
|
|
551
|
+
replayEventId: replayEventId ?? randomUUID()
|
|
1512
552
|
});
|
|
1513
553
|
return;
|
|
1514
554
|
}
|
|
1515
|
-
if (
|
|
555
|
+
if (requestUrl === "/v6/action/evaluate") {
|
|
1516
556
|
const payload = await readJson(request);
|
|
1517
557
|
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
1518
|
-
const
|
|
1519
|
-
const
|
|
1520
|
-
|
|
558
|
+
const authority = sessionState?.authorities.get(payload.authorityId);
|
|
559
|
+
const approvalEnvelope = payload.approvalId && sessionState
|
|
560
|
+
? sessionState.approvalEnvelopes.get(payload.approvalId)
|
|
561
|
+
: undefined;
|
|
562
|
+
const authorityDecision = evaluateCapabilityUseV6(payload, sessionState?.session, authority, {
|
|
563
|
+
alreadyUsed: sessionState?.usedAuthorities.has(payload.authorityId) ?? false,
|
|
564
|
+
approvalEnvelope
|
|
1521
565
|
});
|
|
1522
|
-
if (
|
|
1523
|
-
|
|
566
|
+
if (sessionState && authority && authorityDecision.decision === "ALLOW") {
|
|
567
|
+
consumeAuthority(sessionState, authority);
|
|
568
|
+
if (approvalEnvelope) {
|
|
569
|
+
sessionState.approvalEnvelopes.set(approvalEnvelope.approvalId, {
|
|
570
|
+
...approvalEnvelope,
|
|
571
|
+
consumedAt: new Date().toISOString()
|
|
572
|
+
});
|
|
573
|
+
}
|
|
1524
574
|
sessionState.session = {
|
|
1525
575
|
...sessionState.session,
|
|
1526
576
|
currentStep: sessionState.session.currentStep + 1
|
|
@@ -1530,131 +580,115 @@ export async function createSafeBrowseServer(options = {}) {
|
|
|
1530
580
|
kind: "action",
|
|
1531
581
|
actor: "sdk",
|
|
1532
582
|
payload: {
|
|
1533
|
-
route:
|
|
1534
|
-
authorityId: payload.
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
583
|
+
route: requestUrl,
|
|
584
|
+
authorityId: payload.authorityId,
|
|
585
|
+
decision: authorityDecision.decision,
|
|
586
|
+
operationClass: authority?.operationClass ?? null,
|
|
587
|
+
providerId: authority?.providerId ?? null,
|
|
588
|
+
operationId: authority?.operationId ?? null
|
|
1538
589
|
}
|
|
1539
590
|
});
|
|
1540
591
|
writeJson(response, 200, {
|
|
1541
|
-
|
|
1542
|
-
|
|
592
|
+
observationDecision: sessionState?.latestObservationVerdict ?? {
|
|
593
|
+
decision: "ALLOW",
|
|
594
|
+
reasonCodes: [],
|
|
595
|
+
riskScore: 0
|
|
596
|
+
},
|
|
597
|
+
authorityDecision,
|
|
598
|
+
effectDecision: authorityDecision,
|
|
599
|
+
executionPlan: authorityDecision.decision === "ALLOW" && authority
|
|
600
|
+
? {
|
|
601
|
+
verb: authority.kind,
|
|
602
|
+
operationClass: authority.operationClass,
|
|
603
|
+
targetUrl: authority.targetUrl,
|
|
604
|
+
targetOrigin: authority.targetOrigin,
|
|
605
|
+
selector: authority.selector,
|
|
606
|
+
targetPathClass: authority.targetPathClass,
|
|
607
|
+
derivedSinkClass: authority.derivedSinkClass,
|
|
608
|
+
derivedSensitiveSink: authority.derivedSensitiveSink,
|
|
609
|
+
providerId: authority.providerId,
|
|
610
|
+
operationId: authority.operationId,
|
|
611
|
+
method: authority.method,
|
|
612
|
+
pathTemplate: authority.pathTemplate,
|
|
613
|
+
mailboxId: authority.mailboxId,
|
|
614
|
+
accountId: authority.accountId,
|
|
615
|
+
messageId: authority.messageId,
|
|
616
|
+
threadId: authority.threadId,
|
|
617
|
+
recipientSetHash: authority.recipientSetHash,
|
|
618
|
+
subjectHash: authority.subjectHash,
|
|
619
|
+
bodyDigest: authority.bodyDigest,
|
|
620
|
+
attachmentDigestSet: authority.attachmentDigestSet
|
|
621
|
+
}
|
|
622
|
+
: undefined
|
|
1543
623
|
});
|
|
1544
624
|
return;
|
|
1545
625
|
}
|
|
1546
|
-
if (
|
|
626
|
+
if (requestUrl === "/v6/approval/issue") {
|
|
1547
627
|
const payload = await readJson(request);
|
|
1548
628
|
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
1549
|
-
const
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
riskScore: 0.99,
|
|
1556
|
-
safeConstraints: {
|
|
1557
|
-
claim_profile: "secure_v5"
|
|
1558
|
-
},
|
|
1559
|
-
telemetryTags: ["approval_v5_issue", "block"]
|
|
1560
|
-
}
|
|
1561
|
-
});
|
|
1562
|
-
return;
|
|
1563
|
-
}
|
|
1564
|
-
if (capability && payload.capabilityDigest !== capability.capabilityDigest) {
|
|
1565
|
-
writeJson(response, 200, {
|
|
1566
|
-
verdict: {
|
|
1567
|
-
decision: "BLOCK",
|
|
1568
|
-
reasonCodes: ["CAPABILITY_DIGEST_MISMATCH"],
|
|
1569
|
-
riskScore: 0.99,
|
|
1570
|
-
safeConstraints: {
|
|
1571
|
-
claim_profile: "secure_v5"
|
|
1572
|
-
},
|
|
1573
|
-
telemetryTags: ["approval_v5_issue", "block"]
|
|
1574
|
-
}
|
|
629
|
+
const authorityId = payload.authorityId ?? payload.capabilityId ?? "";
|
|
630
|
+
const authorityDigest = payload.authorityDigest ?? payload.capabilityDigest ?? "";
|
|
631
|
+
const authority = sessionState?.authorities.get(authorityId);
|
|
632
|
+
if (!sessionState || !authority) {
|
|
633
|
+
writeJson(response, 404, {
|
|
634
|
+
error: !sessionState ? "unknown_session" : "unknown_authority"
|
|
1575
635
|
});
|
|
1576
636
|
return;
|
|
1577
637
|
}
|
|
1578
|
-
const
|
|
1579
|
-
|
|
1580
|
-
writeJson(response, 200, {
|
|
1581
|
-
verdict: {
|
|
1582
|
-
decision: "BLOCK",
|
|
1583
|
-
reasonCodes: ["APPROVAL_ALREADY_ISSUED_FOR_CAPABILITY"],
|
|
1584
|
-
riskScore: 0.99,
|
|
1585
|
-
safeConstraints: {
|
|
1586
|
-
claim_profile: "secure_v5"
|
|
1587
|
-
},
|
|
1588
|
-
telemetryTags: ["approval_v5_issue", "block"]
|
|
1589
|
-
},
|
|
1590
|
-
approvalEnvelope: activeApproval
|
|
1591
|
-
});
|
|
1592
|
-
return;
|
|
1593
|
-
}
|
|
1594
|
-
const brokerPayload = sessionState && capability
|
|
1595
|
-
? createApprovalIntentPayloadV5({
|
|
638
|
+
const signatureValid = authority.capabilityDigest === authorityDigest &&
|
|
639
|
+
verifyApprovalIntentSignatureV6(createApprovalIntentPayloadV6({
|
|
1596
640
|
sessionId: sessionState.session.sessionId,
|
|
1597
641
|
workflowHash: sessionState.session.workflowHash,
|
|
1598
|
-
capabilityId:
|
|
1599
|
-
capabilityDigest:
|
|
642
|
+
capabilityId: authority.capabilityId,
|
|
643
|
+
capabilityDigest: authority.capabilityDigest,
|
|
1600
644
|
expiresInSeconds: payload.expiresInSeconds
|
|
1601
|
-
})
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
session: sessionState?.session,
|
|
1606
|
-
capability,
|
|
645
|
+
}), payload.brokerSignature, runtime.approvalBrokerPublicKey);
|
|
646
|
+
const issued = issueApprovalEnvelopeV6({
|
|
647
|
+
session: sessionState.session,
|
|
648
|
+
capability: authority,
|
|
1607
649
|
brokerSignature: payload.brokerSignature,
|
|
1608
|
-
brokerSignatureVerified,
|
|
650
|
+
brokerSignatureVerified: signatureValid,
|
|
1609
651
|
expiresInSeconds: payload.expiresInSeconds
|
|
1610
652
|
});
|
|
1611
|
-
if (issued.approvalEnvelope
|
|
1612
|
-
sessionState.
|
|
653
|
+
if (issued.approvalEnvelope) {
|
|
654
|
+
sessionState.approvalEnvelopes.set(issued.approvalEnvelope.approvalId, issued.approvalEnvelope);
|
|
1613
655
|
}
|
|
1614
656
|
appendReplayEvent(sessionState, {
|
|
1615
657
|
kind: "tool",
|
|
1616
658
|
actor: "sdk",
|
|
1617
659
|
payload: {
|
|
1618
|
-
route:
|
|
1619
|
-
|
|
1620
|
-
|
|
660
|
+
route: requestUrl,
|
|
661
|
+
authorityId,
|
|
662
|
+
decision: issued.verdict.decision
|
|
1621
663
|
}
|
|
1622
664
|
});
|
|
1623
|
-
writeJson(response, 200,
|
|
1624
|
-
...issued,
|
|
1625
|
-
verdict: relabelClaimProfile(issued.verdict, "secure_v5")
|
|
1626
|
-
});
|
|
665
|
+
writeJson(response, 200, issued);
|
|
1627
666
|
return;
|
|
1628
667
|
}
|
|
1629
|
-
if (
|
|
668
|
+
if (requestUrl === "/v6/tool/prepare") {
|
|
1630
669
|
const payload = await readJson(request);
|
|
1631
670
|
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
1632
|
-
const approvalEnvelope = sessionState?.
|
|
1633
|
-
const
|
|
1634
|
-
?
|
|
671
|
+
const approvalEnvelope = sessionState?.approvalEnvelopes.get(payload.approvalId);
|
|
672
|
+
const authority = approvalEnvelope && sessionState
|
|
673
|
+
? sessionState.authorities.get(approvalEnvelope.capabilityId)
|
|
1635
674
|
: undefined;
|
|
1636
|
-
const
|
|
1637
|
-
? lookupVerifiedRegistryEntry(runtime,
|
|
675
|
+
const verifiedRegistryEntry = authority?.registryEntryId
|
|
676
|
+
? lookupVerifiedRegistryEntry(runtime, authority.connectorId, authority.registryEntryId)
|
|
1638
677
|
: undefined;
|
|
1639
|
-
const prepared =
|
|
678
|
+
const prepared = prepareToolOnboardingV6({
|
|
1640
679
|
session: sessionState?.session,
|
|
1641
|
-
capability,
|
|
680
|
+
capability: authority,
|
|
1642
681
|
approvalEnvelope,
|
|
1643
|
-
verifiedRegistryEntry
|
|
1644
|
-
});
|
|
1645
|
-
if (sessionState && prepared.onboardingSession) {
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
consumedAt,
|
|
1654
|
-
onboardingSessionId: prepared.onboardingSession.onboardingSessionId
|
|
1655
|
-
});
|
|
1656
|
-
}
|
|
1657
|
-
sessionState.onboardingSessionsV5.set(prepared.onboardingSession.onboardingSessionId, prepared.onboardingSession);
|
|
682
|
+
verifiedRegistryEntry
|
|
683
|
+
});
|
|
684
|
+
if (sessionState && approvalEnvelope && authority && prepared.onboardingSession) {
|
|
685
|
+
sessionState.onboardingSessions.set(prepared.onboardingSession.onboardingSessionId, prepared.onboardingSession);
|
|
686
|
+
sessionState.approvalEnvelopes.set(approvalEnvelope.approvalId, {
|
|
687
|
+
...approvalEnvelope,
|
|
688
|
+
consumedAt: new Date().toISOString(),
|
|
689
|
+
onboardingSessionId: prepared.onboardingSession.onboardingSessionId
|
|
690
|
+
});
|
|
691
|
+
consumeAuthority(sessionState, authority);
|
|
1658
692
|
sessionState.session = {
|
|
1659
693
|
...sessionState.session,
|
|
1660
694
|
currentStep: sessionState.session.currentStep + 1
|
|
@@ -1664,63 +698,53 @@ export async function createSafeBrowseServer(options = {}) {
|
|
|
1664
698
|
kind: "tool",
|
|
1665
699
|
actor: "sdk",
|
|
1666
700
|
payload: {
|
|
1667
|
-
route:
|
|
701
|
+
route: requestUrl,
|
|
1668
702
|
approvalId: payload.approvalId,
|
|
1669
|
-
|
|
703
|
+
decision: prepared.verdict.decision
|
|
1670
704
|
}
|
|
1671
705
|
});
|
|
1672
|
-
writeJson(response, 200,
|
|
1673
|
-
verdict: relabelClaimProfile(prepared.verdict, "secure_v5"),
|
|
1674
|
-
approvalEnvelope: sessionState?.approvalEnvelopesV5.get(payload.approvalId) ?? approvalEnvelope,
|
|
1675
|
-
verifiedRegistryEntry: verifiedEntry,
|
|
1676
|
-
onboardingSession: prepared.onboardingSession
|
|
1677
|
-
});
|
|
706
|
+
writeJson(response, 200, prepared);
|
|
1678
707
|
return;
|
|
1679
708
|
}
|
|
1680
|
-
if (
|
|
709
|
+
if (requestUrl === "/v6/tool/callback/verify") {
|
|
1681
710
|
const payload = await readJson(request);
|
|
1682
711
|
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
1683
|
-
const approvalEnvelope = sessionState?.
|
|
1684
|
-
const
|
|
1685
|
-
|
|
712
|
+
const approvalEnvelope = sessionState?.approvalEnvelopes.get(payload.approvalId);
|
|
713
|
+
const onboardingSession = sessionState?.onboardingSessions.get(payload.onboardingSessionId);
|
|
714
|
+
const authority = approvalEnvelope && sessionState
|
|
715
|
+
? sessionState.authorities.get(approvalEnvelope.capabilityId)
|
|
1686
716
|
: undefined;
|
|
1687
|
-
const
|
|
1688
|
-
|
|
1689
|
-
? lookupVerifiedRegistryEntry(runtime, approvalEnvelope.connectorId, approvalEnvelope.registryEntryId)
|
|
717
|
+
const verifiedRegistryEntry = authority?.registryEntryId
|
|
718
|
+
? lookupVerifiedRegistryEntry(runtime, authority.connectorId, authority.registryEntryId)
|
|
1690
719
|
: undefined;
|
|
1691
|
-
const verified =
|
|
720
|
+
const verified = verifyToolCallbackV6({
|
|
1692
721
|
session: sessionState?.session,
|
|
1693
|
-
capability,
|
|
722
|
+
capability: authority,
|
|
1694
723
|
approvalEnvelope,
|
|
1695
724
|
onboardingSession,
|
|
1696
|
-
verifiedRegistryEntry
|
|
725
|
+
verifiedRegistryEntry,
|
|
1697
726
|
request: payload.request
|
|
1698
727
|
});
|
|
1699
|
-
if (sessionState && onboardingSession && verified.
|
|
1700
|
-
sessionState.
|
|
728
|
+
if (sessionState && onboardingSession && verified.connectorHandle) {
|
|
729
|
+
sessionState.onboardingSessions.set(onboardingSession.onboardingSessionId, {
|
|
1701
730
|
...onboardingSession,
|
|
1702
731
|
status: "used"
|
|
1703
732
|
});
|
|
1704
|
-
|
|
1705
|
-
if (sessionState && verified.connectorHandle) {
|
|
1706
|
-
sessionState.connectorHandlesV5.set(verified.connectorHandle.handleId, verified.connectorHandle);
|
|
733
|
+
sessionState.connectorHandles.set(verified.connectorHandle.handleId, verified.connectorHandle);
|
|
1707
734
|
}
|
|
1708
735
|
appendReplayEvent(sessionState, {
|
|
1709
736
|
kind: "tool",
|
|
1710
737
|
actor: "sdk",
|
|
1711
738
|
payload: {
|
|
1712
|
-
route:
|
|
739
|
+
route: requestUrl,
|
|
1713
740
|
onboardingSessionId: payload.onboardingSessionId,
|
|
1714
|
-
|
|
741
|
+
decision: verified.verdict.decision
|
|
1715
742
|
}
|
|
1716
743
|
});
|
|
1717
|
-
writeJson(response, 200,
|
|
1718
|
-
...verified,
|
|
1719
|
-
verdict: relabelClaimProfile(verified.verdict, "secure_v5")
|
|
1720
|
-
});
|
|
744
|
+
writeJson(response, 200, verified);
|
|
1721
745
|
return;
|
|
1722
746
|
}
|
|
1723
|
-
if (
|
|
747
|
+
if (requestUrl === "/v6/artifact/ingest") {
|
|
1724
748
|
const payload = await readJson(request);
|
|
1725
749
|
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
1726
750
|
if (!sessionState) {
|
|
@@ -1732,216 +756,125 @@ export async function createSafeBrowseServer(options = {}) {
|
|
|
1732
756
|
sessionId: payload.sessionId,
|
|
1733
757
|
taskId: sessionState.session.taskId
|
|
1734
758
|
};
|
|
1735
|
-
const
|
|
759
|
+
const parsed = await parserIsolationService.compileObservation({
|
|
1736
760
|
capture,
|
|
1737
761
|
workflowHash: sessionState.session.workflowHash,
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
const
|
|
1742
|
-
const
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
surfaceKind: "document",
|
|
1748
|
-
sourceOrigin: capture.url,
|
|
1749
|
-
viewerOrigin: capture.frameUrl ?? capture.url,
|
|
1750
|
-
extractionMethod: "download",
|
|
1751
|
-
trustSignals: capture.trustSignals
|
|
1752
|
-
}, runtime);
|
|
1753
|
-
const artifactAuthoritative = compiledObservation.parseStatus === "compiled" &&
|
|
1754
|
-
compiledObservation.authorityEligible &&
|
|
1755
|
-
!brokeredArtifact.artifact.mismatchSignals.length &&
|
|
1756
|
-
!brokeredArtifact.artifact.metadataSignals.length;
|
|
1757
|
-
const artifactVerdict = relabelClaimProfile(artifactAuthoritative
|
|
1758
|
-
? {
|
|
1759
|
-
decision: "ALLOW",
|
|
1760
|
-
reasonCodes: [],
|
|
1761
|
-
riskScore: Math.max(compiledObservation.riskScore, brokeredArtifact.verdict.riskScore),
|
|
1762
|
-
safeConstraints: {
|
|
1763
|
-
authority_eligible: true,
|
|
1764
|
-
handoff_mode: "artifact_reference"
|
|
1765
|
-
},
|
|
1766
|
-
telemetryTags: ["artifact_v5", "allow"]
|
|
1767
|
-
}
|
|
1768
|
-
: {
|
|
1769
|
-
decision: brokeredArtifact.verdict.decision === "QUARANTINE_ARTIFACT" ||
|
|
1770
|
-
brokeredArtifact.artifact.mismatchSignals.length > 0 ||
|
|
1771
|
-
brokeredArtifact.artifact.metadataSignals.length > 0
|
|
1772
|
-
? "QUARANTINE_ARTIFACT"
|
|
1773
|
-
: "BLOCK",
|
|
1774
|
-
reasonCodes: [...new Set([
|
|
1775
|
-
...(compiledObservation.parseStatus === "compiled"
|
|
1776
|
-
? ["ARTIFACT_NOT_AUTHORITY_ELIGIBLE"]
|
|
1777
|
-
: [
|
|
1778
|
-
compiledObservation.parseStatus === "partial"
|
|
1779
|
-
? "PARSE_STATUS_PARTIAL"
|
|
1780
|
-
: "PARSE_STATUS_UNSUPPORTED"
|
|
1781
|
-
]),
|
|
1782
|
-
...brokeredArtifact.verdict.reasonCodes
|
|
1783
|
-
])],
|
|
1784
|
-
riskScore: Math.max(0.95, brokeredArtifact.verdict.riskScore),
|
|
1785
|
-
safeConstraints: {
|
|
1786
|
-
authority_eligible: false,
|
|
1787
|
-
handoff_mode: "artifact_reference"
|
|
1788
|
-
},
|
|
1789
|
-
telemetryTags: ["artifact_v5", "quarantine"]
|
|
1790
|
-
}, "secure_v5");
|
|
1791
|
-
sessionState.latestObservationV5 = compiledObservation;
|
|
1792
|
-
sessionState.observationsV5.set(compiledObservation.observationId, compiledObservation);
|
|
1793
|
-
const replayEvent = appendReplayEvent(sessionState, {
|
|
762
|
+
runtime,
|
|
763
|
+
compilerVersion: "v6"
|
|
764
|
+
});
|
|
765
|
+
const mediated = applyV6ObservationMediation(parsed.compiledObservation, parsed.plannerView);
|
|
766
|
+
const artifactRef = buildArtifactRef(capture, parsed.compiledObservation);
|
|
767
|
+
sessionState.latestObservation = parsed.compiledObservation;
|
|
768
|
+
sessionState.latestObservationVerdict = mediated.verdict;
|
|
769
|
+
sessionState.observations.set(parsed.compiledObservation.observationId, parsed.compiledObservation);
|
|
770
|
+
const replayEventId = appendReplayEvent(sessionState, {
|
|
1794
771
|
kind: "artifact",
|
|
1795
772
|
actor: "sdk",
|
|
1796
773
|
payload: {
|
|
1797
|
-
route:
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
774
|
+
route: requestUrl,
|
|
775
|
+
observationId: parsed.compiledObservation.observationId,
|
|
776
|
+
decision: mediated.verdict.decision,
|
|
777
|
+
surfaceType: capture.surfaceType,
|
|
778
|
+
providerId: "providerId" in capture ? capture.providerId ?? null : null,
|
|
779
|
+
extractorIds: "extractionAttestation" in capture && capture.extractionAttestation
|
|
780
|
+
? [capture.extractionAttestation.extractorId]
|
|
781
|
+
: []
|
|
1801
782
|
}
|
|
1802
783
|
});
|
|
1803
784
|
writeJson(response, 200, {
|
|
1804
|
-
compiledObservation,
|
|
1805
|
-
plannerView,
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
replayEventId: replayEvent.eventId
|
|
785
|
+
compiledObservation: parsed.compiledObservation,
|
|
786
|
+
plannerView: mediated.plannerView,
|
|
787
|
+
artifactRef,
|
|
788
|
+
mismatchSignals: artifactRef.mismatchSignals,
|
|
789
|
+
artifactVerdict: mediated.verdict,
|
|
790
|
+
replayEventId: replayEventId ?? randomUUID()
|
|
1811
791
|
});
|
|
1812
792
|
return;
|
|
1813
793
|
}
|
|
1814
|
-
if (
|
|
794
|
+
if (requestUrl === "/v6/artifact/extract") {
|
|
1815
795
|
const payload = await readJson(request);
|
|
1816
796
|
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
if (sessionState && result.record) {
|
|
1821
|
-
sessionState.memoryRecords.set(result.record.recordId, result.record);
|
|
1822
|
-
sessionState.memorySourceClassesV5.set(result.record.recordId, payload.sourceClass);
|
|
1823
|
-
if (result.record.tier === "candidate_durable") {
|
|
1824
|
-
promotionCapability = mintMemoryPromotionCapabilityV5(sessionState.session, {
|
|
1825
|
-
recordId: result.record.recordId,
|
|
1826
|
-
sourceDigest: result.record.sourceDigest,
|
|
1827
|
-
sourceObservationId: result.record.sourceObservationId,
|
|
1828
|
-
key: result.record.key,
|
|
1829
|
-
valueDigest: result.record.sourceDigest ?? hashValue(result.record.value)
|
|
1830
|
-
});
|
|
1831
|
-
sessionState.capabilitiesV5.set(promotionCapability.capabilityId, promotionCapability);
|
|
1832
|
-
promotionTicket = {
|
|
1833
|
-
ticketId: promotionCapability.capabilityId,
|
|
1834
|
-
ticketDigest: promotionCapability.capabilityDigest,
|
|
1835
|
-
semanticDigest: promotionCapability.semanticDigest,
|
|
1836
|
-
recordId: result.record.recordId,
|
|
1837
|
-
sourceClass: payload.sourceClass,
|
|
1838
|
-
expiresAt: promotionCapability.expiresAt
|
|
1839
|
-
};
|
|
1840
|
-
}
|
|
797
|
+
if (!sessionState) {
|
|
798
|
+
writeJson(response, 404, { error: "unknown_session" });
|
|
799
|
+
return;
|
|
1841
800
|
}
|
|
1842
|
-
|
|
1843
|
-
|
|
801
|
+
const result = extractAttachmentGraphV6({
|
|
802
|
+
...payload.capture,
|
|
803
|
+
sessionId: payload.sessionId,
|
|
804
|
+
taskId: sessionState.session.taskId
|
|
805
|
+
}, runtime);
|
|
806
|
+
const replayEventId = appendReplayEvent(sessionState, {
|
|
807
|
+
kind: "artifact",
|
|
1844
808
|
actor: "sdk",
|
|
1845
809
|
payload: {
|
|
1846
|
-
route:
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
810
|
+
route: requestUrl,
|
|
811
|
+
rootNodeCount: result.artifactGraph.rootNodeIds.length,
|
|
812
|
+
blockedChildren: result.blockedChildren.length,
|
|
813
|
+
unsupportedChildren: result.unsupportedChildren.length,
|
|
814
|
+
extractorIds: result.extractionAttestations.map((entry) => entry.extractorId),
|
|
815
|
+
decision: result.artifactVerdict.decision
|
|
1850
816
|
}
|
|
1851
817
|
});
|
|
1852
818
|
writeJson(response, 200, {
|
|
1853
819
|
...result,
|
|
1854
|
-
|
|
1855
|
-
record: result.record
|
|
1856
|
-
? {
|
|
1857
|
-
...result.record,
|
|
1858
|
-
sourceClass: payload.sourceClass
|
|
1859
|
-
}
|
|
1860
|
-
: undefined,
|
|
1861
|
-
promotionCapability: promotionCapability
|
|
1862
|
-
? {
|
|
1863
|
-
capabilityId: promotionCapability.capabilityId,
|
|
1864
|
-
capabilityDigest: promotionCapability.capabilityDigest,
|
|
1865
|
-
semanticDigest: promotionCapability.semanticDigest,
|
|
1866
|
-
title: promotionCapability.title,
|
|
1867
|
-
kind: promotionCapability.kind,
|
|
1868
|
-
expiresAt: promotionCapability.expiresAt
|
|
1869
|
-
}
|
|
1870
|
-
: undefined,
|
|
1871
|
-
promotionTicket
|
|
820
|
+
replayEventId: replayEventId ?? randomUUID()
|
|
1872
821
|
});
|
|
1873
822
|
return;
|
|
1874
823
|
}
|
|
1875
|
-
if (
|
|
824
|
+
if (requestUrl === "/v6/memory/stage") {
|
|
1876
825
|
const payload = await readJson(request);
|
|
1877
826
|
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
1878
|
-
const result =
|
|
1879
|
-
let
|
|
827
|
+
const result = stageMemoryRecordV6(payload, sessionState?.session);
|
|
828
|
+
let promotionAuthority;
|
|
1880
829
|
if (sessionState && result.record) {
|
|
1881
830
|
sessionState.memoryRecords.set(result.record.recordId, result.record);
|
|
1882
|
-
sessionState.
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
sessionState.capabilitiesV5.set(promotionCapability.capabilityId, promotionCapability);
|
|
1892
|
-
}
|
|
831
|
+
sessionState.memorySourceClasses.set(result.record.recordId, payload.sourceClass);
|
|
832
|
+
promotionAuthority = mintMemoryPromotionCapabilityV6(sessionState.session, {
|
|
833
|
+
recordId: result.record.recordId,
|
|
834
|
+
sourceDigest: result.record.sourceDigest,
|
|
835
|
+
sourceObservationId: result.record.sourceObservationId,
|
|
836
|
+
key: result.record.key,
|
|
837
|
+
valueDigest: result.record.sourceDigest ?? hashValue(result.record.value)
|
|
838
|
+
});
|
|
839
|
+
sessionState.authorities.set(promotionAuthority.capabilityId, promotionAuthority);
|
|
1893
840
|
}
|
|
1894
841
|
appendReplayEvent(sessionState, {
|
|
1895
842
|
kind: "memory",
|
|
1896
843
|
actor: "sdk",
|
|
1897
844
|
payload: {
|
|
1898
|
-
route:
|
|
845
|
+
route: requestUrl,
|
|
1899
846
|
recordId: result.record?.recordId ?? null,
|
|
1900
|
-
|
|
1901
|
-
verdict: result.verdict.decision
|
|
847
|
+
decision: result.verdict.decision
|
|
1902
848
|
}
|
|
1903
849
|
});
|
|
1904
850
|
writeJson(response, 200, {
|
|
1905
851
|
...result,
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
? {
|
|
1909
|
-
capabilityId: promotionCapability.capabilityId,
|
|
1910
|
-
capabilityDigest: promotionCapability.capabilityDigest,
|
|
1911
|
-
semanticDigest: promotionCapability.semanticDigest,
|
|
1912
|
-
title: promotionCapability.title,
|
|
1913
|
-
kind: promotionCapability.kind,
|
|
1914
|
-
expiresAt: promotionCapability.expiresAt
|
|
1915
|
-
}
|
|
852
|
+
promotionAuthority: promotionAuthority
|
|
853
|
+
? authorityCandidateFromDescriptor(promotionAuthority)
|
|
1916
854
|
: undefined,
|
|
1917
|
-
promotionTicket:
|
|
855
|
+
promotionTicket: promotionAuthority
|
|
1918
856
|
? {
|
|
1919
|
-
ticketId:
|
|
1920
|
-
ticketDigest:
|
|
1921
|
-
semanticDigest:
|
|
857
|
+
ticketId: promotionAuthority.capabilityId,
|
|
858
|
+
ticketDigest: promotionAuthority.capabilityDigest,
|
|
859
|
+
semanticDigest: promotionAuthority.semanticDigest,
|
|
1922
860
|
recordId: result.record?.recordId,
|
|
1923
|
-
sourceClass:
|
|
1924
|
-
expiresAt:
|
|
861
|
+
sourceClass: payload.sourceClass,
|
|
862
|
+
expiresAt: promotionAuthority.expiresAt
|
|
1925
863
|
}
|
|
1926
864
|
: undefined
|
|
1927
865
|
});
|
|
1928
866
|
return;
|
|
1929
867
|
}
|
|
1930
|
-
if (
|
|
868
|
+
if (requestUrl === "/v6/memory/promote") {
|
|
1931
869
|
const payload = await readJson(request);
|
|
1932
|
-
const
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
};
|
|
1937
|
-
const sessionState = findSessionState(sessions, normalizedPayload.sessionId);
|
|
1938
|
-
const record = sessionState?.memoryRecords.get(normalizedPayload.recordId);
|
|
1939
|
-
const capability = sessionState?.capabilitiesV5.get(normalizedPayload.ticketId);
|
|
1940
|
-
const approvalEnvelope = sessionState?.approvalEnvelopesV5.get(normalizedPayload.approvalId);
|
|
870
|
+
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
871
|
+
const record = sessionState?.memoryRecords.get(payload.recordId);
|
|
872
|
+
const authority = sessionState?.authorities.get(payload.ticketId);
|
|
873
|
+
const approvalEnvelope = sessionState?.approvalEnvelopes.get(payload.approvalId);
|
|
1941
874
|
const snapshotState = sessionState && record ? buildMemorySnapshotState(sessionState, record) : undefined;
|
|
1942
|
-
const result =
|
|
875
|
+
const result = promoteMemoryRecordV6(payload, sessionState?.session, record, authority, approvalEnvelope, {
|
|
1943
876
|
sourceClass: record
|
|
1944
|
-
?
|
|
877
|
+
? sessionState?.memorySourceClasses.get(record.recordId)
|
|
1945
878
|
: undefined,
|
|
1946
879
|
priorTrustedRecord: snapshotState?.snapshotRecord
|
|
1947
880
|
});
|
|
@@ -1953,11 +886,11 @@ export async function createSafeBrowseServer(options = {}) {
|
|
|
1953
886
|
});
|
|
1954
887
|
}
|
|
1955
888
|
sessionState.memoryRecords.set(result.promotedRecord.recordId, result.promotedRecord);
|
|
1956
|
-
if (
|
|
1957
|
-
|
|
889
|
+
if (authority) {
|
|
890
|
+
consumeAuthority(sessionState, authority);
|
|
1958
891
|
}
|
|
1959
892
|
if (approvalEnvelope) {
|
|
1960
|
-
sessionState.
|
|
893
|
+
sessionState.approvalEnvelopes.set(payload.approvalId, {
|
|
1961
894
|
...approvalEnvelope,
|
|
1962
895
|
consumedAt: new Date().toISOString()
|
|
1963
896
|
});
|
|
@@ -1971,26 +904,25 @@ export async function createSafeBrowseServer(options = {}) {
|
|
|
1971
904
|
kind: "memory",
|
|
1972
905
|
actor: "sdk",
|
|
1973
906
|
payload: {
|
|
1974
|
-
route:
|
|
1975
|
-
recordId:
|
|
1976
|
-
|
|
907
|
+
route: requestUrl,
|
|
908
|
+
recordId: payload.recordId,
|
|
909
|
+
decision: result.verdict.decision
|
|
1977
910
|
}
|
|
1978
911
|
});
|
|
1979
912
|
writeJson(response, 200, {
|
|
1980
913
|
...result,
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
? sessionState.approvalEnvelopesV5.get(normalizedPayload.approvalId)
|
|
914
|
+
approvalEnvelope: payload.approvalId && sessionState
|
|
915
|
+
? sessionState.approvalEnvelopes.get(payload.approvalId)
|
|
1984
916
|
: approvalEnvelope
|
|
1985
917
|
});
|
|
1986
918
|
return;
|
|
1987
919
|
}
|
|
1988
|
-
if (
|
|
920
|
+
if (requestUrl === "/v6/memory/rollback") {
|
|
1989
921
|
const payload = await readJson(request);
|
|
1990
922
|
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
1991
923
|
const record = sessionState?.memoryRecords.get(payload.recordId);
|
|
1992
924
|
const snapshotState = sessionState?.memorySnapshots.get(payload.snapshotId);
|
|
1993
|
-
const result =
|
|
925
|
+
const result = rollbackMemoryRecordV6(payload, sessionState?.session, record, {
|
|
1994
926
|
snapshotRecord: snapshotState?.snapshotRecord,
|
|
1995
927
|
baselineAbsent: snapshotState?.baselineAbsent
|
|
1996
928
|
});
|
|
@@ -2006,15 +938,14 @@ export async function createSafeBrowseServer(options = {}) {
|
|
|
2006
938
|
kind: "memory",
|
|
2007
939
|
actor: "sdk",
|
|
2008
940
|
payload: {
|
|
2009
|
-
route:
|
|
941
|
+
route: requestUrl,
|
|
2010
942
|
recordId: payload.recordId,
|
|
2011
943
|
snapshotId: payload.snapshotId,
|
|
2012
|
-
|
|
944
|
+
decision: result.verdict.decision
|
|
2013
945
|
}
|
|
2014
946
|
});
|
|
2015
947
|
writeJson(response, 200, {
|
|
2016
948
|
...result,
|
|
2017
|
-
verdict: relabelClaimProfile(result.verdict, "secure_v5"),
|
|
2018
949
|
rollbackEvent: result.verdict.decision === "ALLOW"
|
|
2019
950
|
? {
|
|
2020
951
|
recordId: payload.recordId,
|
|
@@ -2025,7 +956,7 @@ export async function createSafeBrowseServer(options = {}) {
|
|
|
2025
956
|
});
|
|
2026
957
|
return;
|
|
2027
958
|
}
|
|
2028
|
-
if (
|
|
959
|
+
if (requestUrl === "/v6/replay/bundle") {
|
|
2029
960
|
const payload = await readJson(request);
|
|
2030
961
|
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
2031
962
|
if (!sessionState) {
|
|
@@ -2035,249 +966,6 @@ export async function createSafeBrowseServer(options = {}) {
|
|
|
2035
966
|
writeJson(response, 200, buildReplayBundle(sessionState.replayEvents, runtime));
|
|
2036
967
|
return;
|
|
2037
968
|
}
|
|
2038
|
-
if (request.url === "/v4/session/start") {
|
|
2039
|
-
const payload = await readJson(request);
|
|
2040
|
-
const sessionState = createSessionState(payload, runtime);
|
|
2041
|
-
sessions.set(sessionState.session.sessionId, sessionState);
|
|
2042
|
-
writeJson(response, 200, {
|
|
2043
|
-
session: sessionState.session
|
|
2044
|
-
});
|
|
2045
|
-
return;
|
|
2046
|
-
}
|
|
2047
|
-
if (request.url === "/v4/observe") {
|
|
2048
|
-
const payload = await readJson(request);
|
|
2049
|
-
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
2050
|
-
if (!sessionState) {
|
|
2051
|
-
writeJson(response, 404, { error: "unknown_session" });
|
|
2052
|
-
return;
|
|
2053
|
-
}
|
|
2054
|
-
const capture = {
|
|
2055
|
-
...payload.capture,
|
|
2056
|
-
sessionId: payload.sessionId,
|
|
2057
|
-
taskId: sessionState.session.taskId
|
|
2058
|
-
};
|
|
2059
|
-
const observation = await parserIsolationService.compileObservation({
|
|
2060
|
-
capture,
|
|
2061
|
-
workflowHash: sessionState.session.workflowHash,
|
|
2062
|
-
compilerVersion: "v4"
|
|
2063
|
-
});
|
|
2064
|
-
const failClosedObservation = applyV4FailClosedMediation(observation.compiledObservation, observation.plannerInput, "observe");
|
|
2065
|
-
let capabilities = mintCapabilitiesForObservation(sessionState.session, observation.compiledObservation, {
|
|
2066
|
-
sourceObservationId: observation.compiledObservation.observationId
|
|
2067
|
-
});
|
|
2068
|
-
let plannerInput = failClosedObservation.plannerInput;
|
|
2069
|
-
if (shouldReduceAuthority(sessionState, observation.compiledObservation)) {
|
|
2070
|
-
plannerInput = applyAuthorityReduction(sessionState, observation.compiledObservation, plannerInput);
|
|
2071
|
-
capabilities = [];
|
|
2072
|
-
}
|
|
2073
|
-
sessionState.capabilities.clear();
|
|
2074
|
-
for (const capability of capabilities) {
|
|
2075
|
-
sessionState.capabilities.set(capability.capabilityId, capability);
|
|
2076
|
-
}
|
|
2077
|
-
sessionState.latestObservation = observation.compiledObservation;
|
|
2078
|
-
sessionState.observations.set(observation.compiledObservation.observationId, observation.compiledObservation);
|
|
2079
|
-
writeJson(response, 200, {
|
|
2080
|
-
compiledObservation: observation.compiledObservation,
|
|
2081
|
-
observationVerdict: failClosedObservation.verdict,
|
|
2082
|
-
plannerInput: attachCapabilitiesToPlannerInput(plannerInput, capabilities)
|
|
2083
|
-
});
|
|
2084
|
-
return;
|
|
2085
|
-
}
|
|
2086
|
-
if (request.url === "/v4/action/evaluate") {
|
|
2087
|
-
const payload = await readJson(request);
|
|
2088
|
-
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
2089
|
-
const capability = sessionState?.capabilities.get(payload.capabilityId);
|
|
2090
|
-
const verdict = evaluateCapabilityUse(payload, sessionState?.session, capability, {
|
|
2091
|
-
alreadyUsed: sessionState?.usedCapabilities.has(payload.capabilityId) ?? false
|
|
2092
|
-
});
|
|
2093
|
-
if (verdict.decision === "ALLOW" && sessionState && capability) {
|
|
2094
|
-
sessionState.capabilities.delete(payload.capabilityId);
|
|
2095
|
-
sessionState.usedCapabilities.add(payload.capabilityId);
|
|
2096
|
-
sessionState.session = {
|
|
2097
|
-
...sessionState.session,
|
|
2098
|
-
currentStep: sessionState.session.currentStep + 1
|
|
2099
|
-
};
|
|
2100
|
-
}
|
|
2101
|
-
writeJson(response, 200, {
|
|
2102
|
-
verdict,
|
|
2103
|
-
executionPlan: verdict.decision === "ALLOW" && capability
|
|
2104
|
-
? {
|
|
2105
|
-
verb: capability.kind,
|
|
2106
|
-
targetUrl: capability.targetUrl,
|
|
2107
|
-
targetOrigin: capability.targetOrigin,
|
|
2108
|
-
selector: capability.selector,
|
|
2109
|
-
derivedSinkClass: capability.derivedSinkClass,
|
|
2110
|
-
derivedSensitiveSink: capability.derivedSensitiveSink
|
|
2111
|
-
}
|
|
2112
|
-
: undefined
|
|
2113
|
-
});
|
|
2114
|
-
return;
|
|
2115
|
-
}
|
|
2116
|
-
if (request.url === "/v4/approval/grant") {
|
|
2117
|
-
const payload = await readJson(request);
|
|
2118
|
-
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
2119
|
-
if (!sessionState) {
|
|
2120
|
-
writeJson(response, 404, { error: "unknown_session" });
|
|
2121
|
-
return;
|
|
2122
|
-
}
|
|
2123
|
-
const unknownCapability = (payload.capabilityIds ?? []).find((capabilityId) => !sessionState.capabilities.has(capabilityId));
|
|
2124
|
-
if (unknownCapability) {
|
|
2125
|
-
writeJson(response, 400, {
|
|
2126
|
-
error: "unknown_capability",
|
|
2127
|
-
capabilityId: unknownCapability
|
|
2128
|
-
});
|
|
2129
|
-
return;
|
|
2130
|
-
}
|
|
2131
|
-
if (payload.sinkClass === "connector_oauth" &&
|
|
2132
|
-
!(payload.capabilityIds ?? []).length) {
|
|
2133
|
-
writeJson(response, 400, {
|
|
2134
|
-
error: "capability_ids_required",
|
|
2135
|
-
sinkClass: payload.sinkClass
|
|
2136
|
-
});
|
|
2137
|
-
return;
|
|
2138
|
-
}
|
|
2139
|
-
const approvalGrant = issueApprovalGrant(payload, sessionState);
|
|
2140
|
-
sessionState.approvalGrants.set(approvalGrant.approvalGrantId, approvalGrant);
|
|
2141
|
-
writeJson(response, 200, {
|
|
2142
|
-
approvalGrant
|
|
2143
|
-
});
|
|
2144
|
-
return;
|
|
2145
|
-
}
|
|
2146
|
-
if (request.url === "/v4/tool/prepare") {
|
|
2147
|
-
const payload = await readJson(request);
|
|
2148
|
-
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
2149
|
-
const approvalGrant = sessionState?.approvalGrants.get(payload.approvalGrantId);
|
|
2150
|
-
const prepared = prepareToolOnboardingV4({
|
|
2151
|
-
...payload.request,
|
|
2152
|
-
approvalGrantId: payload.approvalGrantId
|
|
2153
|
-
}, sessionState?.session, approvalGrant, runtime);
|
|
2154
|
-
const onboardingSession = prepared.verdict.decision === "ALLOW" && payload.request.authType === "oauth" && approvalGrant
|
|
2155
|
-
? createOnboardingSession(payload.request, runtime, approvalGrant, prepared.verifiedRegistryEntry)
|
|
2156
|
-
: undefined;
|
|
2157
|
-
if (sessionState && onboardingSession) {
|
|
2158
|
-
sessionState.onboardingSessions.set(onboardingSession.sessionId, onboardingSession);
|
|
2159
|
-
}
|
|
2160
|
-
writeJson(response, 200, {
|
|
2161
|
-
verdict: prepared.verdict,
|
|
2162
|
-
approvalVerdict: prepared.approvalVerdict,
|
|
2163
|
-
verifiedRegistryEntry: prepared.verifiedRegistryEntry,
|
|
2164
|
-
workflowBinding: prepared.workflowBinding,
|
|
2165
|
-
onboardingSession
|
|
2166
|
-
});
|
|
2167
|
-
return;
|
|
2168
|
-
}
|
|
2169
|
-
if (request.url === "/v4/tool/callback/verify") {
|
|
2170
|
-
const payload = await readJson(request);
|
|
2171
|
-
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
2172
|
-
const approvalGrant = sessionState?.approvalGrants.get(payload.approvalGrantId);
|
|
2173
|
-
const session = sessionState?.onboardingSessions.get(payload.request.sessionId);
|
|
2174
|
-
const result = verifyToolCallbackV4(payload.request, sessionState?.session, session, approvalGrant, runtime);
|
|
2175
|
-
if (sessionState && session) {
|
|
2176
|
-
sessionState.onboardingSessions.set(payload.request.sessionId, {
|
|
2177
|
-
...session,
|
|
2178
|
-
status: result.verdict.decision === "ALLOW" ? "used" : session.status
|
|
2179
|
-
});
|
|
2180
|
-
}
|
|
2181
|
-
writeJson(response, 200, result);
|
|
2182
|
-
return;
|
|
2183
|
-
}
|
|
2184
|
-
if (request.url === "/v4/artifact/ingest") {
|
|
2185
|
-
const payload = await readJson(request);
|
|
2186
|
-
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
2187
|
-
if (!sessionState) {
|
|
2188
|
-
writeJson(response, 404, { error: "unknown_session" });
|
|
2189
|
-
return;
|
|
2190
|
-
}
|
|
2191
|
-
const capture = {
|
|
2192
|
-
...payload.capture,
|
|
2193
|
-
sessionId: payload.sessionId,
|
|
2194
|
-
taskId: sessionState.session.taskId
|
|
2195
|
-
};
|
|
2196
|
-
const observation = await parserIsolationService.compileObservation({
|
|
2197
|
-
capture,
|
|
2198
|
-
workflowHash: sessionState.session.workflowHash,
|
|
2199
|
-
compilerVersion: "v4"
|
|
2200
|
-
});
|
|
2201
|
-
const failClosedArtifact = applyV4FailClosedMediation(observation.compiledObservation, observation.plannerInput, "artifact");
|
|
2202
|
-
const legacyArtifact = buildLegacyObservationCapture(capture);
|
|
2203
|
-
const artifactResult = legacyArtifact !== undefined ? brokerArtifact(legacyArtifact, runtime) : undefined;
|
|
2204
|
-
sessionState.latestObservation = observation.compiledObservation;
|
|
2205
|
-
sessionState.observations.set(observation.compiledObservation.observationId, observation.compiledObservation);
|
|
2206
|
-
const effectiveArtifactVerdict = failClosedArtifact.failClosed
|
|
2207
|
-
? failClosedArtifact.verdict
|
|
2208
|
-
: artifactResult?.verdict;
|
|
2209
|
-
const effectiveArtifact = failClosedArtifact.failClosed && artifactResult?.artifact
|
|
2210
|
-
? {
|
|
2211
|
-
...artifactResult.artifact,
|
|
2212
|
-
toolActivationPolicy: "block",
|
|
2213
|
-
approvalRequiredForFollowOn: true
|
|
2214
|
-
}
|
|
2215
|
-
: artifactResult?.artifact;
|
|
2216
|
-
const plannerInput = shouldReduceAuthority(sessionState, observation.compiledObservation)
|
|
2217
|
-
? applyAuthorityReduction(sessionState, observation.compiledObservation, failClosedArtifact.plannerInput)
|
|
2218
|
-
: failClosedArtifact.plannerInput;
|
|
2219
|
-
writeJson(response, 200, {
|
|
2220
|
-
compiledObservation: observation.compiledObservation,
|
|
2221
|
-
plannerInput,
|
|
2222
|
-
artifactVerdict: effectiveArtifactVerdict,
|
|
2223
|
-
artifact: effectiveArtifact
|
|
2224
|
-
});
|
|
2225
|
-
return;
|
|
2226
|
-
}
|
|
2227
|
-
if (request.url === "/v4/memory/write") {
|
|
2228
|
-
const payload = await readJson(request);
|
|
2229
|
-
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
2230
|
-
const result = evaluateMemoryWriteV4(payload, sessionState?.session, runtime);
|
|
2231
|
-
if (sessionState && result.record) {
|
|
2232
|
-
sessionState.memoryRecords.set(result.record.recordId, result.record);
|
|
2233
|
-
}
|
|
2234
|
-
writeJson(response, 200, result);
|
|
2235
|
-
return;
|
|
2236
|
-
}
|
|
2237
|
-
if (request.url === "/v4/memory/promote") {
|
|
2238
|
-
const payload = await readJson(request);
|
|
2239
|
-
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
2240
|
-
const record = sessionState?.memoryRecords.get(payload.recordId);
|
|
2241
|
-
const approvalGrant = payload.approvalGrantId && sessionState
|
|
2242
|
-
? sessionState.approvalGrants.get(payload.approvalGrantId)
|
|
2243
|
-
: undefined;
|
|
2244
|
-
const result = promoteMemoryRecordV4(payload, sessionState?.session, record, approvalGrant);
|
|
2245
|
-
if (sessionState && result.promotedRecord) {
|
|
2246
|
-
if (record && result.promotedRecord.snapshotId) {
|
|
2247
|
-
sessionState.memorySnapshots.set(result.promotedRecord.snapshotId, {
|
|
2248
|
-
...record,
|
|
2249
|
-
tier: "trusted_durable",
|
|
2250
|
-
summaryOnly: false,
|
|
2251
|
-
snapshotId: result.promotedRecord.snapshotId,
|
|
2252
|
-
rollbackPointId: result.promotedRecord.rollbackPointId
|
|
2253
|
-
});
|
|
2254
|
-
}
|
|
2255
|
-
sessionState.memoryRecords.set(result.promotedRecord.recordId, result.promotedRecord);
|
|
2256
|
-
}
|
|
2257
|
-
writeJson(response, 200, result);
|
|
2258
|
-
return;
|
|
2259
|
-
}
|
|
2260
|
-
if (request.url === "/v4/memory/rollback") {
|
|
2261
|
-
const payload = await readJson(request);
|
|
2262
|
-
const sessionState = findSessionState(sessions, payload.sessionId);
|
|
2263
|
-
const record = sessionState?.memoryRecords.get(payload.recordId);
|
|
2264
|
-
const snapshotRecord = sessionState?.memorySnapshots.get(payload.snapshotId);
|
|
2265
|
-
const result = rollbackMemoryRecordV4(payload, sessionState?.session, record, snapshotRecord);
|
|
2266
|
-
if (sessionState && result.restoredRecord) {
|
|
2267
|
-
sessionState.memoryRecords.set(result.restoredRecord.recordId, result.restoredRecord);
|
|
2268
|
-
}
|
|
2269
|
-
writeJson(response, 200, {
|
|
2270
|
-
...result,
|
|
2271
|
-
rollbackEvent: result.verdict.decision === "ALLOW"
|
|
2272
|
-
? {
|
|
2273
|
-
recordId: payload.recordId,
|
|
2274
|
-
snapshotId: payload.snapshotId,
|
|
2275
|
-
appliedAt: new Date().toISOString()
|
|
2276
|
-
}
|
|
2277
|
-
: undefined
|
|
2278
|
-
});
|
|
2279
|
-
return;
|
|
2280
|
-
}
|
|
2281
969
|
writeJson(response, 404, { error: "not_found" });
|
|
2282
970
|
}
|
|
2283
971
|
catch (error) {
|
|
@@ -2291,7 +979,7 @@ export async function createSafeBrowseServer(options = {}) {
|
|
|
2291
979
|
server.close = ((callback) => {
|
|
2292
980
|
clearInterval(parserHealthRefreshTimer);
|
|
2293
981
|
return originalClose((error) => {
|
|
2294
|
-
void parserIsolationService.close().finally(() => {
|
|
982
|
+
void Promise.allSettled([parserIsolationService.close(), modelGuardClient.close()]).finally(() => {
|
|
2295
983
|
callback?.(error);
|
|
2296
984
|
});
|
|
2297
985
|
});
|