@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
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { fail } from '../../common.js';
|
|
2
|
+
import { sourceSelection } from '../context.js';
|
|
3
|
+
import { ordered, page } from '../ordering.js';
|
|
4
|
+
import { boundedReach, directAccess, OUTSIDE_GUARD_LIMITATION } from '../outside-guard.js';
|
|
5
|
+
import { agentSummary, collection, findSource, focus, receipt, resourceSummary } from '../summaries.js';
|
|
6
|
+
import { matching, string } from '../work.js';
|
|
7
|
+
/** One host-bound resource, the agents mapped to it, and what reaches it outside the guard. */
|
|
8
|
+
export const getResourceAccess = {
|
|
9
|
+
request: args => ({ ...sourceSelection(args), outsideGuard: true, policy: false }),
|
|
10
|
+
run({ scope, args, work, maxBytes }, view) {
|
|
11
|
+
const source = findSource(view, args.sourceId), projection = source.current;
|
|
12
|
+
const resource = projection.nodes.find(node => node.label === 'GuardResourceBinding' && matching(node, args.resourceId, 'binding_id'));
|
|
13
|
+
if (!resource)
|
|
14
|
+
fail('MAP_NOT_FOUND');
|
|
15
|
+
const agents = ordered(projection.nodes
|
|
16
|
+
.filter(node => node.label === 'GuardAgent')
|
|
17
|
+
.filter(agent => {
|
|
18
|
+
work.step();
|
|
19
|
+
return focus(projection, agent, work).nodes.some(node => node.id === resource.id);
|
|
20
|
+
})
|
|
21
|
+
.map(agent => agentSummary(source, projection, agent, work)));
|
|
22
|
+
const { items, ...pagination } = page(agents, args);
|
|
23
|
+
const reach = boundedReach(work.remaining(), maxBytes)(projection, resource, scope, true);
|
|
24
|
+
return {
|
|
25
|
+
data: {
|
|
26
|
+
resource: resourceSummary(source, projection, resource, work),
|
|
27
|
+
agents: items,
|
|
28
|
+
...pagination,
|
|
29
|
+
outsideGuard: {
|
|
30
|
+
status: reach.status,
|
|
31
|
+
directAccess: reach.directAccess,
|
|
32
|
+
directAccessTruncated: reach.directAccessTruncated,
|
|
33
|
+
identities: reach.identities,
|
|
34
|
+
identitiesTruncated: reach.identitiesTruncated,
|
|
35
|
+
unknownReasons: reach.unknownReasons,
|
|
36
|
+
evidenceKind: 'declared',
|
|
37
|
+
},
|
|
38
|
+
receipt: receipt(source),
|
|
39
|
+
assessment: 'unknown',
|
|
40
|
+
collection,
|
|
41
|
+
limitations: [
|
|
42
|
+
'Agents have a declared selector mapped to this resource; this is not proof of current effective access.',
|
|
43
|
+
OUTSIDE_GUARD_LIMITATION,
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
},
|
|
48
|
+
};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { dangerousThree } from '../../boundary-flags.js';
|
|
2
|
+
import { fail } from '../../common.js';
|
|
3
|
+
import { MapQueryError } from '../../types.js';
|
|
4
|
+
import { sourceSelection } from '../context.js';
|
|
5
|
+
import { agentFlags } from '../flags.js';
|
|
6
|
+
import { agentSortKey, page, sortedBy } from '../ordering.js';
|
|
7
|
+
import { agentSummary } from '../summaries.js';
|
|
8
|
+
import { QueryWork, string } from '../work.js';
|
|
9
|
+
/** The agent index, optionally filtered by boundary flag or carrying each row's flag. */
|
|
10
|
+
export const listAgents = {
|
|
11
|
+
request(args) {
|
|
12
|
+
// `flag` lists flagged agents; asking it for the absent ones is a contradiction.
|
|
13
|
+
if (args.flag !== undefined && args.flagStatus === 'absent')
|
|
14
|
+
fail('MAP_REQUEST_INVALID');
|
|
15
|
+
return {
|
|
16
|
+
...sourceSelection(args),
|
|
17
|
+
// Filtering by flag needs every agent's flags; showing them needs only the page's.
|
|
18
|
+
...(args.flag !== undefined || args.flagStatus !== undefined ? { flags: true } : { summary: true }),
|
|
19
|
+
};
|
|
20
|
+
},
|
|
21
|
+
async run({ scope, args, work, maxBytes, read }, view) {
|
|
22
|
+
const search = typeof args.search === 'string' ? args.search.toLocaleLowerCase('en-US') : '';
|
|
23
|
+
const filtered = args.flag !== undefined || args.flagStatus !== undefined;
|
|
24
|
+
const statuses = args.flagStatus !== undefined
|
|
25
|
+
? [args.flagStatus]
|
|
26
|
+
: args.flag !== undefined
|
|
27
|
+
? ['exposed', 'mitigated', 'unknown']
|
|
28
|
+
: ['exposed', 'mitigated', 'unknown', 'absent'];
|
|
29
|
+
const agents = view.sources
|
|
30
|
+
.flatMap(source => {
|
|
31
|
+
work.step(source.current.nodes.length);
|
|
32
|
+
return source.current.nodes
|
|
33
|
+
.filter(node => node.label === 'GuardAgent')
|
|
34
|
+
.flatMap(agent => {
|
|
35
|
+
const summary = agentSummary(source, source.current, agent, work);
|
|
36
|
+
if (!filtered)
|
|
37
|
+
return [summary];
|
|
38
|
+
const status = agentFlags(source.current, agent, scope, work).status;
|
|
39
|
+
return statuses.includes(status) ? [{ ...summary, boundaryFlags: { dangerousThree: status } }] : [];
|
|
40
|
+
});
|
|
41
|
+
})
|
|
42
|
+
.filter(agent => (!search ||
|
|
43
|
+
[agent.name, agent.agentId].some(value => typeof value === 'string' && value.toLocaleLowerCase('en-US').includes(search))) &&
|
|
44
|
+
(args.ownerId === undefined || agent.ownerId === args.ownerId) &&
|
|
45
|
+
(args.teamId === undefined || agent.teamId === args.teamId) &&
|
|
46
|
+
(args.ownershipStatus === undefined || agent.ownershipStatus === args.ownershipStatus) &&
|
|
47
|
+
(args.lifecycleState === undefined || agent.lifecycleState === args.lifecycleState) &&
|
|
48
|
+
(args.environment === undefined ||
|
|
49
|
+
agent.environments.includes(args.environment)));
|
|
50
|
+
// Unnamed agents sort last; code-point order keeps paging identical on every host and locale.
|
|
51
|
+
sortedBy(agents, agentSortKey);
|
|
52
|
+
const { items, ...pagination } = page(agents, args);
|
|
53
|
+
let rows = items;
|
|
54
|
+
let flagsUnavailable = null;
|
|
55
|
+
if (args.includeFlags === true && !filtered && rows.length) {
|
|
56
|
+
// Flags for this page only: one read of these agents under the one-agent rules, so a
|
|
57
|
+
// badge here matches get_agent_access and the work follows the page, not the fleet.
|
|
58
|
+
// If that read is too large or fails, the listing still answers, with every flag unknown.
|
|
59
|
+
// The page flags get what is left of this query's time, keep a margin for the answer,
|
|
60
|
+
// and count their work separately: running out degrades flags, not the listing.
|
|
61
|
+
const budget = work.remaining() - 50;
|
|
62
|
+
const found = new Map();
|
|
63
|
+
try {
|
|
64
|
+
if (budget <= 0)
|
|
65
|
+
fail('MAP_TIMEOUT');
|
|
66
|
+
const flagged = await read({ ...sourceSelection(args), flags: true, agentIds: rows.map(agent => agent.id) }, budget);
|
|
67
|
+
const flagWork = new QueryWork(Math.max(1, work.remaining() - 25), maxBytes);
|
|
68
|
+
for (const agent of rows) {
|
|
69
|
+
const source = flagged.sources.find(item => item.id === agent.sourceId);
|
|
70
|
+
const node = source?.current.nodes.find(item => item.label === 'GuardAgent' && item.entityId === agent.id);
|
|
71
|
+
// An agent gone between the two reads has no evidence now.
|
|
72
|
+
if (source && node)
|
|
73
|
+
found.set(agent, agentFlags(source.current, node, scope, flagWork).status);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
const code = error instanceof MapQueryError ? error.code : 'MAP_UNAVAILABLE';
|
|
78
|
+
if (!['MAP_LIMIT_EXCEEDED', 'MAP_TIMEOUT', 'MAP_UNAVAILABLE'].includes(code))
|
|
79
|
+
throw error;
|
|
80
|
+
found.clear();
|
|
81
|
+
flagsUnavailable =
|
|
82
|
+
code === 'MAP_LIMIT_EXCEEDED'
|
|
83
|
+
? 'flags_limit_exceeded'
|
|
84
|
+
: code === 'MAP_TIMEOUT'
|
|
85
|
+
? 'flags_timeout'
|
|
86
|
+
: 'flags_read_unavailable';
|
|
87
|
+
}
|
|
88
|
+
rows = rows.map(agent => ({ ...agent, boundaryFlags: { dangerousThree: found.get(agent) ?? 'unknown' } }));
|
|
89
|
+
}
|
|
90
|
+
return { data: { agents: rows, ...pagination, ...(flagsUnavailable ? { flagsUnavailable } : {}) } };
|
|
91
|
+
},
|
|
92
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type Operation } from '../context.js';
|
|
2
|
+
/** Owners and teams, with how many visible agents and resources each one owns. */
|
|
3
|
+
export declare const listOwners: Operation;
|
|
4
|
+
/** Host-bound resources, each with whether something reaches it outside the guard. */
|
|
5
|
+
export declare const listResources: Operation;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { sourceSelection } from '../context.js';
|
|
2
|
+
import { ordered, page } from '../ordering.js';
|
|
3
|
+
import { boundedReach, OUTSIDE_GUARD_LIMITATION } from '../outside-guard.js';
|
|
4
|
+
import { ownerSummary, resourceSummary } from '../summaries.js';
|
|
5
|
+
import { string } from '../work.js';
|
|
6
|
+
const matches = (item, search) => !search ||
|
|
7
|
+
[item.name, item.displayName, item.ownerId, item.bindingId, item.resourceRef].some(value => typeof value === 'string' && value.toLocaleLowerCase('en-US').includes(search));
|
|
8
|
+
const searchText = (args) => typeof args.search === 'string' ? args.search.toLocaleLowerCase('en-US') : '';
|
|
9
|
+
/** Owners and teams, with how many visible agents and resources each one owns. */
|
|
10
|
+
export const listOwners = {
|
|
11
|
+
request: args => ({ ...sourceSelection(args), owners: true }),
|
|
12
|
+
run({ args, work }, view) {
|
|
13
|
+
const search = searchText(args);
|
|
14
|
+
const rows = ordered(view.sources
|
|
15
|
+
.flatMap(source => {
|
|
16
|
+
work.step(source.current.nodes.length);
|
|
17
|
+
return source.current.nodes
|
|
18
|
+
.filter(node => node.label === 'GuardOwner')
|
|
19
|
+
.map(node => ownerSummary(source, source.current, node, work));
|
|
20
|
+
})
|
|
21
|
+
.filter(item => matches(item, search) && (args.kind === undefined || item.kind === args.kind)));
|
|
22
|
+
const { items, ...pagination } = page(rows, args);
|
|
23
|
+
return { data: { owners: items, ...pagination } };
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
/** Host-bound resources, each with whether something reaches it outside the guard. */
|
|
27
|
+
export const listResources = {
|
|
28
|
+
// Policy evidence is read only where it is returned: a listing asks for none, so the store's
|
|
29
|
+
// 512-pair policy limit does not bound it.
|
|
30
|
+
request: args => ({ ...sourceSelection(args), outsideGuard: true, policy: false }),
|
|
31
|
+
run({ scope, args, work, maxBytes }, view) {
|
|
32
|
+
const search = searchText(args);
|
|
33
|
+
// Where each resource row came from, so reach can be judged after paging.
|
|
34
|
+
const origins = new Map();
|
|
35
|
+
const rows = ordered(view.sources
|
|
36
|
+
.flatMap(source => {
|
|
37
|
+
work.step(source.current.nodes.length);
|
|
38
|
+
return source.current.nodes
|
|
39
|
+
.filter(node => node.label === 'GuardResourceBinding')
|
|
40
|
+
.map(node => {
|
|
41
|
+
const summary = resourceSummary(source, source.current, node, work);
|
|
42
|
+
origins.set(summary, { projection: source.current, node });
|
|
43
|
+
return summary;
|
|
44
|
+
});
|
|
45
|
+
})
|
|
46
|
+
.filter(item => matches(item, search) &&
|
|
47
|
+
(args.ownerId === undefined || item.ownerId === args.ownerId) &&
|
|
48
|
+
(args.sensitivity === undefined || item.sensitivity === args.sensitivity)));
|
|
49
|
+
const { items, ...pagination } = page(rows, args);
|
|
50
|
+
// Reach is judged for the returned page only: its budget is never spent on rows the
|
|
51
|
+
// caller does not see, and a filter or a later page costs no more than its own rows.
|
|
52
|
+
const reach = boundedReach(work.remaining(), maxBytes);
|
|
53
|
+
const resources = items.map(item => {
|
|
54
|
+
const origin = origins.get(item);
|
|
55
|
+
return { ...item, outsideGuard: reach(origin.projection, origin.node, scope).status };
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
data: {
|
|
59
|
+
resources,
|
|
60
|
+
...pagination,
|
|
61
|
+
resourceCoverage: 'host_bound_resources',
|
|
62
|
+
limitations: [
|
|
63
|
+
'Declared selectors without a host resource binding are not included; see agent permissions.',
|
|
64
|
+
OUTSIDE_GUARD_LIMITATION,
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { JsonObject, JsonValue } from '../types.js';
|
|
2
|
+
export declare const compare: (a: string, b: string) => number;
|
|
3
|
+
export declare const sortText: (value: JsonValue | undefined) => string;
|
|
4
|
+
export declare function compareSortKeys(a: readonly string[], b: readonly string[]): number;
|
|
5
|
+
/** Sorts in place by precomputed keys: each row's key is built once, not once per comparison. */
|
|
6
|
+
export declare function sortedBy<T extends object>(items: T[], key: (item: T) => readonly string[]): T[];
|
|
7
|
+
export declare const agentSortKey: (agent: JsonObject) => readonly string[];
|
|
8
|
+
export declare function ordered(items: JsonObject[]): JsonObject[];
|
|
9
|
+
export declare function page(items: readonly JsonObject[], args: Readonly<Record<string, JsonValue>>): JsonObject;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { compareCodePoints } from '../common.js';
|
|
2
|
+
import { number, string } from './work.js';
|
|
3
|
+
export const compare = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
4
|
+
// Sort keys compare field by field in Unicode code-point order, so "Reader" comes before
|
|
5
|
+
// "Reader 2" and paging is identical on every host and locale. Equal names fall back to the
|
|
6
|
+
// source and the stable id.
|
|
7
|
+
export const sortText = (value) => typeof value === 'string' ? value : value === null || value === undefined ? '' : JSON.stringify(value);
|
|
8
|
+
export function compareSortKeys(a, b) {
|
|
9
|
+
for (let index = 0; index < a.length; index++) {
|
|
10
|
+
const order = compareCodePoints(a[index], b[index]);
|
|
11
|
+
if (order)
|
|
12
|
+
return order;
|
|
13
|
+
}
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
/** Sorts in place by precomputed keys: each row's key is built once, not once per comparison. */
|
|
17
|
+
export function sortedBy(items, key) {
|
|
18
|
+
const keys = new Map(items.map(item => [item, key(item)]));
|
|
19
|
+
return items.sort((a, b) => compareSortKeys(keys.get(a), keys.get(b)));
|
|
20
|
+
}
|
|
21
|
+
// Unnamed agents sort last.
|
|
22
|
+
export const agentSortKey = (agent) => [
|
|
23
|
+
agent.name === null || agent.name === undefined ? '1' : '0',
|
|
24
|
+
sortText(agent.name),
|
|
25
|
+
sortText(agent.sourceId),
|
|
26
|
+
sortText(agent.id),
|
|
27
|
+
];
|
|
28
|
+
export function ordered(items) {
|
|
29
|
+
return sortedBy(items, item => [
|
|
30
|
+
sortText(item.name ?? item.displayName),
|
|
31
|
+
sortText(item.sourceId),
|
|
32
|
+
sortText(item.id ?? item.entityId),
|
|
33
|
+
sortText(item.source),
|
|
34
|
+
sortText(item.target),
|
|
35
|
+
sortText(item.kind),
|
|
36
|
+
]);
|
|
37
|
+
}
|
|
38
|
+
export function page(items, args) {
|
|
39
|
+
const offset = args.offset ?? 0, limit = args.limit ?? 50;
|
|
40
|
+
return {
|
|
41
|
+
items: items.slice(offset, offset + limit),
|
|
42
|
+
total: items.length,
|
|
43
|
+
offset,
|
|
44
|
+
limit,
|
|
45
|
+
truncated: offset + limit < items.length,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type JsonObject, type MapIdentityExtraResource, type MapIdentityScope, type MapNode, type MapProjection, type ReadScope } from '../types.js';
|
|
2
|
+
import { type FocusedGraph } from './summaries.js';
|
|
3
|
+
import { QueryWork } from './work.js';
|
|
4
|
+
/** A declared target names a host resource; name it only when that resource is in the reader's scope. */
|
|
5
|
+
export declare function targetVisibility(projection: MapProjection, scope: ReadScope, work: QueryWork): (target: string) => boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Host-declared paths that act without the guard, per deployment in the agent's focus.
|
|
8
|
+
* Manifest v3 sets `direct_access_status`; older manifests leave it absent, which reads as
|
|
9
|
+
* `unknown` and never as `none_declared`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function directAccess(projection: MapProjection, graph: FocusedGraph, scope: ReadScope, work: QueryWork): JsonObject[];
|
|
12
|
+
/** `data` with each identity-scope list cut to `keep` entries, or null if nothing would change. */
|
|
13
|
+
export declare function shortenIdentityScope(data: JsonObject, keep: number): JsonObject | null;
|
|
14
|
+
export type Reach = 'yes' | 'none_found' | 'unknown';
|
|
15
|
+
export interface ReachIndex {
|
|
16
|
+
readonly undeclaredDeployments: number;
|
|
17
|
+
/** Declared paths without a target, such as a shell, can touch any resource. */
|
|
18
|
+
readonly untargetedPaths: number;
|
|
19
|
+
/** Declared paths whose target matches no resource in this read (perhaps another team's). */
|
|
20
|
+
readonly unresolvedPaths: number;
|
|
21
|
+
/** Visible granted tools with no declared execution identity (ambient or unknown). */
|
|
22
|
+
readonly undeclaredIdentities: number;
|
|
23
|
+
readonly directByTarget: Map<string, MapNode[]>;
|
|
24
|
+
readonly identitiesByBucket: Map<string, {
|
|
25
|
+
record: MapIdentityScope;
|
|
26
|
+
extra: MapIdentityExtraResource;
|
|
27
|
+
}[]>;
|
|
28
|
+
readonly identityUnknown: number;
|
|
29
|
+
readonly identityCollected: boolean;
|
|
30
|
+
/** AWS accounts of the evaluated identities; a bucket elsewhere was never compared. */
|
|
31
|
+
readonly identityAccounts: ReadonlySet<string | null>;
|
|
32
|
+
}
|
|
33
|
+
export declare const reachIndex: WeakMap<MapProjection, ReachIndex>;
|
|
34
|
+
/**
|
|
35
|
+
* Outside-guard reach over the whole visible projection: any declared direct path or any
|
|
36
|
+
* evaluated tool identity can touch any resource, so `none_found` needs every visible
|
|
37
|
+
* deployment to have declared its paths and every visible identity to be evaluated.
|
|
38
|
+
*/
|
|
39
|
+
export declare function outsideGuardIndex(projection: MapProjection, work: QueryWork): ReachIndex;
|
|
40
|
+
/** Entries listed per resource in get_resource_access before the list is marked truncated. */
|
|
41
|
+
export declare const REACH_LIST_LIMIT = 100;
|
|
42
|
+
export interface ReachResult {
|
|
43
|
+
status: Reach;
|
|
44
|
+
unknownReasons: string[];
|
|
45
|
+
directAccess: JsonObject[];
|
|
46
|
+
directAccessTruncated: boolean;
|
|
47
|
+
identities: JsonObject[];
|
|
48
|
+
identitiesTruncated: boolean;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* `detail` builds the (bounded) lists for one resource; the listing only needs the status,
|
|
52
|
+
* which comes from index lookups.
|
|
53
|
+
*/
|
|
54
|
+
export declare function outsideGuardReach(projection: MapProjection, resource: MapNode, scope: ReadScope, work: QueryWork, detail?: boolean): ReachResult;
|
|
55
|
+
/**
|
|
56
|
+
* Reach with its own work budget, shared by every row of one response: if it runs out, that
|
|
57
|
+
* row and every later one read `unknown` (`reach_not_evaluated`) and the response still
|
|
58
|
+
* answers. Rows judged before that keep their status: each used a complete index.
|
|
59
|
+
*/
|
|
60
|
+
export declare function boundedReach(remaining: number, maxBytes: number): (projection: MapProjection, resource: MapNode, scope: ReadScope, detail?: boolean) => ReachResult;
|
|
61
|
+
export declare const OUTSIDE_GUARD_LIMITATION = "Only S3 identity policies and host declarations of this source are checked, within your visible scope; none_found is not proof that no path exists.";
|
|
62
|
+
/**
|
|
63
|
+
* C2 per execution binding in the agent's focus. A store that did not run the reverse identity
|
|
64
|
+
* read reports every binding as `unknown` rather than leaving it out.
|
|
65
|
+
*/
|
|
66
|
+
export declare function identityScope(projection: MapProjection, graph: {
|
|
67
|
+
nodes: MapNode[];
|
|
68
|
+
}): JsonObject[];
|