@atlasent/sdk 2.10.0 → 2.13.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/README.md +9 -0
- package/dist/behavior.cjs +2 -1
- package/dist/behavior.cjs.map +1 -1
- package/dist/behavior.d.cts +32 -5
- package/dist/behavior.d.ts +32 -5
- package/dist/behavior.js +2 -1
- package/dist/behavior.js.map +1 -1
- package/dist/hono.cjs +800 -23
- package/dist/hono.cjs.map +1 -1
- package/dist/hono.d.cts +2 -2
- package/dist/hono.d.ts +2 -2
- package/dist/hono.js +799 -23
- package/dist/hono.js.map +1 -1
- package/dist/index.cjs +4765 -2051
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2375 -565
- package/dist/index.d.ts +2375 -565
- package/dist/index.js +4686 -2039
- package/dist/index.js.map +1 -1
- package/dist/{protect-C0t0fP1y.d.cts → protect-Bk9q12ia.d.cts} +588 -281
- package/dist/{protect-C0t0fP1y.d.ts → protect-Bk9q12ia.d.ts} +588 -281
- package/package.json +1 -1
package/dist/hono.cjs
CHANGED
|
@@ -86,7 +86,8 @@ var KNOWN_PERMIT_OUTCOMES = /* @__PURE__ */ new Set([
|
|
|
86
86
|
"permit_consumed",
|
|
87
87
|
"permit_expired",
|
|
88
88
|
"permit_revoked",
|
|
89
|
-
"permit_not_found"
|
|
89
|
+
"permit_not_found",
|
|
90
|
+
"permit_signing_key_revoked"
|
|
90
91
|
]);
|
|
91
92
|
function normalizePermitOutcome(raw) {
|
|
92
93
|
if (raw !== void 0 && KNOWN_PERMIT_OUTCOMES.has(raw)) {
|
|
@@ -147,7 +148,198 @@ var AtlaSentDeniedError = class extends AtlaSentError {
|
|
|
147
148
|
get isNotFound() {
|
|
148
149
|
return this.outcome === "permit_not_found";
|
|
149
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* `true` when the permit's signing key KID appears in the
|
|
153
|
+
* trust-root revocation list (ADR-005 D3 R2/R3 key rotation).
|
|
154
|
+
*/
|
|
155
|
+
get isSigningKeyRevoked() {
|
|
156
|
+
return this.outcome === "permit_signing_key_revoked";
|
|
157
|
+
}
|
|
150
158
|
};
|
|
159
|
+
var BundleVerificationError = class extends AtlaSentError {
|
|
160
|
+
name = "BundleVerificationError";
|
|
161
|
+
reason;
|
|
162
|
+
snapshotValidUntil;
|
|
163
|
+
snapshotFetchedAt;
|
|
164
|
+
snapshotSource;
|
|
165
|
+
kid;
|
|
166
|
+
constructor(init) {
|
|
167
|
+
super(`AtlaSent audit bundle verification failed: ${init.reason}`);
|
|
168
|
+
this.reason = init.reason;
|
|
169
|
+
this.snapshotValidUntil = init.snapshotValidUntil;
|
|
170
|
+
this.snapshotFetchedAt = init.snapshotFetchedAt;
|
|
171
|
+
this.snapshotSource = init.snapshotSource;
|
|
172
|
+
this.kid = init.kid;
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// src/trustRoot.ts
|
|
177
|
+
var import_node_fs = require("fs");
|
|
178
|
+
var import_node_url = require("url");
|
|
179
|
+
var import_node_path = require("path");
|
|
180
|
+
var import_meta = {};
|
|
181
|
+
var REFRESH_INTERVAL_MS_DEFAULT = 4 * 60 * 60 * 1e3;
|
|
182
|
+
var REFRESH_INTERVAL_MS_FLOOR = 5 * 60 * 1e3;
|
|
183
|
+
var KEYS_BASE_URL = "https://keys.atlasent.io/.well-known";
|
|
184
|
+
var _halfLifeWarningEmitted = false;
|
|
185
|
+
var _expiredWarningEmitted = false;
|
|
186
|
+
var TrustRootManager = class {
|
|
187
|
+
_snapshot;
|
|
188
|
+
_refreshTimer = null;
|
|
189
|
+
_opts;
|
|
190
|
+
constructor(initialSnapshot, opts = {}) {
|
|
191
|
+
this._snapshot = initialSnapshot;
|
|
192
|
+
const intervalMs = Math.max(
|
|
193
|
+
opts.refreshIntervalMs ?? REFRESH_INTERVAL_MS_DEFAULT,
|
|
194
|
+
REFRESH_INTERVAL_MS_FLOOR
|
|
195
|
+
);
|
|
196
|
+
this._opts = {
|
|
197
|
+
refreshBaseUrl: opts.refreshBaseUrl ?? KEYS_BASE_URL,
|
|
198
|
+
refreshIntervalMs: intervalMs,
|
|
199
|
+
disableRefresh: opts.disableRefresh ?? false,
|
|
200
|
+
fetch: opts.fetch ?? (typeof globalThis !== "undefined" && globalThis.fetch ? globalThis.fetch.bind(globalThis) : ((_url) => Promise.reject(new Error("fetch not available"))))
|
|
201
|
+
};
|
|
202
|
+
if (!this._opts.disableRefresh) {
|
|
203
|
+
this._scheduleRefresh();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
getSnapshot() {
|
|
207
|
+
return this._snapshot;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Check whether the snapshot is expired, emit one-time warnings at
|
|
211
|
+
* half-life and expiry. Returns "ok" | "half_life" | "expired".
|
|
212
|
+
*
|
|
213
|
+
* Emits console.warn once per process at half-life (ADR-005 D3).
|
|
214
|
+
* Emits console.warn once per process on expiry.
|
|
215
|
+
*/
|
|
216
|
+
checkExpiry() {
|
|
217
|
+
const snap = this._snapshot;
|
|
218
|
+
const now = Date.now();
|
|
219
|
+
const issuedAt = new Date(snap.issued_at).getTime();
|
|
220
|
+
const validUntil = new Date(snap.valid_until).getTime();
|
|
221
|
+
if (now > validUntil) {
|
|
222
|
+
if (!_expiredWarningEmitted) {
|
|
223
|
+
_expiredWarningEmitted = true;
|
|
224
|
+
const daysAgo = Math.floor((now - validUntil) / (24 * 60 * 60 * 1e3));
|
|
225
|
+
console.warn(
|
|
226
|
+
`[atlasent] Trust snapshot expired ${daysAgo} day(s) ago (valid_until: ${snap.valid_until}). Update to a newer SDK build or enable allowExpiredSnapshot.`
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
return "expired";
|
|
230
|
+
}
|
|
231
|
+
const window = validUntil - issuedAt;
|
|
232
|
+
const halfLife = issuedAt + window / 2;
|
|
233
|
+
if (now > halfLife) {
|
|
234
|
+
if (!_halfLifeWarningEmitted) {
|
|
235
|
+
_halfLifeWarningEmitted = true;
|
|
236
|
+
const daysLeft = Math.floor((validUntil - now) / (24 * 60 * 60 * 1e3));
|
|
237
|
+
console.warn(
|
|
238
|
+
`[atlasent] Trust snapshot at half-life: expires in ${daysLeft} day(s) (valid_until: ${snap.valid_until}). Plan an SDK update.`
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
return "half_life";
|
|
242
|
+
}
|
|
243
|
+
return "ok";
|
|
244
|
+
}
|
|
245
|
+
/** Look up a key entry by kid. Returns undefined if not found. */
|
|
246
|
+
lookupKey(kid) {
|
|
247
|
+
return this._snapshot.keys.find((k) => k.kid === kid);
|
|
248
|
+
}
|
|
249
|
+
/** Returns true if the kid appears in revoked_keys. */
|
|
250
|
+
isRevoked(kid) {
|
|
251
|
+
return this._snapshot.revoked_keys.some((r) => r.kid === kid);
|
|
252
|
+
}
|
|
253
|
+
/** Replace the snapshot (e.g. after a successful refresh). */
|
|
254
|
+
replaceSnapshot(next) {
|
|
255
|
+
this._snapshot = next;
|
|
256
|
+
}
|
|
257
|
+
stopRefresh() {
|
|
258
|
+
if (this._refreshTimer !== null) {
|
|
259
|
+
clearInterval(this._refreshTimer);
|
|
260
|
+
this._refreshTimer = null;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
_scheduleRefresh() {
|
|
264
|
+
this._refreshTimer = setInterval(() => {
|
|
265
|
+
void this._doRefresh();
|
|
266
|
+
}, this._opts.refreshIntervalMs);
|
|
267
|
+
if (this._refreshTimer && typeof this._refreshTimer === "object" && "unref" in this._refreshTimer) {
|
|
268
|
+
this._refreshTimer.unref();
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
async _doRefresh() {
|
|
272
|
+
try {
|
|
273
|
+
const base = this._opts.refreshBaseUrl.replace(/\/$/, "");
|
|
274
|
+
const [keysRes, revocRes] = await Promise.all([
|
|
275
|
+
this._opts.fetch(`${base}/atlasent-verifier-keys.json`),
|
|
276
|
+
this._opts.fetch(`${base}/atlasent-revocations.json`)
|
|
277
|
+
]);
|
|
278
|
+
const indexRes = await this._opts.fetch(`${base}/atlasent-trust-root.json`);
|
|
279
|
+
if (!keysRes.ok || !revocRes.ok || !indexRes.ok) return;
|
|
280
|
+
const [keys, revoc, index] = await Promise.all([
|
|
281
|
+
keysRes.json(),
|
|
282
|
+
revocRes.json(),
|
|
283
|
+
indexRes.json()
|
|
284
|
+
]);
|
|
285
|
+
if (!index.valid_until || !Array.isArray(keys.keys)) return;
|
|
286
|
+
this._snapshot = {
|
|
287
|
+
valid_until: index.valid_until,
|
|
288
|
+
issued_at: index.issued_at ?? this._snapshot.issued_at,
|
|
289
|
+
keys: keys.keys,
|
|
290
|
+
revoked_keys: revoc.revoked_keys ?? [],
|
|
291
|
+
revoked_identities: revoc.revoked_identities ?? []
|
|
292
|
+
};
|
|
293
|
+
} catch {
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
function _loadVendorSnapshot() {
|
|
298
|
+
try {
|
|
299
|
+
let packageRoot;
|
|
300
|
+
try {
|
|
301
|
+
const thisFile = (0, import_node_url.fileURLToPath)(import_meta.url);
|
|
302
|
+
packageRoot = (0, import_node_path.resolve)((0, import_node_path.dirname)(thisFile), "..", "..");
|
|
303
|
+
} catch {
|
|
304
|
+
packageRoot = (0, import_node_path.resolve)(__dirname, "..", "..");
|
|
305
|
+
}
|
|
306
|
+
const vendorDir = (0, import_node_path.resolve)(packageRoot, "vendor", "trust-root");
|
|
307
|
+
const index = JSON.parse(
|
|
308
|
+
(0, import_node_fs.readFileSync)((0, import_node_path.resolve)(vendorDir, "atlasent-trust-root.json"), "utf8")
|
|
309
|
+
);
|
|
310
|
+
const verifierKeys = JSON.parse(
|
|
311
|
+
(0, import_node_fs.readFileSync)((0, import_node_path.resolve)(vendorDir, "atlasent-verifier-keys.json"), "utf8")
|
|
312
|
+
);
|
|
313
|
+
const revocations = JSON.parse(
|
|
314
|
+
(0, import_node_fs.readFileSync)((0, import_node_path.resolve)(vendorDir, "atlasent-revocations.json"), "utf8")
|
|
315
|
+
);
|
|
316
|
+
return {
|
|
317
|
+
valid_until: index.valid_until,
|
|
318
|
+
issued_at: index.issued_at,
|
|
319
|
+
keys: verifierKeys.keys ?? [],
|
|
320
|
+
revoked_keys: revocations.revoked_keys ?? [],
|
|
321
|
+
revoked_identities: revocations.revoked_identities ?? []
|
|
322
|
+
};
|
|
323
|
+
} catch {
|
|
324
|
+
return {
|
|
325
|
+
valid_until: "2099-01-01T00:00:00Z",
|
|
326
|
+
issued_at: "2026-05-26T00:00:00Z",
|
|
327
|
+
keys: [],
|
|
328
|
+
revoked_keys: [],
|
|
329
|
+
revoked_identities: []
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
var _globalManager = null;
|
|
334
|
+
function getGlobalTrustRootManager(opts) {
|
|
335
|
+
if (!_globalManager) {
|
|
336
|
+
_globalManager = new TrustRootManager(
|
|
337
|
+
_loadVendorSnapshot(),
|
|
338
|
+
opts ?? { disableRefresh: false }
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
return _globalManager;
|
|
342
|
+
}
|
|
151
343
|
|
|
152
344
|
// src/types.ts
|
|
153
345
|
var PRODUCTION_DEPLOY_ACTION = "production.deploy";
|
|
@@ -174,7 +366,13 @@ function normalizeEvaluateRequest(input) {
|
|
|
174
366
|
actor_id: legacy.agent
|
|
175
367
|
};
|
|
176
368
|
if (legacy.context !== void 0) normalized.context = legacy.context;
|
|
177
|
-
|
|
369
|
+
const l = legacy;
|
|
370
|
+
if (l.explain !== void 0) normalized.explain = l.explain;
|
|
371
|
+
if (l.environment !== void 0) normalized.environment = l.environment;
|
|
372
|
+
if (l.resource !== void 0) normalized.resource = l.resource;
|
|
373
|
+
if (l.current_state !== void 0) normalized.current_state = l.current_state;
|
|
374
|
+
if (l.proposed_state !== void 0) normalized.proposed_state = l.proposed_state;
|
|
375
|
+
if (l.execution_binding !== void 0) normalized.execution_binding = l.execution_binding;
|
|
178
376
|
return normalized;
|
|
179
377
|
}
|
|
180
378
|
return input;
|
|
@@ -182,9 +380,9 @@ function normalizeEvaluateRequest(input) {
|
|
|
182
380
|
|
|
183
381
|
// src/retry.ts
|
|
184
382
|
var DEFAULT_RETRY_POLICY = {
|
|
185
|
-
maxAttempts:
|
|
186
|
-
baseDelayMs:
|
|
187
|
-
maxDelayMs:
|
|
383
|
+
maxAttempts: 3,
|
|
384
|
+
baseDelayMs: 250,
|
|
385
|
+
maxDelayMs: 1e4
|
|
188
386
|
};
|
|
189
387
|
var RETRYABLE_CODES = /* @__PURE__ */ new Set([
|
|
190
388
|
"network",
|
|
@@ -233,10 +431,371 @@ function clampUnit(n) {
|
|
|
233
431
|
return n;
|
|
234
432
|
}
|
|
235
433
|
|
|
434
|
+
// src/scim.ts
|
|
435
|
+
var SCIM_USER_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:User";
|
|
436
|
+
var SCIM_GROUP_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:Group";
|
|
437
|
+
function scimUsersPath(orgId) {
|
|
438
|
+
return `/scim/v2/${encodeURIComponent(orgId)}/Users`;
|
|
439
|
+
}
|
|
440
|
+
function scimGroupsPath(orgId) {
|
|
441
|
+
return `/scim/v2/${encodeURIComponent(orgId)}/Groups`;
|
|
442
|
+
}
|
|
443
|
+
function buildScimQuery(filter, startIndex, count) {
|
|
444
|
+
const params = new URLSearchParams();
|
|
445
|
+
if (filter !== void 0) params.set("filter", filter);
|
|
446
|
+
if (startIndex !== void 0) params.set("startIndex", String(startIndex));
|
|
447
|
+
if (count !== void 0) params.set("count", String(count));
|
|
448
|
+
return params.size > 0 ? params : void 0;
|
|
449
|
+
}
|
|
450
|
+
function makeScimClient(postFn, getFn, putFn, deleteFn) {
|
|
451
|
+
const users = {
|
|
452
|
+
async list(params) {
|
|
453
|
+
const qs = buildScimQuery(
|
|
454
|
+
params.filter,
|
|
455
|
+
params.startIndex,
|
|
456
|
+
params.count
|
|
457
|
+
);
|
|
458
|
+
const { body } = await getFn(
|
|
459
|
+
scimUsersPath(params.orgId),
|
|
460
|
+
qs
|
|
461
|
+
);
|
|
462
|
+
return body;
|
|
463
|
+
},
|
|
464
|
+
async create(orgId, user) {
|
|
465
|
+
const payload = user.schemas ? user : { ...user, schemas: [SCIM_USER_SCHEMA] };
|
|
466
|
+
const { body } = await postFn(scimUsersPath(orgId), payload);
|
|
467
|
+
return body;
|
|
468
|
+
},
|
|
469
|
+
async update(orgId, id, user) {
|
|
470
|
+
const payload = user.schemas ? user : { ...user, schemas: [SCIM_USER_SCHEMA] };
|
|
471
|
+
const { body } = await putFn(
|
|
472
|
+
`${scimUsersPath(orgId)}/${encodeURIComponent(id)}`,
|
|
473
|
+
payload
|
|
474
|
+
);
|
|
475
|
+
return body;
|
|
476
|
+
},
|
|
477
|
+
async delete(orgId, id) {
|
|
478
|
+
return deleteFn(
|
|
479
|
+
`${scimUsersPath(orgId)}/${encodeURIComponent(id)}`
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
const groups = {
|
|
484
|
+
async list(params) {
|
|
485
|
+
const qs = buildScimQuery(
|
|
486
|
+
params.filter,
|
|
487
|
+
params.startIndex,
|
|
488
|
+
params.count
|
|
489
|
+
);
|
|
490
|
+
const { body } = await getFn(
|
|
491
|
+
scimGroupsPath(params.orgId),
|
|
492
|
+
qs
|
|
493
|
+
);
|
|
494
|
+
return body;
|
|
495
|
+
},
|
|
496
|
+
async create(orgId, group) {
|
|
497
|
+
const payload = group["schemas"] ? group : { ...group, schemas: [SCIM_GROUP_SCHEMA] };
|
|
498
|
+
const { body } = await postFn(
|
|
499
|
+
scimGroupsPath(orgId),
|
|
500
|
+
payload
|
|
501
|
+
);
|
|
502
|
+
return body;
|
|
503
|
+
},
|
|
504
|
+
async delete(orgId, id) {
|
|
505
|
+
return deleteFn(
|
|
506
|
+
`${scimGroupsPath(orgId)}/${encodeURIComponent(id)}`
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
return { users, groups };
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// src/evidence-bundle.ts
|
|
514
|
+
function wireToBundle(w) {
|
|
515
|
+
return {
|
|
516
|
+
bundleId: w.bundle_id,
|
|
517
|
+
orgId: w.org_id,
|
|
518
|
+
incidentId: w.incident_id,
|
|
519
|
+
status: w.status,
|
|
520
|
+
includedPermits: w.included_permits ?? [],
|
|
521
|
+
includeOverrides: w.include_overrides ?? false,
|
|
522
|
+
format: w.format,
|
|
523
|
+
createdAt: w.created_at,
|
|
524
|
+
expiresAt: w.expires_at,
|
|
525
|
+
...w.download_url !== void 0 ? { downloadUrl: w.download_url } : {},
|
|
526
|
+
...w.metadata !== void 0 ? { metadata: w.metadata } : {}
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
function makeEvidenceBundleClient(postFn, getFn, getRawFn) {
|
|
530
|
+
return {
|
|
531
|
+
async list(params = {}) {
|
|
532
|
+
const qs = new URLSearchParams();
|
|
533
|
+
if (params.executionId !== void 0) qs.set("execution_id", params.executionId);
|
|
534
|
+
if (params.limit !== void 0) qs.set("limit", String(params.limit));
|
|
535
|
+
if (params.cursor !== void 0) qs.set("cursor", params.cursor);
|
|
536
|
+
const { body } = await getFn("/v1/evidence-bundles", qs.size > 0 ? qs : void 0);
|
|
537
|
+
return {
|
|
538
|
+
bundles: (body.bundles ?? []).map(wireToBundle),
|
|
539
|
+
nextCursor: body.next_cursor ?? null
|
|
540
|
+
};
|
|
541
|
+
},
|
|
542
|
+
async create(params) {
|
|
543
|
+
const payload = {
|
|
544
|
+
incident_id: params.incidentId
|
|
545
|
+
};
|
|
546
|
+
if (params.includedPermits !== void 0) {
|
|
547
|
+
payload["included_permits"] = params.includedPermits;
|
|
548
|
+
}
|
|
549
|
+
if (params.includeOverrides !== void 0) {
|
|
550
|
+
payload["include_overrides"] = params.includeOverrides;
|
|
551
|
+
}
|
|
552
|
+
const { body } = await postFn(
|
|
553
|
+
"/v1/evidence-bundles",
|
|
554
|
+
payload
|
|
555
|
+
);
|
|
556
|
+
return wireToBundle(body);
|
|
557
|
+
},
|
|
558
|
+
async get(bundleId) {
|
|
559
|
+
const { body } = await getFn(
|
|
560
|
+
`/v1/evidence-bundles/${encodeURIComponent(bundleId)}`
|
|
561
|
+
);
|
|
562
|
+
return wireToBundle(body);
|
|
563
|
+
},
|
|
564
|
+
async download(bundleId, format = "json") {
|
|
565
|
+
const qs = new URLSearchParams({ format });
|
|
566
|
+
const raw = await getRawFn(
|
|
567
|
+
`/v1/evidence-bundles/${encodeURIComponent(bundleId)}/download?${qs}`
|
|
568
|
+
);
|
|
569
|
+
return Buffer.from(raw);
|
|
570
|
+
}
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// src/auth.ts
|
|
575
|
+
function wireToTokenResponse(w) {
|
|
576
|
+
return {
|
|
577
|
+
accessToken: w.access_token,
|
|
578
|
+
refreshToken: w.refresh_token,
|
|
579
|
+
tokenType: w.token_type,
|
|
580
|
+
expiresIn: w.expires_in,
|
|
581
|
+
...w.scope !== void 0 ? { scope: w.scope } : {},
|
|
582
|
+
...w.idp_id !== void 0 ? { idpId: w.idp_id } : {}
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
function wireToIdpConnection(w) {
|
|
586
|
+
return {
|
|
587
|
+
id: w.id,
|
|
588
|
+
name: w.name,
|
|
589
|
+
provider: w.provider,
|
|
590
|
+
enabled: w.enabled,
|
|
591
|
+
isDefault: w.default,
|
|
592
|
+
...w.domains !== void 0 ? { domains: w.domains } : {},
|
|
593
|
+
createdAt: w.created_at
|
|
594
|
+
};
|
|
595
|
+
}
|
|
596
|
+
function makeAuthClient(postFn, getFn) {
|
|
597
|
+
return {
|
|
598
|
+
async refresh(refreshToken) {
|
|
599
|
+
const { body } = await postFn(
|
|
600
|
+
"/v1/auth/token/refresh",
|
|
601
|
+
{ refresh_token: refreshToken, grant_type: "refresh_token" }
|
|
602
|
+
);
|
|
603
|
+
return wireToTokenResponse(body);
|
|
604
|
+
},
|
|
605
|
+
async refreshWithIdp(idpId, refreshToken) {
|
|
606
|
+
const path = `/v1/auth/idp/${encodeURIComponent(idpId)}/token/refresh`;
|
|
607
|
+
const { body } = await postFn(path, {
|
|
608
|
+
refresh_token: refreshToken,
|
|
609
|
+
grant_type: "refresh_token",
|
|
610
|
+
idp_id: idpId
|
|
611
|
+
});
|
|
612
|
+
return wireToTokenResponse(body);
|
|
613
|
+
},
|
|
614
|
+
async listIdpConnections() {
|
|
615
|
+
const { body } = await getFn(
|
|
616
|
+
"/v1/auth/idp-connections"
|
|
617
|
+
);
|
|
618
|
+
return (body.connections ?? []).map(wireToIdpConnection);
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// src/sso.ts
|
|
624
|
+
function wireToSsoConnection(w) {
|
|
625
|
+
return {
|
|
626
|
+
id: w.id,
|
|
627
|
+
organizationId: w.organization_id,
|
|
628
|
+
name: w.name,
|
|
629
|
+
protocol: w.protocol,
|
|
630
|
+
idpEntityId: w.idp_entity_id,
|
|
631
|
+
metadataUrl: w.metadata_url,
|
|
632
|
+
metadataXml: w.metadata_xml,
|
|
633
|
+
emailDomain: w.email_domain,
|
|
634
|
+
enforceForDomain: w.enforce_for_domain,
|
|
635
|
+
isActive: w.is_active,
|
|
636
|
+
supabaseProviderId: w.supabase_provider_id,
|
|
637
|
+
createdBy: w.created_by,
|
|
638
|
+
createdAt: w.created_at,
|
|
639
|
+
updatedAt: w.updated_at
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
function wireToSsoJitRule(w) {
|
|
643
|
+
return {
|
|
644
|
+
id: w.id,
|
|
645
|
+
connectionId: w.connection_id,
|
|
646
|
+
organizationId: w.organization_id,
|
|
647
|
+
claimAttribute: w.claim_attribute,
|
|
648
|
+
claimValue: w.claim_value,
|
|
649
|
+
grantedRole: w.granted_role,
|
|
650
|
+
precedence: w.precedence,
|
|
651
|
+
isActive: w.is_active,
|
|
652
|
+
createdAt: w.created_at,
|
|
653
|
+
updatedAt: w.updated_at
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
function wireToSsoReadiness(w) {
|
|
657
|
+
return {
|
|
658
|
+
connectionConfigured: w.connection_configured,
|
|
659
|
+
connectionTested: w.connection_tested,
|
|
660
|
+
breakGlassSet: w.break_glass_set,
|
|
661
|
+
serviceApiKeysReviewed: w.service_api_keys_reviewed
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
function ssoConnectionInputToWire(input) {
|
|
665
|
+
const w = {};
|
|
666
|
+
if (input.name !== void 0) w["name"] = input.name;
|
|
667
|
+
if (input.protocol !== void 0) w["protocol"] = input.protocol;
|
|
668
|
+
if (input.idpEntityId !== void 0) w["idp_entity_id"] = input.idpEntityId;
|
|
669
|
+
if (input.metadataUrl !== void 0) w["metadata_url"] = input.metadataUrl;
|
|
670
|
+
if (input.metadataXml !== void 0) w["metadata_xml"] = input.metadataXml;
|
|
671
|
+
if (input.emailDomain !== void 0) w["email_domain"] = input.emailDomain;
|
|
672
|
+
if (input.enforceForDomain !== void 0) w["enforce_for_domain"] = input.enforceForDomain;
|
|
673
|
+
return w;
|
|
674
|
+
}
|
|
675
|
+
function makeSsoClient(getFn, postFn, patchFn, deleteFn) {
|
|
676
|
+
return {
|
|
677
|
+
async listConnections() {
|
|
678
|
+
const { body } = await getFn("/v1/sso/connections");
|
|
679
|
+
return { connections: (body.connections ?? []).map(wireToSsoConnection) };
|
|
680
|
+
},
|
|
681
|
+
async getConnection(id) {
|
|
682
|
+
const { body } = await getFn(`/v1/sso/connections/${encodeURIComponent(id)}`);
|
|
683
|
+
return wireToSsoConnection(body);
|
|
684
|
+
},
|
|
685
|
+
async createConnection(input) {
|
|
686
|
+
const { body } = await postFn("/v1/sso/connections", ssoConnectionInputToWire(input));
|
|
687
|
+
return wireToSsoConnection(body);
|
|
688
|
+
},
|
|
689
|
+
async updateConnection(id, input) {
|
|
690
|
+
const { body } = await patchFn(
|
|
691
|
+
`/v1/sso/connections/${encodeURIComponent(id)}`,
|
|
692
|
+
ssoConnectionInputToWire(input)
|
|
693
|
+
);
|
|
694
|
+
return wireToSsoConnection(body);
|
|
695
|
+
},
|
|
696
|
+
async deleteConnection(id) {
|
|
697
|
+
await deleteFn(`/v1/sso/connections/${encodeURIComponent(id)}`);
|
|
698
|
+
},
|
|
699
|
+
async activateConnection(id) {
|
|
700
|
+
const { body } = await postFn(
|
|
701
|
+
`/v1/sso/connections/${encodeURIComponent(id)}/activate`,
|
|
702
|
+
{}
|
|
703
|
+
);
|
|
704
|
+
return { ok: body.ok, supabaseProviderId: body.supabase_provider_id };
|
|
705
|
+
},
|
|
706
|
+
async enforce(action) {
|
|
707
|
+
const { body } = await postFn("/v1/sso/enforce", { action });
|
|
708
|
+
return {
|
|
709
|
+
ok: body.ok,
|
|
710
|
+
action: body.action,
|
|
711
|
+
enforceSso: body.enforce_sso,
|
|
712
|
+
enforceSsoAt: body.enforce_sso_at
|
|
713
|
+
};
|
|
714
|
+
},
|
|
715
|
+
async getStatus() {
|
|
716
|
+
const { body } = await getFn("/v1/sso/status");
|
|
717
|
+
return wireToSsoReadiness(body.readiness);
|
|
718
|
+
},
|
|
719
|
+
async listJitRules(connectionId) {
|
|
720
|
+
const qs = connectionId ? new URLSearchParams({ connection_id: connectionId }) : void 0;
|
|
721
|
+
const { body } = await getFn("/v1/sso/jit-rules", qs);
|
|
722
|
+
return { rules: (body.rules ?? []).map(wireToSsoJitRule) };
|
|
723
|
+
},
|
|
724
|
+
async createJitRule(input) {
|
|
725
|
+
const payload = {
|
|
726
|
+
connection_id: input.connectionId,
|
|
727
|
+
claim_attribute: input.claimAttribute,
|
|
728
|
+
claim_value: input.claimValue,
|
|
729
|
+
granted_role: input.grantedRole
|
|
730
|
+
};
|
|
731
|
+
if (input.precedence !== void 0) payload["precedence"] = input.precedence;
|
|
732
|
+
const { body } = await postFn("/v1/sso/jit-rules", payload);
|
|
733
|
+
return wireToSsoJitRule(body);
|
|
734
|
+
},
|
|
735
|
+
async patchJitRule(id, patch) {
|
|
736
|
+
const payload = {};
|
|
737
|
+
if (patch.claimAttribute !== void 0) payload["claim_attribute"] = patch.claimAttribute;
|
|
738
|
+
if (patch.claimValue !== void 0) payload["claim_value"] = patch.claimValue;
|
|
739
|
+
if (patch.grantedRole !== void 0) payload["granted_role"] = patch.grantedRole;
|
|
740
|
+
if (patch.precedence !== void 0) payload["precedence"] = patch.precedence;
|
|
741
|
+
if (patch.isActive !== void 0) payload["is_active"] = patch.isActive;
|
|
742
|
+
const { body } = await patchFn(
|
|
743
|
+
`/v1/sso/jit-rules/${encodeURIComponent(id)}`,
|
|
744
|
+
payload
|
|
745
|
+
);
|
|
746
|
+
return wireToSsoJitRule(body);
|
|
747
|
+
},
|
|
748
|
+
async deleteJitRule(id) {
|
|
749
|
+
await deleteFn(`/v1/sso/jit-rules/${encodeURIComponent(id)}`);
|
|
750
|
+
}
|
|
751
|
+
};
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// src/access-governance-log.ts
|
|
755
|
+
function wireToEvent(w) {
|
|
756
|
+
return {
|
|
757
|
+
id: w.id,
|
|
758
|
+
eventType: w.event_type,
|
|
759
|
+
orgId: w.org_id,
|
|
760
|
+
actorId: w.actor_id,
|
|
761
|
+
actorEmail: w.actor_email,
|
|
762
|
+
ipAddress: w.ip_address,
|
|
763
|
+
metadata: w.metadata ?? {},
|
|
764
|
+
createdAt: w.created_at
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
function makeAccessGovernanceLogClient(getFn) {
|
|
768
|
+
return {
|
|
769
|
+
async list(query = {}) {
|
|
770
|
+
const qs = new URLSearchParams();
|
|
771
|
+
if (query.limit !== void 0) qs.set("limit", String(query.limit));
|
|
772
|
+
if (query.cursor) qs.set("cursor", query.cursor);
|
|
773
|
+
if (query.eventType) qs.set("event_type", query.eventType);
|
|
774
|
+
if (query.actorId) qs.set("actor_id", query.actorId);
|
|
775
|
+
if (query.from) qs.set("from", query.from);
|
|
776
|
+
if (query.to) qs.set("to", query.to);
|
|
777
|
+
const { body } = await getFn(
|
|
778
|
+
"/v1/access-governance-log",
|
|
779
|
+
qs.size > 0 ? qs : void 0
|
|
780
|
+
);
|
|
781
|
+
return {
|
|
782
|
+
events: (body.events ?? []).map(wireToEvent),
|
|
783
|
+
nextCursor: body.next_cursor,
|
|
784
|
+
totalCount: body.total_count ?? 0
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
|
|
236
790
|
// src/client.ts
|
|
237
791
|
var DEFAULT_BASE_URL = "https://api.atlasent.io";
|
|
238
792
|
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
239
|
-
var SDK_VERSION = "2.
|
|
793
|
+
var SDK_VERSION = "2.10.0";
|
|
794
|
+
var warnedBrowser = false;
|
|
795
|
+
var V1_EVALUATE_BATCH_PATH = "/v1/evaluate/batch";
|
|
796
|
+
var V1_EVALUATE_BATCH_LEGACY_PATH = "/v1-evaluate-batch";
|
|
797
|
+
var V1_EVALUATE_STREAM_PATH = "/v1/evaluate/stream";
|
|
798
|
+
var V1_EVALUATE_STREAM_LEGACY_PATH = "/v1-evaluate-stream";
|
|
240
799
|
function _buildUserAgent() {
|
|
241
800
|
const isNode2 = typeof process !== "undefined" && typeof process?.versions?.node === "string";
|
|
242
801
|
return isNode2 ? `@atlasent/sdk/${SDK_VERSION} node/${process.version}` : `@atlasent/sdk/${SDK_VERSION} browser`;
|
|
@@ -300,6 +859,18 @@ var AtlaSentClient = class {
|
|
|
300
859
|
fetchImpl;
|
|
301
860
|
userAgent;
|
|
302
861
|
retryPolicy;
|
|
862
|
+
/** SCIM 2.0 provisioning sub-client. Access as `client.scim`. */
|
|
863
|
+
scim;
|
|
864
|
+
/** Evidence bundle sub-client. Access as `client.evidenceBundles`. */
|
|
865
|
+
evidenceBundles;
|
|
866
|
+
/** Auth / token management sub-client. Access as `client.auth`. */
|
|
867
|
+
auth;
|
|
868
|
+
/** SSO administration sub-client. Access as `client.sso`. */
|
|
869
|
+
sso;
|
|
870
|
+
/** Access governance log sub-client. Access as `client.accessGovernanceLog`. */
|
|
871
|
+
accessGovernanceLog;
|
|
872
|
+
/** Trust-root snapshot manager for this client instance. */
|
|
873
|
+
trustRoot;
|
|
303
874
|
constructor(options) {
|
|
304
875
|
if (!options.apiKey || typeof options.apiKey !== "string") {
|
|
305
876
|
throw new AtlaSentError("apiKey is required", {
|
|
@@ -312,6 +883,12 @@ var AtlaSentClient = class {
|
|
|
312
883
|
{ code: "network" }
|
|
313
884
|
);
|
|
314
885
|
}
|
|
886
|
+
if (!warnedBrowser && typeof globalThis["window"] !== "undefined" && typeof process === "undefined") {
|
|
887
|
+
warnedBrowser = true;
|
|
888
|
+
console.warn(
|
|
889
|
+
"[@atlasent/sdk] Running in a browser environment. API keys should not be exposed in client-side bundles. Use a server-side proxy instead."
|
|
890
|
+
);
|
|
891
|
+
}
|
|
315
892
|
this.apiKey = _validateApiKey(options.apiKey);
|
|
316
893
|
this.baseUrl = _enforceTls(options.baseUrl ?? DEFAULT_BASE_URL).replace(
|
|
317
894
|
/\/+$/,
|
|
@@ -321,6 +898,44 @@ var AtlaSentClient = class {
|
|
|
321
898
|
this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
322
899
|
this.userAgent = _buildUserAgent();
|
|
323
900
|
this.retryPolicy = mergePolicy(options.retryPolicy ?? {});
|
|
901
|
+
this.scim = makeScimClient(
|
|
902
|
+
(path, body, query) => this._post(path, body, query),
|
|
903
|
+
(path, query) => this._get(path, query),
|
|
904
|
+
(path, body) => this._put(path, body),
|
|
905
|
+
(path) => this._delete(path)
|
|
906
|
+
);
|
|
907
|
+
this.evidenceBundles = makeEvidenceBundleClient(
|
|
908
|
+
(path, body) => this._post(path, body),
|
|
909
|
+
(path, query) => this._get(path, query),
|
|
910
|
+
(path) => this._getRaw(path)
|
|
911
|
+
);
|
|
912
|
+
this.auth = makeAuthClient(
|
|
913
|
+
(path, body) => this._post(path, body),
|
|
914
|
+
(path) => this._get(path)
|
|
915
|
+
);
|
|
916
|
+
this.sso = makeSsoClient(
|
|
917
|
+
(path, query) => this._get(path, query),
|
|
918
|
+
(path, body) => this._post(path, body),
|
|
919
|
+
(path, body) => this._patch(path, body),
|
|
920
|
+
(path) => this._delete(path)
|
|
921
|
+
);
|
|
922
|
+
this.accessGovernanceLog = makeAccessGovernanceLogClient(
|
|
923
|
+
(path, query) => this._get(path, query)
|
|
924
|
+
);
|
|
925
|
+
if (options.trustRootUrl !== void 0 || options.trustSnapshotRefreshMs !== void 0) {
|
|
926
|
+
const globalSnap = getGlobalTrustRootManager({ disableRefresh: true }).getSnapshot();
|
|
927
|
+
this.trustRoot = new TrustRootManager(globalSnap, {
|
|
928
|
+
...options.trustRootUrl !== void 0 && { refreshBaseUrl: options.trustRootUrl },
|
|
929
|
+
...options.trustSnapshotRefreshMs !== void 0 && { refreshIntervalMs: options.trustSnapshotRefreshMs }
|
|
930
|
+
});
|
|
931
|
+
} else {
|
|
932
|
+
this.trustRoot = getGlobalTrustRootManager();
|
|
933
|
+
}
|
|
934
|
+
this.trustRoot.checkExpiry();
|
|
935
|
+
}
|
|
936
|
+
/** Return the current trust-root snapshot (pinned or last successful refresh). */
|
|
937
|
+
getTrustSnapshot() {
|
|
938
|
+
return this.trustRoot.getSnapshot();
|
|
324
939
|
}
|
|
325
940
|
/**
|
|
326
941
|
* Ask the policy engine whether an agent action is permitted.
|
|
@@ -347,6 +962,11 @@ var AtlaSentClient = class {
|
|
|
347
962
|
context: normalized.context ?? {}
|
|
348
963
|
};
|
|
349
964
|
if (normalized.explain !== void 0) body.explain = normalized.explain;
|
|
965
|
+
if (normalized.environment !== void 0) body.environment = normalized.environment;
|
|
966
|
+
if (normalized.resource !== void 0) body.resource = normalized.resource;
|
|
967
|
+
if (normalized.current_state !== void 0) body.current_state = normalized.current_state;
|
|
968
|
+
if (normalized.proposed_state !== void 0) body.proposed_state = normalized.proposed_state;
|
|
969
|
+
if (normalized.execution_binding !== void 0) body.execution_binding = normalized.execution_binding;
|
|
350
970
|
const { body: wire, rateLimit } = await this.post(
|
|
351
971
|
"/v1-evaluate",
|
|
352
972
|
body
|
|
@@ -393,13 +1013,25 @@ var AtlaSentClient = class {
|
|
|
393
1013
|
hardBlocks: wire.risk_envelope.hard_blocks ?? [],
|
|
394
1014
|
...wire.risk_envelope.factors && { factors: wire.risk_envelope.factors }
|
|
395
1015
|
}
|
|
396
|
-
}
|
|
1016
|
+
},
|
|
1017
|
+
...wire.risk_class !== void 0 && { riskClass: wire.risk_class },
|
|
1018
|
+
...wire.authority_basis && {
|
|
1019
|
+
authorityBasis: {
|
|
1020
|
+
kind: wire.authority_basis.kind,
|
|
1021
|
+
...wire.authority_basis.reference !== void 0 && { reference: wire.authority_basis.reference },
|
|
1022
|
+
...wire.authority_basis.granted_by !== void 0 && { grantedBy: wire.authority_basis.granted_by },
|
|
1023
|
+
...wire.authority_basis.rationale !== void 0 && { rationale: wire.authority_basis.rationale },
|
|
1024
|
+
...wire.authority_basis.expires_at !== void 0 && { expiresAt: wire.authority_basis.expires_at }
|
|
1025
|
+
}
|
|
1026
|
+
},
|
|
1027
|
+
...wire.escalation_id !== void 0 && { escalationId: wire.escalation_id }
|
|
397
1028
|
};
|
|
398
1029
|
}
|
|
399
1030
|
/**
|
|
400
1031
|
* Batch evaluate — send up to 100 decisions in a single round-trip.
|
|
401
1032
|
*
|
|
402
|
-
|
|
1033
|
+
* Wraps `POST /v1/evaluate/batch` (with fallback to
|
|
1034
|
+
* `POST /v1-evaluate-batch` on older runtimes). The server evaluates each item
|
|
403
1035
|
* against the active policy bundle and returns results in the same
|
|
404
1036
|
* order as the input. One rate-limit token is consumed for the
|
|
405
1037
|
* whole batch, and one audit-chain entry lists every included
|
|
@@ -437,8 +1069,9 @@ var AtlaSentClient = class {
|
|
|
437
1069
|
}));
|
|
438
1070
|
const wireBody = { items: wireItems };
|
|
439
1071
|
if (batchId) wireBody.batch_id = batchId;
|
|
440
|
-
const { body: wire, rateLimit } = await this.
|
|
441
|
-
|
|
1072
|
+
const { body: wire, rateLimit } = await this.postWithPathFallback(
|
|
1073
|
+
V1_EVALUATE_BATCH_PATH,
|
|
1074
|
+
V1_EVALUATE_BATCH_LEGACY_PATH,
|
|
442
1075
|
wireBody
|
|
443
1076
|
);
|
|
444
1077
|
const items = (wire.items ?? []).map(
|
|
@@ -731,6 +1364,7 @@ var AtlaSentClient = class {
|
|
|
731
1364
|
const agent = input.agent ?? "ci-deploy-bot";
|
|
732
1365
|
const action = input.action ?? PRODUCTION_DEPLOY_ACTION;
|
|
733
1366
|
const context = input.context ?? {};
|
|
1367
|
+
const environment = typeof context.environment === "string" ? context.environment : typeof context.environment_name === "string" ? context.environment_name : void 0;
|
|
734
1368
|
const evaluation = await this.evaluate({ agent, action, context });
|
|
735
1369
|
if (evaluation.decision !== "allow") {
|
|
736
1370
|
return {
|
|
@@ -747,7 +1381,8 @@ var AtlaSentClient = class {
|
|
|
747
1381
|
permitId: evaluation.permitId,
|
|
748
1382
|
agent,
|
|
749
1383
|
action,
|
|
750
|
-
context
|
|
1384
|
+
context,
|
|
1385
|
+
...environment !== void 0 ? { environment } : {}
|
|
751
1386
|
});
|
|
752
1387
|
if (!verification.verified) {
|
|
753
1388
|
return {
|
|
@@ -961,15 +1596,15 @@ var AtlaSentClient = class {
|
|
|
961
1596
|
*/
|
|
962
1597
|
async keySelf() {
|
|
963
1598
|
const { body: wire, rateLimit } = await this.get("/v1-api-key-self");
|
|
964
|
-
if (typeof wire.key_id !== "string" || typeof wire.
|
|
1599
|
+
if (typeof wire.key_id !== "string" || typeof wire.org_id !== "string") {
|
|
965
1600
|
throw new AtlaSentError(
|
|
966
|
-
"Malformed response from /v1-api-key-self: missing `key_id` or `
|
|
1601
|
+
"Malformed response from /v1-api-key-self: missing `key_id` or `org_id`",
|
|
967
1602
|
{ code: "bad_response" }
|
|
968
1603
|
);
|
|
969
1604
|
}
|
|
970
1605
|
return {
|
|
971
1606
|
keyId: wire.key_id,
|
|
972
|
-
|
|
1607
|
+
orgId: wire.org_id,
|
|
973
1608
|
environment: wire.environment,
|
|
974
1609
|
scopes: wire.scopes ?? [],
|
|
975
1610
|
allowedCidrs: wire.allowed_cidrs ?? null,
|
|
@@ -1181,7 +1816,8 @@ var AtlaSentClient = class {
|
|
|
1181
1816
|
return response;
|
|
1182
1817
|
}
|
|
1183
1818
|
/**
|
|
1184
|
-
|
|
1819
|
+
* Open a streaming evaluation session against `POST /v1/evaluate/stream`
|
|
1820
|
+
* (with fallback to `POST /v1-evaluate-stream` on older runtimes).
|
|
1185
1821
|
*
|
|
1186
1822
|
* Yields {@link StreamDecisionEvent} and {@link StreamProgressEvent} objects
|
|
1187
1823
|
* as the server emits them. The iterator ends cleanly when the server sends
|
|
@@ -1219,7 +1855,7 @@ var AtlaSentClient = class {
|
|
|
1219
1855
|
api_key: this.apiKey
|
|
1220
1856
|
};
|
|
1221
1857
|
const requestId = globalThis.crypto.randomUUID();
|
|
1222
|
-
|
|
1858
|
+
let streamPath = V1_EVALUATE_STREAM_PATH;
|
|
1223
1859
|
let lastEventId;
|
|
1224
1860
|
let retryCount = 0;
|
|
1225
1861
|
while (true) {
|
|
@@ -1239,7 +1875,7 @@ var AtlaSentClient = class {
|
|
|
1239
1875
|
const signal = opts.signal ? AbortSignal.any([connectionTimeoutSignal, opts.signal]) : connectionTimeoutSignal;
|
|
1240
1876
|
let response;
|
|
1241
1877
|
try {
|
|
1242
|
-
response = await this.fetchImpl(
|
|
1878
|
+
response = await this.fetchImpl(`${this.baseUrl}${streamPath}`, {
|
|
1243
1879
|
method: "POST",
|
|
1244
1880
|
headers,
|
|
1245
1881
|
body: JSON.stringify(body),
|
|
@@ -1255,6 +1891,10 @@ var AtlaSentClient = class {
|
|
|
1255
1891
|
throw mapped;
|
|
1256
1892
|
}
|
|
1257
1893
|
if (!response.ok) {
|
|
1894
|
+
if (streamPath === V1_EVALUATE_STREAM_PATH && (response.status === 404 || response.status === 405)) {
|
|
1895
|
+
streamPath = V1_EVALUATE_STREAM_LEGACY_PATH;
|
|
1896
|
+
continue;
|
|
1897
|
+
}
|
|
1258
1898
|
throw await buildHttpError(response, requestId);
|
|
1259
1899
|
}
|
|
1260
1900
|
if (!response.body) {
|
|
@@ -1303,9 +1943,61 @@ var AtlaSentClient = class {
|
|
|
1303
1943
|
break;
|
|
1304
1944
|
}
|
|
1305
1945
|
}
|
|
1946
|
+
// ── License verification (self-hosted / air-gapped) ──────────────────────
|
|
1947
|
+
/**
|
|
1948
|
+
* Retrieve the license status of this self-hosted or air-gapped deployment.
|
|
1949
|
+
*
|
|
1950
|
+
* Calls `GET /v1/license`. Returns the current validity state, expiry,
|
|
1951
|
+
* enabled feature flags, and optional capacity limits for the installed
|
|
1952
|
+
* license key.
|
|
1953
|
+
*
|
|
1954
|
+
* Callers should check `result.status === "active"` before proceeding.
|
|
1955
|
+
* A `"grace"` status means the license has lapsed but a grace window
|
|
1956
|
+
* (`grace_until`) is still open — the deployment continues to function
|
|
1957
|
+
* but the license should be renewed immediately.
|
|
1958
|
+
*
|
|
1959
|
+
* Throws {@link AtlaSentError} on transport / auth failures.
|
|
1960
|
+
*/
|
|
1961
|
+
async getLicense() {
|
|
1962
|
+
const { body, rateLimit } = await this.get("/v1/license");
|
|
1963
|
+
return { ...body, rateLimit };
|
|
1964
|
+
}
|
|
1965
|
+
/**
|
|
1966
|
+
* Validate a signed license blob against this deployment's installed
|
|
1967
|
+
* public key.
|
|
1968
|
+
*
|
|
1969
|
+
* Calls `POST /v1/license/verify`. Use this when onboarding a new license
|
|
1970
|
+
* key or rotating an expiring one — submit the blob received from AtlaSent
|
|
1971
|
+
* and check `result.valid` before applying the new license.
|
|
1972
|
+
*
|
|
1973
|
+
* A `valid: false` response is **not** thrown — inspect the returned
|
|
1974
|
+
* object. Only transport / server errors throw {@link AtlaSentError}.
|
|
1975
|
+
*
|
|
1976
|
+
* @param blob — The signed license blob string provided by AtlaSent.
|
|
1977
|
+
*/
|
|
1978
|
+
async verifyLicense(blob) {
|
|
1979
|
+
if (!blob || typeof blob !== "string") {
|
|
1980
|
+
throw new AtlaSentError("blob is required", { code: "bad_request" });
|
|
1981
|
+
}
|
|
1982
|
+
const { body, rateLimit } = await this.post(
|
|
1983
|
+
"/v1/license/verify",
|
|
1984
|
+
{ blob }
|
|
1985
|
+
);
|
|
1986
|
+
return { ...body, rateLimit };
|
|
1987
|
+
}
|
|
1306
1988
|
async post(path, body, query) {
|
|
1307
1989
|
return this.request(path, "POST", body, query);
|
|
1308
1990
|
}
|
|
1991
|
+
async postWithPathFallback(primaryPath, fallbackPath, body, query) {
|
|
1992
|
+
try {
|
|
1993
|
+
return await this.post(primaryPath, body, query);
|
|
1994
|
+
} catch (err) {
|
|
1995
|
+
if (err instanceof AtlaSentError && (err.status === 404 || err.status === 405)) {
|
|
1996
|
+
return this.post(fallbackPath, body, query);
|
|
1997
|
+
}
|
|
1998
|
+
throw err;
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
1309
2001
|
async get(path, query) {
|
|
1310
2002
|
return this.request(path, "GET", void 0, query);
|
|
1311
2003
|
}
|
|
@@ -1998,6 +2690,82 @@ var AtlaSentClient = class {
|
|
|
1998
2690
|
);
|
|
1999
2691
|
return [...body.evaluations ?? []];
|
|
2000
2692
|
}
|
|
2693
|
+
// ── Private adapters for sub-client factories ──────────────────────────────
|
|
2694
|
+
// Thin wrappers that expose the private request infrastructure to sub-client
|
|
2695
|
+
// factories (scim, evidenceBundles, auth) without widening the public API.
|
|
2696
|
+
async _post(path, body, query) {
|
|
2697
|
+
const { body: b } = await this.post(path, body, query);
|
|
2698
|
+
return { body: b };
|
|
2699
|
+
}
|
|
2700
|
+
async _get(path, query) {
|
|
2701
|
+
const { body: b } = await this.get(path, query);
|
|
2702
|
+
return { body: b };
|
|
2703
|
+
}
|
|
2704
|
+
async _put(path, body) {
|
|
2705
|
+
return this._requestRaw(path, "PUT", body, void 0);
|
|
2706
|
+
}
|
|
2707
|
+
async _patch(path, body) {
|
|
2708
|
+
return this._requestRaw(path, "PATCH", body, void 0);
|
|
2709
|
+
}
|
|
2710
|
+
async _delete(path) {
|
|
2711
|
+
await this._requestRaw(path, "DELETE", void 0, void 0);
|
|
2712
|
+
}
|
|
2713
|
+
async _getRaw(path) {
|
|
2714
|
+
const url = `${this.baseUrl}${path}`;
|
|
2715
|
+
const requestId = globalThis.crypto.randomUUID();
|
|
2716
|
+
const headers = {
|
|
2717
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
2718
|
+
"User-Agent": this.userAgent,
|
|
2719
|
+
"X-Request-ID": requestId,
|
|
2720
|
+
"X-AtlaSent-Protocol-Version": "1"
|
|
2721
|
+
};
|
|
2722
|
+
const response = await this.fetchImpl(url, {
|
|
2723
|
+
method: "GET",
|
|
2724
|
+
headers,
|
|
2725
|
+
signal: AbortSignal.timeout(this.timeoutMs)
|
|
2726
|
+
});
|
|
2727
|
+
if (!response.ok) {
|
|
2728
|
+
const text = await response.text().catch(() => "");
|
|
2729
|
+
throw new AtlaSentError(`GET ${path} returned ${response.status}`, {
|
|
2730
|
+
code: response.status >= 500 ? "server_error" : "bad_request",
|
|
2731
|
+
status: response.status,
|
|
2732
|
+
requestId
|
|
2733
|
+
});
|
|
2734
|
+
}
|
|
2735
|
+
return response.arrayBuffer();
|
|
2736
|
+
}
|
|
2737
|
+
async _requestRaw(path, method, body, query) {
|
|
2738
|
+
const qs = query && Array.from(query).length > 0 ? `?${query.toString()}` : "";
|
|
2739
|
+
const url = `${this.baseUrl}${path}${qs}`;
|
|
2740
|
+
const requestId = globalThis.crypto.randomUUID();
|
|
2741
|
+
const headers = {
|
|
2742
|
+
Accept: "application/json",
|
|
2743
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
2744
|
+
"User-Agent": this.userAgent,
|
|
2745
|
+
"X-Request-ID": requestId,
|
|
2746
|
+
"X-AtlaSent-Protocol-Version": "1"
|
|
2747
|
+
};
|
|
2748
|
+
if (method === "PUT" && body !== void 0) {
|
|
2749
|
+
headers["Content-Type"] = "application/json";
|
|
2750
|
+
}
|
|
2751
|
+
const init = { method, headers, signal: AbortSignal.timeout(this.timeoutMs) };
|
|
2752
|
+
if (method === "PUT" && body !== void 0) {
|
|
2753
|
+
init.body = JSON.stringify(body);
|
|
2754
|
+
}
|
|
2755
|
+
const response = await this.fetchImpl(url, init);
|
|
2756
|
+
if (!response.ok) {
|
|
2757
|
+
const text = await response.text().catch(() => "");
|
|
2758
|
+
throw new AtlaSentError(`${method} ${path} returned ${response.status}`, {
|
|
2759
|
+
code: response.status >= 500 ? "server_error" : "bad_request",
|
|
2760
|
+
status: response.status,
|
|
2761
|
+
requestId
|
|
2762
|
+
});
|
|
2763
|
+
}
|
|
2764
|
+
if (method === "DELETE") {
|
|
2765
|
+
return { body: {} };
|
|
2766
|
+
}
|
|
2767
|
+
return { body: await response.json() };
|
|
2768
|
+
}
|
|
2001
2769
|
};
|
|
2002
2770
|
function parseRateLimitHeaders(headers) {
|
|
2003
2771
|
const rawLimit = headers.get("x-ratelimit-limit");
|
|
@@ -2143,7 +2911,7 @@ function buildAuditEventsQuery(query) {
|
|
|
2143
2911
|
return params;
|
|
2144
2912
|
}
|
|
2145
2913
|
function sleep(ms) {
|
|
2146
|
-
return new Promise((
|
|
2914
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
2147
2915
|
}
|
|
2148
2916
|
function parseRetryAfter(raw) {
|
|
2149
2917
|
if (!raw) return void 0;
|
|
@@ -2164,14 +2932,14 @@ async function* parseSseStream(body, requestId, timeoutMs, onEventId) {
|
|
|
2164
2932
|
if (timeoutMs <= 0) {
|
|
2165
2933
|
return reader.read();
|
|
2166
2934
|
}
|
|
2167
|
-
return new Promise((
|
|
2935
|
+
return new Promise((resolve3, reject) => {
|
|
2168
2936
|
const timer = setTimeout(() => {
|
|
2169
2937
|
reject(new StreamTimeoutError(timeoutMs));
|
|
2170
2938
|
}, timeoutMs);
|
|
2171
2939
|
reader.read().then(
|
|
2172
2940
|
(result) => {
|
|
2173
2941
|
clearTimeout(timer);
|
|
2174
|
-
|
|
2942
|
+
resolve3(result);
|
|
2175
2943
|
},
|
|
2176
2944
|
(err) => {
|
|
2177
2945
|
clearTimeout(timer);
|
|
@@ -2344,6 +3112,15 @@ async function protect(request) {
|
|
|
2344
3112
|
{ code: "bad_request" }
|
|
2345
3113
|
);
|
|
2346
3114
|
}
|
|
3115
|
+
const trustMgr = getGlobalTrustRootManager({ disableRefresh: false });
|
|
3116
|
+
if (trustMgr.checkExpiry() === "expired") {
|
|
3117
|
+
const snap = trustMgr.getSnapshot();
|
|
3118
|
+
throw new BundleVerificationError({
|
|
3119
|
+
reason: "trust_snapshot_expired",
|
|
3120
|
+
snapshotValidUntil: snap.valid_until,
|
|
3121
|
+
snapshotFetchedAt: snap.issued_at
|
|
3122
|
+
});
|
|
3123
|
+
}
|
|
2347
3124
|
const client = getClient();
|
|
2348
3125
|
const evaluation = await client.evaluate(request);
|
|
2349
3126
|
if (evaluation.decision !== "allow") {
|
|
@@ -2398,15 +3175,15 @@ async function protect(request) {
|
|
|
2398
3175
|
|
|
2399
3176
|
// src/hono.ts
|
|
2400
3177
|
var DEFAULT_CONTEXT_KEY = "atlasent";
|
|
2401
|
-
async function
|
|
3178
|
+
async function resolve2(value, c) {
|
|
2402
3179
|
return typeof value === "function" ? await value(c) : value;
|
|
2403
3180
|
}
|
|
2404
3181
|
function atlaSentGuard(options) {
|
|
2405
3182
|
const contextKey = options.key ?? DEFAULT_CONTEXT_KEY;
|
|
2406
3183
|
return async (c, next) => {
|
|
2407
3184
|
const [agent, action, ctx] = await Promise.all([
|
|
2408
|
-
|
|
2409
|
-
|
|
3185
|
+
resolve2(options.agent, c),
|
|
3186
|
+
resolve2(options.action, c),
|
|
2410
3187
|
options.context ? options.context(c) : Promise.resolve(void 0)
|
|
2411
3188
|
]);
|
|
2412
3189
|
const request = { agent, action };
|