@opengeni/db 0.12.0 → 0.12.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-VUKRIBO5.js → chunk-CYA6BHV6.js} +150 -7
- package/dist/chunk-CYA6BHV6.js.map +1 -0
- package/dist/chunk-XRVNI2GB.js +896 -0
- package/dist/chunk-XRVNI2GB.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1166 -265
- package/dist/index.js.map +1 -1
- package/dist/provision-roles.d.ts +324 -38
- package/dist/provision-roles.js +1 -1
- package/dist/{schema-CnpD6BcX.d.ts → schema-CeXWlYcG.d.ts} +441 -12
- package/dist/schema.d.ts +1 -1
- package/dist/schema.js +1 -1
- package/drizzle/0122_codex_capacity_same_turn.sql +59 -0
- package/drizzle/0123_session_tool_policy_version.sql +14 -0
- package/drizzle/0124_session_event_duplicate_lookup.sql +4 -0
- package/drizzle/0125_document_drops_visibility.sql +29 -0
- package/drizzle/0126_document_access_constraints.sql +138 -0
- package/drizzle/0127_document_default_base_index.sql +5 -0
- package/drizzle/0128_github_installation_authority.sql +69 -0
- package/drizzle/0129_retained_process_reconciliation.sql +356 -0
- package/package.json +4 -3
- package/src/event-payload-sanitizer.ts +167 -55
- package/src/index.ts +1494 -258
- package/src/new-session-drafts.ts +127 -6
- package/src/provision-roles.ts +184 -2
- package/src/runtime-posture-cli.ts +57 -0
- package/src/runtime-posture.ts +760 -0
- package/src/schema.ts +156 -3
- package/src/session-control.ts +1 -0
- package/dist/chunk-BMFDXFPA.js +0 -155
- package/dist/chunk-BMFDXFPA.js.map +0 -1
- package/dist/chunk-VUKRIBO5.js.map +0 -1
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
boundSessionEventPayload,
|
|
3
|
+
isCredentialHeaderName,
|
|
4
|
+
isSensitiveFieldName,
|
|
5
|
+
redactSensitiveKey,
|
|
6
|
+
redactSensitiveText,
|
|
7
|
+
type SecretForRedaction,
|
|
8
|
+
} from "@opengeni/contracts";
|
|
2
9
|
|
|
3
10
|
/**
|
|
4
11
|
* Last line of defense against a session event crashing a whole turn.
|
|
@@ -18,21 +25,6 @@ import { boundSessionEventPayload } from "@opengeni/contracts";
|
|
|
18
25
|
|
|
19
26
|
const REPLACEMENT = "�";
|
|
20
27
|
const REDACTED = "[redacted]";
|
|
21
|
-
const SENSITIVE_FIELD_NAMES = new Set([
|
|
22
|
-
"authorization",
|
|
23
|
-
"headers",
|
|
24
|
-
"accesstoken",
|
|
25
|
-
"refreshtoken",
|
|
26
|
-
"idtoken",
|
|
27
|
-
"token",
|
|
28
|
-
"apikey",
|
|
29
|
-
"secret",
|
|
30
|
-
"clientsecret",
|
|
31
|
-
"credential",
|
|
32
|
-
"credentialencrypted",
|
|
33
|
-
"encryptedpkceverifier",
|
|
34
|
-
"codeverifier",
|
|
35
|
-
]);
|
|
36
28
|
|
|
37
29
|
/**
|
|
38
30
|
* Strip NUL and repair invalid/lone UTF-16 surrogates in a single string.
|
|
@@ -94,6 +86,8 @@ export type SanitizeEventPayloadOptions = {
|
|
|
94
86
|
* this from a producer-controlled payload field.
|
|
95
87
|
*/
|
|
96
88
|
fullEvidence?: unknown;
|
|
89
|
+
/** Exact runtime credential provenance to remove from keys and values. */
|
|
90
|
+
knownSecrets?: readonly SecretForRedaction[];
|
|
97
91
|
};
|
|
98
92
|
|
|
99
93
|
export function sanitizeEventPayload<T>(payload: T, options: SanitizeEventPayloadOptions = {}): T {
|
|
@@ -106,6 +100,7 @@ export function sanitizeEventPayload<T>(payload: T, options: SanitizeEventPayloa
|
|
|
106
100
|
});
|
|
107
101
|
return sanitizeEventPayloadDeep(
|
|
108
102
|
bounded === payload ? removeProducerTruncationMetadata(bounded) : bounded,
|
|
103
|
+
options.knownSecrets ?? [],
|
|
109
104
|
);
|
|
110
105
|
}
|
|
111
106
|
|
|
@@ -126,46 +121,60 @@ function removeProducerTruncationMetadata<T>(payload: T): T {
|
|
|
126
121
|
return cleaned as T;
|
|
127
122
|
}
|
|
128
123
|
|
|
129
|
-
function sanitizeEventPayloadDeep<T>(
|
|
124
|
+
function sanitizeEventPayloadDeep<T>(
|
|
125
|
+
payload: T,
|
|
126
|
+
knownSecrets: readonly SecretForRedaction[] = [],
|
|
127
|
+
): T {
|
|
130
128
|
if (typeof payload === "string") {
|
|
131
|
-
return sanitizeEventString(payload) as unknown as T;
|
|
129
|
+
return sanitizeEventString(redactSensitiveText(payload, knownSecrets)) as unknown as T;
|
|
132
130
|
}
|
|
133
131
|
if (Array.isArray(payload)) {
|
|
134
|
-
return payload.map((item) => sanitizeEventPayloadDeep(item)) as unknown as T;
|
|
132
|
+
return payload.map((item) => sanitizeEventPayloadDeep(item, knownSecrets)) as unknown as T;
|
|
135
133
|
}
|
|
136
134
|
if (payload instanceof Date) {
|
|
137
135
|
return safeDateIso(payload) as unknown as T;
|
|
138
136
|
}
|
|
139
137
|
if (payload && typeof payload === "object") {
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
138
|
+
const usedKeys = new Set<string>();
|
|
139
|
+
const entries = Object.entries(payload as Record<string, unknown>).map(([key, value]) => {
|
|
140
|
+
const safeKey = nextUniqueKey(
|
|
141
|
+
sanitizeEventString(redactSensitiveKey(key, knownSecrets)),
|
|
142
|
+
usedKeys,
|
|
143
|
+
);
|
|
144
|
+
return [safeKey, sanitizeSensitiveEventField(key, value, knownSecrets)] as const;
|
|
145
|
+
});
|
|
144
146
|
return Object.fromEntries(entries) as unknown as T;
|
|
145
147
|
}
|
|
146
148
|
return payload;
|
|
147
149
|
}
|
|
148
150
|
|
|
149
151
|
/**
|
|
150
|
-
* Make model-facing conversation data safe for Postgres
|
|
152
|
+
* Make model-facing conversation data safe for Postgres and secret-redacted.
|
|
151
153
|
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
* performs only the database-safety repair (NUL removal and UTF-16 repair).
|
|
154
|
+
* Conversation history is the replay source for the model, so this boundary
|
|
155
|
+
* retains protocol structure and non-sensitive diagnostics while removing
|
|
156
|
+
* credential-bearing fields and text before durable storage. It also performs
|
|
157
|
+
* the database-safety repair (NUL removal and UTF-16 repair).
|
|
157
158
|
*/
|
|
158
|
-
export function sanitizeModelPayload<T>(
|
|
159
|
-
|
|
159
|
+
export function sanitizeModelPayload<T>(
|
|
160
|
+
payload: T,
|
|
161
|
+
knownSecrets: readonly SecretForRedaction[] = [],
|
|
162
|
+
): T {
|
|
163
|
+
return sanitizeModelPayloadDeep(payload, new WeakSet<object>(), 0, knownSecrets);
|
|
160
164
|
}
|
|
161
165
|
|
|
162
166
|
const MODEL_PAYLOAD_SANITIZE_MAX_DEPTH = 64;
|
|
163
167
|
const MODEL_PAYLOAD_CYCLE_MARKER = "[OpenGeni omitted cyclic model payload]";
|
|
164
168
|
const MODEL_PAYLOAD_DEPTH_MARKER = "[OpenGeni omitted model payload beyond database-safety depth]";
|
|
165
169
|
|
|
166
|
-
function sanitizeModelPayloadDeep<T>(
|
|
170
|
+
function sanitizeModelPayloadDeep<T>(
|
|
171
|
+
payload: T,
|
|
172
|
+
seen: WeakSet<object>,
|
|
173
|
+
depth: number,
|
|
174
|
+
knownSecrets: readonly SecretForRedaction[] = [],
|
|
175
|
+
): T {
|
|
167
176
|
if (typeof payload === "string") {
|
|
168
|
-
return sanitizeEventString(payload) as unknown as T;
|
|
177
|
+
return sanitizeEventString(redactSensitiveText(payload, knownSecrets)) as unknown as T;
|
|
169
178
|
}
|
|
170
179
|
if (!payload || typeof payload !== "object") return payload;
|
|
171
180
|
if (payload instanceof Date) {
|
|
@@ -178,13 +187,25 @@ function sanitizeModelPayloadDeep<T>(payload: T, seen: WeakSet<object>, depth: n
|
|
|
178
187
|
seen.add(payload);
|
|
179
188
|
try {
|
|
180
189
|
if (Array.isArray(payload)) {
|
|
181
|
-
return payload.map((item) =>
|
|
190
|
+
return payload.map((item) =>
|
|
191
|
+
sanitizeModelPayloadDeep(item, seen, depth + 1, knownSecrets),
|
|
192
|
+
) as unknown as T;
|
|
182
193
|
}
|
|
194
|
+
const usedKeys = new Set<string>();
|
|
183
195
|
return Object.fromEntries(
|
|
184
|
-
Object.entries(payload as Record<string, unknown>).map(([key, value]) =>
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
196
|
+
Object.entries(payload as Record<string, unknown>).map(([key, value]) => {
|
|
197
|
+
const safeKey = nextUniqueKey(
|
|
198
|
+
sanitizeEventString(redactSensitiveKey(key, knownSecrets)),
|
|
199
|
+
usedKeys,
|
|
200
|
+
);
|
|
201
|
+
if (isSensitiveFieldName(key)) {
|
|
202
|
+
return [safeKey, REDACTED] as const;
|
|
203
|
+
}
|
|
204
|
+
if (normalizeFieldName(key) === "headers") {
|
|
205
|
+
return [safeKey, sanitizeModelHeaders(value, seen, depth + 1, knownSecrets)] as const;
|
|
206
|
+
}
|
|
207
|
+
return [safeKey, sanitizeModelPayloadDeep(value, seen, depth + 1, knownSecrets)] as const;
|
|
208
|
+
}),
|
|
188
209
|
) as unknown as T;
|
|
189
210
|
} finally {
|
|
190
211
|
seen.delete(payload);
|
|
@@ -200,30 +221,98 @@ function safeDateIso(value: Date): string | null {
|
|
|
200
221
|
}
|
|
201
222
|
}
|
|
202
223
|
|
|
203
|
-
function sanitizeSensitiveEventField(
|
|
224
|
+
function sanitizeSensitiveEventField(
|
|
225
|
+
key: string,
|
|
226
|
+
value: unknown,
|
|
227
|
+
knownSecrets: readonly SecretForRedaction[],
|
|
228
|
+
): unknown {
|
|
204
229
|
if (key === "mcpServers") {
|
|
205
|
-
return sanitizeSessionMcpServerList(value);
|
|
230
|
+
return sanitizeSessionMcpServerList(value, knownSecrets);
|
|
206
231
|
}
|
|
207
232
|
if (key === "mcpCredentialUpdates") {
|
|
208
|
-
return sanitizeMcpCredentialUpdateList(value);
|
|
233
|
+
return sanitizeMcpCredentialUpdateList(value, knownSecrets);
|
|
234
|
+
}
|
|
235
|
+
if (normalizeFieldName(key) === "headers") {
|
|
236
|
+
return sanitizeEventHeaders(value, knownSecrets);
|
|
209
237
|
}
|
|
210
|
-
if (
|
|
238
|
+
if (isSensitiveFieldName(key)) {
|
|
211
239
|
return REDACTED;
|
|
212
240
|
}
|
|
213
|
-
return sanitizeEventPayloadDeep(value);
|
|
241
|
+
return sanitizeEventPayloadDeep(value, knownSecrets);
|
|
214
242
|
}
|
|
215
243
|
|
|
216
|
-
function
|
|
244
|
+
function sanitizeEventHeaders(
|
|
245
|
+
value: unknown,
|
|
246
|
+
knownSecrets: readonly SecretForRedaction[],
|
|
247
|
+
): unknown {
|
|
248
|
+
if (!isPlainObject(value)) {
|
|
249
|
+
return sanitizeEventPayloadDeep(value, knownSecrets);
|
|
250
|
+
}
|
|
251
|
+
const usedKeys = new Set<string>();
|
|
252
|
+
return Object.fromEntries(
|
|
253
|
+
Object.entries(value).map(([key, child]) => {
|
|
254
|
+
const safeKey = nextUniqueKey(
|
|
255
|
+
sanitizeEventString(redactSensitiveKey(key, knownSecrets)),
|
|
256
|
+
usedKeys,
|
|
257
|
+
);
|
|
258
|
+
return [
|
|
259
|
+
safeKey,
|
|
260
|
+
isCredentialHeaderName(key) ? REDACTED : sanitizeEventPayloadDeep(child, knownSecrets),
|
|
261
|
+
];
|
|
262
|
+
}),
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function sanitizeModelHeaders(
|
|
267
|
+
value: unknown,
|
|
268
|
+
seen: WeakSet<object>,
|
|
269
|
+
depth: number,
|
|
270
|
+
knownSecrets: readonly SecretForRedaction[],
|
|
271
|
+
): unknown {
|
|
272
|
+
if (!isPlainObject(value)) {
|
|
273
|
+
return sanitizeModelPayloadDeep(value, seen, depth, knownSecrets);
|
|
274
|
+
}
|
|
275
|
+
if (depth >= MODEL_PAYLOAD_SANITIZE_MAX_DEPTH) {
|
|
276
|
+
return MODEL_PAYLOAD_DEPTH_MARKER;
|
|
277
|
+
}
|
|
278
|
+
if (seen.has(value)) return MODEL_PAYLOAD_CYCLE_MARKER;
|
|
279
|
+
seen.add(value);
|
|
280
|
+
try {
|
|
281
|
+
const usedKeys = new Set<string>();
|
|
282
|
+
return Object.fromEntries(
|
|
283
|
+
Object.entries(value).map(([key, child]) => {
|
|
284
|
+
const safeKey = nextUniqueKey(
|
|
285
|
+
sanitizeEventString(redactSensitiveKey(key, knownSecrets)),
|
|
286
|
+
usedKeys,
|
|
287
|
+
);
|
|
288
|
+
return [
|
|
289
|
+
safeKey,
|
|
290
|
+
isCredentialHeaderName(key)
|
|
291
|
+
? REDACTED
|
|
292
|
+
: sanitizeModelPayloadDeep(child, seen, depth + 1, knownSecrets),
|
|
293
|
+
];
|
|
294
|
+
}),
|
|
295
|
+
);
|
|
296
|
+
} finally {
|
|
297
|
+
seen.delete(value);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function sanitizeSessionMcpServerList(
|
|
302
|
+
value: unknown,
|
|
303
|
+
knownSecrets: readonly SecretForRedaction[],
|
|
304
|
+
): unknown {
|
|
217
305
|
if (!Array.isArray(value)) {
|
|
218
|
-
return sanitizeEventPayloadDeep(value);
|
|
306
|
+
return sanitizeEventPayloadDeep(value, knownSecrets);
|
|
219
307
|
}
|
|
220
308
|
return value.map((item) => {
|
|
221
309
|
if (!isPlainObject(item)) {
|
|
222
|
-
return sanitizeEventPayloadDeep(item);
|
|
310
|
+
return sanitizeEventPayloadDeep(item, knownSecrets);
|
|
223
311
|
}
|
|
224
312
|
const { headers, headersEncrypted, ...rest } = item;
|
|
225
|
-
const cleaned = sanitizeEventPayloadDeep(rest) as Record<string, unknown>;
|
|
226
|
-
const headerNames =
|
|
313
|
+
const cleaned = sanitizeEventPayloadDeep(rest, knownSecrets) as Record<string, unknown>;
|
|
314
|
+
const headerNames =
|
|
315
|
+
safeHeaderNames(headers, knownSecrets) ?? safeHeaderNames(headersEncrypted, knownSecrets);
|
|
227
316
|
if (headerNames) {
|
|
228
317
|
cleaned.headerNames = headerNames;
|
|
229
318
|
}
|
|
@@ -231,17 +320,21 @@ function sanitizeSessionMcpServerList(value: unknown): unknown {
|
|
|
231
320
|
});
|
|
232
321
|
}
|
|
233
322
|
|
|
234
|
-
function sanitizeMcpCredentialUpdateList(
|
|
323
|
+
function sanitizeMcpCredentialUpdateList(
|
|
324
|
+
value: unknown,
|
|
325
|
+
knownSecrets: readonly SecretForRedaction[],
|
|
326
|
+
): unknown {
|
|
235
327
|
if (!Array.isArray(value)) {
|
|
236
|
-
return sanitizeEventPayloadDeep(value);
|
|
328
|
+
return sanitizeEventPayloadDeep(value, knownSecrets);
|
|
237
329
|
}
|
|
238
330
|
return value.map((item) => {
|
|
239
331
|
if (!isPlainObject(item)) {
|
|
240
|
-
return sanitizeEventPayloadDeep(item);
|
|
332
|
+
return sanitizeEventPayloadDeep(item, knownSecrets);
|
|
241
333
|
}
|
|
242
334
|
const { headers, headersEncrypted, ...rest } = item;
|
|
243
|
-
const cleaned = sanitizeEventPayloadDeep(rest) as Record<string, unknown>;
|
|
244
|
-
const headerNames =
|
|
335
|
+
const cleaned = sanitizeEventPayloadDeep(rest, knownSecrets) as Record<string, unknown>;
|
|
336
|
+
const headerNames =
|
|
337
|
+
safeHeaderNames(headers, knownSecrets) ?? safeHeaderNames(headersEncrypted, knownSecrets);
|
|
245
338
|
if (headerNames) {
|
|
246
339
|
cleaned.headerNames = headerNames;
|
|
247
340
|
}
|
|
@@ -249,11 +342,30 @@ function sanitizeMcpCredentialUpdateList(value: unknown): unknown {
|
|
|
249
342
|
});
|
|
250
343
|
}
|
|
251
344
|
|
|
252
|
-
function safeHeaderNames(
|
|
345
|
+
function safeHeaderNames(
|
|
346
|
+
value: unknown,
|
|
347
|
+
knownSecrets: readonly SecretForRedaction[],
|
|
348
|
+
): string[] | null {
|
|
253
349
|
if (!isPlainObject(value)) {
|
|
254
350
|
return null;
|
|
255
351
|
}
|
|
256
|
-
|
|
352
|
+
const usedKeys = new Set<string>();
|
|
353
|
+
return Object.keys(value)
|
|
354
|
+
.map((key) =>
|
|
355
|
+
nextUniqueKey(sanitizeEventString(redactSensitiveKey(key, knownSecrets)), usedKeys),
|
|
356
|
+
)
|
|
357
|
+
.sort();
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function nextUniqueKey(base: string, usedKeys: Set<string>): string {
|
|
361
|
+
let candidate = base;
|
|
362
|
+
let suffix = 2;
|
|
363
|
+
while (usedKeys.has(candidate)) {
|
|
364
|
+
candidate = `${base}#${suffix}`;
|
|
365
|
+
suffix += 1;
|
|
366
|
+
}
|
|
367
|
+
usedKeys.add(candidate);
|
|
368
|
+
return candidate;
|
|
257
369
|
}
|
|
258
370
|
|
|
259
371
|
function isPlainObject(value: unknown): value is Record<string, unknown> {
|