@shieldaai/agent-map 0.1.0-alpha.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +653 -268
- package/dist/assets.js +16 -26
- package/dist/boundary-flags.d.ts +73 -0
- package/dist/boundary-flags.js +352 -0
- package/dist/boundary.d.ts +19 -2
- package/dist/boundary.js +75 -19
- package/dist/cli-entry.d.ts +13 -0
- package/dist/cli-entry.js +66 -0
- package/dist/cli.js +41 -44
- package/dist/common.d.ts +34 -0
- package/dist/common.js +94 -0
- package/dist/http.js +78 -51
- package/dist/identity-cli.js +94 -47
- package/dist/identity-http.js +226 -94
- package/dist/index.d.ts +4 -4
- package/dist/index.js +4 -4
- package/dist/mcp.js +40 -5
- package/dist/oidc.d.ts +5 -0
- package/dist/oidc.js +121 -38
- package/dist/policy-reader.d.ts +56 -3
- package/dist/policy-reader.js +823 -110
- package/dist/projection-fields.d.ts +4 -0
- package/dist/projection-fields.js +144 -0
- package/dist/service/changes.d.ts +5 -0
- package/dist/service/changes.js +255 -0
- package/dist/service/context.d.ts +33 -0
- package/dist/service/context.js +2 -0
- package/dist/service/discovery.d.ts +6 -0
- package/dist/service/discovery.js +31 -0
- package/dist/service/explain.d.ts +12 -0
- package/dist/service/explain.js +126 -0
- package/dist/service/flags.d.ts +20 -0
- package/dist/service/flags.js +70 -0
- package/dist/service/inventory.d.ts +4 -0
- package/dist/service/inventory.js +62 -0
- package/dist/service/json.d.ts +8 -0
- package/dist/service/json.js +7 -0
- package/dist/service/operations/discovery.d.ts +5 -0
- package/dist/service/operations/discovery.js +90 -0
- package/dist/service/operations/explain-access.d.ts +3 -0
- package/dist/service/operations/explain-access.js +25 -0
- package/dist/service/operations/find-agents-with-access.d.ts +3 -0
- package/dist/service/operations/find-agents-with-access.js +120 -0
- package/dist/service/operations/get-agent-access.d.ts +3 -0
- package/dist/service/operations/get-agent-access.js +75 -0
- package/dist/service/operations/get-agent-inventory.d.ts +3 -0
- package/dist/service/operations/get-agent-inventory.js +42 -0
- package/dist/service/operations/get-changes.d.ts +3 -0
- package/dist/service/operations/get-changes.js +11 -0
- package/dist/service/operations/get-inventory-health.d.ts +3 -0
- package/dist/service/operations/get-inventory-health.js +81 -0
- package/dist/service/operations/get-resource-access.d.ts +3 -0
- package/dist/service/operations/get-resource-access.js +48 -0
- package/dist/service/operations/list-agents.d.ts +3 -0
- package/dist/service/operations/list-agents.js +92 -0
- package/dist/service/operations/list-owners-resources.d.ts +5 -0
- package/dist/service/operations/list-owners-resources.js +69 -0
- package/dist/service/ordering.d.ts +9 -0
- package/dist/service/ordering.js +47 -0
- package/dist/service/outside-guard.d.ts +68 -0
- package/dist/service/outside-guard.js +313 -0
- package/dist/service/policy.d.ts +26 -0
- package/dist/service/policy.js +299 -0
- package/dist/service/schemas.d.ts +16 -0
- package/dist/service/schemas.js +192 -0
- package/dist/service/summaries.d.ts +32 -0
- package/dist/service/summaries.js +247 -0
- package/dist/service/work.d.ts +33 -0
- package/dist/service/work.js +116 -0
- package/dist/service.d.ts +5 -5
- package/dist/service.js +110 -645
- package/dist/store/ancestors.d.ts +9 -0
- package/dist/store/ancestors.js +86 -0
- package/dist/store/discovery.d.ts +8 -0
- package/dist/store/discovery.js +288 -0
- package/dist/store/evidence.d.ts +12 -0
- package/dist/store/evidence.js +90 -0
- package/dist/store/identity-scope.d.ts +9 -0
- package/dist/store/identity-scope.js +71 -0
- package/dist/store/nodes.d.ts +24 -0
- package/dist/store/nodes.js +237 -0
- package/dist/store/projection.d.ts +19 -0
- package/dist/store/projection.js +242 -0
- package/dist/store/queries.d.ts +48 -0
- package/dist/store/queries.js +302 -0
- package/dist/store/read.d.ts +81 -0
- package/dist/store/read.js +135 -0
- package/dist/store/selection.d.ts +3 -0
- package/dist/store/selection.js +102 -0
- package/dist/store.d.ts +15 -0
- package/dist/store.js +102 -395
- package/dist/transport.d.ts +42 -2
- package/dist/transport.js +144 -16
- package/dist/types.d.ts +184 -4
- package/dist/types.js +52 -11
- package/package.json +32 -5
package/dist/oidc.js
CHANGED
|
@@ -2,9 +2,10 @@ import { request as httpsRequest } from 'node:https';
|
|
|
2
2
|
import { lookup } from 'node:dns';
|
|
3
3
|
import { X509Certificate } from 'node:crypto';
|
|
4
4
|
import { rootCertificates, createSecureContext } from 'node:tls';
|
|
5
|
-
import { immutableJson } from '@shieldaai/agent-guard';
|
|
5
|
+
import { immutableJson } from '@shieldaai/agent-guard/internal';
|
|
6
6
|
import * as oidc from 'openid-client';
|
|
7
|
-
import {
|
|
7
|
+
import { integer, fail } from './common.js';
|
|
8
|
+
import { fields, parseRequest } from './boundary.js';
|
|
8
9
|
import { MapQueryError, mapIdentifier } from './types.js';
|
|
9
10
|
const INVALID = 'MAP_IDENTITY_INVALID', UNAVAILABLE = 'MAP_IDENTITY_UNAVAILABLE';
|
|
10
11
|
function httpsUrl(value, code, query = false) {
|
|
@@ -35,8 +36,18 @@ export async function createMapOidcClient(options) {
|
|
|
35
36
|
catch {
|
|
36
37
|
return fail('MAP_CONFIG_INVALID');
|
|
37
38
|
}
|
|
38
|
-
const config = fields(copied, [
|
|
39
|
-
'
|
|
39
|
+
const config = fields(copied, [
|
|
40
|
+
'issuer',
|
|
41
|
+
'clientId',
|
|
42
|
+
'clientSecret',
|
|
43
|
+
'redirectUri',
|
|
44
|
+
'clientAuthMethod',
|
|
45
|
+
'endpointOrigins',
|
|
46
|
+
'ca',
|
|
47
|
+
'requestTimeoutMs',
|
|
48
|
+
'maxResponseBytes',
|
|
49
|
+
'clockToleranceSeconds',
|
|
50
|
+
], ['issuer', 'clientId', 'clientSecret', 'redirectUri']);
|
|
40
51
|
const issuer = httpsUrl(config.issuer, 'MAP_CONFIG_INVALID');
|
|
41
52
|
if (issuer.pathname.includes('/.well-known/'))
|
|
42
53
|
fail('MAP_CONFIG_INVALID');
|
|
@@ -53,6 +64,9 @@ export async function createMapOidcClient(options) {
|
|
|
53
64
|
const authentication = () => method === 'client_secret_basic' ? oidc.ClientSecretBasic(secret) : oidc.ClientSecretPost(secret);
|
|
54
65
|
const timeout = integer(config.requestTimeoutMs, 5000, 50, 10_000);
|
|
55
66
|
const maxBytes = integer(config.maxResponseBytes, 256 * 1024, 1024, 1024 * 1024);
|
|
67
|
+
// An identity provider's clock is rarely the same second as ours. The tolerance lets a token
|
|
68
|
+
// issued (or valid from) slightly in our future through; expiry is checked below without it.
|
|
69
|
+
const clockTolerance = integer(config.clockToleranceSeconds, 5, 0, 60);
|
|
56
70
|
const origins = new Set([issuer.origin]);
|
|
57
71
|
if (config.endpointOrigins !== undefined) {
|
|
58
72
|
if (!Array.isArray(config.endpointOrigins) || config.endpointOrigins.length > 8)
|
|
@@ -69,7 +83,9 @@ export async function createMapOidcClient(options) {
|
|
|
69
83
|
if (typeof config.ca !== 'string' || !config.ca || Buffer.byteLength(config.ca) > 256 * 1024)
|
|
70
84
|
fail('MAP_CONFIG_INVALID');
|
|
71
85
|
const certificates = config.ca.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g);
|
|
72
|
-
if (!certificates?.length ||
|
|
86
|
+
if (!certificates?.length ||
|
|
87
|
+
certificates.length > 16 ||
|
|
88
|
+
config.ca.replace(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g, '').trim())
|
|
73
89
|
fail('MAP_CONFIG_INVALID');
|
|
74
90
|
try {
|
|
75
91
|
for (const certificate of certificates)
|
|
@@ -90,8 +106,10 @@ export async function createMapOidcClient(options) {
|
|
|
90
106
|
function operation(external) {
|
|
91
107
|
const deadline = performance.now() + timeout, active = new Set();
|
|
92
108
|
let closed = false, failure;
|
|
93
|
-
const check = () => {
|
|
94
|
-
|
|
109
|
+
const check = () => {
|
|
110
|
+
if (closed || external?.aborted || performance.now() >= deadline)
|
|
111
|
+
fail(UNAVAILABLE);
|
|
112
|
+
};
|
|
95
113
|
const fetch = (input, init) => new Promise((resolve, reject) => {
|
|
96
114
|
let request, settled = false, counted = false;
|
|
97
115
|
let timer;
|
|
@@ -154,17 +172,30 @@ export async function createMapOidcClient(options) {
|
|
|
154
172
|
callback(error, addresses?.[0]?.address ?? '', addresses?.[0]?.family);
|
|
155
173
|
});
|
|
156
174
|
};
|
|
157
|
-
request = httpsRequest(url, {
|
|
158
|
-
|
|
159
|
-
|
|
175
|
+
request = httpsRequest(url, {
|
|
176
|
+
method: init.method,
|
|
177
|
+
headers: { ...init.headers, 'accept-encoding': 'identity', connection: 'close' },
|
|
178
|
+
agent: false,
|
|
179
|
+
rejectUnauthorized: true,
|
|
180
|
+
minVersion: 'TLSv1.2',
|
|
181
|
+
maxHeaderSize: 8192,
|
|
182
|
+
lookup: boundedLookup,
|
|
183
|
+
...(ca === undefined ? {} : { ca }),
|
|
184
|
+
}, response => {
|
|
160
185
|
const status = response.statusCode ?? 0;
|
|
161
186
|
const count = (name) => response.rawHeaders.filter((item, index) => index % 2 === 0 && item.toLowerCase() === name).length;
|
|
162
187
|
const length = response.headers['content-length'];
|
|
163
|
-
if ((status >= 300 && status < 400) ||
|
|
164
|
-
|
|
165
|
-
|
|
188
|
+
if ((status >= 300 && status < 400) ||
|
|
189
|
+
status >= 500 ||
|
|
190
|
+
status < 200 ||
|
|
191
|
+
count('content-length') > 1 ||
|
|
192
|
+
count('content-type') !== 1 ||
|
|
193
|
+
count('transfer-encoding') > 1 ||
|
|
194
|
+
response.headers['content-encoding'] !== undefined ||
|
|
195
|
+
(length !== undefined && response.headers['transfer-encoding'] !== undefined) ||
|
|
166
196
|
!/^application\/(?:json|jwk-set\+json)(?:\s*;\s*charset=utf-8)?$/i.test(response.headers['content-type'] ?? '') ||
|
|
167
|
-
(length !== undefined &&
|
|
197
|
+
(length !== undefined &&
|
|
198
|
+
(!/^\d+$/.test(length) || !Number.isSafeInteger(Number(length)) || Number(length) > maxBytes))) {
|
|
168
199
|
response.destroy();
|
|
169
200
|
stop(UNAVAILABLE);
|
|
170
201
|
return;
|
|
@@ -221,30 +252,53 @@ export async function createMapOidcClient(options) {
|
|
|
221
252
|
stop(error instanceof MapQueryError ? error.code : UNAVAILABLE);
|
|
222
253
|
}
|
|
223
254
|
});
|
|
224
|
-
return {
|
|
225
|
-
|
|
255
|
+
return {
|
|
256
|
+
fetch,
|
|
257
|
+
check,
|
|
258
|
+
get failure() {
|
|
259
|
+
return failure;
|
|
260
|
+
},
|
|
261
|
+
close() {
|
|
262
|
+
closed = true;
|
|
263
|
+
for (const request of active)
|
|
264
|
+
request.destroy();
|
|
265
|
+
},
|
|
266
|
+
};
|
|
226
267
|
}
|
|
227
|
-
const clientMetadata = { id_token_signed_response_alg: 'RS256', [oidc.clockTolerance]:
|
|
268
|
+
const clientMetadata = { id_token_signed_response_alg: 'RS256', [oidc.clockTolerance]: clockTolerance };
|
|
228
269
|
const discovery = operation();
|
|
229
270
|
let metadata;
|
|
230
271
|
try {
|
|
231
|
-
const discovered = await oidc.discovery(issuer, clientId, clientMetadata, authentication(), {
|
|
272
|
+
const discovered = await oidc.discovery(issuer, clientId, clientMetadata, authentication(), {
|
|
273
|
+
[oidc.customFetch]: discovery.fetch,
|
|
274
|
+
timeout: timeout / 1000,
|
|
275
|
+
});
|
|
232
276
|
discovery.check();
|
|
233
277
|
const remote = discovered.serverMetadata();
|
|
234
|
-
if (remote.issuer !== config.issuer ||
|
|
278
|
+
if (remote.issuer !== config.issuer ||
|
|
279
|
+
!Array.isArray(remote.id_token_signing_alg_values_supported) ||
|
|
235
280
|
!remote.id_token_signing_alg_values_supported.includes('RS256') ||
|
|
236
|
-
!Array.isArray(remote.code_challenge_methods_supported) ||
|
|
237
|
-
|
|
238
|
-
|
|
281
|
+
!Array.isArray(remote.code_challenge_methods_supported) ||
|
|
282
|
+
!remote.code_challenge_methods_supported.includes('S256') ||
|
|
283
|
+
(remote.token_endpoint_auth_methods_supported === undefined
|
|
284
|
+
? method !== 'client_secret_basic'
|
|
285
|
+
: !Array.isArray(remote.token_endpoint_auth_methods_supported) ||
|
|
286
|
+
!remote.token_endpoint_auth_methods_supported.includes(method)))
|
|
239
287
|
fail(INVALID);
|
|
240
288
|
for (const name of ['authorization_endpoint', 'token_endpoint', 'jwks_uri']) {
|
|
241
289
|
if (!origins.has(httpsUrl(remote[name], INVALID).origin))
|
|
242
290
|
fail(INVALID);
|
|
243
291
|
}
|
|
244
|
-
metadata = {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
292
|
+
metadata = {
|
|
293
|
+
issuer: remote.issuer,
|
|
294
|
+
authorization_endpoint: remote.authorization_endpoint,
|
|
295
|
+
token_endpoint: remote.token_endpoint,
|
|
296
|
+
jwks_uri: remote.jwks_uri,
|
|
297
|
+
id_token_signing_alg_values_supported: ['RS256'],
|
|
298
|
+
code_challenge_methods_supported: ['S256'],
|
|
299
|
+
token_endpoint_auth_methods_supported: [method],
|
|
300
|
+
authorization_response_iss_parameter_supported: remote.authorization_response_iss_parameter_supported === true,
|
|
301
|
+
};
|
|
248
302
|
}
|
|
249
303
|
catch (error) {
|
|
250
304
|
throw discovery.failure ?? new MapQueryError(error instanceof MapQueryError ? error.code : INVALID);
|
|
@@ -264,8 +318,15 @@ export async function createMapOidcClient(options) {
|
|
|
264
318
|
const op = operation();
|
|
265
319
|
try {
|
|
266
320
|
const state = oidc.randomState(), nonce = oidc.randomNonce(), codeVerifier = oidc.randomPKCECodeVerifier();
|
|
267
|
-
const authorizationUrl = oidc.buildAuthorizationUrl(configuration(op.fetch), {
|
|
268
|
-
|
|
321
|
+
const authorizationUrl = oidc.buildAuthorizationUrl(configuration(op.fetch), {
|
|
322
|
+
response_type: 'code',
|
|
323
|
+
scope: 'openid',
|
|
324
|
+
redirect_uri: redirect.href,
|
|
325
|
+
state,
|
|
326
|
+
nonce,
|
|
327
|
+
code_challenge_method: 'S256',
|
|
328
|
+
code_challenge: await oidc.calculatePKCECodeChallenge(codeVerifier),
|
|
329
|
+
});
|
|
269
330
|
op.check();
|
|
270
331
|
return Object.freeze({ authorizationUrl: authorizationUrl.href, state, nonce, codeVerifier });
|
|
271
332
|
}
|
|
@@ -280,28 +341,50 @@ export async function createMapOidcClient(options) {
|
|
|
280
341
|
const op = operation(signal);
|
|
281
342
|
try {
|
|
282
343
|
op.check();
|
|
283
|
-
if (!(callbackUrl instanceof URL) ||
|
|
284
|
-
callbackUrl.
|
|
344
|
+
if (!(callbackUrl instanceof URL) ||
|
|
345
|
+
callbackUrl.href.length > 8192 ||
|
|
346
|
+
callbackUrl.origin !== redirect.origin ||
|
|
347
|
+
callbackUrl.pathname !== redirect.pathname ||
|
|
348
|
+
callbackUrl.hash ||
|
|
349
|
+
callbackUrl.username ||
|
|
350
|
+
callbackUrl.password)
|
|
285
351
|
fail(INVALID);
|
|
286
352
|
const callback = new URL(callbackUrl.href), names = [...callback.searchParams.keys()];
|
|
287
|
-
if (names.length > 16 ||
|
|
288
|
-
|
|
289
|
-
|
|
353
|
+
if (names.length > 16 ||
|
|
354
|
+
new Set(names).size !== names.length ||
|
|
355
|
+
names.some(name => !name || name.length > 128) ||
|
|
356
|
+
callback.searchParams.get('error') !== null ||
|
|
357
|
+
callback.searchParams.get('response') !== null ||
|
|
358
|
+
!callback.searchParams.get('code') ||
|
|
359
|
+
!callback.searchParams.get('state'))
|
|
290
360
|
fail(INVALID);
|
|
291
361
|
const value = fields(transaction, ['state', 'nonce', 'codeVerifier'], ['state', 'nonce', 'codeVerifier']);
|
|
292
362
|
const state = opaque(value.state), nonce = opaque(value.nonce), verifier = opaque(value.codeVerifier);
|
|
293
|
-
const tokens = await oidc.authorizationCodeGrant(configuration(op.fetch), callback, {
|
|
363
|
+
const tokens = await oidc.authorizationCodeGrant(configuration(op.fetch), callback, {
|
|
364
|
+
expectedState: state,
|
|
365
|
+
expectedNonce: nonce,
|
|
366
|
+
pkceCodeVerifier: verifier,
|
|
367
|
+
idTokenExpected: true,
|
|
368
|
+
});
|
|
294
369
|
op.check();
|
|
295
370
|
const claims = tokens.claims(), now = Date.now();
|
|
296
|
-
if (!claims ||
|
|
297
|
-
|
|
298
|
-
claims.
|
|
371
|
+
if (!claims ||
|
|
372
|
+
claims.iss !== config.issuer ||
|
|
373
|
+
(claims.azp !== undefined && claims.azp !== clientId) ||
|
|
374
|
+
!Number.isSafeInteger(claims.exp) ||
|
|
375
|
+
!Number.isSafeInteger(claims.iat) ||
|
|
376
|
+
claims.iat > Math.floor(now / 1000) + clockTolerance ||
|
|
377
|
+
claims.exp <= claims.iat ||
|
|
378
|
+
!Number.isSafeInteger(claims.exp * 1000) ||
|
|
379
|
+
// The protocol library's tolerance also covers `exp`; this check does not.
|
|
380
|
+
claims.exp * 1000 <= now)
|
|
299
381
|
fail(INVALID);
|
|
300
382
|
const subject = mapIdentifier(claims.sub, INVALID);
|
|
301
383
|
return Object.freeze({ subject, expiresAt: claims.exp * 1000 });
|
|
302
384
|
}
|
|
303
385
|
catch (error) {
|
|
304
|
-
throw op.failure ??
|
|
386
|
+
throw (op.failure ??
|
|
387
|
+
new MapQueryError(error instanceof MapQueryError && error.code === UNAVAILABLE ? UNAVAILABLE : INVALID));
|
|
305
388
|
}
|
|
306
389
|
finally {
|
|
307
390
|
op.close();
|
package/dist/policy-reader.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type JsonObject, type MapEdge, type MapNode, type MapPolicyCandidate, type MapPolicyEvidence } from './types.js';
|
|
1
|
+
import { type JsonObject, type MapEdge, type MapNode, type MapPolicyCandidate, type MapPolicyEvidence, type MapPolicySelection, type MapIdentityScope } from './types.js';
|
|
2
2
|
interface Probe {
|
|
3
3
|
readonly key: number;
|
|
4
4
|
readonly identity: MapNode;
|
|
@@ -17,12 +17,65 @@ export interface PolicyPlan {
|
|
|
17
17
|
readonly records: readonly MapPolicyEvidence[];
|
|
18
18
|
readonly probes: readonly Probe[];
|
|
19
19
|
}
|
|
20
|
-
/**
|
|
21
|
-
export declare function
|
|
20
|
+
/** Python's scope ID uses compact ensure_ascii JSON; preserve that byte contract. */
|
|
21
|
+
export declare function collectionKey(companyId: string, tenantId: string, partition: string, accountId: string): string;
|
|
22
|
+
/**
|
|
23
|
+
* Pair only the selected task's grants with its deployment's declared execution identity.
|
|
24
|
+
* `select` narrows the pairs to one task, tool, resource reference (or mapped ARN) or action,
|
|
25
|
+
* so a read that shows a few pairs is not bound by the size of the whole fleet.
|
|
26
|
+
*/
|
|
27
|
+
export declare function policyPlan(companyId: string, tenantId: string, nodes: readonly MapNode[], edges: readonly MapEdge[], maximum: number, select?: MapPolicySelection): PolicyPlan;
|
|
22
28
|
export declare const POLICY_QUERY: string;
|
|
23
29
|
export declare const POLICY_COLUMNS: readonly ['key', 'bindingProof', 'identity', 'resource', 'identityLink', 'resourceLink', 'principal', 'bucket', 'principalAccount', 'bucketAccount', 'principalOwnership', 'bucketOwnership', 'collection', 'principalMember', 'bucketMember', 'candidate'];
|
|
24
30
|
export declare function policyParameters(plan: PolicyPlan): readonly Record<string, unknown>[];
|
|
25
31
|
export declare function safePolicyRow(raw: unknown): JsonObject;
|
|
26
32
|
/** Pure classification: no positive candidate survives a malformed endpoint or stale receipt. */
|
|
27
33
|
export declare function policyEvidence(plan: PolicyPlan, rows: readonly JsonObject[], now: number, maximumAge: number): readonly MapPolicyEvidence[];
|
|
34
|
+
/** Buckets listed per identity before the comparison reports `truncated`. */
|
|
35
|
+
export declare const IDENTITY_SCOPE_BUCKETS = 500;
|
|
36
|
+
interface ScopeProbe {
|
|
37
|
+
readonly key: number;
|
|
38
|
+
readonly identity: MapNode;
|
|
39
|
+
readonly accountId: string;
|
|
40
|
+
readonly partition: string;
|
|
41
|
+
readonly principalArn: string;
|
|
42
|
+
readonly principalLabel: 'AWSRole' | 'AWSUser';
|
|
43
|
+
readonly collectionId: string;
|
|
44
|
+
readonly proof: Readonly<Record<string, string | null>>;
|
|
45
|
+
}
|
|
46
|
+
/** One execution binding's comparison against its identity's probe. */
|
|
47
|
+
interface ScopeLink {
|
|
48
|
+
readonly record: number;
|
|
49
|
+
readonly probe: number;
|
|
50
|
+
/** Buckets this tool's live grants bind as a whole bucket. */
|
|
51
|
+
readonly bound: ReadonlySet<string>;
|
|
52
|
+
/** Buckets bound only as an object or prefix: a bucket-level candidate may reach further. */
|
|
53
|
+
readonly partial: ReadonlySet<string>;
|
|
54
|
+
}
|
|
55
|
+
export interface IdentityScopePlan {
|
|
56
|
+
readonly records: readonly MapIdentityScope[];
|
|
57
|
+
/** One probe per distinct identity; several tools sharing a role share its probe. */
|
|
58
|
+
readonly probes: readonly ScopeProbe[];
|
|
59
|
+
readonly links: readonly ScopeLink[];
|
|
60
|
+
/** Only these bucket names are read, or every collected bucket when null. */
|
|
61
|
+
readonly buckets: readonly string[] | null;
|
|
62
|
+
/** Distinct buckets kept per identity before it reports `truncated`. */
|
|
63
|
+
readonly bucketLimit: number;
|
|
64
|
+
/** Rows read per identity; one past the expected maximum marks a longer list. */
|
|
65
|
+
readonly candidateLimit: number;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* One record per execution binding; a probe per distinct, unambiguous, resolved static AWS
|
|
69
|
+
* identity. Past `maximum` identities the rest report `not_evaluated` instead of failing the
|
|
70
|
+
* read. `buckets` limits the read to those bucket names (the reach check behind
|
|
71
|
+
* `list_resources`). Grants count only on live tasks at `observedAt` (the snapshot time).
|
|
72
|
+
*/
|
|
73
|
+
export declare function identityScopePlan(companyId: string, tenantId: string, nodes: readonly MapNode[], edges: readonly MapEdge[], maximum: number, buckets?: readonly string[] | null, observedAt?: number): IdentityScopePlan;
|
|
74
|
+
export declare const IDENTITY_SCOPE_QUERY: string;
|
|
75
|
+
export declare const IDENTITY_SCOPE_COLUMNS: readonly ['key', 'executionProof', 'identity', 'identityLink', 'principal', 'principalAccount', 'principalOwnership', 'collection', 'principalMember', 'candidates'];
|
|
76
|
+
export declare function identityScopeParameters(plan: IdentityScopePlan): readonly Record<string, unknown>[];
|
|
77
|
+
export declare function safeIdentityScopeRow(raw: unknown): JsonObject;
|
|
78
|
+
export declare function identityScopeEvidence(plan: IdentityScopePlan, rows: readonly JsonObject[], now: number, maximumAge: number,
|
|
79
|
+
/** For a team-scoped reader: the only bucket names it may see; null for an all-teams reader. */
|
|
80
|
+
readable?: ReadonlySet<string> | null): readonly MapIdentityScope[];
|
|
28
81
|
export {};
|