@peac/protocol 0.13.0 → 0.13.2
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/_internal/record-core/bounded-validator.d.ts +83 -0
- package/dist/_internal/record-core/bounded-validator.d.ts.map +1 -0
- package/dist/_internal/record-core/codec/index.d.ts +27 -0
- package/dist/_internal/record-core/codec/index.d.ts.map +1 -0
- package/dist/_internal/record-core/codec/jws-jwt.d.ts +25 -0
- package/dist/_internal/record-core/codec/jws-jwt.d.ts.map +1 -0
- package/dist/_internal/record-core/codec/types.d.ts +80 -0
- package/dist/_internal/record-core/codec/types.d.ts.map +1 -0
- package/dist/_internal/record-core/index.d.ts +14 -0
- package/dist/_internal/record-core/index.d.ts.map +1 -0
- package/dist/_internal/record-core/normalize.d.ts +24 -0
- package/dist/_internal/record-core/normalize.d.ts.map +1 -0
- package/dist/_internal/record-core/shadow-hook.d.ts +46 -0
- package/dist/_internal/record-core/shadow-hook.d.ts.map +1 -0
- package/dist/_internal/record-core/types.d.ts +15 -0
- package/dist/_internal/record-core/types.d.ts.map +1 -0
- package/dist/_internal/record-core/validators/extension-budget.d.ts +47 -0
- package/dist/_internal/record-core/validators/extension-budget.d.ts.map +1 -0
- package/dist/_internal/record-core/validators/index.d.ts +13 -0
- package/dist/_internal/record-core/validators/index.d.ts.map +1 -0
- package/dist/_internal/record-core/validators/issuer-form.d.ts +58 -0
- package/dist/_internal/record-core/validators/issuer-form.d.ts.map +1 -0
- package/dist/_internal/record-core/validators/jose-hardening.d.ts +58 -0
- package/dist/_internal/record-core/validators/jose-hardening.d.ts.map +1 -0
- package/dist/_internal/record-core/validators/kernel-constraints.d.ts +48 -0
- package/dist/_internal/record-core/validators/kernel-constraints.d.ts.map +1 -0
- package/dist/_internal/record-core/validators/temporal.d.ts +50 -0
- package/dist/_internal/record-core/validators/temporal.d.ts.map +1 -0
- package/dist/_internal/record-core/validators/type-extension-mapping.d.ts +51 -0
- package/dist/_internal/record-core/validators/type-extension-mapping.d.ts.map +1 -0
- package/dist/_internal/record-core/validators/types.d.ts +42 -0
- package/dist/_internal/record-core/validators/types.d.ts.map +1 -0
- package/dist/_internal/shadow-observe.d.ts +93 -0
- package/dist/_internal/shadow-observe.d.ts.map +1 -0
- package/dist/_internal/shadow-redact.d.ts +74 -0
- package/dist/_internal/shadow-redact.d.ts.map +1 -0
- package/dist/_internal/shadow-types.d.ts +62 -0
- package/dist/_internal/shadow-types.d.ts.map +1 -0
- package/dist/_internal/shadow.d.ts +134 -0
- package/dist/_internal/shadow.d.ts.map +1 -0
- package/dist/_internal/test-helpers/canonical-runner.d.ts +43 -0
- package/dist/_internal/test-helpers/canonical-runner.d.ts.map +1 -0
- package/dist/_internal/test-helpers/corpus-loader.d.ts +61 -0
- package/dist/_internal/test-helpers/corpus-loader.d.ts.map +1 -0
- package/dist/_internal/test-helpers/fixture-manifest.d.ts +46 -0
- package/dist/_internal/test-helpers/fixture-manifest.d.ts.map +1 -0
- package/dist/_internal/test-helpers/parity-verdict.d.ts +31 -0
- package/dist/_internal/test-helpers/parity-verdict.d.ts.map +1 -0
- package/dist/index.cjs +684 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +685 -12
- package/dist/index.mjs.map +1 -1
- package/dist/issue.d.ts +12 -12
- package/dist/issue.d.ts.map +1 -1
- package/dist/policy-binding.d.ts +1 -1
- package/dist/verify-local.cjs +645 -3
- package/dist/verify-local.cjs.map +1 -1
- package/dist/verify-local.d.ts.map +1 -1
- package/dist/verify-local.mjs +647 -5
- package/dist/verify-local.mjs.map +1 -1
- package/package.json +6 -4
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,634 @@
|
|
|
1
1
|
import { uuidv7 } from 'uuidv7';
|
|
2
|
-
import { sign,
|
|
2
|
+
import { sign, decode, verify, jcsHash, sha256Hex, computeJwkThumbprint, jwkToPublicKeyBytes, base64urlDecode, signWire02, base64urlDecodeString, base64urlEncode } from '@peac/crypto';
|
|
3
3
|
export { base64urlDecode, base64urlEncode, computeJwkThumbprint, generateKeypair, jwkToPublicKeyBytes, sha256Bytes, sha256Hex, verify } from '@peac/crypto';
|
|
4
|
+
import { isValidPurposeToken, isCanonicalPurpose, isValidPurposeReason, isValidWorkflowContext, createWorkflowContextInvalidError, hasValidDagSemantics, createWorkflowDagInvalidError, WORKFLOW_EXTENSION_KEY, validateKernelConstraints, createConstraintViolationError, ReceiptClaims, createEvidenceNotJsonError, validateSubjectSnapshot, isCanonicalIss, Wire02ClaimsSchema, PEAC_ISSUER_CONFIG_MAX_BYTES, validateRevokedKeys, PEAC_ISSUER_CONFIG_PATH, PEAC_POLICY_MAX_BYTES, PEAC_POLICY_PATH, PEAC_POLICY_FALLBACK_PATH, WARNING_TYP_MISSING, parseReceiptClaims, checkOccurredAtSkew, REGISTERED_RECEIPT_TYPES, WARNING_TYPE_UNREGISTERED, REGISTERED_EXTENSION_GROUP_KEYS, isValidExtensionKey, WARNING_UNKNOWN_EXTENSION, WARNING_EXTENSION_GROUP_MISSING, WARNING_EXTENSION_GROUP_MISMATCH, verifyPolicyBinding, sortWarnings, PEAC_RECEIPT_HEADER, PEAC_PURPOSE_HEADER, parsePurposeHeader, PEAC_PURPOSE_APPLIED_HEADER, PEAC_PURPOSE_REASON_HEADER, KERNEL_CONSTRAINTS } from '@peac/schema';
|
|
5
|
+
import { VERIFIER_LIMITS, VERIFIER_NETWORK, TYPE_TO_EXTENSION_MAP, HASH, VERIFIER_POLICY_VERSION, VERIFICATION_REPORT_VERSION, WIRE_TYPE, OCCURRED_AT_TOLERANCE_SECONDS, ISS_CANONICAL, EXTENSION_BUDGET } from '@peac/kernel';
|
|
4
6
|
import { ZodError } from 'zod';
|
|
5
|
-
import { isValidPurposeToken, isCanonicalPurpose, isValidPurposeReason, isValidWorkflowContext, createWorkflowContextInvalidError, hasValidDagSemantics, createWorkflowDagInvalidError, WORKFLOW_EXTENSION_KEY, validateKernelConstraints, createConstraintViolationError, ReceiptClaims, createEvidenceNotJsonError, validateSubjectSnapshot, isCanonicalIss, Wire02ClaimsSchema, PEAC_ISSUER_CONFIG_MAX_BYTES, validateRevokedKeys, PEAC_ISSUER_CONFIG_PATH, PEAC_POLICY_MAX_BYTES, PEAC_POLICY_PATH, PEAC_POLICY_FALLBACK_PATH, WARNING_TYP_MISSING, parseReceiptClaims, checkOccurredAtSkew, REGISTERED_RECEIPT_TYPES, WARNING_TYPE_UNREGISTERED, REGISTERED_EXTENSION_GROUP_KEYS, isValidExtensionKey, WARNING_UNKNOWN_EXTENSION, WARNING_EXTENSION_GROUP_MISSING, WARNING_EXTENSION_GROUP_MISMATCH, verifyPolicyBinding, sortWarnings, PEAC_RECEIPT_HEADER, PEAC_PURPOSE_HEADER, parsePurposeHeader, PEAC_PURPOSE_APPLIED_HEADER, PEAC_PURPOSE_REASON_HEADER } from '@peac/schema';
|
|
6
7
|
import { createHash } from 'crypto';
|
|
7
|
-
import { VERIFIER_LIMITS, VERIFIER_NETWORK, TYPE_TO_EXTENSION_MAP, HASH, VERIFIER_POLICY_VERSION, VERIFICATION_REPORT_VERSION, WIRE_TYPE } from '@peac/kernel';
|
|
8
8
|
|
|
9
9
|
// src/issue.ts
|
|
10
|
+
|
|
11
|
+
// src/_internal/record-core/codec/types.ts
|
|
12
|
+
var CodecError = class extends Error {
|
|
13
|
+
constructor(code, message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.code = code;
|
|
16
|
+
this.name = "CodecError";
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// src/_internal/record-core/codec/jws-jwt.ts
|
|
21
|
+
var JwsJwtCodec = class {
|
|
22
|
+
name = "jws-jwt";
|
|
23
|
+
async encode(payload, privateKey, kid) {
|
|
24
|
+
return signWire02(payload, privateKey, kid);
|
|
25
|
+
}
|
|
26
|
+
async decode(serialized, publicKey) {
|
|
27
|
+
return verify(serialized, publicKey);
|
|
28
|
+
}
|
|
29
|
+
peekHeader(serialized) {
|
|
30
|
+
const segments = serialized.split(".");
|
|
31
|
+
if (segments.length !== 3) {
|
|
32
|
+
throw new CodecError(
|
|
33
|
+
"CODEC_PEEK_MALFORMED",
|
|
34
|
+
`jws-jwt: malformed compact JWS (expected 3 segments, got ${segments.length})`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
const [headerB64, payloadB64, signatureB64] = segments;
|
|
38
|
+
if (headerB64.length === 0) {
|
|
39
|
+
throw new CodecError(
|
|
40
|
+
"CODEC_PEEK_MALFORMED",
|
|
41
|
+
"jws-jwt: malformed compact JWS (empty header segment)"
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
if (payloadB64.length === 0) {
|
|
45
|
+
throw new CodecError(
|
|
46
|
+
"CODEC_PEEK_MALFORMED",
|
|
47
|
+
"jws-jwt: malformed compact JWS (empty payload segment)"
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
if (signatureB64.length === 0) {
|
|
51
|
+
throw new CodecError(
|
|
52
|
+
"CODEC_PEEK_MALFORMED",
|
|
53
|
+
"jws-jwt: malformed compact JWS (empty signature segment)"
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
let header;
|
|
57
|
+
try {
|
|
58
|
+
const headerJson = base64urlDecodeString(headerB64);
|
|
59
|
+
header = JSON.parse(headerJson);
|
|
60
|
+
} catch (err) {
|
|
61
|
+
throw new CodecError(
|
|
62
|
+
"CODEC_PEEK_INVALID_HEADER",
|
|
63
|
+
`jws-jwt: protected header JSON parse failed: ${err.message}`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
if (typeof header.typ !== "string") {
|
|
67
|
+
throw new CodecError(
|
|
68
|
+
"CODEC_PEEK_MISSING_TYP",
|
|
69
|
+
'jws-jwt: protected header missing required field "typ" (string)'
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
if (typeof header.alg !== "string") {
|
|
73
|
+
throw new CodecError(
|
|
74
|
+
"CODEC_PEEK_MISSING_ALG",
|
|
75
|
+
'jws-jwt: protected header missing required field "alg" (string)'
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
typ: header.typ,
|
|
80
|
+
alg: header.alg,
|
|
81
|
+
kid: typeof header.kid === "string" ? header.kid : void 0,
|
|
82
|
+
raw: header
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
var defaultCodec = new JwsJwtCodec();
|
|
87
|
+
function validateKernelConstraintsInternal(claims) {
|
|
88
|
+
const violations = [];
|
|
89
|
+
if (claims === null || claims === void 0 || typeof claims !== "object") {
|
|
90
|
+
return { valid: true, violations };
|
|
91
|
+
}
|
|
92
|
+
let totalNodes = 0;
|
|
93
|
+
const stack = [
|
|
94
|
+
{ value: claims, depth: 0, path: "" }
|
|
95
|
+
];
|
|
96
|
+
while (stack.length > 0) {
|
|
97
|
+
const item = stack.pop();
|
|
98
|
+
totalNodes++;
|
|
99
|
+
if (totalNodes > KERNEL_CONSTRAINTS.MAX_TOTAL_NODES) {
|
|
100
|
+
violations.push({
|
|
101
|
+
constraint: "MAX_TOTAL_NODES",
|
|
102
|
+
actual: totalNodes,
|
|
103
|
+
limit: KERNEL_CONSTRAINTS.MAX_TOTAL_NODES,
|
|
104
|
+
path: item.path
|
|
105
|
+
});
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
if (item.depth > KERNEL_CONSTRAINTS.MAX_NESTED_DEPTH) {
|
|
109
|
+
violations.push({
|
|
110
|
+
constraint: "MAX_NESTED_DEPTH",
|
|
111
|
+
actual: item.depth,
|
|
112
|
+
limit: KERNEL_CONSTRAINTS.MAX_NESTED_DEPTH,
|
|
113
|
+
path: item.path
|
|
114
|
+
});
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (item.value === null || typeof item.value !== "object") {
|
|
118
|
+
if (typeof item.value === "string") {
|
|
119
|
+
if (item.value.length > KERNEL_CONSTRAINTS.MAX_STRING_LENGTH) {
|
|
120
|
+
violations.push({
|
|
121
|
+
constraint: "MAX_STRING_LENGTH",
|
|
122
|
+
actual: item.value.length,
|
|
123
|
+
limit: KERNEL_CONSTRAINTS.MAX_STRING_LENGTH,
|
|
124
|
+
path: item.path
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (Array.isArray(item.value)) {
|
|
131
|
+
if (item.value.length > KERNEL_CONSTRAINTS.MAX_ARRAY_LENGTH) {
|
|
132
|
+
violations.push({
|
|
133
|
+
constraint: "MAX_ARRAY_LENGTH",
|
|
134
|
+
actual: item.value.length,
|
|
135
|
+
limit: KERNEL_CONSTRAINTS.MAX_ARRAY_LENGTH,
|
|
136
|
+
path: item.path
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
for (let i = item.value.length - 1; i >= 0; i--) {
|
|
140
|
+
stack.push({
|
|
141
|
+
value: item.value[i],
|
|
142
|
+
depth: item.depth + 1,
|
|
143
|
+
path: `${item.path}[${i}]`
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const keys = Object.keys(item.value);
|
|
149
|
+
if (keys.length > KERNEL_CONSTRAINTS.MAX_OBJECT_KEYS) {
|
|
150
|
+
violations.push({
|
|
151
|
+
constraint: "MAX_OBJECT_KEYS",
|
|
152
|
+
actual: keys.length,
|
|
153
|
+
limit: KERNEL_CONSTRAINTS.MAX_OBJECT_KEYS,
|
|
154
|
+
path: item.path
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
for (let i = keys.length - 1; i >= 0; i--) {
|
|
158
|
+
const key = keys[i];
|
|
159
|
+
const childPath = item.path ? `${item.path}.${key}` : key;
|
|
160
|
+
stack.push({
|
|
161
|
+
value: item.value[key],
|
|
162
|
+
depth: item.depth + 1,
|
|
163
|
+
path: childPath
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return { valid: violations.length === 0, violations };
|
|
168
|
+
}
|
|
169
|
+
function escapeJsonPointerSegment(segment) {
|
|
170
|
+
return segment.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
171
|
+
}
|
|
172
|
+
function classifyExpectedGroup(kind, type, extensions) {
|
|
173
|
+
if (kind === "challenge") return "skip";
|
|
174
|
+
const expectedGroup = TYPE_TO_EXTENSION_MAP.get(type);
|
|
175
|
+
if (expectedGroup === void 0) return "skip";
|
|
176
|
+
if (extensions !== void 0 && Object.prototype.hasOwnProperty.call(extensions, expectedGroup)) {
|
|
177
|
+
return "ok";
|
|
178
|
+
}
|
|
179
|
+
if (extensions !== void 0) {
|
|
180
|
+
for (const key of Object.keys(extensions)) {
|
|
181
|
+
if (key !== expectedGroup && REGISTERED_EXTENSION_GROUP_KEYS.has(key)) {
|
|
182
|
+
return "mismatch";
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return "missing";
|
|
187
|
+
}
|
|
188
|
+
function validateTypeExtensionMappingInternal(input) {
|
|
189
|
+
const warnings = [];
|
|
190
|
+
if (!REGISTERED_RECEIPT_TYPES.has(input.type)) {
|
|
191
|
+
warnings.push({ code: WARNING_TYPE_UNREGISTERED, pointer: "/type" });
|
|
192
|
+
}
|
|
193
|
+
if (input.extensions !== void 0) {
|
|
194
|
+
for (const key of Object.keys(input.extensions)) {
|
|
195
|
+
if (!REGISTERED_EXTENSION_GROUP_KEYS.has(key) && isValidExtensionKey(key)) {
|
|
196
|
+
warnings.push({
|
|
197
|
+
code: WARNING_UNKNOWN_EXTENSION,
|
|
198
|
+
pointer: `/extensions/${escapeJsonPointerSegment(key)}`
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const status = classifyExpectedGroup(input.kind, input.type, input.extensions);
|
|
204
|
+
if (status === "missing") {
|
|
205
|
+
warnings.push({ code: WARNING_EXTENSION_GROUP_MISSING, pointer: "/type" });
|
|
206
|
+
} else if (status === "mismatch") {
|
|
207
|
+
warnings.push({ code: WARNING_EXTENSION_GROUP_MISMATCH, pointer: "/type" });
|
|
208
|
+
}
|
|
209
|
+
return warnings;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// src/_internal/record-core/validators/jose-hardening.ts
|
|
213
|
+
var KID_MAX_LENGTH = 256;
|
|
214
|
+
var ACCEPTED = { accepted: true };
|
|
215
|
+
function rejected(errorCode) {
|
|
216
|
+
return { accepted: false, errorCode };
|
|
217
|
+
}
|
|
218
|
+
function validateJoseHardeningInternal(header) {
|
|
219
|
+
if (!header.kid || typeof header.kid !== "string" || header.kid.length === 0 || header.kid.length > KID_MAX_LENGTH) {
|
|
220
|
+
return rejected("CRYPTO_JWS_MISSING_KID");
|
|
221
|
+
}
|
|
222
|
+
if (header.jwk !== void 0 || header.x5c !== void 0 || header.x5u !== void 0 || header.jku !== void 0) {
|
|
223
|
+
return rejected("CRYPTO_JWS_EMBEDDED_KEY");
|
|
224
|
+
}
|
|
225
|
+
if (header.crit !== void 0) {
|
|
226
|
+
return rejected("CRYPTO_JWS_CRIT_REJECTED");
|
|
227
|
+
}
|
|
228
|
+
if (header.b64 === false) {
|
|
229
|
+
return rejected("CRYPTO_JWS_B64_REJECTED");
|
|
230
|
+
}
|
|
231
|
+
if (header.zip !== void 0) {
|
|
232
|
+
return rejected("CRYPTO_JWS_ZIP_REJECTED");
|
|
233
|
+
}
|
|
234
|
+
return ACCEPTED;
|
|
235
|
+
}
|
|
236
|
+
var ACCEPTED2 = { accepted: true };
|
|
237
|
+
var REJECTED = { accepted: false, errorCode: "E_ISS_NOT_CANONICAL" };
|
|
238
|
+
var DID_PATTERN = /^did:[a-z0-9]+:[^#?/]+$/;
|
|
239
|
+
function validateIssuerFormInternal(iss) {
|
|
240
|
+
if (typeof iss !== "string" || iss.length === 0 || iss.length > ISS_CANONICAL.maxLength) {
|
|
241
|
+
return REJECTED;
|
|
242
|
+
}
|
|
243
|
+
if (iss.startsWith("did:")) {
|
|
244
|
+
return DID_PATTERN.test(iss) ? ACCEPTED2 : REJECTED;
|
|
245
|
+
}
|
|
246
|
+
let url;
|
|
247
|
+
try {
|
|
248
|
+
url = new URL(iss);
|
|
249
|
+
} catch {
|
|
250
|
+
return REJECTED;
|
|
251
|
+
}
|
|
252
|
+
if (url.protocol !== "https:") return REJECTED;
|
|
253
|
+
if (!url.hostname) return REJECTED;
|
|
254
|
+
if (url.username !== "" || url.password !== "") return REJECTED;
|
|
255
|
+
const origin = `${url.protocol}//${url.host}`;
|
|
256
|
+
return iss === origin ? ACCEPTED2 : REJECTED;
|
|
257
|
+
}
|
|
258
|
+
var ACCEPTED3 = { accepted: true };
|
|
259
|
+
function validateTemporalInternal(occurredAt, iat, now, tolerance = OCCURRED_AT_TOLERANCE_SECONDS) {
|
|
260
|
+
if (occurredAt === void 0) return ACCEPTED3;
|
|
261
|
+
const ts = Date.parse(occurredAt) / 1e3;
|
|
262
|
+
if (isNaN(ts)) return ACCEPTED3;
|
|
263
|
+
if (ts > now + tolerance) {
|
|
264
|
+
return {
|
|
265
|
+
accepted: false,
|
|
266
|
+
errorCode: "E_OCCURRED_AT_FUTURE",
|
|
267
|
+
pointer: "/occurred_at"
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
if (ts > iat) {
|
|
271
|
+
return {
|
|
272
|
+
accepted: true,
|
|
273
|
+
warnings: [{ code: "occurred_at_skew", pointer: "/occurred_at" }]
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
return ACCEPTED3;
|
|
277
|
+
}
|
|
278
|
+
var ACCEPTED4 = { accepted: true, violations: [] };
|
|
279
|
+
var SIZE_EXCEEDED = "E_EXTENSION_SIZE_EXCEEDED";
|
|
280
|
+
var textEncoder = new TextEncoder();
|
|
281
|
+
function jsonUtf8ByteLength(value) {
|
|
282
|
+
try {
|
|
283
|
+
return textEncoder.encode(JSON.stringify(value)).byteLength;
|
|
284
|
+
} catch {
|
|
285
|
+
return Infinity;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function escapeJsonPointerSegment2(segment) {
|
|
289
|
+
return segment.replace(/~/g, "~0").replace(/\//g, "~1");
|
|
290
|
+
}
|
|
291
|
+
function validateExtensionBudgetInternal(extensions) {
|
|
292
|
+
if (extensions === void 0) return ACCEPTED4;
|
|
293
|
+
const totalBytes = jsonUtf8ByteLength(extensions);
|
|
294
|
+
if (totalBytes > EXTENSION_BUDGET.maxTotalBytes) {
|
|
295
|
+
return {
|
|
296
|
+
accepted: false,
|
|
297
|
+
violations: [{ code: SIZE_EXCEEDED, path: "/extensions" }]
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
const violations = [];
|
|
301
|
+
for (const key of Object.keys(extensions)) {
|
|
302
|
+
const groupBytes = jsonUtf8ByteLength(extensions[key]);
|
|
303
|
+
if (groupBytes > EXTENSION_BUDGET.maxGroupBytes) {
|
|
304
|
+
violations.push({
|
|
305
|
+
code: SIZE_EXCEEDED,
|
|
306
|
+
path: `/extensions/${escapeJsonPointerSegment2(key)}`
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (violations.length === 0) return ACCEPTED4;
|
|
311
|
+
return { accepted: false, violations };
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// src/_internal/record-core/bounded-validator.ts
|
|
315
|
+
function runBoundedValidatorShadow(input) {
|
|
316
|
+
const violations = [];
|
|
317
|
+
const warnings = [];
|
|
318
|
+
const kernelRes = validateKernelConstraintsInternal(input.claims);
|
|
319
|
+
if (!kernelRes.valid) {
|
|
320
|
+
for (const v of kernelRes.violations) {
|
|
321
|
+
violations.push({
|
|
322
|
+
layer: "kernel-constraints",
|
|
323
|
+
code: v.constraint,
|
|
324
|
+
path: v.path || void 0
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
const issRes = validateIssuerFormInternal(input.claims.iss);
|
|
329
|
+
if (!issRes.accepted) {
|
|
330
|
+
violations.push({ layer: "issuer-form", code: issRes.errorCode });
|
|
331
|
+
}
|
|
332
|
+
if (input.header !== void 0) {
|
|
333
|
+
const joseRes = validateJoseHardeningInternal(input.header);
|
|
334
|
+
if (!joseRes.accepted) {
|
|
335
|
+
violations.push({ layer: "jose-header-hardening", code: joseRes.errorCode });
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
const typeExtWarnings = validateTypeExtensionMappingInternal({
|
|
339
|
+
kind: input.claims.kind,
|
|
340
|
+
type: input.claims.type,
|
|
341
|
+
extensions: input.claims.extensions
|
|
342
|
+
});
|
|
343
|
+
for (const w of typeExtWarnings) {
|
|
344
|
+
warnings.push({ layer: "type-extension-mapping", code: w.code, path: w.pointer });
|
|
345
|
+
}
|
|
346
|
+
if (input.claims.kind === "evidence") {
|
|
347
|
+
const temporalRes = validateTemporalInternal(
|
|
348
|
+
input.claims.occurred_at,
|
|
349
|
+
input.claims.iat,
|
|
350
|
+
input.now
|
|
351
|
+
);
|
|
352
|
+
if (!temporalRes.accepted) {
|
|
353
|
+
violations.push({
|
|
354
|
+
layer: "temporal",
|
|
355
|
+
code: temporalRes.errorCode,
|
|
356
|
+
path: temporalRes.pointer
|
|
357
|
+
});
|
|
358
|
+
} else if (temporalRes.warnings) {
|
|
359
|
+
const list = temporalRes.warnings;
|
|
360
|
+
for (const w of list) {
|
|
361
|
+
warnings.push({ layer: "temporal", code: w.code, path: w.pointer });
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
const budgetRes = validateExtensionBudgetInternal(input.claims.extensions);
|
|
366
|
+
if (!budgetRes.accepted) {
|
|
367
|
+
const list = budgetRes.violations;
|
|
368
|
+
for (const v of list) {
|
|
369
|
+
violations.push({ layer: "extension-budget", code: v.code, path: v.path });
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return {
|
|
373
|
+
accepted: violations.length === 0,
|
|
374
|
+
violations,
|
|
375
|
+
warnings
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
var SECRET_PATTERNS = [
|
|
379
|
+
// Compact JWS / JWT (header.payload.signature with eyJ-prefixed header).
|
|
380
|
+
/eyJ[A-Za-z0-9_-]{20,}\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g,
|
|
381
|
+
// PEM-encoded blocks. Covers RSA / EC / OPENSSH / generic PRIVATE KEY
|
|
382
|
+
// header variants in a single pattern.
|
|
383
|
+
/-----BEGIN [A-Z0-9 ]+-----[\s\S]+?-----END [A-Z0-9 ]+-----/g,
|
|
384
|
+
// Long base64 runs (>=40 chars). The character class is the standard
|
|
385
|
+
// base64 alphabet only (no `_` or `-`) so that kebab-case and
|
|
386
|
+
// snake_case identifier strings do not collateral-match. base64url
|
|
387
|
+
// payloads carrying credentials are reached via more specific patterns
|
|
388
|
+
// (compact JWS, Bearer, URL query token) and via the wrapper headers
|
|
389
|
+
// they appear in.
|
|
390
|
+
/\b[A-Za-z0-9+/]{40,}={0,2}\b/g,
|
|
391
|
+
// Bearer token (Authorization header value with Bearer scheme).
|
|
392
|
+
/Bearer\s+[A-Za-z0-9._\-/+=]+/gi,
|
|
393
|
+
// Generic Authorization header line (any scheme, including Basic /
|
|
394
|
+
// Digest / Negotiate). Multiline-anchored.
|
|
395
|
+
/^authorization:\s*[^\r\n]+/gim,
|
|
396
|
+
// Generic auth-token header line (X-Auth-Token, X-API-Key).
|
|
397
|
+
/^x-(?:auth-token|api-key):\s*[^\r\n]+/gim,
|
|
398
|
+
// Cookie request header line (raw client cookies).
|
|
399
|
+
/^cookie:\s*[^\r\n]+/gim,
|
|
400
|
+
// Set-Cookie response header line (server-issued sessions).
|
|
401
|
+
/^set-cookie:\s*[^\r\n]+/gim,
|
|
402
|
+
// URL query token / API key / secret / access_token.
|
|
403
|
+
/[?&](?:token|key|secret|access_token|api_key|apikey)=[^&\s#]+/gi,
|
|
404
|
+
// AWS access key ID (AKIA + 16 alphanumeric).
|
|
405
|
+
/\bAKIA[0-9A-Z]{16}\b/g,
|
|
406
|
+
// Email address (PII).
|
|
407
|
+
/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g,
|
|
408
|
+
// Phone numbers. Three explicit forms (E.164 + parenthesized NA + dashed
|
|
409
|
+
// NA / international) so plain consecutive-digit runs (sequence
|
|
410
|
+
// numbers, hashes, identifiers) do not collateral-match.
|
|
411
|
+
/\+\d[\d\s().-]{6,}\d/g,
|
|
412
|
+
/\(\d{3}\)\s*\d{3}[\s.-]?\d{4}/g,
|
|
413
|
+
/\b\d{3}[\s.-]\d{3}[\s.-]\d{4}\b/g
|
|
414
|
+
];
|
|
415
|
+
var REDACTION_MARKER = "[REDACTED]";
|
|
416
|
+
var TEXT_ENCODER = /* @__PURE__ */ new TextEncoder();
|
|
417
|
+
var TEXT_DECODER = /* @__PURE__ */ new TextDecoder("utf-8", { fatal: false });
|
|
418
|
+
var REDACTION_MARKER_BYTES = TEXT_ENCODER.encode(REDACTION_MARKER).length;
|
|
419
|
+
function utf8ByteLength(value) {
|
|
420
|
+
return TEXT_ENCODER.encode(value).length;
|
|
421
|
+
}
|
|
422
|
+
function redactNote(input, maxBytes) {
|
|
423
|
+
if (typeof input !== "string") return REDACTION_MARKER;
|
|
424
|
+
let redacted = input;
|
|
425
|
+
for (const pattern of SECRET_PATTERNS) {
|
|
426
|
+
redacted = redacted.replace(pattern, REDACTION_MARKER);
|
|
427
|
+
}
|
|
428
|
+
const bytes = TEXT_ENCODER.encode(redacted);
|
|
429
|
+
if (bytes.length <= maxBytes) return redacted;
|
|
430
|
+
if (maxBytes <= REDACTION_MARKER_BYTES) {
|
|
431
|
+
return REDACTION_MARKER.slice(0, maxBytes);
|
|
432
|
+
}
|
|
433
|
+
const headBytes = maxBytes - REDACTION_MARKER_BYTES;
|
|
434
|
+
return safeUtf8Slice(bytes, headBytes) + REDACTION_MARKER;
|
|
435
|
+
}
|
|
436
|
+
async function canonicalHashOf(value) {
|
|
437
|
+
return sha256Hex(canonicalStringify(value));
|
|
438
|
+
}
|
|
439
|
+
async function hashJws(jws) {
|
|
440
|
+
return sha256Hex(jws);
|
|
441
|
+
}
|
|
442
|
+
function canonicalStringify(value) {
|
|
443
|
+
if (value === null) return "null";
|
|
444
|
+
if (typeof value === "undefined") return "null";
|
|
445
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
446
|
+
if (typeof value === "number") {
|
|
447
|
+
return Number.isFinite(value) ? String(value) : "null";
|
|
448
|
+
}
|
|
449
|
+
if (typeof value === "boolean") return String(value);
|
|
450
|
+
if (typeof value === "bigint") return String(value);
|
|
451
|
+
if (Array.isArray(value)) {
|
|
452
|
+
return "[" + value.map(canonicalStringify).join(",") + "]";
|
|
453
|
+
}
|
|
454
|
+
if (value instanceof Uint8Array) {
|
|
455
|
+
return JSON.stringify(base64urlEncode(value));
|
|
456
|
+
}
|
|
457
|
+
if (typeof value === "object") {
|
|
458
|
+
const obj = value;
|
|
459
|
+
const keys = Object.keys(obj).sort();
|
|
460
|
+
return "{" + keys.map((k) => JSON.stringify(k) + ":" + canonicalStringify(obj[k])).join(",") + "}";
|
|
461
|
+
}
|
|
462
|
+
return "null";
|
|
463
|
+
}
|
|
464
|
+
function safeUtf8Slice(bytes, byteLen) {
|
|
465
|
+
if (byteLen <= 0) return "";
|
|
466
|
+
if (bytes.length <= byteLen) return TEXT_DECODER.decode(bytes);
|
|
467
|
+
let end = byteLen;
|
|
468
|
+
while (end > 0 && (bytes[end] & 192) === 128) {
|
|
469
|
+
end -= 1;
|
|
470
|
+
}
|
|
471
|
+
return TEXT_DECODER.decode(bytes.subarray(0, end));
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// src/_internal/shadow.ts
|
|
475
|
+
var MAX_LOG_ENTRIES = 1e3;
|
|
476
|
+
var MAX_NOTE_BYTES = 128;
|
|
477
|
+
var RETENTION_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
478
|
+
var SHADOW_TIMEOUT_MS = 250;
|
|
479
|
+
var SHADOW_LOG = [];
|
|
480
|
+
var PENDING_SHADOW_TASKS = /* @__PURE__ */ new Set();
|
|
481
|
+
function isShadowEnabled(options) {
|
|
482
|
+
if (options?._internal?.shadowCore === true) return true;
|
|
483
|
+
return readEnvFlag();
|
|
484
|
+
}
|
|
485
|
+
function readEnvFlag() {
|
|
486
|
+
if (typeof process === "undefined" || process === null) return false;
|
|
487
|
+
const env = process.env;
|
|
488
|
+
return env?.PEAC_INTERNAL_SHADOW_CORE === "1";
|
|
489
|
+
}
|
|
490
|
+
function scheduleShadow(args) {
|
|
491
|
+
setTimeout(() => {
|
|
492
|
+
const task = runShadowTask(args);
|
|
493
|
+
PENDING_SHADOW_TASKS.add(task);
|
|
494
|
+
const removeFromPending = () => {
|
|
495
|
+
PENDING_SHADOW_TASKS.delete(task);
|
|
496
|
+
};
|
|
497
|
+
void task.then(removeFromPending, removeFromPending);
|
|
498
|
+
}, 0);
|
|
499
|
+
}
|
|
500
|
+
async function runShadowTask(args) {
|
|
501
|
+
const recordRefHash = await hashJws(args.recordRef);
|
|
502
|
+
let shadowResult;
|
|
503
|
+
let shadowErrorCode;
|
|
504
|
+
try {
|
|
505
|
+
shadowResult = await runWithTimeout(args.shadowFn, SHADOW_TIMEOUT_MS);
|
|
506
|
+
} catch (err) {
|
|
507
|
+
shadowErrorCode = extractErrorCode(err);
|
|
508
|
+
appendDivergence({
|
|
509
|
+
kind: shadowErrorCode === "SHADOW_TIMEOUT" ? "timing-diff" : "shadow-error",
|
|
510
|
+
call: args.call,
|
|
511
|
+
recordRefHash,
|
|
512
|
+
realErrorCode: args.realError?.code,
|
|
513
|
+
shadowErrorCode,
|
|
514
|
+
notes: redactNote(`shadow threw ${shadowErrorCode}`, MAX_NOTE_BYTES),
|
|
515
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
516
|
+
});
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
const divergence = await compareResults(args, shadowResult, recordRefHash);
|
|
520
|
+
if (divergence) appendDivergence(divergence);
|
|
521
|
+
}
|
|
522
|
+
function runWithTimeout(fn, ms) {
|
|
523
|
+
const controller = new AbortController();
|
|
524
|
+
return new Promise((resolve, reject) => {
|
|
525
|
+
const timer = setTimeout(() => {
|
|
526
|
+
controller.abort();
|
|
527
|
+
reject(new Error("SHADOW_TIMEOUT"));
|
|
528
|
+
}, ms);
|
|
529
|
+
fn(controller.signal).then(
|
|
530
|
+
(v) => {
|
|
531
|
+
clearTimeout(timer);
|
|
532
|
+
resolve(v);
|
|
533
|
+
},
|
|
534
|
+
(e) => {
|
|
535
|
+
clearTimeout(timer);
|
|
536
|
+
reject(e);
|
|
537
|
+
}
|
|
538
|
+
);
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
async function compareResults(args, shadowResult, recordRefHash) {
|
|
542
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
543
|
+
if (!args.realError && shadowResult !== void 0 && args.realResult !== void 0) {
|
|
544
|
+
const realHash = await canonicalHashOf(args.realResult);
|
|
545
|
+
const shadowHash = await canonicalHashOf(shadowResult);
|
|
546
|
+
if (realHash !== shadowHash) {
|
|
547
|
+
return {
|
|
548
|
+
kind: "output-byte-diff",
|
|
549
|
+
call: args.call,
|
|
550
|
+
recordRefHash,
|
|
551
|
+
realResultHash: realHash,
|
|
552
|
+
shadowResultHash: shadowHash,
|
|
553
|
+
realByteLen: byteLenOf(args.realResult),
|
|
554
|
+
shadowByteLen: byteLenOf(shadowResult),
|
|
555
|
+
notes: "canonical-hash-mismatch",
|
|
556
|
+
timestamp: ts
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
return null;
|
|
560
|
+
}
|
|
561
|
+
if (args.realError && shadowResult === void 0) {
|
|
562
|
+
return null;
|
|
563
|
+
}
|
|
564
|
+
return {
|
|
565
|
+
kind: "error-code-diff",
|
|
566
|
+
call: args.call,
|
|
567
|
+
recordRefHash,
|
|
568
|
+
realErrorCode: args.realError?.code,
|
|
569
|
+
notes: args.realError ? "real-errored-shadow-succeeded" : "real-succeeded-shadow-errored",
|
|
570
|
+
timestamp: ts
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
function byteLenOf(value) {
|
|
574
|
+
if (typeof value === "string") return utf8ByteLength(value);
|
|
575
|
+
if (value instanceof Uint8Array) return value.byteLength;
|
|
576
|
+
return utf8ByteLength(JSON.stringify(value) ?? "");
|
|
577
|
+
}
|
|
578
|
+
function extractErrorCode(err) {
|
|
579
|
+
if (err && typeof err === "object") {
|
|
580
|
+
const candidate = err.code;
|
|
581
|
+
if (typeof candidate === "string" && candidate.length > 0) return candidate;
|
|
582
|
+
const message = err.message;
|
|
583
|
+
if (typeof message === "string" && /^[A-Z_][A-Z0-9_]*$/.test(message)) return message;
|
|
584
|
+
}
|
|
585
|
+
return "SHADOW_UNKNOWN_ERROR";
|
|
586
|
+
}
|
|
587
|
+
function appendDivergence(d) {
|
|
588
|
+
pruneStale();
|
|
589
|
+
if (SHADOW_LOG.length >= MAX_LOG_ENTRIES) SHADOW_LOG.shift();
|
|
590
|
+
SHADOW_LOG.push(d);
|
|
591
|
+
}
|
|
592
|
+
function pruneStale() {
|
|
593
|
+
const cutoffIso = new Date(Date.now() - RETENTION_MS).toISOString();
|
|
594
|
+
while (SHADOW_LOG.length > 0 && SHADOW_LOG[0].timestamp < cutoffIso) SHADOW_LOG.shift();
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// src/_internal/shadow-observe.ts
|
|
598
|
+
function realObservationForIssue() {
|
|
599
|
+
return EMPTY_ACCEPTED;
|
|
600
|
+
}
|
|
601
|
+
function realObservationForVerifyLocalSuccess(warnings) {
|
|
602
|
+
if (!warnings || warnings.length === 0) return EMPTY_ACCEPTED;
|
|
603
|
+
return {
|
|
604
|
+
accepted: true,
|
|
605
|
+
violationCodes: [],
|
|
606
|
+
warningCodes: sortedDedupedCodes(warnings.map((w) => w.code))
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
function shadowObservationFromBounded(bounded) {
|
|
610
|
+
return {
|
|
611
|
+
accepted: bounded.accepted,
|
|
612
|
+
violationCodes: sortedDedupedCodes(bounded.violations.map((v) => v.code)),
|
|
613
|
+
warningCodes: sortedDedupedCodes(bounded.warnings.map((w) => w.code))
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
function shadowObservationFromBoundedAcceptedOnly(bounded) {
|
|
617
|
+
return {
|
|
618
|
+
accepted: bounded.accepted,
|
|
619
|
+
violationCodes: sortedDedupedCodes(bounded.violations.map((v) => v.code)),
|
|
620
|
+
warningCodes: []
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
var EMPTY_ACCEPTED = Object.freeze({
|
|
624
|
+
accepted: true,
|
|
625
|
+
violationCodes: Object.freeze([]),
|
|
626
|
+
warningCodes: Object.freeze([])
|
|
627
|
+
});
|
|
628
|
+
function sortedDedupedCodes(codes) {
|
|
629
|
+
if (codes.length === 0) return [];
|
|
630
|
+
return Array.from(new Set(codes)).sort();
|
|
631
|
+
}
|
|
10
632
|
function fireTelemetryHook(fn, input) {
|
|
11
633
|
if (!fn) return;
|
|
12
634
|
try {
|
|
@@ -224,7 +846,28 @@ async function issueWire02(options) {
|
|
|
224
846
|
}
|
|
225
847
|
});
|
|
226
848
|
}
|
|
227
|
-
const jws = await
|
|
849
|
+
const jws = await defaultCodec.encode(claims, options.privateKey, options.kid);
|
|
850
|
+
if (isShadowEnabled(options)) {
|
|
851
|
+
scheduleShadow({
|
|
852
|
+
call: "issue",
|
|
853
|
+
realResult: realObservationForIssue(),
|
|
854
|
+
realError: void 0,
|
|
855
|
+
shadowFn: async () => shadowObservationFromBoundedAcceptedOnly(
|
|
856
|
+
runBoundedValidatorShadow({
|
|
857
|
+
claims: {
|
|
858
|
+
kind: claims.kind,
|
|
859
|
+
type: claims.type,
|
|
860
|
+
iss: claims.iss,
|
|
861
|
+
iat: claims.iat,
|
|
862
|
+
occurred_at: claims.occurred_at,
|
|
863
|
+
extensions: claims.extensions
|
|
864
|
+
},
|
|
865
|
+
now: iat
|
|
866
|
+
})
|
|
867
|
+
),
|
|
868
|
+
recordRef: jws
|
|
869
|
+
});
|
|
870
|
+
}
|
|
228
871
|
return { jws };
|
|
229
872
|
}
|
|
230
873
|
function parseIssuerConfig(json) {
|
|
@@ -236,8 +879,8 @@ function parseIssuerConfig(json) {
|
|
|
236
879
|
}
|
|
237
880
|
try {
|
|
238
881
|
config = JSON.parse(json);
|
|
239
|
-
} catch {
|
|
240
|
-
throw new Error("Issuer config is not valid JSON");
|
|
882
|
+
} catch (err) {
|
|
883
|
+
throw new Error("Issuer config is not valid JSON", { cause: err });
|
|
241
884
|
}
|
|
242
885
|
} else {
|
|
243
886
|
config = json;
|
|
@@ -260,8 +903,8 @@ function parseIssuerConfig(json) {
|
|
|
260
903
|
}
|
|
261
904
|
try {
|
|
262
905
|
new URL(obj.jwks_uri);
|
|
263
|
-
} catch {
|
|
264
|
-
throw new Error("jwks_uri must be a valid URL");
|
|
906
|
+
} catch (err) {
|
|
907
|
+
throw new Error("jwks_uri must be a valid URL", { cause: err });
|
|
265
908
|
}
|
|
266
909
|
if (obj.verify_endpoint !== void 0) {
|
|
267
910
|
if (typeof obj.verify_endpoint !== "string") {
|
|
@@ -354,8 +997,8 @@ function parsePolicyManifest(text, contentType) {
|
|
|
354
997
|
if (isJsonContent(text, contentType)) {
|
|
355
998
|
try {
|
|
356
999
|
manifest = JSON.parse(text);
|
|
357
|
-
} catch {
|
|
358
|
-
throw new Error("Policy manifest is not valid JSON");
|
|
1000
|
+
} catch (err) {
|
|
1001
|
+
throw new Error("Policy manifest is not valid JSON", { cause: err });
|
|
359
1002
|
}
|
|
360
1003
|
} else {
|
|
361
1004
|
manifest = parseSimpleYaml(text);
|
|
@@ -1435,6 +2078,11 @@ function checkTypeExtensionMapping(kind, type, extensions, typeToExtensionMap, r
|
|
|
1435
2078
|
};
|
|
1436
2079
|
}
|
|
1437
2080
|
|
|
2081
|
+
// src/_internal/record-core/shadow-hook.ts
|
|
2082
|
+
function maybeRunShadowValidation(input) {
|
|
2083
|
+
return void 0;
|
|
2084
|
+
}
|
|
2085
|
+
|
|
1438
2086
|
// src/verify-local.ts
|
|
1439
2087
|
function isCryptoError(err) {
|
|
1440
2088
|
return err !== null && typeof err === "object" && "name" in err && err.name === "CryptoError" && "code" in err && typeof err.code === "string" && err.code.startsWith("CRYPTO_") && "message" in err && typeof err.message === "string";
|
|
@@ -1471,7 +2119,7 @@ async function verifyLocal(jws, publicKey, options = {}) {
|
|
|
1471
2119
|
} = options;
|
|
1472
2120
|
const now = options.now ?? Math.floor(Date.now() / 1e3);
|
|
1473
2121
|
try {
|
|
1474
|
-
const result = await
|
|
2122
|
+
const result = await defaultCodec.decode(jws, publicKey);
|
|
1475
2123
|
if (!result.valid) {
|
|
1476
2124
|
return {
|
|
1477
2125
|
valid: false,
|
|
@@ -1516,6 +2164,7 @@ async function verifyLocal(jws, publicKey, options = {}) {
|
|
|
1516
2164
|
}
|
|
1517
2165
|
if (pr.wireVersion === "0.2") {
|
|
1518
2166
|
const claims = pr.claims;
|
|
2167
|
+
maybeRunShadowValidation({ mode: "disabled", claims });
|
|
1519
2168
|
if (issuer !== void 0 && claims.iss !== issuer) {
|
|
1520
2169
|
return {
|
|
1521
2170
|
valid: false,
|
|
@@ -1623,13 +2272,37 @@ async function verifyLocal(jws, publicKey, options = {}) {
|
|
|
1623
2272
|
...callerBindings?.terms !== void 0 && { terms: callerBindings.terms },
|
|
1624
2273
|
...callerBindings?.documents !== void 0 && callerBindings.documents.length > 0 && { documents: callerBindings.documents }
|
|
1625
2274
|
};
|
|
2275
|
+
const sortedWarnings = sortWarnings(accumulatedWarnings);
|
|
2276
|
+
if (isShadowEnabled(options)) {
|
|
2277
|
+
const headerSnapshot = { ...result.header };
|
|
2278
|
+
scheduleShadow({
|
|
2279
|
+
call: "verifyLocal",
|
|
2280
|
+
realResult: realObservationForVerifyLocalSuccess(sortedWarnings),
|
|
2281
|
+
realError: void 0,
|
|
2282
|
+
shadowFn: async () => shadowObservationFromBounded(
|
|
2283
|
+
runBoundedValidatorShadow({
|
|
2284
|
+
claims: {
|
|
2285
|
+
kind: claims.kind,
|
|
2286
|
+
type: claims.type,
|
|
2287
|
+
iss: claims.iss,
|
|
2288
|
+
iat: claims.iat,
|
|
2289
|
+
occurred_at: claims.occurred_at,
|
|
2290
|
+
extensions: claims.extensions
|
|
2291
|
+
},
|
|
2292
|
+
header: headerSnapshot,
|
|
2293
|
+
now
|
|
2294
|
+
})
|
|
2295
|
+
),
|
|
2296
|
+
recordRef: jws
|
|
2297
|
+
});
|
|
2298
|
+
}
|
|
1626
2299
|
return {
|
|
1627
2300
|
valid: true,
|
|
1628
2301
|
variant: "wire-02",
|
|
1629
2302
|
claims,
|
|
1630
2303
|
kid: result.header.kid,
|
|
1631
2304
|
wireVersion: "0.2",
|
|
1632
|
-
warnings:
|
|
2305
|
+
warnings: sortedWarnings,
|
|
1633
2306
|
policy_binding: bindingStatus,
|
|
1634
2307
|
bindings
|
|
1635
2308
|
};
|