@cloud-copilot/iam-lens 0.1.76 → 0.1.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/utils/workerScript.d.ts +2 -2
- package/dist/cjs/utils/workerScript.d.ts.map +1 -1
- package/dist/cjs/utils/workerScript.js +7 -2
- package/dist/cjs/utils/workerScript.js.map +1 -1
- package/dist/cjs/whoCan/WhoCanMainThreadWorker.d.ts +4 -3
- package/dist/cjs/whoCan/WhoCanMainThreadWorker.d.ts.map +1 -1
- package/dist/cjs/whoCan/WhoCanMainThreadWorker.js +49 -4
- package/dist/cjs/whoCan/WhoCanMainThreadWorker.js.map +1 -1
- package/dist/cjs/whoCan/WhoCanWorker.d.ts +23 -2
- package/dist/cjs/whoCan/WhoCanWorker.d.ts.map +1 -1
- package/dist/cjs/whoCan/WhoCanWorker.js +23 -13
- package/dist/cjs/whoCan/WhoCanWorker.js.map +1 -1
- package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.js +68 -3
- package/dist/cjs/whoCan/WhoCanWorkerThreadWorker.js.map +1 -1
- package/dist/cjs/whoCan/requestAnalysis.d.ts +22 -0
- package/dist/cjs/whoCan/requestAnalysis.d.ts.map +1 -0
- package/dist/cjs/whoCan/requestAnalysis.js +27 -0
- package/dist/cjs/whoCan/requestAnalysis.js.map +1 -0
- package/dist/cjs/whoCan/whoCan.d.ts +35 -0
- package/dist/cjs/whoCan/whoCan.d.ts.map +1 -1
- package/dist/cjs/whoCan/whoCan.js +57 -16
- package/dist/cjs/whoCan/whoCan.js.map +1 -1
- package/dist/esm/utils/workerScript.d.ts.map +1 -1
- package/dist/esm/whoCan/WhoCanMainThreadWorker.d.ts +4 -3
- package/dist/esm/whoCan/WhoCanMainThreadWorker.d.ts.map +1 -1
- package/dist/esm/whoCan/WhoCanMainThreadWorker.js +49 -4
- package/dist/esm/whoCan/WhoCanMainThreadWorker.js.map +1 -1
- package/dist/esm/whoCan/WhoCanWorker.d.ts +23 -2
- package/dist/esm/whoCan/WhoCanWorker.d.ts.map +1 -1
- package/dist/esm/whoCan/WhoCanWorker.js +23 -13
- package/dist/esm/whoCan/WhoCanWorker.js.map +1 -1
- package/dist/esm/whoCan/WhoCanWorkerThreadWorker.js +68 -3
- package/dist/esm/whoCan/WhoCanWorkerThreadWorker.js.map +1 -1
- package/dist/esm/whoCan/requestAnalysis.d.ts +22 -0
- package/dist/esm/whoCan/requestAnalysis.d.ts.map +1 -0
- package/dist/esm/whoCan/requestAnalysis.js +24 -0
- package/dist/esm/whoCan/requestAnalysis.js.map +1 -0
- package/dist/esm/whoCan/whoCan.d.ts +35 -0
- package/dist/esm/whoCan/whoCan.d.ts.map +1 -1
- package/dist/esm/whoCan/whoCan.js +57 -16
- package/dist/esm/whoCan/whoCan.js.map +1 -1
- package/package.json +2 -2
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Get the path to a worker script, adjusted for the build output location.
|
|
3
3
|
*
|
|
4
4
|
* @param path the relative path to the worker script from the project src directory and with a .js extension
|
|
5
|
-
* @returns the absolute path to the worker script
|
|
5
|
+
* @returns the absolute path to the worker script, or undefined if the worker script path does not exist
|
|
6
6
|
*/
|
|
7
|
-
export declare function getWorkerScriptPath(path: string): string;
|
|
7
|
+
export declare function getWorkerScriptPath(path: string): string | undefined;
|
|
8
8
|
//# sourceMappingURL=workerScript.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workerScript.d.ts","sourceRoot":"","sources":["../../../src/utils/workerScript.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"workerScript.d.ts","sourceRoot":"","sources":["../../../src/utils/workerScript.ts"],"names":[],"mappings":"AAQA;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMpE"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.getWorkerScriptPath = getWorkerScriptPath;
|
|
4
|
+
const fs_1 = require("fs");
|
|
4
5
|
const path_1 = require("path");
|
|
5
6
|
let root = (0, path_1.resolve)(__dirname, '../');
|
|
6
7
|
if (process.env.NODE_ENV === 'test') {
|
|
@@ -10,9 +11,13 @@ if (process.env.NODE_ENV === 'test') {
|
|
|
10
11
|
* Get the path to a worker script, adjusted for the build output location.
|
|
11
12
|
*
|
|
12
13
|
* @param path the relative path to the worker script from the project src directory and with a .js extension
|
|
13
|
-
* @returns the absolute path to the worker script
|
|
14
|
+
* @returns the absolute path to the worker script, or undefined if the worker script path does not exist
|
|
14
15
|
*/
|
|
15
16
|
function getWorkerScriptPath(path) {
|
|
16
|
-
|
|
17
|
+
const fullPath = (0, path_1.join)(root, path);
|
|
18
|
+
if (!(0, fs_1.existsSync)(fullPath)) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
return fullPath;
|
|
17
22
|
}
|
|
18
23
|
//# sourceMappingURL=workerScript.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workerScript.js","sourceRoot":"","sources":["../../../src/utils/workerScript.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"workerScript.js","sourceRoot":"","sources":["../../../src/utils/workerScript.ts"],"names":[],"mappings":";;AAcA,kDAMC;AApBD,2BAA+B;AAC/B,+BAAoC;AAEpC,IAAI,IAAI,GAAG,IAAA,cAAO,EAAC,SAAS,EAAE,KAAK,CAAC,CAAA;AACpC,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;IACpC,IAAI,GAAG,IAAA,cAAO,EAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;AAC3C,CAAC;AAED;;;;;GAKG;AACH,SAAgB,mBAAmB,CAAC,IAAY;IAC9C,MAAM,QAAQ,GAAG,IAAA,WAAI,EAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IACjC,IAAI,CAAC,IAAA,eAAU,EAAC,QAAQ,CAAC,EAAE,CAAC;QAC1B,OAAO,SAAS,CAAA;IAClB,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC"}
|
|
@@ -4,7 +4,8 @@ import { S3AbacOverride } from '../utils/s3Abac.js';
|
|
|
4
4
|
import { ArrayStreamingWorkQueue } from '../workers/ArrayStreamingWorkQueue.js';
|
|
5
5
|
import { PullBasedJobRunner } from '../workers/JobRunner.js';
|
|
6
6
|
import { StreamingWorkQueue } from '../workers/StreamingWorkQueue.js';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
7
|
+
import { LightRequestAnalysis } from './requestAnalysis.js';
|
|
8
|
+
import { WhoCanAllowed, WhoCanDenyDetail } from './whoCan.js';
|
|
9
|
+
import { WhoCanExecutionResult, WhoCanWorkItem } from './WhoCanWorker.js';
|
|
10
|
+
export declare function createMainThreadStreamingWorkQueue(queue: StreamingWorkQueue<WhoCanWorkItem> | ArrayStreamingWorkQueue<WhoCanWorkItem>, collectClient: IamCollectClient, s3AbacOverride: S3AbacOverride | undefined, onComplete: (result: JobResult<WhoCanAllowed | undefined, Record<string, unknown>>) => void, denyDetailsCallback?: (details: LightRequestAnalysis) => boolean, onDenyDetail?: (detail: WhoCanDenyDetail) => void): PullBasedJobRunner<WhoCanExecutionResult, Record<string, unknown>, WhoCanWorkItem>;
|
|
10
11
|
//# sourceMappingURL=WhoCanMainThreadWorker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WhoCanMainThreadWorker.d.ts","sourceRoot":"","sources":["../../../src/whoCan/WhoCanMainThreadWorker.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"WhoCanMainThreadWorker.d.ts","sourceRoot":"","sources":["../../../src/whoCan/WhoCanMainThreadWorker.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAC9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EAAE,uBAAuB,EAAE,MAAM,uCAAuC,CAAA;AAC/E,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAA;AACrE,OAAO,EAAE,oBAAoB,EAA0B,MAAM,sBAAsB,CAAA;AACnF,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC7D,OAAO,EAEL,qBAAqB,EACrB,cAAc,EACf,MAAM,mBAAmB,CAAA;AAE1B,wBAAgB,kCAAkC,CAChD,KAAK,EAAE,kBAAkB,CAAC,cAAc,CAAC,GAAG,uBAAuB,CAAC,cAAc,CAAC,EACnF,aAAa,EAAE,gBAAgB,EAC/B,cAAc,EAAE,cAAc,GAAG,SAAS,EAC1C,UAAU,EAAE,CAAC,MAAM,EAAE,SAAS,CAAC,aAAa,GAAG,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,IAAI,EAC3F,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,OAAO,EAChE,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,sFA6DlD"}
|
|
@@ -1,18 +1,63 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createMainThreadStreamingWorkQueue = createMainThreadStreamingWorkQueue;
|
|
4
|
+
const iam_simulate_1 = require("@cloud-copilot/iam-simulate");
|
|
4
5
|
const JobRunner_js_1 = require("../workers/JobRunner.js");
|
|
6
|
+
const requestAnalysis_js_1 = require("./requestAnalysis.js");
|
|
5
7
|
const WhoCanWorker_js_1 = require("./WhoCanWorker.js");
|
|
6
|
-
function createMainThreadStreamingWorkQueue(queue, collectClient, s3AbacOverride, onComplete) {
|
|
8
|
+
function createMainThreadStreamingWorkQueue(queue, collectClient, s3AbacOverride, onComplete, denyDetailsCallback, onDenyDetail) {
|
|
9
|
+
const collectDenyDetails = !!denyDetailsCallback;
|
|
7
10
|
return new JobRunner_js_1.PullBasedJobRunner(50, async () => {
|
|
8
11
|
return queue.dequeue();
|
|
9
12
|
}, (workItem) => {
|
|
10
13
|
return (0, WhoCanWorker_js_1.createJobForWhoCanWorkItem)(workItem, collectClient, {
|
|
11
|
-
s3AbacOverride
|
|
14
|
+
s3AbacOverride,
|
|
15
|
+
collectDenyDetails
|
|
12
16
|
});
|
|
13
17
|
}, async (result) => {
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
if (result.status === 'fulfilled') {
|
|
19
|
+
const executionResult = result.value;
|
|
20
|
+
if (executionResult.allowed) {
|
|
21
|
+
// Simulation was allowed - pass through to onComplete
|
|
22
|
+
onComplete({
|
|
23
|
+
status: 'fulfilled',
|
|
24
|
+
value: executionResult.allowed,
|
|
25
|
+
properties: result.properties
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
// Simulation was denied
|
|
30
|
+
onComplete({
|
|
31
|
+
status: 'fulfilled',
|
|
32
|
+
value: undefined,
|
|
33
|
+
properties: result.properties
|
|
34
|
+
});
|
|
35
|
+
// Check if we should include deny details
|
|
36
|
+
if (denyDetailsCallback && onDenyDetail && executionResult.denyAnalysis) {
|
|
37
|
+
const lightAnalysis = (0, requestAnalysis_js_1.toLightRequestAnalysis)(executionResult.denyAnalysis);
|
|
38
|
+
const shouldInclude = denyDetailsCallback(lightAnalysis);
|
|
39
|
+
if (shouldInclude) {
|
|
40
|
+
const denialReasons = (0, iam_simulate_1.getDenialReasons)(executionResult.denyAnalysis);
|
|
41
|
+
const { workItem } = executionResult;
|
|
42
|
+
const [service, action] = workItem.action.split(':');
|
|
43
|
+
onDenyDetail({
|
|
44
|
+
principal: workItem.principal,
|
|
45
|
+
service,
|
|
46
|
+
action,
|
|
47
|
+
details: denialReasons
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
// Error case - pass through as rejected
|
|
55
|
+
onComplete({
|
|
56
|
+
status: 'rejected',
|
|
57
|
+
reason: result.reason,
|
|
58
|
+
properties: result.properties
|
|
59
|
+
});
|
|
60
|
+
}
|
|
16
61
|
});
|
|
17
62
|
}
|
|
18
63
|
//# sourceMappingURL=WhoCanMainThreadWorker.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WhoCanMainThreadWorker.js","sourceRoot":"","sources":["../../../src/whoCan/WhoCanMainThreadWorker.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"WhoCanMainThreadWorker.js","sourceRoot":"","sources":["../../../src/whoCan/WhoCanMainThreadWorker.ts"],"names":[],"mappings":";;AAeA,gFAmEC;AAlFD,8DAA8D;AAK9D,0DAA4D;AAE5D,6DAAmF;AAEnF,uDAI0B;AAE1B,SAAgB,kCAAkC,CAChD,KAAmF,EACnF,aAA+B,EAC/B,cAA0C,EAC1C,UAA2F,EAC3F,mBAAgE,EAChE,YAAiD;IAEjD,MAAM,kBAAkB,GAAG,CAAC,CAAC,mBAAmB,CAAA;IAEhD,OAAO,IAAI,iCAAkB,CAC3B,EAAE,EACF,KAAK,IAAI,EAAE;QACT,OAAO,KAAK,CAAC,OAAO,EAAE,CAAA;IACxB,CAAC,EACD,CAAC,QAAQ,EAAE,EAAE;QACX,OAAO,IAAA,4CAA0B,EAAC,QAAQ,EAAE,aAAa,EAAE;YACzD,cAAc;YACd,kBAAkB;SACnB,CAAC,CAAA;IACJ,CAAC,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACf,IAAI,MAAM,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YAClC,MAAM,eAAe,GAAG,MAAM,CAAC,KAAK,CAAA;YACpC,IAAI,eAAe,CAAC,OAAO,EAAE,CAAC;gBAC5B,sDAAsD;gBACtD,UAAU,CAAC;oBACT,MAAM,EAAE,WAAW;oBACnB,KAAK,EAAE,eAAe,CAAC,OAAO;oBAC9B,UAAU,EAAE,MAAM,CAAC,UAAU;iBAC9B,CAAC,CAAA;YACJ,CAAC;iBAAM,CAAC;gBACN,wBAAwB;gBACxB,UAAU,CAAC;oBACT,MAAM,EAAE,WAAW;oBACnB,KAAK,EAAE,SAAS;oBAChB,UAAU,EAAE,MAAM,CAAC,UAAU;iBAC9B,CAAC,CAAA;gBAEF,0CAA0C;gBAC1C,IAAI,mBAAmB,IAAI,YAAY,IAAI,eAAe,CAAC,YAAY,EAAE,CAAC;oBACxE,MAAM,aAAa,GAAG,IAAA,2CAAsB,EAAC,eAAe,CAAC,YAAY,CAAC,CAAA;oBAC1E,MAAM,aAAa,GAAG,mBAAmB,CAAC,aAAa,CAAC,CAAA;oBAExD,IAAI,aAAa,EAAE,CAAC;wBAClB,MAAM,aAAa,GAAG,IAAA,+BAAgB,EAAC,eAAe,CAAC,YAAY,CAAC,CAAA;wBACpE,MAAM,EAAE,QAAQ,EAAE,GAAG,eAAe,CAAA;wBACpC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;wBACpD,YAAY,CAAC;4BACX,SAAS,EAAE,QAAQ,CAAC,SAAS;4BAC7B,OAAO;4BACP,MAAM;4BACN,OAAO,EAAE,aAAa;yBACvB,CAAC,CAAA;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;aAAM,CAAC;YACN,wCAAwC;YACxC,UAAU,CAAC;gBACT,MAAM,EAAE,UAAU;gBAClB,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,UAAU,EAAE,MAAM,CAAC,UAAU;aAC9B,CAAC,CAAA;QACJ,CAAC;IACH,CAAC,CACF,CAAA;AACH,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RequestAnalysis } from '@cloud-copilot/iam-simulate';
|
|
1
2
|
import { Job } from '@cloud-copilot/job';
|
|
2
3
|
import { IamCollectClient } from '../collect/client.js';
|
|
3
4
|
import { S3AbacOverride } from '../utils/s3Abac.js';
|
|
@@ -8,9 +9,29 @@ export interface WhoCanWorkItem {
|
|
|
8
9
|
action: string;
|
|
9
10
|
principal: string;
|
|
10
11
|
}
|
|
11
|
-
|
|
12
|
+
/**
|
|
13
|
+
* The result of executing a whoCan work item.
|
|
14
|
+
* Contains either the allowed result or the deny analysis (but not both).
|
|
15
|
+
*/
|
|
16
|
+
export interface WhoCanExecutionResult {
|
|
17
|
+
/**
|
|
18
|
+
* The allowed result if the simulation was successful
|
|
19
|
+
*/
|
|
20
|
+
allowed?: WhoCanAllowed;
|
|
21
|
+
/**
|
|
22
|
+
* The deny analysis if the simulation was not allowed.
|
|
23
|
+
* Only populated when collectDenyDetails is true.
|
|
24
|
+
*/
|
|
25
|
+
denyAnalysis?: RequestAnalysis;
|
|
26
|
+
/**
|
|
27
|
+
* The work item that was executed, for context in deny details
|
|
28
|
+
*/
|
|
29
|
+
workItem: WhoCanWorkItem;
|
|
30
|
+
}
|
|
31
|
+
export declare function createJobForWhoCanWorkItem(workItem: WhoCanWorkItem, collectClient: IamCollectClient, whoCanOptions: WhoCanOptions): Job<WhoCanExecutionResult, Record<string, unknown>>;
|
|
12
32
|
export interface WhoCanOptions {
|
|
13
33
|
s3AbacOverride?: S3AbacOverride;
|
|
34
|
+
collectDenyDetails?: boolean;
|
|
14
35
|
}
|
|
15
|
-
export declare function executeWhoCan(workItem: WhoCanWorkItem, collectClient: IamCollectClient, whoCanOptions: WhoCanOptions): Promise<
|
|
36
|
+
export declare function executeWhoCan(workItem: WhoCanWorkItem, collectClient: IamCollectClient, whoCanOptions: WhoCanOptions): Promise<WhoCanExecutionResult>;
|
|
16
37
|
//# sourceMappingURL=WhoCanWorker.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WhoCanWorker.d.ts","sourceRoot":"","sources":["../../../src/whoCan/WhoCanWorker.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAEvD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE3C,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;IAC5B,eAAe,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,cAAc,EACxB,aAAa,EAAE,gBAAgB,EAC/B,aAAa,EAAE,aAAa,GAC3B,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"WhoCanWorker.d.ts","sourceRoot":"","sources":["../../../src/whoCan/WhoCanWorker.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAC7D,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAA;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAEvD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE3C,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;IAC5B,eAAe,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,OAAO,CAAC,EAAE,aAAa,CAAA;IAEvB;;;OAGG;IACH,YAAY,CAAC,EAAE,eAAe,CAAA;IAE9B;;OAEG;IACH,QAAQ,EAAE,cAAc,CAAA;CACzB;AAED,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,cAAc,EACxB,aAAa,EAAE,gBAAgB,EAC/B,aAAa,EAAE,aAAa,GAC3B,GAAG,CAAC,qBAAqB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAOrD;AAED,MAAM,WAAW,aAAa;IAC5B,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B;AAED,wBAAsB,aAAa,CACjC,QAAQ,EAAE,cAAc,EACxB,aAAa,EAAE,gBAAgB,EAC/B,aAAa,EAAE,aAAa,GAC3B,OAAO,CAAC,qBAAqB,CAAC,CA+DhC"}
|
|
@@ -37,27 +37,37 @@ async function executeWhoCan(workItem, collectClient, whoCanOptions) {
|
|
|
37
37
|
if (result?.result.analysis?.result === 'Allowed') {
|
|
38
38
|
const actionType = await getActionLevel(service, serviceAction);
|
|
39
39
|
return {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
workItem,
|
|
41
|
+
allowed: {
|
|
42
|
+
principal,
|
|
43
|
+
service,
|
|
44
|
+
action: serviceAction,
|
|
45
|
+
level: actionType.toLowerCase()
|
|
46
|
+
}
|
|
44
47
|
};
|
|
45
48
|
}
|
|
46
49
|
else {
|
|
47
50
|
const actionType = await getActionLevel(service, serviceAction);
|
|
48
51
|
return {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
:
|
|
52
|
+
workItem,
|
|
53
|
+
allowed: {
|
|
54
|
+
principal,
|
|
55
|
+
service: service,
|
|
56
|
+
action: serviceAction,
|
|
57
|
+
level: actionType.toLowerCase(),
|
|
58
|
+
conditions: discoveryResult?.result.analysis.ignoredConditions,
|
|
59
|
+
dependsOnSessionName: discoveryResult?.result.analysis.ignoredRoleSessionName
|
|
60
|
+
? true
|
|
61
|
+
: undefined
|
|
62
|
+
}
|
|
57
63
|
};
|
|
58
64
|
}
|
|
59
65
|
}
|
|
60
|
-
return
|
|
66
|
+
// Not allowed - return deny analysis if requested
|
|
67
|
+
return {
|
|
68
|
+
workItem,
|
|
69
|
+
denyAnalysis: whoCanOptions.collectDenyDetails ? discoveryResult?.result.analysis : undefined
|
|
70
|
+
};
|
|
61
71
|
}
|
|
62
72
|
/**
|
|
63
73
|
* Get the action level for a specific service action, will fail if the service or action does not exist.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WhoCanWorker.js","sourceRoot":"","sources":["../../../src/whoCan/WhoCanWorker.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"WhoCanWorker.js","sourceRoot":"","sources":["../../../src/whoCan/WhoCanWorker.ts"],"names":[],"mappings":";;AAqCA,gEAWC;AAOD,sCAmEC;AA1HD,sDAA0D;AAI1D,yDAAyD;AAiCzD,SAAgB,0BAA0B,CACxC,QAAwB,EACxB,aAA+B,EAC/B,aAA4B;IAE5B,OAAO;QACL,UAAU,EAAE,EAAE;QACd,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;YACzB,OAAO,aAAa,CAAC,QAAQ,EAAE,aAAa,EAAE,aAAa,CAAC,CAAA;QAC9D,CAAC;KACF,CAAA;AACH,CAAC;AAOM,KAAK,UAAU,aAAa,CACjC,QAAwB,EACxB,aAA+B,EAC/B,aAA4B;IAE5B,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,EAAE,GAAG,QAAQ,CAAA;IACjE,MAAM,CAAC,OAAO,EAAE,aAAa,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAClD,MAAM,eAAe,GAAG,MAAM,IAAA,6BAAe,EAC3C;QACE,SAAS;QACT,WAAW,EAAE,QAAQ;QACrB,eAAe,EAAE,eAAe;QAChC,MAAM;QACN,iBAAiB,EAAE,EAAE;QACrB,cAAc,EAAE,WAAW;QAC3B,cAAc,EAAE,aAAa,CAAC,cAAc;KAC7C,EACD,aAAa,CACd,CAAA;IAED,IAAI,eAAe,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;QAC3D,MAAM,MAAM,GAAG,MAAM,IAAA,6BAAe,EAClC;YACE,SAAS;YACT,WAAW,EAAE,QAAQ;YACrB,eAAe;YACf,MAAM;YACN,iBAAiB,EAAE,EAAE;YACrB,cAAc,EAAE,QAAQ;YACxB,cAAc,EAAE,aAAa,CAAC,cAAc;SAC7C,EACD,aAAa,CACd,CAAA;QACD,IAAI,MAAM,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,KAAK,SAAS,EAAE,CAAC;YAClD,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,aAAa,CAAC,CAAA;YAC/D,OAAO;gBACL,QAAQ;gBACR,OAAO,EAAE;oBACP,SAAS;oBACT,OAAO;oBACP,MAAM,EAAE,aAAa;oBACrB,KAAK,EAAE,UAAU,CAAC,WAAW,EAAE;iBAChC;aACF,CAAA;QACH,CAAC;aAAM,CAAC;YACN,MAAM,UAAU,GAAG,MAAM,cAAc,CAAC,OAAO,EAAE,aAAa,CAAC,CAAA;YAC/D,OAAO;gBACL,QAAQ;gBACR,OAAO,EAAE;oBACP,SAAS;oBACT,OAAO,EAAE,OAAO;oBAChB,MAAM,EAAE,aAAa;oBACrB,KAAK,EAAE,UAAU,CAAC,WAAW,EAAE;oBAC/B,UAAU,EAAE,eAAe,EAAE,MAAM,CAAC,QAAQ,CAAC,iBAAiB;oBAC9D,oBAAoB,EAAE,eAAe,EAAE,MAAM,CAAC,QAAQ,CAAC,sBAAsB;wBAC3E,CAAC,CAAC,IAAI;wBACN,CAAC,CAAC,SAAS;iBACd;aACF,CAAA;QACH,CAAC;IACH,CAAC;IAED,kDAAkD;IAClD,OAAO;QACL,QAAQ;QACR,YAAY,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC,CAAC,eAAe,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS;KAC9F,CAAA;AACH,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,cAAc,CAAC,OAAe,EAAE,MAAc;IAC3D,MAAM,OAAO,GAAG,MAAM,IAAA,2BAAgB,EAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IACvD,OAAO,OAAO,CAAC,WAAW,CAAA;AAC5B,CAAC"}
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const iam_simulate_1 = require("@cloud-copilot/iam-simulate");
|
|
3
4
|
const worker_threads_1 = require("worker_threads");
|
|
4
5
|
const collect_js_1 = require("../collect/collect.js");
|
|
5
6
|
const JobRunner_js_1 = require("../workers/JobRunner.js");
|
|
6
7
|
const SharedArrayBufferWorkerCache_js_1 = require("../workers/SharedArrayBufferWorkerCache.js");
|
|
8
|
+
const requestAnalysis_js_1 = require("./requestAnalysis.js");
|
|
7
9
|
const WhoCanWorker_js_1 = require("./WhoCanWorker.js");
|
|
8
10
|
if (!worker_threads_1.parentPort) {
|
|
9
11
|
throw new Error('Must be run as a worker thread');
|
|
10
12
|
}
|
|
11
13
|
// Get config from the main thread
|
|
12
|
-
const { concurrency, collectConfigs, partition, s3AbacOverride } = worker_threads_1.workerData;
|
|
14
|
+
const { concurrency, collectConfigs, partition, s3AbacOverride, collectDenyDetails } = worker_threads_1.workerData;
|
|
13
15
|
const taskPromises = {};
|
|
16
|
+
// Pending deny details checks - keyed by a unique id for each check
|
|
17
|
+
let denyDetailsCheckId = 0;
|
|
18
|
+
const pendingDenyDetailsChecks = {};
|
|
14
19
|
worker_threads_1.parentPort.on('message', (msg) => {
|
|
15
20
|
if (msg.type === 'task' && msg.workerId in taskPromises) {
|
|
16
21
|
taskPromises[msg.workerId](msg.task);
|
|
@@ -24,6 +29,15 @@ worker_threads_1.parentPort.on('message', (msg) => {
|
|
|
24
29
|
worker_threads_1.parentPort.postMessage({ type: 'finished' });
|
|
25
30
|
});
|
|
26
31
|
}
|
|
32
|
+
else if (msg.type === 'denyDetailsCheckResult') {
|
|
33
|
+
// Handle response from main thread about whether to include deny details
|
|
34
|
+
const checkId = msg.checkId;
|
|
35
|
+
const resolveFn = pendingDenyDetailsChecks[checkId];
|
|
36
|
+
if (resolveFn) {
|
|
37
|
+
resolveFn(msg.shouldInclude);
|
|
38
|
+
delete pendingDenyDetailsChecks[checkId];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
27
41
|
});
|
|
28
42
|
const collectClient = (0, collect_js_1.getCollectClient)(collectConfigs, partition, {
|
|
29
43
|
cacheProvider: new SharedArrayBufferWorkerCache_js_1.SharedArrayBufferWorkerCache(worker_threads_1.parentPort)
|
|
@@ -38,11 +52,62 @@ const jobRunner = new JobRunner_js_1.PullBasedJobRunner(concurrency, async (work
|
|
|
38
52
|
properties: {},
|
|
39
53
|
execute: async (context) => {
|
|
40
54
|
return (0, WhoCanWorker_js_1.executeWhoCan)(taskDetails, collectClient, {
|
|
41
|
-
s3AbacOverride
|
|
55
|
+
s3AbacOverride,
|
|
56
|
+
collectDenyDetails
|
|
42
57
|
});
|
|
43
58
|
}
|
|
44
59
|
};
|
|
45
60
|
}, async (result) => {
|
|
46
|
-
|
|
61
|
+
if (result.status === 'fulfilled') {
|
|
62
|
+
const executionResult = result.value;
|
|
63
|
+
if (executionResult.allowed) {
|
|
64
|
+
// Allowed - send result back to main thread
|
|
65
|
+
worker_threads_1.parentPort.postMessage({
|
|
66
|
+
type: 'result',
|
|
67
|
+
result: {
|
|
68
|
+
status: 'fulfilled',
|
|
69
|
+
value: executionResult.allowed,
|
|
70
|
+
properties: result.properties
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// If we have deny analysis and collectDenyDetails is enabled, check with main thread
|
|
76
|
+
if (collectDenyDetails && executionResult.denyAnalysis) {
|
|
77
|
+
const lightAnalysis = (0, requestAnalysis_js_1.toLightRequestAnalysis)(executionResult.denyAnalysis);
|
|
78
|
+
const checkId = denyDetailsCheckId++;
|
|
79
|
+
// Send check request to main thread
|
|
80
|
+
worker_threads_1.parentPort.postMessage({
|
|
81
|
+
type: 'checkDenyDetails',
|
|
82
|
+
checkId,
|
|
83
|
+
workItem: executionResult.workItem,
|
|
84
|
+
lightAnalysis
|
|
85
|
+
});
|
|
86
|
+
// Wait for response from main thread
|
|
87
|
+
const shouldInclude = await new Promise((resolve) => {
|
|
88
|
+
pendingDenyDetailsChecks[checkId] = resolve;
|
|
89
|
+
});
|
|
90
|
+
if (shouldInclude) {
|
|
91
|
+
// Get full denial reasons and send to main thread
|
|
92
|
+
const denialReasons = (0, iam_simulate_1.getDenialReasons)(executionResult.denyAnalysis);
|
|
93
|
+
const { workItem } = executionResult;
|
|
94
|
+
const [service, action] = workItem.action.split(':');
|
|
95
|
+
worker_threads_1.parentPort.postMessage({
|
|
96
|
+
type: 'denyDetailsResult',
|
|
97
|
+
denyDetail: {
|
|
98
|
+
principal: workItem.principal,
|
|
99
|
+
service,
|
|
100
|
+
action,
|
|
101
|
+
details: denialReasons
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// Error case - pass through
|
|
110
|
+
worker_threads_1.parentPort.postMessage({ type: 'result', result });
|
|
111
|
+
}
|
|
47
112
|
});
|
|
48
113
|
//# sourceMappingURL=WhoCanWorkerThreadWorker.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WhoCanWorkerThreadWorker.js","sourceRoot":"","sources":["../../../src/whoCan/WhoCanWorkerThreadWorker.ts"],"names":[],"mappings":";;AAEA,mDAAuD;AACvD,sDAAwD;AAExD,0DAA4D;AAC5D,gGAAyF;AACzF,
|
|
1
|
+
{"version":3,"file":"WhoCanWorkerThreadWorker.js","sourceRoot":"","sources":["../../../src/whoCan/WhoCanWorkerThreadWorker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { RequestAnalysis } from '@cloud-copilot/iam-simulate';
|
|
2
|
+
/**
|
|
3
|
+
* A light version of RequestAnalysis containing only the result and sameAccount fields,
|
|
4
|
+
* along with the result fields of the various analyses.
|
|
5
|
+
*/
|
|
6
|
+
export interface LightRequestAnalysis {
|
|
7
|
+
result: RequestAnalysis['result'];
|
|
8
|
+
sameAccount: RequestAnalysis['sameAccount'];
|
|
9
|
+
identityAnalysis?: Pick<NonNullable<RequestAnalysis['identityAnalysis']>, 'result'>;
|
|
10
|
+
resourceAnalysis?: Pick<NonNullable<RequestAnalysis['resourceAnalysis']>, 'result'>;
|
|
11
|
+
scpAnalysis?: Pick<NonNullable<RequestAnalysis['scpAnalysis']>, 'result'>;
|
|
12
|
+
rcpAnalysis?: Pick<NonNullable<RequestAnalysis['rcpAnalysis']>, 'result'>;
|
|
13
|
+
permissionBoundaryAnalysis?: Pick<NonNullable<RequestAnalysis['permissionBoundaryAnalysis']>, 'result'>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Convert a full RequestAnalysis to a LightRequestAnalysis
|
|
17
|
+
*
|
|
18
|
+
* @param analysis the full RequestAnalysis to convert
|
|
19
|
+
* @returns a LightRequestAnalysis with only the essential fields
|
|
20
|
+
*/
|
|
21
|
+
export declare function toLightRequestAnalysis(analysis: RequestAnalysis): LightRequestAnalysis;
|
|
22
|
+
//# sourceMappingURL=requestAnalysis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requestAnalysis.d.ts","sourceRoot":"","sources":["../../../src/whoCan/requestAnalysis.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAA;AAE7D;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAA;IACjC,WAAW,EAAE,eAAe,CAAC,aAAa,CAAC,CAAA;IAC3C,gBAAgB,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,eAAe,CAAC,kBAAkB,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;IACnF,gBAAgB,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,eAAe,CAAC,kBAAkB,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;IACnF,WAAW,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;IACzE,WAAW,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAA;IACzE,0BAA0B,CAAC,EAAE,IAAI,CAC/B,WAAW,CAAC,eAAe,CAAC,4BAA4B,CAAC,CAAC,EAC1D,QAAQ,CACT,CAAA;CACF;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,eAAe,GAAG,oBAAoB,CAgBtF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toLightRequestAnalysis = toLightRequestAnalysis;
|
|
4
|
+
/**
|
|
5
|
+
* Convert a full RequestAnalysis to a LightRequestAnalysis
|
|
6
|
+
*
|
|
7
|
+
* @param analysis the full RequestAnalysis to convert
|
|
8
|
+
* @returns a LightRequestAnalysis with only the essential fields
|
|
9
|
+
*/
|
|
10
|
+
function toLightRequestAnalysis(analysis) {
|
|
11
|
+
return {
|
|
12
|
+
result: analysis.result,
|
|
13
|
+
sameAccount: analysis.sameAccount,
|
|
14
|
+
identityAnalysis: analysis.identityAnalysis
|
|
15
|
+
? { result: analysis.identityAnalysis.result }
|
|
16
|
+
: undefined,
|
|
17
|
+
resourceAnalysis: analysis.resourceAnalysis
|
|
18
|
+
? { result: analysis.resourceAnalysis.result }
|
|
19
|
+
: undefined,
|
|
20
|
+
scpAnalysis: analysis.scpAnalysis ? { result: analysis.scpAnalysis.result } : undefined,
|
|
21
|
+
rcpAnalysis: analysis.rcpAnalysis ? { result: analysis.rcpAnalysis.result } : undefined,
|
|
22
|
+
permissionBoundaryAnalysis: analysis.permissionBoundaryAnalysis
|
|
23
|
+
? { result: analysis.permissionBoundaryAnalysis.result }
|
|
24
|
+
: undefined
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=requestAnalysis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requestAnalysis.js","sourceRoot":"","sources":["../../../src/whoCan/requestAnalysis.ts"],"names":[],"mappings":";;AAyBA,wDAgBC;AAtBD;;;;;GAKG;AACH,SAAgB,sBAAsB,CAAC,QAAyB;IAC9D,OAAO;QACL,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,WAAW,EAAE,QAAQ,CAAC,WAAW;QACjC,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;YACzC,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,gBAAgB,CAAC,MAAM,EAAE;YAC9C,CAAC,CAAC,SAAS;QACb,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB;YACzC,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,gBAAgB,CAAC,MAAM,EAAE;YAC9C,CAAC,CAAC,SAAS;QACb,WAAW,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS;QACvF,WAAW,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS;QACvF,0BAA0B,EAAE,QAAQ,CAAC,0BAA0B;YAC7D,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,0BAA0B,CAAC,MAAM,EAAE;YACxD,CAAC,CAAC,SAAS;KACd,CAAA;AACH,CAAC"}
|
|
@@ -1,13 +1,41 @@
|
|
|
1
1
|
import { TopLevelConfig } from '@cloud-copilot/iam-collect';
|
|
2
2
|
import { ResourceType } from '@cloud-copilot/iam-data';
|
|
3
|
+
import { RequestDenial } from '@cloud-copilot/iam-simulate';
|
|
3
4
|
import { IamCollectClient } from '../collect/client.js';
|
|
4
5
|
import { S3AbacOverride } from '../utils/s3Abac.js';
|
|
6
|
+
import { LightRequestAnalysis } from './requestAnalysis.js';
|
|
5
7
|
export interface ResourceAccessRequest {
|
|
8
|
+
/**
|
|
9
|
+
* The ARN of the resource to check access for. If not provided, actions must be specified.
|
|
10
|
+
*/
|
|
6
11
|
resource?: string;
|
|
12
|
+
/**
|
|
13
|
+
* The account ID the resource belongs to.
|
|
14
|
+
* By default this will be looked up based on the resource ARN, but that may
|
|
15
|
+
* not be possible for all actions, such as wildcard actions like `s3:ListAllMyBuckets`.
|
|
16
|
+
*/
|
|
7
17
|
resourceAccount?: string;
|
|
18
|
+
/**
|
|
19
|
+
* The actions to check access for. If not provided, actions will be looked up based on the resource ARN.
|
|
20
|
+
*/
|
|
8
21
|
actions: string[];
|
|
22
|
+
/**
|
|
23
|
+
* Whether to sort the results for consistent output.
|
|
24
|
+
*/
|
|
9
25
|
sort?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* An override for S3 ABAC being enabled when checking access to S3 Bucket resources.
|
|
28
|
+
*/
|
|
10
29
|
s3AbacOverride?: S3AbacOverride;
|
|
30
|
+
/**
|
|
31
|
+
* The number of worker threads to use for simulations beyond the main thread.
|
|
32
|
+
* If not provided, defaults to number of CPUs - 1.
|
|
33
|
+
*/
|
|
34
|
+
workerThreads?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Deny details callback for simulations. If the callback returns true, deny details will be included for that simulation.
|
|
37
|
+
*/
|
|
38
|
+
denyDetailsCallback?: (details: LightRequestAnalysis) => boolean;
|
|
11
39
|
}
|
|
12
40
|
export interface WhoCanAllowed {
|
|
13
41
|
principal: string;
|
|
@@ -17,6 +45,12 @@ export interface WhoCanAllowed {
|
|
|
17
45
|
conditions?: any;
|
|
18
46
|
dependsOnSessionName?: boolean;
|
|
19
47
|
}
|
|
48
|
+
export interface WhoCanDenyDetail {
|
|
49
|
+
principal: string;
|
|
50
|
+
service: string;
|
|
51
|
+
action: string;
|
|
52
|
+
details: RequestDenial[];
|
|
53
|
+
}
|
|
20
54
|
export interface WhoCanResponse {
|
|
21
55
|
simulationCount: number;
|
|
22
56
|
allowed: WhoCanAllowed[];
|
|
@@ -25,6 +59,7 @@ export interface WhoCanResponse {
|
|
|
25
59
|
organizationsNotFound: string[];
|
|
26
60
|
organizationalUnitsNotFound: string[];
|
|
27
61
|
principalsNotFound: string[];
|
|
62
|
+
denyDetails?: WhoCanDenyDetail[] | undefined;
|
|
28
63
|
}
|
|
29
64
|
export declare function whoCan(collectConfigs: TopLevelConfig[], partition: string, request: ResourceAccessRequest): Promise<WhoCanResponse>;
|
|
30
65
|
export declare function uniqueAccountsToCheck(collectClient: IamCollectClient, accountsToCheck: AccountsToCheck): Promise<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"whoCan.d.ts","sourceRoot":"","sources":["../../../src/whoCan/whoCan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAC3D,OAAO,EAOL,YAAY,EACb,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"whoCan.d.ts","sourceRoot":"","sources":["../../../src/whoCan/whoCan.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAC3D,OAAO,EAOL,YAAY,EACb,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAU3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAIvD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAQnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAE3D,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;IAExB;;OAEG;IACH,OAAO,EAAE,MAAM,EAAE,CAAA;IAEjB;;OAEG;IACH,IAAI,CAAC,EAAE,OAAO,CAAA;IAEd;;OAEG;IACH,cAAc,CAAC,EAAE,cAAc,CAAA;IAE/B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB;;OAEG;IACH,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,OAAO,CAAA;CACjE;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,GAAG,CAAA;IAChB,oBAAoB,CAAC,EAAE,OAAO,CAAA;CAC/B;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,aAAa,EAAE,CAAA;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,eAAe,EAAE,MAAM,CAAA;IACvB,OAAO,EAAE,aAAa,EAAE,CAAA;IACxB,kBAAkB,EAAE,OAAO,CAAA;IAC3B,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,qBAAqB,EAAE,MAAM,EAAE,CAAA;IAC/B,2BAA2B,EAAE,MAAM,EAAE,CAAA;IACrC,kBAAkB,EAAE,MAAM,EAAE,CAAA;IAC5B,WAAW,CAAC,EAAE,gBAAgB,EAAE,GAAG,SAAS,CAAA;CAC7C;AAeD,wBAAsB,MAAM,CAC1B,cAAc,EAAE,cAAc,EAAE,EAChC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,cAAc,CAAC,CA6PzB;AAoBD,wBAAsB,qBAAqB,CACzC,aAAa,EAAE,gBAAgB,EAC/B,eAAe,EAAE,eAAe,GAC/B,OAAO,CAAC;IACT,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,qBAAqB,EAAE,MAAM,EAAE,CAAA;IAC/B,2BAA2B,EAAE,MAAM,EAAE,CAAA;IACrC,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB,CAAC,CAiDD;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,OAAO,CAAA;IACpB,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,kBAAkB,EAAE,MAAM,EAAE,CAAA;IAC5B,qBAAqB,EAAE,MAAM,EAAE,CAAA;IAC/B,2BAA2B,EAAE,MAAM,EAAE,CAAA;CACtC;AAED,wBAAsB,oCAAoC,CACxD,cAAc,EAAE,GAAG,EACnB,eAAe,EAAE,MAAM,GAAG,SAAS,GAClC,OAAO,CAAC,eAAe,CAAC,CA2E1B;AAED,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CA4BxF;AAED;;;;;;;GAOG;AACH,wBAAsB,2BAA2B,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAsBxF;AAED,wBAAsB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CAqBjG;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAOrE;AAcD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,cAAc,EAAE,cAAc,QAyB/D"}
|
|
@@ -21,26 +21,38 @@ const workerScript_js_1 = require("../utils/workerScript.js");
|
|
|
21
21
|
const SharedArrayBufferMainCache_js_1 = require("../workers/SharedArrayBufferMainCache.js");
|
|
22
22
|
const StreamingWorkQueue_js_1 = require("../workers/StreamingWorkQueue.js");
|
|
23
23
|
const WhoCanMainThreadWorker_js_1 = require("./WhoCanMainThreadWorker.js");
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Get the number of worker threads to use, defaulting to number of CPUs - 1
|
|
26
|
+
*
|
|
27
|
+
* @param overrideValue the override value, if any
|
|
28
|
+
* @returns the override value if provided, otherwise number of CPUs - 1
|
|
29
|
+
*/
|
|
30
|
+
function getNumberOfWorkers(overrideValue) {
|
|
31
|
+
if (typeof overrideValue === 'number' && overrideValue >= 0) {
|
|
32
|
+
return Math.floor(overrideValue);
|
|
27
33
|
}
|
|
28
|
-
return (0, job_1.numberOfCpus)();
|
|
34
|
+
return Math.max(0, (0, job_1.numberOfCpus)() - 1);
|
|
29
35
|
}
|
|
30
36
|
async function whoCan(collectConfigs, partition, request) {
|
|
31
|
-
const cpus = getCpuCount();
|
|
32
37
|
const { resource } = request;
|
|
38
|
+
// Get the number of workers and the worker script path.
|
|
39
|
+
// It's possible in bundled environments that the worker script path may not be found, so handle that gracefully.
|
|
40
|
+
const numWorkers = getNumberOfWorkers(request.workerThreads);
|
|
33
41
|
const workerPath = (0, workerScript_js_1.getWorkerScriptPath)('whoCan/WhoCanWorkerThreadWorker.js');
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
const collectDenyDetails = !!request.denyDetailsCallback;
|
|
43
|
+
const workers = !workerPath
|
|
44
|
+
? []
|
|
45
|
+
: new Array(numWorkers).fill(undefined).map((val) => {
|
|
46
|
+
return new worker_threads_1.Worker(workerPath, {
|
|
47
|
+
workerData: {
|
|
48
|
+
collectConfigs: collectConfigs,
|
|
49
|
+
partition,
|
|
50
|
+
concurrency: 50,
|
|
51
|
+
s3AbacOverride: request.s3AbacOverride,
|
|
52
|
+
collectDenyDetails
|
|
53
|
+
}
|
|
54
|
+
});
|
|
42
55
|
});
|
|
43
|
-
});
|
|
44
56
|
const collectClient = (0, collect_js_1.getCollectClient)(collectConfigs, partition, {
|
|
45
57
|
cacheProvider: new SharedArrayBufferMainCache_js_1.SharedArrayBufferMainCache(workers)
|
|
46
58
|
});
|
|
@@ -75,6 +87,7 @@ async function whoCan(collectConfigs, partition, request) {
|
|
|
75
87
|
let simulationCount = 0;
|
|
76
88
|
const simulateQueue = new StreamingWorkQueue_js_1.StreamingWorkQueue();
|
|
77
89
|
const simulationErrors = [];
|
|
90
|
+
const denyDetails = [];
|
|
78
91
|
const onComplete = (result) => {
|
|
79
92
|
simulationCount++;
|
|
80
93
|
if (result.status === 'fulfilled' && result.value) {
|
|
@@ -85,7 +98,7 @@ async function whoCan(collectConfigs, partition, request) {
|
|
|
85
98
|
simulationErrors.push(result);
|
|
86
99
|
}
|
|
87
100
|
};
|
|
88
|
-
const mainThreadWorker = (0, WhoCanMainThreadWorker_js_1.createMainThreadStreamingWorkQueue)(simulateQueue, collectClient, request.s3AbacOverride, onComplete);
|
|
101
|
+
const mainThreadWorker = (0, WhoCanMainThreadWorker_js_1.createMainThreadStreamingWorkQueue)(simulateQueue, collectClient, request.s3AbacOverride, onComplete, request.denyDetailsCallback, (detail) => denyDetails.push(detail));
|
|
89
102
|
workers.forEach((worker) => {
|
|
90
103
|
worker.on('message', (msg) => {
|
|
91
104
|
if (msg.type === 'requestTask') {
|
|
@@ -95,6 +108,18 @@ async function whoCan(collectConfigs, partition, request) {
|
|
|
95
108
|
if (msg.type === 'result') {
|
|
96
109
|
onComplete(msg.result);
|
|
97
110
|
}
|
|
111
|
+
if (msg.type === 'checkDenyDetails') {
|
|
112
|
+
// Run the callback on main thread to check if we should include deny details
|
|
113
|
+
const shouldInclude = request.denyDetailsCallback?.(msg.lightAnalysis) ?? false;
|
|
114
|
+
worker.postMessage({
|
|
115
|
+
type: 'denyDetailsCheckResult',
|
|
116
|
+
checkId: msg.checkId,
|
|
117
|
+
shouldInclude
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
if (msg.type === 'denyDetailsResult') {
|
|
121
|
+
denyDetails.push(msg.denyDetail);
|
|
122
|
+
}
|
|
98
123
|
});
|
|
99
124
|
});
|
|
100
125
|
simulateQueue.setWorkAvailableCallback(() => {
|
|
@@ -183,7 +208,8 @@ async function whoCan(collectConfigs, partition, request) {
|
|
|
183
208
|
accountsNotFound: uniqueAccounts.accountsNotFound,
|
|
184
209
|
organizationsNotFound: uniqueAccounts.organizationsNotFound,
|
|
185
210
|
organizationalUnitsNotFound: uniqueAccounts.organizationalUnitsNotFound,
|
|
186
|
-
principalsNotFound: principalsNotFound
|
|
211
|
+
principalsNotFound: principalsNotFound,
|
|
212
|
+
denyDetails: request.denyDetailsCallback ? denyDetails : undefined
|
|
187
213
|
};
|
|
188
214
|
if (request.sort) {
|
|
189
215
|
sortWhoCanResults(results);
|
|
@@ -438,6 +464,21 @@ function sortWhoCanResults(whoCanResponse) {
|
|
|
438
464
|
return 1;
|
|
439
465
|
return 0;
|
|
440
466
|
});
|
|
467
|
+
whoCanResponse.denyDetails?.sort((a, b) => {
|
|
468
|
+
if (a.principal < b.principal)
|
|
469
|
+
return -1;
|
|
470
|
+
if (a.principal > b.principal)
|
|
471
|
+
return 1;
|
|
472
|
+
if (a.service < b.service)
|
|
473
|
+
return -1;
|
|
474
|
+
if (a.service > b.service)
|
|
475
|
+
return 1;
|
|
476
|
+
if (a.action < b.action)
|
|
477
|
+
return -1;
|
|
478
|
+
if (a.action > b.action)
|
|
479
|
+
return 1;
|
|
480
|
+
return 0;
|
|
481
|
+
});
|
|
441
482
|
whoCanResponse.accountsNotFound.sort();
|
|
442
483
|
whoCanResponse.organizationsNotFound.sort();
|
|
443
484
|
whoCanResponse.organizationalUnitsNotFound.sort();
|