@mcpose/audit 2.0.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +437 -28
- package/dist/chain.d.ts +55 -0
- package/dist/chain.d.ts.map +1 -0
- package/dist/chain.js +265 -0
- package/dist/chain.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware.d.ts +5 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +488 -0
- package/dist/middleware.js.map +1 -0
- package/dist/sensitivity.d.ts +12 -0
- package/dist/sensitivity.d.ts.map +1 -0
- package/dist/sensitivity.js +24 -0
- package/dist/sensitivity.js.map +1 -0
- package/dist/signingKey.d.ts +11 -0
- package/dist/signingKey.d.ts.map +1 -0
- package/dist/signingKey.js +30 -0
- package/dist/signingKey.js.map +1 -0
- package/dist/subjectKeyStore.d.ts +17 -0
- package/dist/subjectKeyStore.d.ts.map +1 -0
- package/dist/subjectKeyStore.js +37 -0
- package/dist/subjectKeyStore.js.map +1 -0
- package/dist/types.d.ts +244 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/verify.d.ts +24 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +50 -0
- package/dist/verify.js.map +1 -0
- package/package.json +43 -14
- package/src/__tests__/chain.batch.test.ts +30 -0
- package/src/__tests__/chain.test.ts +399 -0
- package/src/__tests__/erasable.test.ts +499 -0
- package/src/__tests__/merkle-batch.middleware.test.ts +96 -0
- package/src/__tests__/middleware.http.test.ts +172 -0
- package/src/__tests__/middleware.robustness.test.ts +737 -0
- package/src/__tests__/middleware.test.ts +1121 -0
- package/src/__tests__/properties.test.ts +737 -0
- package/src/__tests__/sensitivity.test.ts +72 -0
- package/src/__tests__/signingKey.test.ts +76 -0
- package/src/chain.ts +317 -0
- package/src/index.ts +31 -0
- package/src/middleware.ts +690 -0
- package/src/sensitivity.ts +33 -0
- package/src/signingKey.ts +35 -0
- package/src/subjectKeyStore.ts +38 -0
- package/src/types.ts +281 -0
- package/src/verify.ts +76 -0
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
import { createCipheriv, randomBytes, createHmac } from 'node:crypto';
|
|
2
|
+
import { canonicalJson, chainPreimageFields, computeChainHash, computeMerkleRootAndProofs, sha256hex, stableStringify, } from './chain.js';
|
|
3
|
+
import { hasToolContent, markPassThroughObserver } from 'mcpose';
|
|
4
|
+
// Domain-separation labels for subkey derivation. The version segment lets the
|
|
5
|
+
// derivation scheme rotate without colliding with chains written under an old
|
|
6
|
+
// scheme. v1 → v2: canonical-JSON preimages, signed full manifest,
|
|
7
|
+
// domain-separated Merkle, per-position event keys, AEAD-bound ciphertexts.
|
|
8
|
+
// See ADR-0003 and ADR-0004.
|
|
9
|
+
const DOMAIN_CHAIN = Buffer.from('mcpose/v2/chain');
|
|
10
|
+
const DOMAIN_ENC = Buffer.from('mcpose/v2/enc');
|
|
11
|
+
const DOMAIN_MANIFEST = 'mcpose/v2/manifest';
|
|
12
|
+
const DOMAIN_EVENT_KEY = 'mcpose/v2/eventkey\0';
|
|
13
|
+
const DOMAIN_AAD = 'mcpose/v2/aad\0';
|
|
14
|
+
// Erasable mode only (ADR-0018): keys the payload hashes so that destroying a
|
|
15
|
+
// subject key destroys confirmability along with decryptability. A new label
|
|
16
|
+
// for a new mechanism is additive within v2 — no existing label changes, and
|
|
17
|
+
// no default-mode event is derived under it.
|
|
18
|
+
const DOMAIN_HASH_KEY = 'mcpose/v2/hashkey';
|
|
19
|
+
function aesEncrypt(plaintext, key, aad) {
|
|
20
|
+
if (key.length !== 32) {
|
|
21
|
+
throw new RangeError(`aesEncrypt: expected a 32-byte key, got ${key.length}`);
|
|
22
|
+
}
|
|
23
|
+
const iv = randomBytes(12);
|
|
24
|
+
const cipher = createCipheriv('aes-256-gcm', key, iv);
|
|
25
|
+
cipher.setAAD(Buffer.from(aad));
|
|
26
|
+
const encrypted = Buffer.concat([
|
|
27
|
+
cipher.update(plaintext, 'utf8'),
|
|
28
|
+
cipher.final(),
|
|
29
|
+
]);
|
|
30
|
+
const tag = cipher.getAuthTag();
|
|
31
|
+
return Buffer.concat([iv, tag, encrypted]).toString('base64');
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Per-event AES key. Bound to session + position + event id so no two
|
|
35
|
+
* events can share a key even if a host reuses a ProxyContext (and with it
|
|
36
|
+
* a requestId) across calls.
|
|
37
|
+
*/
|
|
38
|
+
function deriveEventKey(encRoot, sessionId, position, eventId) {
|
|
39
|
+
return createHmac('sha256', encRoot)
|
|
40
|
+
.update(`${DOMAIN_EVENT_KEY}${sessionId ?? ''}\0${position}\0${eventId}`)
|
|
41
|
+
.digest();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Payload hash for one event.
|
|
45
|
+
*
|
|
46
|
+
* Default mode: plain `sha256`, unchanged and unkeyed, so a replay verifier
|
|
47
|
+
* needs no key custody. Erasable mode: `HMAC-SHA256` under a subkey of the
|
|
48
|
+
* subject key, so an adversary holding a candidate payload cannot confirm it
|
|
49
|
+
* against an erased event's hash (ADR-0018). Both hash the same
|
|
50
|
+
* `stableStringify` bytes, so only the key changes.
|
|
51
|
+
*/
|
|
52
|
+
function payloadHash(value, hashSubkey) {
|
|
53
|
+
const serialized = stableStringify(value);
|
|
54
|
+
return hashSubkey === undefined
|
|
55
|
+
? sha256hex(serialized)
|
|
56
|
+
: createHmac('sha256', hashSubkey).update(serialized).digest('hex');
|
|
57
|
+
}
|
|
58
|
+
function anonymousIdentity() {
|
|
59
|
+
return {
|
|
60
|
+
sub: 'anonymous',
|
|
61
|
+
type: 'service',
|
|
62
|
+
roles: [],
|
|
63
|
+
claims: {},
|
|
64
|
+
resolvedAt: new Date().toISOString(),
|
|
65
|
+
source: 'custom',
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function getRejectionReason(err) {
|
|
69
|
+
const data = err?.data;
|
|
70
|
+
return typeof data?.rejectionReason === 'string'
|
|
71
|
+
? data.rejectionReason
|
|
72
|
+
: undefined;
|
|
73
|
+
}
|
|
74
|
+
export function createAuditMiddleware(options) {
|
|
75
|
+
const sessions = new Map();
|
|
76
|
+
// Drained sessions awaiting signature or delivery, retried by closeSession.
|
|
77
|
+
const sealed = new Map();
|
|
78
|
+
const includeRejections = options.includeRejections ?? true;
|
|
79
|
+
const closeDrainTimeoutMs = options.closeDrainTimeoutMs ?? 30_000;
|
|
80
|
+
const reportAuditError = options.onAuditError ?? ((err) => console.error(err));
|
|
81
|
+
// A throwing reporter is outside the audited call path too (#172): it must
|
|
82
|
+
// not replace a tool result, mask an upstream error, or abort a drain.
|
|
83
|
+
const onAuditError = (err, info) => {
|
|
84
|
+
try {
|
|
85
|
+
reportAuditError(err, info);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Nothing left to report it to.
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
// Private subkeys, derived once from the signing secret THROUGH the oracle with
|
|
92
|
+
// domain separation. The provider never exposes raw key bytes, and keyId must
|
|
93
|
+
// NOT be used as key material — keyId is a public identifier, published in
|
|
94
|
+
// ReplayManifest.signedBy. sign() is HMAC-SHA256 per the SigningKeyProvider
|
|
95
|
+
// contract, hence a PRF suitable for key derivation.
|
|
96
|
+
// chainKey — keys the per-entry HMAC chain (forgery resistance)
|
|
97
|
+
// encRoot — root for per-event AES-256 keys (high-tier confidentiality)
|
|
98
|
+
// Cache the promise so concurrent first calls share one derivation; a failed
|
|
99
|
+
// derivation clears the cache so a transient provider error is retryable.
|
|
100
|
+
let subkeys;
|
|
101
|
+
const deriveSubkeys = () => {
|
|
102
|
+
subkeys ??= (async () => {
|
|
103
|
+
const [chainKey, encRoot] = await Promise.all([
|
|
104
|
+
options.signingKey.sign(DOMAIN_CHAIN),
|
|
105
|
+
options.signingKey.sign(DOMAIN_ENC),
|
|
106
|
+
]);
|
|
107
|
+
return { chainKey, encRoot };
|
|
108
|
+
})();
|
|
109
|
+
subkeys.catch(() => {
|
|
110
|
+
subkeys = undefined;
|
|
111
|
+
});
|
|
112
|
+
return subkeys;
|
|
113
|
+
};
|
|
114
|
+
// One implementation for both audited surfaces. A tool call and a prompt
|
|
115
|
+
// fetch differ only in the event's `kind`: both requests carry a name and
|
|
116
|
+
// optional arguments, and both share the session chain, so duplicating the
|
|
117
|
+
// body would duplicate every invariant below with it (ADR-0014).
|
|
118
|
+
const observe = async (kind, req, next, ctx, isInBandError) => {
|
|
119
|
+
// The proxy identity is a required covered field (ADR-0019), so a context
|
|
120
|
+
// without one cannot produce a verifiable event. That is a configuration
|
|
121
|
+
// error rather than a runtime condition: core has stamped `ctx.proxy` on
|
|
122
|
+
// every ProxyContext since 3.0.0, so reaching here without it means a host
|
|
123
|
+
// invoked this middleware with a hand-built context.
|
|
124
|
+
//
|
|
125
|
+
// Handled here, at the pre-call stage alongside subkey derivation, which is
|
|
126
|
+
// the one failure point allowed to fail the call. Throwing after the call
|
|
127
|
+
// would mask the tool result, and substituting a sentinel identity would
|
|
128
|
+
// write a trail that looks attributed and is not, which is the failure
|
|
129
|
+
// #85 and #122 exist to prevent.
|
|
130
|
+
const proxy = ctx.proxy;
|
|
131
|
+
if (proxy === undefined) {
|
|
132
|
+
throw new Error('mcpose/audit: ctx.proxy is required, because the proxy identity is a covered field of every audit event. mcpose core stamps it on every ProxyContext; a host invoking this middleware with its own context must supply it.');
|
|
133
|
+
}
|
|
134
|
+
const identity = ctx.identity ?? anonymousIdentity();
|
|
135
|
+
const sessionId = ctx.sessionId;
|
|
136
|
+
const tool = req.params.name;
|
|
137
|
+
const args = req.params.arguments ?? {};
|
|
138
|
+
if (sessionId && !sessions.has(sessionId)) {
|
|
139
|
+
// First-seen wins: the manifest records the proxy identity of the
|
|
140
|
+
// request that opened the session, and each event still records its own.
|
|
141
|
+
// No backfill branch is needed now that `proxy` is always present.
|
|
142
|
+
sessions.set(sessionId, {
|
|
143
|
+
events: [],
|
|
144
|
+
prevChainHash: '',
|
|
145
|
+
startedAt: new Date().toISOString(),
|
|
146
|
+
identity,
|
|
147
|
+
proxy,
|
|
148
|
+
inflight: new Map(),
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
// Admission (#168): registered synchronously, before the first await of
|
|
152
|
+
// this function, and captured once. From here until the `finally` below
|
|
153
|
+
// the call counts as in flight: `closeSession` drains that set before it
|
|
154
|
+
// seals, so the session cannot vanish under a call that is still deriving
|
|
155
|
+
// keys, awaiting the upstream, or persisting its event. Re-resolving the
|
|
156
|
+
// id after an await is what produced position-0 orphans with no manifest.
|
|
157
|
+
const session = sessionId ? sessions.get(sessionId) : undefined;
|
|
158
|
+
const admission = Symbol(tool);
|
|
159
|
+
session?.inflight.set(admission, { tool, requestId: ctx.requestId });
|
|
160
|
+
try {
|
|
161
|
+
// Subkey derivation runs BEFORE the upstream call: if the signing
|
|
162
|
+
// provider is unavailable the call fails fast rather than running
|
|
163
|
+
// unaudited.
|
|
164
|
+
const { chainKey, encRoot } = await deriveSubkeys();
|
|
165
|
+
// Erasable mode (ADR-0018). Fetched at the pre-call stage alongside subkey
|
|
166
|
+
// derivation, for the same reason: without the subject key there is no way
|
|
167
|
+
// to record this event, so the call fails fast rather than running
|
|
168
|
+
// unaudited. Nothing is cached across calls — `destroy` must take effect
|
|
169
|
+
// immediately, and a subject that calls again after erasure gets a fresh
|
|
170
|
+
// key from the store rather than a stale one from here. A rejected fetch
|
|
171
|
+
// therefore leaves no poisoned state and the next call retries.
|
|
172
|
+
//
|
|
173
|
+
// The subject is the RESOLVED identity's `sub`, so anonymous events all
|
|
174
|
+
// land in the `anonymousIdentity()` bucket, which is the designated
|
|
175
|
+
// single bucket the ADR calls for.
|
|
176
|
+
// Written as a ternary rather than `await options.keyStore?.…` so default
|
|
177
|
+
// mode does not even take the extra microtask tick an `await undefined`
|
|
178
|
+
// would cost it.
|
|
179
|
+
const subjectKey = options.keyStore
|
|
180
|
+
? await options.keyStore.getOrCreate(identity.sub)
|
|
181
|
+
: undefined;
|
|
182
|
+
const startedAt = new Date().toISOString();
|
|
183
|
+
const start = performance.now();
|
|
184
|
+
let result;
|
|
185
|
+
let thrown;
|
|
186
|
+
let threw = false;
|
|
187
|
+
try {
|
|
188
|
+
result = await next(req);
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
thrown = err;
|
|
192
|
+
threw = true;
|
|
193
|
+
}
|
|
194
|
+
// Post-call audit section. Two invariants:
|
|
195
|
+
// 1. Atomic append — no `await` between reading the position and
|
|
196
|
+
// pushing the event, so concurrent calls in one session cannot
|
|
197
|
+
// allocate duplicate positions (buildEvent is fully synchronous).
|
|
198
|
+
// 2. Never throws — an audit failure is reported via onAuditError and
|
|
199
|
+
// must not fail (or mask the failure of) the audited call itself.
|
|
200
|
+
try {
|
|
201
|
+
const rejectionReason = threw ? getRejectionReason(thrown) : undefined;
|
|
202
|
+
// A call can fail without throwing: an MCP tool reports failure
|
|
203
|
+
// in-band with `isError: true` (#171). The caller supplies the
|
|
204
|
+
// predicate, so only tool calls pass one and prompts never match.
|
|
205
|
+
const inBandError = !threw && isInBandError?.(result) === true;
|
|
206
|
+
const outcome = rejectionReason !== undefined
|
|
207
|
+
? 'rejected'
|
|
208
|
+
: threw || inBandError
|
|
209
|
+
? 'error'
|
|
210
|
+
: 'success';
|
|
211
|
+
if (outcome !== 'rejected' || includeRejections) {
|
|
212
|
+
let tier;
|
|
213
|
+
try {
|
|
214
|
+
tier = options.sensitivityResolver(tool, identity, args);
|
|
215
|
+
}
|
|
216
|
+
catch (err) {
|
|
217
|
+
onAuditError(err, {
|
|
218
|
+
tool,
|
|
219
|
+
requestId: ctx.requestId,
|
|
220
|
+
...(sessionId === undefined ? {} : { sessionId }),
|
|
221
|
+
});
|
|
222
|
+
tier = 'high';
|
|
223
|
+
}
|
|
224
|
+
const position = session?.events.length ?? 0;
|
|
225
|
+
const event = buildEvent({
|
|
226
|
+
ctx,
|
|
227
|
+
identity,
|
|
228
|
+
proxy,
|
|
229
|
+
kind,
|
|
230
|
+
tool,
|
|
231
|
+
args,
|
|
232
|
+
result: threw ? undefined : result,
|
|
233
|
+
startedAt,
|
|
234
|
+
endedAt: new Date().toISOString(),
|
|
235
|
+
duration_ms: Math.round(performance.now() - start),
|
|
236
|
+
outcome,
|
|
237
|
+
rejectionReason,
|
|
238
|
+
error: outcome !== 'error'
|
|
239
|
+
? undefined
|
|
240
|
+
: threw
|
|
241
|
+
? {
|
|
242
|
+
name: thrown instanceof Error ? thrown.name : 'Error',
|
|
243
|
+
message: thrown instanceof Error
|
|
244
|
+
? thrown.message
|
|
245
|
+
: String(thrown),
|
|
246
|
+
}
|
|
247
|
+
: // A fixed message: `error` is never encrypted, so copying the
|
|
248
|
+
// tool's own text here would leak a high-tier payload.
|
|
249
|
+
{
|
|
250
|
+
name: 'ToolError',
|
|
251
|
+
message: 'Tool result returned isError: true',
|
|
252
|
+
},
|
|
253
|
+
position,
|
|
254
|
+
prevChainHash: session?.prevChainHash ?? '',
|
|
255
|
+
tier,
|
|
256
|
+
chainKey,
|
|
257
|
+
encRoot,
|
|
258
|
+
subjectKey,
|
|
259
|
+
});
|
|
260
|
+
if (session) {
|
|
261
|
+
session.events.push(event);
|
|
262
|
+
session.prevChainHash = event.chainHash;
|
|
263
|
+
}
|
|
264
|
+
await options.onEvent(event);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
catch (err) {
|
|
268
|
+
onAuditError(err, {
|
|
269
|
+
tool,
|
|
270
|
+
requestId: ctx.requestId,
|
|
271
|
+
...(sessionId === undefined ? {} : { sessionId }),
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
if (threw)
|
|
275
|
+
throw thrown;
|
|
276
|
+
return result;
|
|
277
|
+
}
|
|
278
|
+
finally {
|
|
279
|
+
// Settle after the event is appended and `onEvent` has settled, so a
|
|
280
|
+
// draining close also waits for pending persistence. A pre-call failure
|
|
281
|
+
// lands here too, so a rejected key fetch never leaves the session
|
|
282
|
+
// looking busy.
|
|
283
|
+
if (session) {
|
|
284
|
+
session.inflight.delete(admission);
|
|
285
|
+
if (session.inflight.size === 0)
|
|
286
|
+
session.onDrained?.();
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
// Tool calls: wrapped so `passThroughTools` stay audited. Prompts have no
|
|
291
|
+
// pass-through concept, so the prompt middleware needs no wrapper.
|
|
292
|
+
const inner = (req, next, ctx) => observe(undefined, req, next, ctx, (result) => hasToolContent(result) && result.isError === true);
|
|
293
|
+
const middleware = markPassThroughObserver(inner);
|
|
294
|
+
const promptMiddleware = (req, next, ctx) => observe('prompt', req, next, ctx);
|
|
295
|
+
const closeSession = (sessionId) => {
|
|
296
|
+
const session = sessions.get(sessionId);
|
|
297
|
+
if (session) {
|
|
298
|
+
// A close already in progress is the close: joining it is what keeps a
|
|
299
|
+
// DELETE that races a TTL expiry from signing two manifests.
|
|
300
|
+
session.closing ??= sealSession(sessionId, session);
|
|
301
|
+
return session.closing;
|
|
302
|
+
}
|
|
303
|
+
// Sealed but undelivered: a signer or sink failed on an earlier close.
|
|
304
|
+
// Retry from where it stopped instead of reporting the session unknown.
|
|
305
|
+
const pending = sealed.get(sessionId);
|
|
306
|
+
if (pending)
|
|
307
|
+
return finalize(sessionId, pending);
|
|
308
|
+
return Promise.resolve(undefined);
|
|
309
|
+
};
|
|
310
|
+
const sealSession = async (sessionId, session) => {
|
|
311
|
+
// Drain: every admitted call appends its event before it settles, so the
|
|
312
|
+
// manifest sealed below covers them all with continuous positions. The
|
|
313
|
+
// loop re-checks because a call can be admitted while one drain waits.
|
|
314
|
+
// Bounded, because a handler or sink that never settles must not hold
|
|
315
|
+
// the session's already-recorded events unsigned forever: past the
|
|
316
|
+
// deadline the manifest seals what it has and each straggler is reported.
|
|
317
|
+
const deadline = performance.now() + closeDrainTimeoutMs;
|
|
318
|
+
while (session.inflight.size > 0) {
|
|
319
|
+
const remaining = deadline - performance.now();
|
|
320
|
+
if (remaining <= 0) {
|
|
321
|
+
for (const { tool, requestId } of session.inflight.values()) {
|
|
322
|
+
onAuditError(new Error(`mcpose/audit: closeSession(${sessionId}) sealed after ${closeDrainTimeoutMs} ms with this call still in flight; its event is not in the manifest`), { tool, requestId, sessionId });
|
|
323
|
+
}
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
await new Promise((resolve) => {
|
|
327
|
+
const timer = Number.isFinite(remaining)
|
|
328
|
+
? setTimeout(resolve, remaining)
|
|
329
|
+
: undefined;
|
|
330
|
+
session.onDrained = () => {
|
|
331
|
+
if (timer !== undefined)
|
|
332
|
+
clearTimeout(timer);
|
|
333
|
+
resolve();
|
|
334
|
+
};
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
delete session.onDrained;
|
|
338
|
+
// Sealed: the session leaves the admission map here, so no later call can
|
|
339
|
+
// append to a manifest that is already being signed. What follows can
|
|
340
|
+
// still fail, so the sealed state is kept until delivery succeeds (#169).
|
|
341
|
+
sessions.delete(sessionId);
|
|
342
|
+
if (session.events.length === 0)
|
|
343
|
+
return undefined;
|
|
344
|
+
const hashes = session.events.map((e) => e.chainHash);
|
|
345
|
+
const { merkleRoot, merkleProofs } = computeMerkleRootAndProofs(hashes);
|
|
346
|
+
// The signature covers the ENTIRE manifest (domain-separated, canonical
|
|
347
|
+
// serialization) — signing only the Merkle root would leave sessionId,
|
|
348
|
+
// identity, eventCount, and the proofs swappable after the fact.
|
|
349
|
+
const unsigned = {
|
|
350
|
+
sessionId,
|
|
351
|
+
identity: session.identity,
|
|
352
|
+
proxy: session.proxy,
|
|
353
|
+
startedAt: session.startedAt,
|
|
354
|
+
closedAt: new Date().toISOString(),
|
|
355
|
+
eventCount: session.events.length,
|
|
356
|
+
merkleRoot,
|
|
357
|
+
merkleProofs,
|
|
358
|
+
signedBy: options.signingKey.keyId,
|
|
359
|
+
};
|
|
360
|
+
const previous = sealed.get(sessionId);
|
|
361
|
+
if (previous) {
|
|
362
|
+
// The id was reused before the earlier session's manifest was delivered.
|
|
363
|
+
// The host had every chance to retry; keeping the newer one is the only
|
|
364
|
+
// choice that does not lose the session that is closing now.
|
|
365
|
+
onAuditError(new Error(`mcpose/audit: closeSession(${sessionId}) replaced an undelivered manifest for an earlier session with the same id`), { tool: '', requestId: '', sessionId });
|
|
366
|
+
}
|
|
367
|
+
const pending = { unsigned };
|
|
368
|
+
sealed.set(sessionId, pending);
|
|
369
|
+
return finalize(sessionId, pending);
|
|
370
|
+
};
|
|
371
|
+
/**
|
|
372
|
+
* Signs and delivers a sealed manifest. Each step is memoized on the sealed
|
|
373
|
+
* state, so a retry after a transient signer failure signs the same payload
|
|
374
|
+
* (same `closedAt`, so the same bytes) and a retry after a sink failure
|
|
375
|
+
* re-delivers the identical signed manifest rather than minting a second
|
|
376
|
+
* one. Concurrent retries share one attempt.
|
|
377
|
+
*/
|
|
378
|
+
const finalize = (sessionId, pending) => {
|
|
379
|
+
pending.attempt ??= (async () => {
|
|
380
|
+
try {
|
|
381
|
+
if (pending.signature === undefined) {
|
|
382
|
+
const payload = canonicalJson({
|
|
383
|
+
domain: DOMAIN_MANIFEST,
|
|
384
|
+
manifest: pending.unsigned,
|
|
385
|
+
});
|
|
386
|
+
pending.signature = (await options.signingKey.sign(Buffer.from(payload))).toString('hex');
|
|
387
|
+
}
|
|
388
|
+
const manifest = {
|
|
389
|
+
...pending.unsigned,
|
|
390
|
+
signature: pending.signature,
|
|
391
|
+
};
|
|
392
|
+
await options.onManifest?.(manifest);
|
|
393
|
+
sealed.delete(sessionId);
|
|
394
|
+
return manifest;
|
|
395
|
+
}
|
|
396
|
+
finally {
|
|
397
|
+
delete pending.attempt;
|
|
398
|
+
}
|
|
399
|
+
})();
|
|
400
|
+
return pending.attempt;
|
|
401
|
+
};
|
|
402
|
+
return { middleware, promptMiddleware, closeSession };
|
|
403
|
+
}
|
|
404
|
+
/** Rebuilds the exact signed payload for a manifest (used by verifiers). */
|
|
405
|
+
export function manifestSigningPayload(manifest) {
|
|
406
|
+
const { sessionId, identity, proxy, startedAt, closedAt, eventCount, merkleRoot, merkleProofs, signedBy, } = manifest;
|
|
407
|
+
return canonicalJson({
|
|
408
|
+
domain: DOMAIN_MANIFEST,
|
|
409
|
+
manifest: {
|
|
410
|
+
sessionId,
|
|
411
|
+
identity,
|
|
412
|
+
// Required covered field, included unconditionally (ADR-0019). Under
|
|
413
|
+
// the omission pattern a manifest with `proxy` stripped rebuilt the
|
|
414
|
+
// same payload as one that never had it, so the signature could not
|
|
415
|
+
// detect the removal.
|
|
416
|
+
proxy,
|
|
417
|
+
startedAt,
|
|
418
|
+
closedAt,
|
|
419
|
+
eventCount,
|
|
420
|
+
merkleRoot,
|
|
421
|
+
merkleProofs,
|
|
422
|
+
signedBy,
|
|
423
|
+
},
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
/** Fully synchronous — position allocation relies on that (no await between read and append). */
|
|
427
|
+
function buildEvent(p) {
|
|
428
|
+
// Fail CLOSED: only explicit low/medium get plaintext; any other tier
|
|
429
|
+
// value — including garbage from a user-supplied resolver — encrypts.
|
|
430
|
+
// Decided before the preimage is built, because the tier is a covered
|
|
431
|
+
// field and must be the one the event actually ships with (ADR-0015).
|
|
432
|
+
const sensitivityTier = p.tier === 'low' || p.tier === 'medium' ? p.tier : 'high';
|
|
433
|
+
// Erasable mode: one subkey off the subject key keys both payload hashes.
|
|
434
|
+
// Derived per event rather than held anywhere, so it dies with the subject
|
|
435
|
+
// key it came from (ADR-0018).
|
|
436
|
+
const hashSubkey = p.subjectKey === undefined
|
|
437
|
+
? undefined
|
|
438
|
+
: createHmac('sha256', p.subjectKey).update(DOMAIN_HASH_KEY).digest();
|
|
439
|
+
const withoutChainHash = {
|
|
440
|
+
id: p.ctx.requestId,
|
|
441
|
+
startedAt: p.startedAt,
|
|
442
|
+
endedAt: p.endedAt,
|
|
443
|
+
...(p.ctx.sessionId !== undefined ? { sessionId: p.ctx.sessionId } : {}),
|
|
444
|
+
...(p.ctx.delegatedFrom !== undefined
|
|
445
|
+
? { delegatedFrom: p.ctx.delegatedFrom }
|
|
446
|
+
: {}),
|
|
447
|
+
proxy: p.proxy,
|
|
448
|
+
...(p.kind !== undefined ? { kind: p.kind } : {}),
|
|
449
|
+
// Optional covered field under the ADR-0012 omission rule, exactly as
|
|
450
|
+
// `kind` is: default-mode events do not carry the key at all, so their
|
|
451
|
+
// preimage is unchanged (ADR-0018).
|
|
452
|
+
...(p.subjectKey !== undefined ? { erasable: true } : {}),
|
|
453
|
+
identity: p.identity,
|
|
454
|
+
tool: p.tool,
|
|
455
|
+
duration_ms: p.duration_ms,
|
|
456
|
+
outcome: p.outcome,
|
|
457
|
+
sensitivityTier,
|
|
458
|
+
...(p.rejectionReason !== undefined
|
|
459
|
+
? { rejectionReason: p.rejectionReason }
|
|
460
|
+
: {}),
|
|
461
|
+
...(p.error !== undefined ? { error: p.error } : {}),
|
|
462
|
+
inputHash: payloadHash(p.args, hashSubkey),
|
|
463
|
+
outputHash: payloadHash(p.result ?? null, hashSubkey),
|
|
464
|
+
replayManifestPosition: p.position,
|
|
465
|
+
};
|
|
466
|
+
const chainHash = computeChainHash(chainPreimageFields(withoutChainHash), p.prevChainHash, p.chainKey);
|
|
467
|
+
const base = { ...withoutChainHash, chainHash };
|
|
468
|
+
if (sensitivityTier !== 'high') {
|
|
469
|
+
return {
|
|
470
|
+
...base,
|
|
471
|
+
sensitivityTier,
|
|
472
|
+
inputRaw: p.args,
|
|
473
|
+
outputRaw: p.result,
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
// Erasable mode swaps the ROOT and nothing else: same label, same session,
|
|
477
|
+
// position and event-id inputs, same AES-256-GCM layout and AAD. Only the
|
|
478
|
+
// key's provenance changes, from the pure-derived `encRoot` to the stored,
|
|
479
|
+
// destroyable subject key (ADR-0018).
|
|
480
|
+
const eventKey = deriveEventKey(p.subjectKey ?? p.encRoot, p.ctx.sessionId, p.position, base.id);
|
|
481
|
+
return {
|
|
482
|
+
...base,
|
|
483
|
+
sensitivityTier: 'high',
|
|
484
|
+
inputEncrypted: aesEncrypt(stableStringify(p.args), eventKey, `${DOMAIN_AAD}${base.id}\0input`),
|
|
485
|
+
outputEncrypted: aesEncrypt(stableStringify(p.result ?? null), eventKey, `${DOMAIN_AAD}${base.id}\0output`),
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SensitivityTier, SensitivityResolverFn, SensitivityOverrideFn } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Builds a resolver from a static tool→tier map, with an optional dynamic
|
|
4
|
+
* override that receives the map's resolution as its fourth argument.
|
|
5
|
+
*
|
|
6
|
+
* Unknown tools resolve to `'high'` (encrypt by default). The lookup uses
|
|
7
|
+
* `Object.hasOwn`, so prototype-inherited keys (a tool named `toString` or
|
|
8
|
+
* `constructor` — tool names are attacker-controlled) cannot bypass that
|
|
9
|
+
* default.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createSensitivityResolver(map: Record<string, SensitivityTier>, override?: SensitivityOverrideFn): SensitivityResolverFn;
|
|
12
|
+
//# sourceMappingURL=sensitivity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sensitivity.d.ts","sourceRoot":"","sources":["../src/sensitivity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAUpB;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,EACpC,QAAQ,CAAC,EAAE,qBAAqB,GAC/B,qBAAqB,CAMvB"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const TIERS = new Set(['low', 'medium', 'high']);
|
|
2
|
+
function validTier(value) {
|
|
3
|
+
// Fail CLOSED: anything that is not a known tier resolves to 'high'
|
|
4
|
+
// (encrypt). Guards against typos in the map and misbehaving overrides.
|
|
5
|
+
return TIERS.has(value) ? value : 'high';
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Builds a resolver from a static tool→tier map, with an optional dynamic
|
|
9
|
+
* override that receives the map's resolution as its fourth argument.
|
|
10
|
+
*
|
|
11
|
+
* Unknown tools resolve to `'high'` (encrypt by default). The lookup uses
|
|
12
|
+
* `Object.hasOwn`, so prototype-inherited keys (a tool named `toString` or
|
|
13
|
+
* `constructor` — tool names are attacker-controlled) cannot bypass that
|
|
14
|
+
* default.
|
|
15
|
+
*/
|
|
16
|
+
export function createSensitivityResolver(map, override) {
|
|
17
|
+
return (tool, identity, args) => {
|
|
18
|
+
const mapTier = validTier(Object.hasOwn(map, tool) ? map[tool] : undefined);
|
|
19
|
+
if (override)
|
|
20
|
+
return validTier(override(tool, identity, args, mapTier));
|
|
21
|
+
return mapTier;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=sensitivity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sensitivity.js","sourceRoot":"","sources":["../src/sensitivity.ts"],"names":[],"mappings":"AAMA,MAAM,KAAK,GAAwB,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;AAEtE,SAAS,SAAS,CAAC,KAAc;IAC/B,oEAAoE;IACpE,wEAAwE;IACxE,OAAO,KAAK,CAAC,GAAG,CAAC,KAAe,CAAC,CAAC,CAAC,CAAE,KAAyB,CAAC,CAAC,CAAC,MAAM,CAAC;AAC1E,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,yBAAyB,CACvC,GAAoC,EACpC,QAAgC;IAEhC,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE;QAC9B,MAAM,OAAO,GAAG,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QAC5E,IAAI,QAAQ;YAAE,OAAO,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QACxE,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SigningKeyProvider } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* In-process HMAC-SHA256 signing provider.
|
|
4
|
+
*
|
|
5
|
+
* The secret must be high-entropy (>= 32 random bytes). `keyId` is PUBLIC —
|
|
6
|
+
* it is published in every ReplayManifest — so it must never be used as key
|
|
7
|
+
* material (ADR-0003), and a guessable secret is offline-attackable from
|
|
8
|
+
* any published manifest.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createDefaultSigningKeyProvider(secret: Buffer | string): SigningKeyProvider;
|
|
11
|
+
//# sourceMappingURL=signingKey.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signingKey.d.ts","sourceRoot":"","sources":["../src/signingKey.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AASrD;;;;;;;GAOG;AACH,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,MAAM,GAAG,MAAM,GACtB,kBAAkB,CAcpB"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createHmac } from 'node:crypto';
|
|
2
|
+
// keyId derivation label. v1 used bare SHA256(secret), which let anyone
|
|
3
|
+
// holding a manifest brute-force a low-entropy secret offline from the
|
|
4
|
+
// published keyId. HMAC with a domain label is not invertible into a
|
|
5
|
+
// dictionary check any faster than attacking the signature itself, and
|
|
6
|
+
// keeps keyId stable per secret. See ADR-0004.
|
|
7
|
+
const DOMAIN_KEYID = 'mcpose/v2/keyid';
|
|
8
|
+
/**
|
|
9
|
+
* In-process HMAC-SHA256 signing provider.
|
|
10
|
+
*
|
|
11
|
+
* The secret must be high-entropy (>= 32 random bytes). `keyId` is PUBLIC —
|
|
12
|
+
* it is published in every ReplayManifest — so it must never be used as key
|
|
13
|
+
* material (ADR-0003), and a guessable secret is offline-attackable from
|
|
14
|
+
* any published manifest.
|
|
15
|
+
*/
|
|
16
|
+
export function createDefaultSigningKeyProvider(secret) {
|
|
17
|
+
// The secret is handed to createHmac as given. Normalizing a string to a
|
|
18
|
+
// Buffer first was a no-op: createHmac encodes a string key as utf8, byte
|
|
19
|
+
// for byte what Buffer.from produces, so no input could tell the two
|
|
20
|
+
// apart. The equivalence is pinned by a test (#110).
|
|
21
|
+
const keyId = createHmac('sha256', secret).update(DOMAIN_KEYID).digest('hex');
|
|
22
|
+
return {
|
|
23
|
+
algorithm: 'HMAC-SHA256',
|
|
24
|
+
keyId,
|
|
25
|
+
async sign(data) {
|
|
26
|
+
return createHmac('sha256', secret).update(data).digest();
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=signingKey.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signingKey.js","sourceRoot":"","sources":["../src/signingKey.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,wEAAwE;AACxE,uEAAuE;AACvE,qEAAqE;AACrE,uEAAuE;AACvE,+CAA+C;AAC/C,MAAM,YAAY,GAAG,iBAAiB,CAAC;AAEvC;;;;;;;GAOG;AACH,MAAM,UAAU,+BAA+B,CAC7C,MAAuB;IAEvB,yEAAyE;IACzE,0EAA0E;IAC1E,qEAAqE;IACrE,qDAAqD;IACrD,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAE9E,OAAO;QACL,SAAS,EAAE,aAAa;QACxB,KAAK;QACL,KAAK,CAAC,IAAI,CAAC,IAAY;YACrB,OAAO,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;QAC5D,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { SubjectKeyStore } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Reference {@link SubjectKeyStore}, backed by a `Map` in this process.
|
|
4
|
+
*
|
|
5
|
+
* NOT DURABLE, and not for production. The keys live only in this process's
|
|
6
|
+
* heap, so a restart erases every subject at once and a second proxy instance
|
|
7
|
+
* cannot decrypt what the first recorded. Both are silent failures: the chain
|
|
8
|
+
* still verifies, and the loss shows up only when someone tries to read a
|
|
9
|
+
* payload back.
|
|
10
|
+
*
|
|
11
|
+
* A real deployment needs a durable, access-controlled store — a KMS, an HSM,
|
|
12
|
+
* or an encrypted table whose read path is audited — because in erasable mode
|
|
13
|
+
* this store IS the confidentiality of every high-tier payload it holds a key
|
|
14
|
+
* for (ADR-0018).
|
|
15
|
+
*/
|
|
16
|
+
export declare function createInMemorySubjectKeyStore(): SubjectKeyStore;
|
|
17
|
+
//# sourceMappingURL=subjectKeyStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subjectKeyStore.d.ts","sourceRoot":"","sources":["../src/subjectKeyStore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,6BAA6B,IAAI,eAAe,CAoB/D"}
|