@attestry/sdk 0.6.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 +190 -0
- package/README.md +1269 -0
- package/dist/client.d.ts +58 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +74 -0
- package/dist/client.js.map +1 -0
- package/dist/constants.d.ts +7 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +43 -0
- package/dist/constants.js.map +1 -0
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +41 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/lines-parser.d.ts +50 -0
- package/dist/lines-parser.d.ts.map +1 -0
- package/dist/lines-parser.js +211 -0
- package/dist/lines-parser.js.map +1 -0
- package/dist/ndjson-parser.d.ts +57 -0
- package/dist/ndjson-parser.d.ts.map +1 -0
- package/dist/ndjson-parser.js +245 -0
- package/dist/ndjson-parser.js.map +1 -0
- package/dist/resources/abac-policies.d.ts +1034 -0
- package/dist/resources/abac-policies.d.ts.map +1 -0
- package/dist/resources/abac-policies.js +1519 -0
- package/dist/resources/abac-policies.js.map +1 -0
- package/dist/resources/audit-log.d.ts +588 -0
- package/dist/resources/audit-log.d.ts.map +1 -0
- package/dist/resources/audit-log.js +629 -0
- package/dist/resources/audit-log.js.map +1 -0
- package/dist/resources/batch.d.ts +845 -0
- package/dist/resources/batch.d.ts.map +1 -0
- package/dist/resources/batch.js +1074 -0
- package/dist/resources/batch.js.map +1 -0
- package/dist/resources/chat.d.ts +151 -0
- package/dist/resources/chat.d.ts.map +1 -0
- package/dist/resources/chat.js +124 -0
- package/dist/resources/chat.js.map +1 -0
- package/dist/resources/check.d.ts +348 -0
- package/dist/resources/check.d.ts.map +1 -0
- package/dist/resources/check.js +543 -0
- package/dist/resources/check.js.map +1 -0
- package/dist/resources/compliance-check.d.ts +330 -0
- package/dist/resources/compliance-check.d.ts.map +1 -0
- package/dist/resources/compliance-check.js +402 -0
- package/dist/resources/compliance-check.js.map +1 -0
- package/dist/resources/decisions.d.ts +1208 -0
- package/dist/resources/decisions.d.ts.map +1 -0
- package/dist/resources/decisions.js +1362 -0
- package/dist/resources/decisions.js.map +1 -0
- package/dist/resources/evidence-pack.d.ts +1080 -0
- package/dist/resources/evidence-pack.d.ts.map +1 -0
- package/dist/resources/evidence-pack.js +1789 -0
- package/dist/resources/evidence-pack.js.map +1 -0
- package/dist/resources/gate.d.ts +613 -0
- package/dist/resources/gate.d.ts.map +1 -0
- package/dist/resources/gate.js +737 -0
- package/dist/resources/gate.js.map +1 -0
- package/dist/resources/incidents.d.ts +136 -0
- package/dist/resources/incidents.d.ts.map +1 -0
- package/dist/resources/incidents.js +229 -0
- package/dist/resources/incidents.js.map +1 -0
- package/dist/resources/regulatory-changes.d.ts +307 -0
- package/dist/resources/regulatory-changes.d.ts.map +1 -0
- package/dist/resources/regulatory-changes.js +365 -0
- package/dist/resources/regulatory-changes.js.map +1 -0
- package/dist/resources/safe-input-read.d.ts +21 -0
- package/dist/resources/safe-input-read.d.ts.map +1 -0
- package/dist/resources/safe-input-read.js +57 -0
- package/dist/resources/safe-input-read.js.map +1 -0
- package/dist/resources/ship-gate.d.ts +475 -0
- package/dist/resources/ship-gate.d.ts.map +1 -0
- package/dist/resources/ship-gate.js +727 -0
- package/dist/resources/ship-gate.js.map +1 -0
- package/dist/resources/vision.d.ts +540 -0
- package/dist/resources/vision.d.ts.map +1 -0
- package/dist/resources/vision.js +1036 -0
- package/dist/resources/vision.js.map +1 -0
- package/dist/retry.d.ts +103 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +224 -0
- package/dist/retry.js.map +1 -0
- package/dist/sse-parser.d.ts +64 -0
- package/dist/sse-parser.d.ts.map +1 -0
- package/dist/sse-parser.js +271 -0
- package/dist/sse-parser.js.map +1 -0
- package/dist/transport.d.ts +142 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +455 -0
- package/dist/transport.js.map +1 -0
- package/dist/types.d.ts +61 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1,629 @@
|
|
|
1
|
+
// ─── AuditLog resource ──────────────────────────────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// Wraps the audit-log SIEM export surface (Prompt C.4) + the org-wide
|
|
4
|
+
// audit-log hash-chain verifier (session 19):
|
|
5
|
+
//
|
|
6
|
+
// - GET /api/v1/audit-log/export NDJSON / ECS / CEF stream of audit_log rows
|
|
7
|
+
// - GET /api/v1/audit-chain/verify Org-wide hash-chain integrity verdict
|
|
8
|
+
//
|
|
9
|
+
// First non-decisions resource on `@attestry/sdk`. Sibling to
|
|
10
|
+
// `IncidentsResource`, `DecisionsResource`, `ChatResource`. Two
|
|
11
|
+
// public methods today (`export` + `verifyChain`); the resource class
|
|
12
|
+
// is the landing pad for future audit-log methods if/when the kernel
|
|
13
|
+
// adds them.
|
|
14
|
+
//
|
|
15
|
+
// **`verifyChain()` vs `decisions.verifyChain()`** — DISTINCT surfaces:
|
|
16
|
+
// - `decisions.verifyChain(systemId)` — verifies a SINGLE system's
|
|
17
|
+
// decision chain (per-system hash-chain integrity). Takes a UUID
|
|
18
|
+
// path parameter; emits `ChainVerificationResult` with per-record
|
|
19
|
+
// tampered/broken arrays. Used for security/ops signals on a
|
|
20
|
+
// specific system.
|
|
21
|
+
// - `auditLog.verifyChain()` — verifies the entire ORG's audit log
|
|
22
|
+
// chain (org-wide tamper-evidence). Takes NO arguments; emits
|
|
23
|
+
// `AuditChainVerificationResult` with one `brokenAt` UUID (if any).
|
|
24
|
+
// Used by compliance auditors for end-to-end audit-log integrity.
|
|
25
|
+
// Different responsibility, different kernel route, different consumer
|
|
26
|
+
// audience. The two methods complement each other.
|
|
27
|
+
//
|
|
28
|
+
// Dual-auth admin scope: the kernel route gates on
|
|
29
|
+
// `requireSessionOrApiKey(request, { sessionRoles: ["admin"],
|
|
30
|
+
// apiKeyPermissions: [API_KEY_PERMISSIONS.ADMIN] })` (verified at
|
|
31
|
+
// `src/app/api/v1/audit-log/export/route.ts:66-68`) — the identical
|
|
32
|
+
// dual-auth pattern the abacPolicies cluster uses. The SDK's transport
|
|
33
|
+
// always sends `x-api-key`, so the api-key path is the only one
|
|
34
|
+
// reachable from SDK consumers: **HTTP 401** for no / invalid /
|
|
35
|
+
// expired api-key, **HTTP 403** for a VALID api-key whose permissions
|
|
36
|
+
// do NOT include `ADMIN`. The two are DISTINCT.
|
|
37
|
+
//
|
|
38
|
+
// (Corrected — session-22 hostile review #2. The prior comment claimed
|
|
39
|
+
// "HTTP 401 for both no-auth AND insufficient-permission"; that
|
|
40
|
+
// mis-read the kernel TEST at `audit-log/export/__tests__/route.test.ts`,
|
|
41
|
+
// which MOCKS `AuthError(401)` and never exercises the real
|
|
42
|
+
// `requireSessionOrApiKey` middleware. The middleware's
|
|
43
|
+
// `requireApiKeyWithPermission` path returns 403 for the
|
|
44
|
+
// insufficient-permission case — same surface as every abacPolicies
|
|
45
|
+
// method.)
|
|
46
|
+
//
|
|
47
|
+
// Three wire formats:
|
|
48
|
+
// - `jsonl` (default): one JSON object per line, structured
|
|
49
|
+
// `AuditLogRecord` shape. SDK validates wire shape strictly and
|
|
50
|
+
// yields parsed objects.
|
|
51
|
+
// - `ecs`: one Elastic Common Schema 8.x event per line (JSON-encoded).
|
|
52
|
+
// Rides the same `application/x-ndjson` content-type as `jsonl` —
|
|
53
|
+
// consumers parse the ECS shape themselves; SDK yields `unknown`.
|
|
54
|
+
// - `cef`: one ArcSight CEF v0 line per row. Plain text, NOT JSON.
|
|
55
|
+
// Content-Type: `text/plain`. SDK yields raw `string`.
|
|
56
|
+
//
|
|
57
|
+
// Cursor pagination via response HEADER (`x-attestry-next-cursor`) —
|
|
58
|
+
// NOT a body trailer (asymmetric with `decisions.export`). The SDK
|
|
59
|
+
// auto-paginates by default: the iterator transparently fetches the
|
|
60
|
+
// next page whenever the current page exhausts and the kernel emitted
|
|
61
|
+
// a next-cursor header. Single-page behavior is opt-in via
|
|
62
|
+
// `autoPaginate: false`.
|
|
63
|
+
//
|
|
64
|
+
// Compound cursor format: `<ISO-8601-UTC>:<UUID>`. Bare ISO is a legacy
|
|
65
|
+
// fallback (may skip same-microsecond rows; documented kernel behavior).
|
|
66
|
+
import { AttestryError } from "../errors.js";
|
|
67
|
+
import { parseLinesResponse } from "../lines-parser.js";
|
|
68
|
+
import { parseNDJSONResponse } from "../ndjson-parser.js";
|
|
69
|
+
import { readInputField } from "./safe-input-read.js";
|
|
70
|
+
// Module-load snapshot of `Object.hasOwn` — defends against a
|
|
71
|
+
// late-loading hostile/buggy npm dependency that overrides the global
|
|
72
|
+
// (e.g., `Object.hasOwn = () => true`). Without the snapshot, the
|
|
73
|
+
// prototype-pollution defenses in `validateAuditChainVerificationResponse`
|
|
74
|
+
// would use whatever Object.hasOwn the dependency replaced it with at
|
|
75
|
+
// request time. Snapshotting at module load captures the original
|
|
76
|
+
// implementation BEFORE most consumer code has a chance to monkey-
|
|
77
|
+
// patch.
|
|
78
|
+
//
|
|
79
|
+
// Mirror of `batch.ts` / `gate.ts` / `check.ts` / `compliance-check.ts`
|
|
80
|
+
// pattern. Used on the response side of `verifyChain()` (this method
|
|
81
|
+
// has no input; the input-side defense is N/A — no fields to guard).
|
|
82
|
+
// Carry-forward of session-16 second-hostile-review MEDIUM #3 +
|
|
83
|
+
// session-17 build-round baked-in pattern.
|
|
84
|
+
const objectHasOwn = Object.hasOwn;
|
|
85
|
+
/**
|
|
86
|
+
* Public closed-enum of supported wire formats. Mirrors the kernel's
|
|
87
|
+
* `VALID_FORMATS` const at `src/lib/audit-log/export-helpers.ts:15-19`.
|
|
88
|
+
* Drift-pinned in `src/lib/incidents/__tests__/sdk-drift.test.ts`.
|
|
89
|
+
*
|
|
90
|
+
* Forward-compat: when a future format is added (e.g., `csv`), bump the
|
|
91
|
+
* SDK minor version and extend this array. The kernel's parseFormat
|
|
92
|
+
* function returns 400 for any value outside this set; the SDK
|
|
93
|
+
* pre-rejects invalid values synchronously as `TypeError` (build-round
|
|
94
|
+
* D5 — closed-enum input validates at the SDK boundary so the failure
|
|
95
|
+
* is faster + clearer than waiting for the server's 400).
|
|
96
|
+
*/
|
|
97
|
+
export const AUDIT_LOG_EXPORT_FORMATS = Object.freeze([
|
|
98
|
+
"jsonl",
|
|
99
|
+
"ecs",
|
|
100
|
+
"cef",
|
|
101
|
+
]);
|
|
102
|
+
/**
|
|
103
|
+
* AuditLog resource — sibling to `IncidentsResource`, `DecisionsResource`,
|
|
104
|
+
* `ChatResource`. Today wraps two endpoints (`export` + `verifyChain`);
|
|
105
|
+
* the class is the landing pad for future audit-log methods.
|
|
106
|
+
*/
|
|
107
|
+
export class AuditLogResource {
|
|
108
|
+
client;
|
|
109
|
+
constructor(client) {
|
|
110
|
+
this.client = client;
|
|
111
|
+
}
|
|
112
|
+
export(input, options) {
|
|
113
|
+
// Top-level shape — when provided, must be a non-null, non-array
|
|
114
|
+
// object. typeof null === "object" and typeof [] === "object", so
|
|
115
|
+
// guard both explicitly. Unlike decisions.export (which requires
|
|
116
|
+
// `input.systemId`), auditLog.export's input is OPTIONAL — `()`
|
|
117
|
+
// and `(undefined)` are both valid.
|
|
118
|
+
if (input !== undefined) {
|
|
119
|
+
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
120
|
+
throw new TypeError("auditLog.export: `input` must be an object when provided");
|
|
121
|
+
}
|
|
122
|
+
// Snapshot each input field via `readInputField` — a throwing
|
|
123
|
+
// accessor surfaces as the documented synchronous `TypeError`
|
|
124
|
+
// rather than the getter's raw exception (session-22 hostile
|
|
125
|
+
// review #1 — the SDK-wide MEDIUM-1 getter-throws fix). The
|
|
126
|
+
// export helper below still receives the original `input` (a
|
|
127
|
+
// throwing getter is caught here first, so it is never reached).
|
|
128
|
+
const format = readInputField(input, "format", "auditLog.export");
|
|
129
|
+
const cursor = readInputField(input, "cursor", "auditLog.export");
|
|
130
|
+
const limit = readInputField(input, "limit", "auditLog.export");
|
|
131
|
+
const autoPaginate = readInputField(input, "autoPaginate", "auditLog.export");
|
|
132
|
+
// format: closed enum. Pre-reject invalid values synchronously
|
|
133
|
+
// (faster + clearer than waiting for server's 400). Build-round D5.
|
|
134
|
+
if (format !== undefined) {
|
|
135
|
+
if (typeof format !== "string" ||
|
|
136
|
+
!AUDIT_LOG_EXPORT_FORMATS.includes(format)) {
|
|
137
|
+
throw new TypeError(`auditLog.export: \`format\` must be one of ${AUDIT_LOG_EXPORT_FORMATS.join(", ")} when provided`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// cursor: non-empty string + lone-surrogate guard.
|
|
141
|
+
if (cursor !== undefined) {
|
|
142
|
+
if (typeof cursor !== "string" || cursor.length === 0) {
|
|
143
|
+
throw new TypeError("auditLog.export: `cursor` must be a non-empty string when provided");
|
|
144
|
+
}
|
|
145
|
+
assertEncodableQueryString(cursor, "cursor", "auditLog.export");
|
|
146
|
+
}
|
|
147
|
+
// limit: positive finite integer. NaN / Infinity / fractional /
|
|
148
|
+
// <= 0 rejected. Stricter than kernel's silent coerce-to-1000
|
|
149
|
+
// (build-round D4).
|
|
150
|
+
if (limit !== undefined) {
|
|
151
|
+
if (typeof limit !== "number" ||
|
|
152
|
+
!Number.isInteger(limit) ||
|
|
153
|
+
limit <= 0) {
|
|
154
|
+
throw new TypeError("auditLog.export: `limit` must be a positive integer when provided");
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
// autoPaginate: strict boolean.
|
|
158
|
+
if (autoPaginate !== undefined && typeof autoPaginate !== "boolean") {
|
|
159
|
+
throw new TypeError("auditLog.export: `autoPaginate` must be a boolean when provided");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return runAuditLogExport(this.client, input, options);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Verify the integrity of the org's audit-log hash chain. Returns
|
|
166
|
+
* an `AuditChainVerificationResult` describing whether the chain is
|
|
167
|
+
* intact, and (when broken) the UUID of the entry where verification
|
|
168
|
+
* failed.
|
|
169
|
+
*
|
|
170
|
+
* Wraps `GET /api/v1/audit-chain/verify` — no input, no query
|
|
171
|
+
* parameters, no body. The caller's org is implicit from auth; the
|
|
172
|
+
* kernel fetches up to 5000 audit-log entries ordered ascending by
|
|
173
|
+
* timestamp and runs `verifyAuditChain()` on them.
|
|
174
|
+
*
|
|
175
|
+
* **API-key auth scope** (uses `requireApiKey` DIRECT — distinct
|
|
176
|
+
* from BOTH siblings): the kernel route calls `requireApiKey(request)`
|
|
177
|
+
* at `route.ts:31` with NO permission scoping AND NO subsequent
|
|
178
|
+
* role check. Any valid API key for the org can verify the chain.
|
|
179
|
+
* Returns **HTTP 401** for no/invalid API key; the `requireApiKey`
|
|
180
|
+
* branch does NOT distinguish "no key" from "invalid key". **Note**:
|
|
181
|
+
* `auditLog.export` ALSO calls `requireApiKey(request)` directly at
|
|
182
|
+
* its route but then performs a separate role/permission check
|
|
183
|
+
* (ADMIN-only — see audit-log/export route). `verifyChain()` is
|
|
184
|
+
* distinct: NO role check, NO permission filter. The 403 path is
|
|
185
|
+
* unreachable for this route, and ALL valid api-keys in the org
|
|
186
|
+
* succeed (in contrast to `auditLog.export` where only ADMIN keys
|
|
187
|
+
* succeed).
|
|
188
|
+
*
|
|
189
|
+
* **Kernel-side invariant (session-19 review-3 L2 carry-forward)**:
|
|
190
|
+
* the route at `route.ts:32` reads `apiKeyUser.orgId` and passes
|
|
191
|
+
* it directly to the Drizzle `eq(schema.auditLog.orgId, orgId)`
|
|
192
|
+
* filter without a null-guard. The SDK assumes the kernel's
|
|
193
|
+
* `requireApiKey` returns an `ApiKeyUser` with a non-null `orgId`
|
|
194
|
+
* (i.e., the `apiKeys` table's `orgId` column is NOT NULL). If
|
|
195
|
+
* a future schema migration relaxes that constraint, the route
|
|
196
|
+
* could match zero rows on a malformed key and return a vacuous-
|
|
197
|
+
* truth `valid: true` verdict, masking actual chain tampering.
|
|
198
|
+
* This is a kernel-side hardening concern (SDK cannot detect
|
|
199
|
+
* it from the wire); flagged here so a future kernel-hardening
|
|
200
|
+
* audit knows to add a runtime guard in `requireApiKey`.
|
|
201
|
+
*
|
|
202
|
+
* **CRITICAL contract — does NOT throw on `valid: false`** (carry-
|
|
203
|
+
* forward invariant #12). The kernel returns HTTP 200 with
|
|
204
|
+
* `valid: false` on a tampered chain; the SDK MUST resolve the
|
|
205
|
+
* Promise with the verdict body. Top-level structural failures
|
|
206
|
+
* (auth, rate limit, internal) throw `AttestryAPIError`. Mirror of
|
|
207
|
+
* `decisions.verifyChain`'s same contract.
|
|
208
|
+
*
|
|
209
|
+
* **NO `writeAuditLog` side effect** — the verifier is quiet
|
|
210
|
+
* (asymmetric with `gate.evaluate` / `batch.submit` which both write
|
|
211
|
+
* audit-log entries). Writing to the audit log while verifying it
|
|
212
|
+
* would be ironic; the kernel team avoided this. `auditLog.verifyChain`
|
|
213
|
+
* is a pure read.
|
|
214
|
+
*
|
|
215
|
+
* **Silent kernel-side truncation at 5000 entries** (invariant #50).
|
|
216
|
+
* The kernel's audit-log fetch is capped at 5000 entries
|
|
217
|
+
* (`route.ts:51`: `.limit(5000)`). For an org with more than 5000
|
|
218
|
+
* audit-log entries, only the OLDEST 5000 are verified by this call.
|
|
219
|
+
* The kernel does NOT emit a "truncated" flag — `totalEntries`
|
|
220
|
+
* equals the number of rows fetched, NOT the org's full audit-log
|
|
221
|
+
* row count. **Documented kernel surface gap**; the SDK does NOT
|
|
222
|
+
* mask. Consumers with high-volume audit logs should be aware that
|
|
223
|
+
* the kernel's verifier sees a stale window of the chain. A future
|
|
224
|
+
* kernel pagination or higher limit would be additive (the SDK
|
|
225
|
+
* forwards `totalEntries` verbatim).
|
|
226
|
+
*
|
|
227
|
+
* **Kernel-side 30-second timeout** (`maxDuration = 30` at
|
|
228
|
+
* `route.ts:14`). The SDK does NOT enforce a client-side timeout
|
|
229
|
+
* (consumers manage via `options.signal`), but the kernel's
|
|
230
|
+
* function-runtime cap bounds the verification latency on the
|
|
231
|
+
* server side. Cron-job consumers should budget call latency
|
|
232
|
+
* relative to this cap — a near-5000-entry verification can take
|
|
233
|
+
* tens of seconds under high SHA-256 load. A future kernel raise
|
|
234
|
+
* (e.g., 30 → 60s) would relax this cap; downstream cron-job
|
|
235
|
+
* sizing assumptions should be revisited.
|
|
236
|
+
*
|
|
237
|
+
* **Pollution-safe discriminator pattern** — branch on
|
|
238
|
+
* `result.valid` (closed-enum boolean) to detect a broken chain,
|
|
239
|
+
* NOT on `result.brokenAt === undefined`. The kernel emits
|
|
240
|
+
* `brokenAt` as an OWN-PROPERTY of the response only when the
|
|
241
|
+
* chain is broken; on a valid chain it's omitted entirely (kernel
|
|
242
|
+
* uses a conditional spread at `route.ts:72`). Under
|
|
243
|
+
* `Object.prototype.brokenAt = <value>` pollution, the equality
|
|
244
|
+
* check walks the prototype and reads the polluted value —
|
|
245
|
+
* returning false (i.e., "field is present") even when the
|
|
246
|
+
* own-property is genuinely absent. The SDK's response-side
|
|
247
|
+
* validator uses `Object.hasOwn` (snapshotted at module load) to
|
|
248
|
+
* defend against this; consumers should use `result.valid`
|
|
249
|
+
* directly. Carry-forward of session-17 first-hostile-review
|
|
250
|
+
* MEDIUM #3 + session-18 build-round baked-in pattern.
|
|
251
|
+
*
|
|
252
|
+
* **Errors** — ordered by kernel firing precedence (rate-limit →
|
|
253
|
+
* auth-pass-or-401 → DB fetch → verifier → 500-catchall):
|
|
254
|
+
* - `AttestryAPIError` (status 429) — rate limit FIRES FIRST
|
|
255
|
+
* (auto-retried by default — invariant #18; per-IP rate-limit
|
|
256
|
+
* key `audit-chain-verify:${ip}` against the standard
|
|
257
|
+
* `apiLimiter`).
|
|
258
|
+
* - `AttestryAPIError` (status 401) — no API key OR invalid key.
|
|
259
|
+
* Single 401 surface (NO 403 — the route has no permission
|
|
260
|
+
* filter). Surfaces only when `requireApiKey` throws an
|
|
261
|
+
* `AuthError`; the route's catch (route.ts:74-77) propagates
|
|
262
|
+
* `error.statusCode` (401 for `AuthError`).
|
|
263
|
+
* - `AttestryAPIError` (status 500) — internal kernel error
|
|
264
|
+
* (scrubbed message via `internalErrorResponse`). Surfaces
|
|
265
|
+
* when the DB connection drops mid-fetch, the verifier
|
|
266
|
+
* throws, OR `requireApiKey`'s INFRASTRUCTURE fails (e.g., a
|
|
267
|
+
* DB error during the API-key lookup, not an auth-rejection;
|
|
268
|
+
* non-`AuthError` errors fall through to the route's
|
|
269
|
+
* `internalErrorResponse` catchall at route.ts:78).
|
|
270
|
+
* - `AttestryError` ("request aborted by caller") — caller-
|
|
271
|
+
* supplied `options.signal` fired (pre-aborted or mid-flight).
|
|
272
|
+
* - `AttestryError` (P2 hardening) — kernel response failed
|
|
273
|
+
* SDK-side shape validation. See "Response-shape validation"
|
|
274
|
+
* below.
|
|
275
|
+
* - `AttestryAPIError` (P3 hardening) — kernel response had a
|
|
276
|
+
* wrong Content-Type (transport-level guard).
|
|
277
|
+
*
|
|
278
|
+
* **Notably ABSENT from the error surface**:
|
|
279
|
+
* - **No 400** — this method has no input; nothing to reject for
|
|
280
|
+
* malformed input. The route doesn't parse query params or
|
|
281
|
+
* body.
|
|
282
|
+
* - **No 402 plan-limit** — verifyChain is a READ; no quota.
|
|
283
|
+
* - **No 403** — no permission filter on the route (any key with
|
|
284
|
+
* a valid org binding succeeds). Asymmetric with
|
|
285
|
+
* `auditLog.export` (which gates on ADMIN role).
|
|
286
|
+
* - **No 404** — orgId is implicit from auth; no path/query
|
|
287
|
+
* parameters that could mismatch.
|
|
288
|
+
* - **No 413** — the kernel's `.limit(5000)` silently caps the
|
|
289
|
+
* fetch; oversize orgs see a truncated verification (NOT a
|
|
290
|
+
* 413). Documented kernel surface gap.
|
|
291
|
+
* - **No 422** — no Zod schema (no body, no query).
|
|
292
|
+
* - **No TypeError from SDK** — this method has no input to
|
|
293
|
+
* validate.
|
|
294
|
+
*
|
|
295
|
+
* **Response-shape validation** (P2 hardening — symmetric defense
|
|
296
|
+
* on response side via the module-load `objectHasOwn` snapshot;
|
|
297
|
+
* mirror of `batch.ts` / `gate.ts` patterns):
|
|
298
|
+
* - Rejects with `AttestryError` if the response isn't a non-null,
|
|
299
|
+
* non-array object.
|
|
300
|
+
* - Rejects if `valid` isn't a boolean.
|
|
301
|
+
* - Rejects if `entriesVerified` / `totalEntries` aren't numbers.
|
|
302
|
+
* - Rejects if `firstEntry` / `lastEntry` aren't `null` OR a
|
|
303
|
+
* string.
|
|
304
|
+
* - Rejects if `brokenAt` is OWN-PROPERTY present but NOT a
|
|
305
|
+
* string. (When absent, the field is forward-compatibly
|
|
306
|
+
* undefined — kernel omits it on valid chains.)
|
|
307
|
+
* - Each response field read goes through the module-load
|
|
308
|
+
* `objectHasOwn` snapshot — defends against
|
|
309
|
+
* `Object.prototype.<field>` pollution masking a missing field.
|
|
310
|
+
*
|
|
311
|
+
* **Transport-shape validation** (P3 hardening):
|
|
312
|
+
* - Rejects with `AttestryAPIError` if the kernel responds with
|
|
313
|
+
* a non-`application/json` Content-Type. NOTE: `valid: false`
|
|
314
|
+
* is a normal 200 response and resolves the promise (carry-
|
|
315
|
+
* forward invariant #12); only structural failures throw.
|
|
316
|
+
*
|
|
317
|
+
* @example Detect a tampered audit log
|
|
318
|
+
* ```ts
|
|
319
|
+
* const verdict = await client.auditLog.verifyChain();
|
|
320
|
+
* if (!verdict.valid) {
|
|
321
|
+
* // brokenAt is an OWN-PROPERTY only on broken chains.
|
|
322
|
+
* await notifySecurity({
|
|
323
|
+
* entryId: verdict.brokenAt,
|
|
324
|
+
* verifiedUpTo: verdict.entriesVerified,
|
|
325
|
+
* totalEntries: verdict.totalEntries,
|
|
326
|
+
* });
|
|
327
|
+
* }
|
|
328
|
+
* console.log(`Verified ${verdict.entriesVerified}/${verdict.totalEntries} entries`);
|
|
329
|
+
* ```
|
|
330
|
+
*
|
|
331
|
+
* @example Schedule periodic verification (cron job)
|
|
332
|
+
* ```ts
|
|
333
|
+
* // Run hourly from a cron — surfaces tampering within an hour
|
|
334
|
+
* // of occurrence (high-frequency monitoring for compliance-
|
|
335
|
+
* // critical orgs).
|
|
336
|
+
* try {
|
|
337
|
+
* const verdict = await client.auditLog.verifyChain();
|
|
338
|
+
* if (!verdict.valid) {
|
|
339
|
+
* await pageOncall({ brokenAt: verdict.brokenAt });
|
|
340
|
+
* }
|
|
341
|
+
* } catch (err) {
|
|
342
|
+
* if (err instanceof AttestryAPIError && err.status === 429) {
|
|
343
|
+
* // Back off — verifier is rate-limited per IP.
|
|
344
|
+
* return;
|
|
345
|
+
* }
|
|
346
|
+
* throw err;
|
|
347
|
+
* }
|
|
348
|
+
* ```
|
|
349
|
+
*/
|
|
350
|
+
verifyChain(options) {
|
|
351
|
+
// No input → no SDK-side input validation. Mirror of decisions'
|
|
352
|
+
// `verifyChain(systemId)` minus the path-segment validation.
|
|
353
|
+
return this.client
|
|
354
|
+
._request({
|
|
355
|
+
method: "GET",
|
|
356
|
+
path: "/api/v1/audit-chain/verify",
|
|
357
|
+
options,
|
|
358
|
+
})
|
|
359
|
+
.then((result) => validateAuditChainVerificationResponse(result));
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Synchronously verify a query-string value is encodable via
|
|
364
|
+
* `encodeURIComponent`. Mirrors the helper at `decisions.ts` (carry-
|
|
365
|
+
* forward invariant #32 — URIError defect-class is uniformly handled).
|
|
366
|
+
*
|
|
367
|
+
* Duplicated rather than shared because cross-resource imports between
|
|
368
|
+
* `audit-log.ts` and `decisions.ts` would create a graph cycle hazard
|
|
369
|
+
* — both files want to remain leaf-resource modules. A future SDK
|
|
370
|
+
* refactor may extract validation helpers to a shared module
|
|
371
|
+
* (e.g., `src/validate.ts`) when a third caller shows up; for now the
|
|
372
|
+
* duplication is intentional and documented.
|
|
373
|
+
*/
|
|
374
|
+
function assertEncodableQueryString(value, fieldName, methodName) {
|
|
375
|
+
try {
|
|
376
|
+
encodeURIComponent(value);
|
|
377
|
+
}
|
|
378
|
+
catch (err) {
|
|
379
|
+
throw new TypeError(`${methodName}: \`${fieldName}\` contains invalid UTF-16 sequences (${
|
|
380
|
+
// encodeURIComponent always throws URIError (an Error subclass),
|
|
381
|
+
// so the String(err) branch is unreachable. Defense-in-depth
|
|
382
|
+
// marker for the v8 coverage tool.
|
|
383
|
+
/* v8 ignore next */
|
|
384
|
+
err instanceof Error ? err.message : String(err)})`, { cause: err });
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Internal — async generator backing `auditLog.export`. Lazy: the
|
|
389
|
+
* request is NOT issued until the first iteration.
|
|
390
|
+
*
|
|
391
|
+
* Auto-pagination loop: each iteration fetches one page; in
|
|
392
|
+
* `autoPaginate: true` (the default), continues until the kernel stops
|
|
393
|
+
* emitting `x-attestry-next-cursor`. Each page's INITIAL fetch goes
|
|
394
|
+
* through the retry middleware (429 + Retry-After). Mid-stream errors
|
|
395
|
+
* bubble per invariant #20.
|
|
396
|
+
*
|
|
397
|
+
* Format dispatch:
|
|
398
|
+
* - `cef` → `parseLinesResponse` (raw line splitter; yields strings)
|
|
399
|
+
* - `jsonl` → `parseNDJSONResponse` + per-row shape validation; yields
|
|
400
|
+
* `AuditLogRecord`. The SDK is the typed boundary — a malformed row
|
|
401
|
+
* (schema bug, version skew) throws `AttestryError` rather than
|
|
402
|
+
* yielding `undefined as string`.
|
|
403
|
+
* - `ecs` → `parseNDJSONResponse`; yields `unknown` (consumer parses
|
|
404
|
+
* ECS event shape themselves).
|
|
405
|
+
*/
|
|
406
|
+
async function* runAuditLogExport(client, input, options) {
|
|
407
|
+
const format = input?.format ?? "jsonl";
|
|
408
|
+
const autoPaginate = input?.autoPaginate ?? true;
|
|
409
|
+
// The transport's `expectedContentType` guard runs per-request;
|
|
410
|
+
// jsonl/ecs ride `application/x-ndjson`, cef rides `text/plain`.
|
|
411
|
+
// Drives both the `Accept:` request header AND the response
|
|
412
|
+
// content-type fail-fast guard (single source of truth).
|
|
413
|
+
const expectedContentType = format === "cef" ? "text/plain" : "application/x-ndjson";
|
|
414
|
+
let cursor = input?.cursor;
|
|
415
|
+
while (true) {
|
|
416
|
+
// Build query — `format` always, `cursor` and `limit` only when
|
|
417
|
+
// provided. `encodeQuery` skips `undefined` values.
|
|
418
|
+
const query = {
|
|
419
|
+
format,
|
|
420
|
+
cursor,
|
|
421
|
+
limit: input?.limit,
|
|
422
|
+
};
|
|
423
|
+
const response = await client._streamRequest({
|
|
424
|
+
path: "/api/v1/audit-log/export",
|
|
425
|
+
query,
|
|
426
|
+
options,
|
|
427
|
+
expectedContentType,
|
|
428
|
+
});
|
|
429
|
+
if (format === "cef") {
|
|
430
|
+
// Raw line splitter — yields strings.
|
|
431
|
+
for await (const line of parseLinesResponse(response)) {
|
|
432
|
+
yield line;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
else if (format === "ecs") {
|
|
436
|
+
// ECS rides NDJSON; SDK doesn't enforce ECS shape — consumers
|
|
437
|
+
// parse via their own ECS schema. Forward-compatible with
|
|
438
|
+
// future ECS-version additions.
|
|
439
|
+
for await (const raw of parseNDJSONResponse(response)) {
|
|
440
|
+
yield raw;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
// jsonl — validate AuditLogRecord shape per row.
|
|
445
|
+
for await (const raw of parseNDJSONResponse(response)) {
|
|
446
|
+
// Every NDJSON line must be a JSON object — neither primitives
|
|
447
|
+
// nor arrays nor nulls are valid rows. Defensive: kernel always
|
|
448
|
+
// emits objects, but a parser yielding e.g. a bare number would
|
|
449
|
+
// otherwise pass through as `frame` of type `unknown`.
|
|
450
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
|
|
451
|
+
throw new AttestryError("auditLog.export: NDJSON line was not a JSON object");
|
|
452
|
+
}
|
|
453
|
+
const obj = raw;
|
|
454
|
+
// Validate per-row shape. The SDK is the typed boundary — a
|
|
455
|
+
// malformed row (schema bug, version skew) throws here rather
|
|
456
|
+
// than yielding `undefined as string` to the caller. Same
|
|
457
|
+
// invariant as decisions.export's per-record validation.
|
|
458
|
+
if (typeof obj.id !== "string" ||
|
|
459
|
+
typeof obj.timestamp !== "string" ||
|
|
460
|
+
typeof obj.orgId !== "string" ||
|
|
461
|
+
(obj.userId !== null && typeof obj.userId !== "string") ||
|
|
462
|
+
typeof obj.action !== "string" ||
|
|
463
|
+
(obj.resourceType !== null && typeof obj.resourceType !== "string") ||
|
|
464
|
+
(obj.resourceId !== null && typeof obj.resourceId !== "string") ||
|
|
465
|
+
// `details` is `unknown` (jsonb) — pass through as-is.
|
|
466
|
+
(obj.ipAddress !== null && typeof obj.ipAddress !== "string") ||
|
|
467
|
+
(obj.userAgent !== null && typeof obj.userAgent !== "string") ||
|
|
468
|
+
(obj.sessionId !== null && typeof obj.sessionId !== "string") ||
|
|
469
|
+
(obj.entryHash !== null && typeof obj.entryHash !== "string") ||
|
|
470
|
+
(obj.previousEntryHash !== null &&
|
|
471
|
+
typeof obj.previousEntryHash !== "string")) {
|
|
472
|
+
throw new AttestryError("auditLog.export: NDJSON record missing required fields or wrong type");
|
|
473
|
+
}
|
|
474
|
+
yield {
|
|
475
|
+
id: obj.id,
|
|
476
|
+
timestamp: obj.timestamp,
|
|
477
|
+
orgId: obj.orgId,
|
|
478
|
+
userId: obj.userId,
|
|
479
|
+
action: obj.action,
|
|
480
|
+
resourceType: obj.resourceType,
|
|
481
|
+
resourceId: obj.resourceId,
|
|
482
|
+
details: obj.details,
|
|
483
|
+
ipAddress: obj.ipAddress,
|
|
484
|
+
userAgent: obj.userAgent,
|
|
485
|
+
sessionId: obj.sessionId,
|
|
486
|
+
entryHash: obj.entryHash,
|
|
487
|
+
previousEntryHash: obj.previousEntryHash,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
// Pagination decision. The cursor lives in the response HEADER
|
|
492
|
+
// (NOT a body trailer — asymmetric with decisions.export, build-
|
|
493
|
+
// round D8). After draining the body, read the header. If absent
|
|
494
|
+
// OR autoPaginate is false, exit. Otherwise feed the cursor back
|
|
495
|
+
// into the next page's query.
|
|
496
|
+
const nextCursor = response.headers.get("x-attestry-next-cursor");
|
|
497
|
+
if (!autoPaginate || nextCursor === null) {
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
cursor = nextCursor;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
/**
|
|
504
|
+
* P2 hardening: validate the `verifyChain()` response's 5 always-
|
|
505
|
+
* present fields plus the optional `brokenAt` field. Symmetric
|
|
506
|
+
* prototype-pollution defense — read EACH field via the module-load
|
|
507
|
+
* `objectHasOwn` snapshot so a hostile npm dep polluting
|
|
508
|
+
* `Object.prototype.<field>` cannot mask a kernel regression that
|
|
509
|
+
* drops the field (per session-16 second-hostile-review MEDIUM #3
|
|
510
|
+
* carry-forward — defense applied on the response boundary even when
|
|
511
|
+
* the input boundary is empty).
|
|
512
|
+
*
|
|
513
|
+
* Returns the validated `result` (typed `AuditChainVerificationResult`)
|
|
514
|
+
* on success; throws `AttestryError` on any shape violation. Extracted
|
|
515
|
+
* as a free function so the resource method body stays focused on
|
|
516
|
+
* request construction.
|
|
517
|
+
*
|
|
518
|
+
* **`brokenAt` is INTENTIONALLY omitted from the wire on valid chains**
|
|
519
|
+
* — the kernel uses a conditional spread
|
|
520
|
+
* `...(result.brokenAtId ? { brokenAt: result.brokenAtId } : {})` at
|
|
521
|
+
* `route.ts:72`. When `valid: true`, the field is NOT an own-property
|
|
522
|
+
* of the response. The validator checks `objectHasOwn` BEFORE
|
|
523
|
+
* type-checking, so absent-and-untyped is forward-compatible —
|
|
524
|
+
* present-but-non-string is the actual regression signal.
|
|
525
|
+
*
|
|
526
|
+
* **Number-field validation is `typeof X === "number"` ONLY**
|
|
527
|
+
* (session-19 review-2 LOW-2 carry-forward — faithful-courier
|
|
528
|
+
* documented asymmetry). The check accepts `NaN`, `Infinity`,
|
|
529
|
+
* `-Infinity`, `-0`, and `MAX_SAFE_INTEGER+1` (which loses
|
|
530
|
+
* precision) — these would all pass `typeof === "number"`. This
|
|
531
|
+
* is INTENTIONAL: JSON.parse on a kernel-emitted JSON string can
|
|
532
|
+
* NEVER produce NaN / Infinity (JSON spec doesn't represent them);
|
|
533
|
+
* `-0` and large numbers round-trip with whatever precision the
|
|
534
|
+
* JSON gave. Tightening to `Number.isFinite` / `Number.isInteger`
|
|
535
|
+
* would be a stricter contract than the kernel emits — the SDK
|
|
536
|
+
* stays faithful-courier on numbers (symmetric with batch /
|
|
537
|
+
* decisions / gate's response validators). **If a future wire
|
|
538
|
+
* format (msgpack, CBOR) is added, this asymmetry must be
|
|
539
|
+
* revisited** — the new wire could carry NaN literally, and the
|
|
540
|
+
* faithful-courier semantic would leak NaN to consumers.
|
|
541
|
+
*
|
|
542
|
+
* **Single-field rejection semantics** (session-19 review-3 M1
|
|
543
|
+
* carry-forward — UX/diagnostic clarification). The validator
|
|
544
|
+
* checks fields SEQUENTIALLY in declaration order (valid →
|
|
545
|
+
* entriesVerified → totalEntries → firstEntry → lastEntry →
|
|
546
|
+
* brokenAt) and throws on the FIRST failing field. If a kernel
|
|
547
|
+
* regression drops MULTIPLE fields at once, the consumer sees
|
|
548
|
+
* ONLY the first failing field's diagnostic — they must fix
|
|
549
|
+
* the fixture and re-run to surface the next failure. This
|
|
550
|
+
* matches batch.ts / gate.ts / check.ts patterns (project
|
|
551
|
+
* convention for response-shape validators); accumulating into
|
|
552
|
+
* a multi-field message would diverge from the rest of the SDK.
|
|
553
|
+
* Trade-off accepted: consistency-with-project-pattern wins
|
|
554
|
+
* over single-cycle full-diagnostic.
|
|
555
|
+
*/
|
|
556
|
+
function validateAuditChainVerificationResponse(result) {
|
|
557
|
+
if (result === null ||
|
|
558
|
+
typeof result !== "object" ||
|
|
559
|
+
Array.isArray(result)) {
|
|
560
|
+
throw new AttestryError(`auditLog.verifyChain: expected an object response from the kernel ` +
|
|
561
|
+
`(got ${describeType(result)})`);
|
|
562
|
+
}
|
|
563
|
+
const obj = result;
|
|
564
|
+
const valid = objectHasOwn(obj, "valid") ? obj.valid : undefined;
|
|
565
|
+
if (typeof valid !== "boolean") {
|
|
566
|
+
throw new AttestryError(`auditLog.verifyChain: expected response.valid to be a boolean ` +
|
|
567
|
+
`(got ${describeType(valid)})`);
|
|
568
|
+
}
|
|
569
|
+
const entriesVerified = objectHasOwn(obj, "entriesVerified")
|
|
570
|
+
? obj.entriesVerified
|
|
571
|
+
: undefined;
|
|
572
|
+
if (typeof entriesVerified !== "number") {
|
|
573
|
+
throw new AttestryError(`auditLog.verifyChain: expected response.entriesVerified to be a number ` +
|
|
574
|
+
`(got ${describeType(entriesVerified)})`);
|
|
575
|
+
}
|
|
576
|
+
const totalEntries = objectHasOwn(obj, "totalEntries")
|
|
577
|
+
? obj.totalEntries
|
|
578
|
+
: undefined;
|
|
579
|
+
if (typeof totalEntries !== "number") {
|
|
580
|
+
throw new AttestryError(`auditLog.verifyChain: expected response.totalEntries to be a number ` +
|
|
581
|
+
`(got ${describeType(totalEntries)})`);
|
|
582
|
+
}
|
|
583
|
+
const firstEntry = objectHasOwn(obj, "firstEntry")
|
|
584
|
+
? obj.firstEntry
|
|
585
|
+
: undefined;
|
|
586
|
+
if (firstEntry !== null && typeof firstEntry !== "string") {
|
|
587
|
+
throw new AttestryError(`auditLog.verifyChain: expected response.firstEntry to be a string or null ` +
|
|
588
|
+
`(got ${describeType(firstEntry)})`);
|
|
589
|
+
}
|
|
590
|
+
const lastEntry = objectHasOwn(obj, "lastEntry")
|
|
591
|
+
? obj.lastEntry
|
|
592
|
+
: undefined;
|
|
593
|
+
if (lastEntry !== null && typeof lastEntry !== "string") {
|
|
594
|
+
throw new AttestryError(`auditLog.verifyChain: expected response.lastEntry to be a string or null ` +
|
|
595
|
+
`(got ${describeType(lastEntry)})`);
|
|
596
|
+
}
|
|
597
|
+
// `brokenAt` is OPTIONAL — kernel omits it entirely on valid chains.
|
|
598
|
+
// Only enforce the type guard when the field is an own-property of
|
|
599
|
+
// the response. Absent-AND-untyped is the valid-chain shape.
|
|
600
|
+
if (objectHasOwn(obj, "brokenAt")) {
|
|
601
|
+
const brokenAt = obj.brokenAt;
|
|
602
|
+
if (typeof brokenAt !== "string") {
|
|
603
|
+
throw new AttestryError(`auditLog.verifyChain: expected response.brokenAt to be a string when present ` +
|
|
604
|
+
`(got ${describeType(brokenAt)})`);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return result;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Human-readable type description for error messages. Distinguishes
|
|
611
|
+
* `null` and `array` from generic `object`. Duplicated in
|
|
612
|
+
* `decisions.ts`, `incidents.ts`, `regulatory-changes.ts`,
|
|
613
|
+
* `compliance-check.ts`, `check.ts`, `gate.ts`, `batch.ts` per
|
|
614
|
+
* project pattern (small helper, leaf-resource modules, no shared
|
|
615
|
+
* module yet).
|
|
616
|
+
*
|
|
617
|
+
* All four branches are reachable through `validateAuditChainVerificationResponse`'s
|
|
618
|
+
* call sites: top-level shape check (null + array + non-object scalar),
|
|
619
|
+
* per-field type guards (each field's `describeType(<wrong type>)`
|
|
620
|
+
* exercised by tests).
|
|
621
|
+
*/
|
|
622
|
+
function describeType(value) {
|
|
623
|
+
if (value === null)
|
|
624
|
+
return "null";
|
|
625
|
+
if (Array.isArray(value))
|
|
626
|
+
return "array";
|
|
627
|
+
return typeof value;
|
|
628
|
+
}
|
|
629
|
+
//# sourceMappingURL=audit-log.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit-log.js","sourceRoot":"","sources":["../../src/resources/audit-log.ts"],"names":[],"mappings":"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"}
|