@aikidosec/ci-api-client 1.0.16 → 1.0.18
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/.env +1 -0
- package/.github/workflows/build-and-release.yml +4 -4
- package/README.md +2 -1
- package/lib/aikidoApi.d.ts +16 -0
- package/lib/aikidoApi.js +3 -0
- package/lib/commands/scan.js +1 -1
- package/lib/commands/scanRelease.d.ts +1 -0
- package/lib/commands/scanRelease.js +21 -3
- package/lib/index.js +1 -1
- package/lib/output.d.ts +1 -0
- package/lib/output.js +3 -0
- package/npm-shrinkwrap.json +101 -27
- package/package.json +2 -2
- package/src/aikidoApi.ts +22 -0
- package/src/commands/scan.ts +1 -1
- package/src/commands/scanRelease.ts +33 -2
- package/src/index.ts +1 -1
- package/src/output.ts +7 -0
package/.env
CHANGED
|
@@ -9,7 +9,7 @@ permissions:
|
|
|
9
9
|
|
|
10
10
|
jobs:
|
|
11
11
|
build:
|
|
12
|
-
runs-on:
|
|
12
|
+
runs-on: open-source-releaser
|
|
13
13
|
permissions:
|
|
14
14
|
contents: read
|
|
15
15
|
id-token: write
|
|
@@ -18,13 +18,13 @@ jobs:
|
|
|
18
18
|
- uses: actions/checkout@v4
|
|
19
19
|
- uses: actions/setup-node@v4
|
|
20
20
|
with:
|
|
21
|
-
node-version: "22"
|
|
21
|
+
node-version: "22.14.0"
|
|
22
22
|
registry-url: "https://registry.npmjs.org"
|
|
23
23
|
scope: "@aikidosec"
|
|
24
24
|
cache: 'npm'
|
|
25
25
|
|
|
26
26
|
- name: Update npm
|
|
27
|
-
run: npm install -g npm@11.
|
|
27
|
+
run: npm install -g npm@11.15.0
|
|
28
28
|
|
|
29
29
|
- name: Install dependencies
|
|
30
30
|
run: npm ci
|
|
@@ -42,4 +42,4 @@ jobs:
|
|
|
42
42
|
- name: Publish to NPM
|
|
43
43
|
run: |
|
|
44
44
|
echo "Publishing version ${{ steps.get_version.outputs.tag }} to NPM"
|
|
45
|
-
npm publish --access public
|
|
45
|
+
npm stage publish --access public --verbose
|
package/README.md
CHANGED
|
@@ -49,6 +49,7 @@ Options:
|
|
|
49
49
|
"HIGH", "CRITICAL")
|
|
50
50
|
--poll-interval [interval] The poll interval when checking for an updated scan result (preset: 10)
|
|
51
51
|
--sla-mode Let Aikido only fail only on open issues that have gone out of SLA.
|
|
52
|
+
--json Output the scan result as JSON (gate_passed and issue_breakdown)
|
|
52
53
|
-h, --help display help for command
|
|
53
54
|
```
|
|
54
55
|
|
|
@@ -91,7 +92,7 @@ Options:
|
|
|
91
92
|
|
|
92
93
|
The CLI will spawn a cloud-based scan and then report scan progress. It will exit with exitCode `0` if the scan was successfull (`gate_passed: true`). The process will exit with exitCode `10` if the scan was unsuccesfull (`gate_passed: false`). If anything else goes wrong (e.g. API unavailable, scanning unavailable, other unexpected issue) the process will exit with exitCode `1`.
|
|
93
94
|
|
|
94
|
-
If you want the scan to run quietly (without output), you can add the `--quiet` option to the command.
|
|
95
|
+
If you want the scan to run quietly (without output), you can add the `--quiet` option to the command. If you want machine-readable output instead, add `--json` to `scan-release`. That prints a JSON object with `gate_passed` and `issue_breakdown` and skips spinners and other extra info.
|
|
95
96
|
|
|
96
97
|
Please note that the repository_id which you need to provide to the CLI is the unique ID of the Git provider you are using, not the ID of the repository in Aikido. You can also find this ID in Aikido, by going to the repository's detail page and clicking on the Git provider's icon in the header. Alternatively, you can pass the repository name as it is defined in Aikido.
|
|
97
98
|
|
package/lib/aikidoApi.d.ts
CHANGED
|
@@ -30,8 +30,24 @@ export type TPollIsScanningResult = {
|
|
|
30
30
|
secrets_scan_completed: boolean;
|
|
31
31
|
sbom_scan_completed?: boolean;
|
|
32
32
|
};
|
|
33
|
+
export type TIssueSeverityBreakdown = {
|
|
34
|
+
critical: number;
|
|
35
|
+
high: number;
|
|
36
|
+
medium: number;
|
|
37
|
+
low: number;
|
|
38
|
+
};
|
|
39
|
+
export type TIssueBreakdown = {
|
|
40
|
+
sast: TIssueSeverityBreakdown;
|
|
41
|
+
sca: TIssueSeverityBreakdown;
|
|
42
|
+
iac: TIssueSeverityBreakdown;
|
|
43
|
+
secrets: TIssueSeverityBreakdown;
|
|
44
|
+
malware: TIssueSeverityBreakdown;
|
|
45
|
+
license: TIssueSeverityBreakdown;
|
|
46
|
+
code_quality: TIssueSeverityBreakdown;
|
|
47
|
+
};
|
|
33
48
|
type TPollScanCompletedOptions = {
|
|
34
49
|
gate_passed: boolean;
|
|
50
|
+
issue_breakdown?: TIssueBreakdown;
|
|
35
51
|
};
|
|
36
52
|
type TPollScanFeatureBranchCompletedOptions = {
|
|
37
53
|
new_issues_found: number;
|
package/lib/aikidoApi.js
CHANGED
|
@@ -9,6 +9,9 @@ const getApiUrl = () => {
|
|
|
9
9
|
if (apiKey.startsWith('AIK_CI_ME_')) {
|
|
10
10
|
return process.env.AIKIDO_ME_API || 'https://app.me.aikido.dev';
|
|
11
11
|
}
|
|
12
|
+
if (apiKey.startsWith('AIK_CI_AU_')) {
|
|
13
|
+
return process.env.AIKIDO_AU_API || 'https://app.au.aikido.dev';
|
|
14
|
+
}
|
|
12
15
|
return process.env.AIKIDO_API || 'https://app.aikido.dev';
|
|
13
16
|
};
|
|
14
17
|
const getApiHeaders = () => ({
|
package/lib/commands/scan.js
CHANGED
|
@@ -118,7 +118,7 @@ export const scan = async ({ repoId, baseCommitId, headCommitId, branchName, opt
|
|
|
118
118
|
pollStatus();
|
|
119
119
|
};
|
|
120
120
|
const parseCliOptions = (userCliOptions) => {
|
|
121
|
-
const apiOptions = { version: '1.0.
|
|
121
|
+
const apiOptions = { version: '1.0.18' };
|
|
122
122
|
const cliOptions = { pollInterval: 5 };
|
|
123
123
|
if (userCliOptions.pullRequestTitle) {
|
|
124
124
|
apiOptions.pull_request_metadata = {
|
|
@@ -25,6 +25,7 @@ type TScanUserCliOptions = {
|
|
|
25
25
|
pollInterval?: number;
|
|
26
26
|
baseBranch?: string;
|
|
27
27
|
slaMode?: boolean;
|
|
28
|
+
json?: boolean;
|
|
28
29
|
};
|
|
29
30
|
declare function cli(repoId: string, commitId: string, options: TScanUserCliOptions, command: string): Promise<void>;
|
|
30
31
|
export declare const scan: ({ repoId, commitId, options, pollInterval, onStart, onStartComplete, onStartFail, onScanStart, onNextPoll, onScanComplete, onScanFail, }: TScanArguments) => Promise<void>;
|
|
@@ -2,7 +2,7 @@ import chalk from 'chalk';
|
|
|
2
2
|
import { Argument, InvalidArgumentError, Option } from 'commander';
|
|
3
3
|
import { pollScanStatus, startScan, } from '../aikidoApi.js';
|
|
4
4
|
import { getApiKey } from '../configuration.js';
|
|
5
|
-
import { outputError, outputHttpError, outputLog, startSpinner, } from '../output.js';
|
|
5
|
+
import { outputError, outputHttpError, outputJson, outputLog, startSpinner, } from '../output.js';
|
|
6
6
|
async function cli(repoId, commitId, options, command) {
|
|
7
7
|
const apiKey = getApiKey();
|
|
8
8
|
if (!apiKey) {
|
|
@@ -11,6 +11,10 @@ async function cli(repoId, commitId, options, command) {
|
|
|
11
11
|
const { apiOptions, cliOptions } = parseCliOptions(options);
|
|
12
12
|
let loader;
|
|
13
13
|
let pollCount = 1;
|
|
14
|
+
const jsonOutput = cliOptions.json;
|
|
15
|
+
if (jsonOutput) {
|
|
16
|
+
process.env.QUIET = true;
|
|
17
|
+
}
|
|
14
18
|
const onStart = () => {
|
|
15
19
|
loader = startSpinner('Starting Aikido Security release scan');
|
|
16
20
|
};
|
|
@@ -27,6 +31,16 @@ async function cli(repoId, commitId, options, command) {
|
|
|
27
31
|
loader = startSpinner('Waiting for scan to complete');
|
|
28
32
|
};
|
|
29
33
|
const onScanComplete = (pollResult) => {
|
|
34
|
+
if (jsonOutput) {
|
|
35
|
+
outputJson({
|
|
36
|
+
gate_passed: pollResult.gate_passed === true,
|
|
37
|
+
issue_breakdown: pollResult.issue_breakdown,
|
|
38
|
+
});
|
|
39
|
+
if (pollResult.gate_passed !== true) {
|
|
40
|
+
process.exitCode = 10;
|
|
41
|
+
}
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
30
44
|
if (pollResult.gate_passed === true) {
|
|
31
45
|
loader?.succeed('Scan completed, no open issues found');
|
|
32
46
|
if (pollResult.diff_url) {
|
|
@@ -123,8 +137,8 @@ export const scan = async ({ repoId, commitId, options, pollInterval = 10, onSta
|
|
|
123
137
|
pollStatus();
|
|
124
138
|
};
|
|
125
139
|
const parseCliOptions = (userCliOptions) => {
|
|
126
|
-
const apiOptions = { version: '1.0.
|
|
127
|
-
const cliOptions = { pollInterval: 10 };
|
|
140
|
+
const apiOptions = { version: '1.0.18' };
|
|
141
|
+
const cliOptions = { pollInterval: 10, json: false };
|
|
128
142
|
if (userCliOptions.pullRequestTitle) {
|
|
129
143
|
apiOptions.pull_request_metadata = {
|
|
130
144
|
...(apiOptions.pull_request_metadata ?? {}),
|
|
@@ -168,6 +182,9 @@ const parseCliOptions = (userCliOptions) => {
|
|
|
168
182
|
else if (userCliOptions.pollInterval) {
|
|
169
183
|
cliOptions.pollInterval = userCliOptions.pollInterval;
|
|
170
184
|
}
|
|
185
|
+
if (userCliOptions.json) {
|
|
186
|
+
cliOptions.json = true;
|
|
187
|
+
}
|
|
171
188
|
return { apiOptions, cliOptions };
|
|
172
189
|
};
|
|
173
190
|
const validateCommitId = (value) => {
|
|
@@ -193,6 +210,7 @@ export const cliSetup = (program) => program
|
|
|
193
210
|
.argParser(parseFloat))
|
|
194
211
|
.addOption(new Option('--base-branch <branchname>', 'Base branch for the release gated scan.'))
|
|
195
212
|
.option('--sla-mode', 'Let Aikido only fail only on open issues that have gone out of SLA.')
|
|
213
|
+
.option('--json', 'Output the scan result as JSON (gate_passed and issue_breakdown)')
|
|
196
214
|
.description('Run a release scan of an Aikido repo.')
|
|
197
215
|
.action(cli);
|
|
198
216
|
export default { cli, cliSetup, scan };
|
package/lib/index.js
CHANGED
|
@@ -11,7 +11,7 @@ export const program = new Command();
|
|
|
11
11
|
program
|
|
12
12
|
.name('Aikido API Client')
|
|
13
13
|
.description('CLI api client to easily integrate the Aikido public CI API into custom deploy scripts')
|
|
14
|
-
.version('1.0.
|
|
14
|
+
.version('1.0.18');
|
|
15
15
|
apiKey.cliSetup(program);
|
|
16
16
|
scan.cliSetup(program);
|
|
17
17
|
scanRelease.cliSetup(program);
|
package/lib/output.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Ora } from 'ora';
|
|
2
2
|
import { AxiosError } from 'axios';
|
|
3
3
|
export declare const outputLog: (message: string) => void;
|
|
4
|
+
export declare const outputJson: (data: unknown) => void;
|
|
4
5
|
export declare const outputDebug: (variable: any) => void;
|
|
5
6
|
export declare const outputError: (message: string, exitCode?: number) => void;
|
|
6
7
|
export declare const outputHttpError: (axiosError: AxiosError) => void;
|
package/lib/output.js
CHANGED
|
@@ -6,6 +6,9 @@ export const outputLog = (message) => {
|
|
|
6
6
|
console.log(message);
|
|
7
7
|
}
|
|
8
8
|
};
|
|
9
|
+
export const outputJson = (data) => {
|
|
10
|
+
console.log(JSON.stringify(data, null, 2));
|
|
11
|
+
};
|
|
9
12
|
export const outputDebug = (variable) => {
|
|
10
13
|
if (process.env.CLI_DEBUG == true || process.env.CLI_DEBUG == 'true') {
|
|
11
14
|
console.log(chalk.blue(typeof variable == 'object' ? JSON.stringify(variable) : variable));
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aikidosec/ci-api-client",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@aikidosec/ci-api-client",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.18",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"axios": "1.
|
|
12
|
+
"axios": "1.18.0",
|
|
13
13
|
"chalk": "5.3.0",
|
|
14
14
|
"commander": "11.0.0",
|
|
15
15
|
"configstore": "6.0.0",
|
|
@@ -116,8 +116,7 @@
|
|
|
116
116
|
"node_modules/@types/node": {
|
|
117
117
|
"version": "20.3.3",
|
|
118
118
|
"dev": true,
|
|
119
|
-
"license": "MIT"
|
|
120
|
-
"peer": true
|
|
119
|
+
"license": "MIT"
|
|
121
120
|
},
|
|
122
121
|
"node_modules/abbrev": {
|
|
123
122
|
"version": "1.1.1",
|
|
@@ -143,6 +142,35 @@
|
|
|
143
142
|
"node": ">=0.4.0"
|
|
144
143
|
}
|
|
145
144
|
},
|
|
145
|
+
"node_modules/agent-base": {
|
|
146
|
+
"version": "6.0.2",
|
|
147
|
+
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
|
148
|
+
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
|
|
149
|
+
"license": "MIT",
|
|
150
|
+
"dependencies": {
|
|
151
|
+
"debug": "4"
|
|
152
|
+
},
|
|
153
|
+
"engines": {
|
|
154
|
+
"node": ">= 6.0.0"
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"node_modules/agent-base/node_modules/debug": {
|
|
158
|
+
"version": "4.4.3",
|
|
159
|
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
|
160
|
+
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
|
161
|
+
"license": "MIT",
|
|
162
|
+
"dependencies": {
|
|
163
|
+
"ms": "^2.1.3"
|
|
164
|
+
},
|
|
165
|
+
"engines": {
|
|
166
|
+
"node": ">=6.0"
|
|
167
|
+
},
|
|
168
|
+
"peerDependenciesMeta": {
|
|
169
|
+
"supports-color": {
|
|
170
|
+
"optional": true
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
},
|
|
146
174
|
"node_modules/ansi-regex": {
|
|
147
175
|
"version": "6.0.1",
|
|
148
176
|
"license": "MIT",
|
|
@@ -172,17 +200,20 @@
|
|
|
172
200
|
},
|
|
173
201
|
"node_modules/asynckit": {
|
|
174
202
|
"version": "0.4.0",
|
|
203
|
+
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
|
204
|
+
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
|
|
175
205
|
"license": "MIT"
|
|
176
206
|
},
|
|
177
207
|
"node_modules/axios": {
|
|
178
|
-
"version": "1.
|
|
179
|
-
"resolved": "https://registry.npmjs.org/axios/-/axios-1.
|
|
180
|
-
"integrity": "sha512-
|
|
208
|
+
"version": "1.18.0",
|
|
209
|
+
"resolved": "https://registry.npmjs.org/axios/-/axios-1.18.0.tgz",
|
|
210
|
+
"integrity": "sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==",
|
|
181
211
|
"license": "MIT",
|
|
182
212
|
"dependencies": {
|
|
183
|
-
"follow-redirects": "^1.
|
|
184
|
-
"form-data": "^4.0.
|
|
185
|
-
"proxy-
|
|
213
|
+
"follow-redirects": "^1.16.0",
|
|
214
|
+
"form-data": "^4.0.5",
|
|
215
|
+
"https-proxy-agent": "^5.0.1",
|
|
216
|
+
"proxy-from-env": "^2.1.0"
|
|
186
217
|
}
|
|
187
218
|
},
|
|
188
219
|
"node_modules/balanced-match": {
|
|
@@ -348,6 +379,8 @@
|
|
|
348
379
|
},
|
|
349
380
|
"node_modules/combined-stream": {
|
|
350
381
|
"version": "1.0.8",
|
|
382
|
+
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
|
383
|
+
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
|
351
384
|
"license": "MIT",
|
|
352
385
|
"dependencies": {
|
|
353
386
|
"delayed-stream": "~1.0.0"
|
|
@@ -423,6 +456,8 @@
|
|
|
423
456
|
},
|
|
424
457
|
"node_modules/delayed-stream": {
|
|
425
458
|
"version": "1.0.0",
|
|
459
|
+
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
|
460
|
+
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
|
426
461
|
"license": "MIT",
|
|
427
462
|
"engines": {
|
|
428
463
|
"node": ">=0.4.0"
|
|
@@ -492,9 +527,9 @@
|
|
|
492
527
|
}
|
|
493
528
|
},
|
|
494
529
|
"node_modules/es-object-atoms": {
|
|
495
|
-
"version": "1.1.
|
|
496
|
-
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.
|
|
497
|
-
"integrity": "sha512-
|
|
530
|
+
"version": "1.1.2",
|
|
531
|
+
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
|
|
532
|
+
"integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
|
|
498
533
|
"license": "MIT",
|
|
499
534
|
"dependencies": {
|
|
500
535
|
"es-errors": "^1.3.0"
|
|
@@ -530,7 +565,9 @@
|
|
|
530
565
|
}
|
|
531
566
|
},
|
|
532
567
|
"node_modules/follow-redirects": {
|
|
533
|
-
"version": "1.
|
|
568
|
+
"version": "1.16.0",
|
|
569
|
+
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
|
|
570
|
+
"integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
|
|
534
571
|
"funding": [
|
|
535
572
|
{
|
|
536
573
|
"type": "individual",
|
|
@@ -548,16 +585,16 @@
|
|
|
548
585
|
}
|
|
549
586
|
},
|
|
550
587
|
"node_modules/form-data": {
|
|
551
|
-
"version": "4.0.
|
|
552
|
-
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.
|
|
553
|
-
"integrity": "sha512-
|
|
588
|
+
"version": "4.0.6",
|
|
589
|
+
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz",
|
|
590
|
+
"integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
|
|
554
591
|
"license": "MIT",
|
|
555
592
|
"dependencies": {
|
|
556
593
|
"asynckit": "^0.4.0",
|
|
557
594
|
"combined-stream": "^1.0.8",
|
|
558
595
|
"es-set-tostringtag": "^2.1.0",
|
|
559
|
-
"hasown": "^2.0.
|
|
560
|
-
"mime-types": "^2.1.
|
|
596
|
+
"hasown": "^2.0.4",
|
|
597
|
+
"mime-types": "^2.1.35"
|
|
561
598
|
},
|
|
562
599
|
"engines": {
|
|
563
600
|
"node": ">= 6"
|
|
@@ -684,9 +721,9 @@
|
|
|
684
721
|
}
|
|
685
722
|
},
|
|
686
723
|
"node_modules/hasown": {
|
|
687
|
-
"version": "2.0.
|
|
688
|
-
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.
|
|
689
|
-
"integrity": "sha512-
|
|
724
|
+
"version": "2.0.4",
|
|
725
|
+
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
|
|
726
|
+
"integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
|
|
690
727
|
"license": "MIT",
|
|
691
728
|
"dependencies": {
|
|
692
729
|
"function-bind": "^1.1.2"
|
|
@@ -695,6 +732,36 @@
|
|
|
695
732
|
"node": ">= 0.4"
|
|
696
733
|
}
|
|
697
734
|
},
|
|
735
|
+
"node_modules/https-proxy-agent": {
|
|
736
|
+
"version": "5.0.1",
|
|
737
|
+
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
|
738
|
+
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
|
739
|
+
"license": "MIT",
|
|
740
|
+
"dependencies": {
|
|
741
|
+
"agent-base": "6",
|
|
742
|
+
"debug": "4"
|
|
743
|
+
},
|
|
744
|
+
"engines": {
|
|
745
|
+
"node": ">= 6"
|
|
746
|
+
}
|
|
747
|
+
},
|
|
748
|
+
"node_modules/https-proxy-agent/node_modules/debug": {
|
|
749
|
+
"version": "4.4.3",
|
|
750
|
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
|
|
751
|
+
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
|
|
752
|
+
"license": "MIT",
|
|
753
|
+
"dependencies": {
|
|
754
|
+
"ms": "^2.1.3"
|
|
755
|
+
},
|
|
756
|
+
"engines": {
|
|
757
|
+
"node": ">=6.0"
|
|
758
|
+
},
|
|
759
|
+
"peerDependenciesMeta": {
|
|
760
|
+
"supports-color": {
|
|
761
|
+
"optional": true
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
},
|
|
698
765
|
"node_modules/ieee754": {
|
|
699
766
|
"version": "1.2.1",
|
|
700
767
|
"funding": [
|
|
@@ -828,6 +895,8 @@
|
|
|
828
895
|
},
|
|
829
896
|
"node_modules/mime-db": {
|
|
830
897
|
"version": "1.52.0",
|
|
898
|
+
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
|
899
|
+
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
|
831
900
|
"license": "MIT",
|
|
832
901
|
"engines": {
|
|
833
902
|
"node": ">= 0.6"
|
|
@@ -835,6 +904,8 @@
|
|
|
835
904
|
},
|
|
836
905
|
"node_modules/mime-types": {
|
|
837
906
|
"version": "2.1.35",
|
|
907
|
+
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
|
908
|
+
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
|
838
909
|
"license": "MIT",
|
|
839
910
|
"dependencies": {
|
|
840
911
|
"mime-db": "1.52.0"
|
|
@@ -863,7 +934,6 @@
|
|
|
863
934
|
},
|
|
864
935
|
"node_modules/ms": {
|
|
865
936
|
"version": "2.1.3",
|
|
866
|
-
"dev": true,
|
|
867
937
|
"license": "MIT"
|
|
868
938
|
},
|
|
869
939
|
"node_modules/nodemon": {
|
|
@@ -972,8 +1042,13 @@
|
|
|
972
1042
|
}
|
|
973
1043
|
},
|
|
974
1044
|
"node_modules/proxy-from-env": {
|
|
975
|
-
"version": "
|
|
976
|
-
"
|
|
1045
|
+
"version": "2.1.0",
|
|
1046
|
+
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
|
|
1047
|
+
"integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
|
|
1048
|
+
"license": "MIT",
|
|
1049
|
+
"engines": {
|
|
1050
|
+
"node": ">=10"
|
|
1051
|
+
}
|
|
977
1052
|
},
|
|
978
1053
|
"node_modules/pstree.remy": {
|
|
979
1054
|
"version": "1.1.8",
|
|
@@ -1195,7 +1270,6 @@
|
|
|
1195
1270
|
"version": "5.1.6",
|
|
1196
1271
|
"dev": true,
|
|
1197
1272
|
"license": "Apache-2.0",
|
|
1198
|
-
"peer": true,
|
|
1199
1273
|
"bin": {
|
|
1200
1274
|
"tsc": "bin/tsc",
|
|
1201
1275
|
"tsserver": "bin/tsserver"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aikidosec/ci-api-client",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.18",
|
|
4
4
|
"description": "CLI api client to easily integrate the Aikido public CI API into custom deploy scripts",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Bert Devriese <bert@builtinbruges.com>",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"axios": "1.
|
|
28
|
+
"axios": "1.18.0",
|
|
29
29
|
"chalk": "5.3.0",
|
|
30
30
|
"commander": "11.0.0",
|
|
31
31
|
"configstore": "6.0.0",
|
package/src/aikidoApi.ts
CHANGED
|
@@ -13,6 +13,10 @@ const getApiUrl = () => {
|
|
|
13
13
|
return process.env.AIKIDO_ME_API || 'https://app.me.aikido.dev';
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
if (apiKey.startsWith('AIK_CI_AU_')) {
|
|
17
|
+
return process.env.AIKIDO_AU_API || 'https://app.au.aikido.dev';
|
|
18
|
+
}
|
|
19
|
+
|
|
16
20
|
return process.env.AIKIDO_API || 'https://app.aikido.dev';
|
|
17
21
|
};
|
|
18
22
|
const getApiHeaders = () => ({
|
|
@@ -78,8 +82,26 @@ export type TPollIsScanningResult = {
|
|
|
78
82
|
sbom_scan_completed?: boolean;
|
|
79
83
|
};
|
|
80
84
|
|
|
85
|
+
export type TIssueSeverityBreakdown = {
|
|
86
|
+
critical: number;
|
|
87
|
+
high: number;
|
|
88
|
+
medium: number;
|
|
89
|
+
low: number;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export type TIssueBreakdown = {
|
|
93
|
+
sast: TIssueSeverityBreakdown;
|
|
94
|
+
sca: TIssueSeverityBreakdown;
|
|
95
|
+
iac: TIssueSeverityBreakdown;
|
|
96
|
+
secrets: TIssueSeverityBreakdown;
|
|
97
|
+
malware: TIssueSeverityBreakdown;
|
|
98
|
+
license: TIssueSeverityBreakdown;
|
|
99
|
+
code_quality: TIssueSeverityBreakdown;
|
|
100
|
+
};
|
|
101
|
+
|
|
81
102
|
type TPollScanCompletedOptions = {
|
|
82
103
|
gate_passed: boolean;
|
|
104
|
+
issue_breakdown?: TIssueBreakdown;
|
|
83
105
|
};
|
|
84
106
|
|
|
85
107
|
type TPollScanFeatureBranchCompletedOptions = {
|
package/src/commands/scan.ts
CHANGED
|
@@ -245,7 +245,7 @@ export const scan = async ({
|
|
|
245
245
|
const parseCliOptions = (userCliOptions: TScanUserCliOptions) => {
|
|
246
246
|
// Version provided to the API corresponds with the version in package.json
|
|
247
247
|
// of the cli client
|
|
248
|
-
const apiOptions: TScanApiOptions = { version: '1.0.
|
|
248
|
+
const apiOptions: TScanApiOptions = { version: '1.0.18' };
|
|
249
249
|
const cliOptions: TScanCliOptions = { pollInterval: 5 };
|
|
250
250
|
|
|
251
251
|
if (userCliOptions.pullRequestTitle) {
|
|
@@ -14,6 +14,7 @@ import { getApiKey } from '../configuration.js';
|
|
|
14
14
|
import {
|
|
15
15
|
outputError,
|
|
16
16
|
outputHttpError,
|
|
17
|
+
outputJson,
|
|
17
18
|
outputLog,
|
|
18
19
|
startSpinner,
|
|
19
20
|
} from '../output.js';
|
|
@@ -49,6 +50,7 @@ type TScanArguments = {
|
|
|
49
50
|
|
|
50
51
|
type TScanCliOptions = {
|
|
51
52
|
pollInterval: number;
|
|
53
|
+
json: boolean;
|
|
52
54
|
};
|
|
53
55
|
|
|
54
56
|
type TScanUserCliOptions = {
|
|
@@ -63,6 +65,7 @@ type TScanUserCliOptions = {
|
|
|
63
65
|
pollInterval?: number;
|
|
64
66
|
baseBranch?: string;
|
|
65
67
|
slaMode?: boolean;
|
|
68
|
+
json?: boolean;
|
|
66
69
|
};
|
|
67
70
|
|
|
68
71
|
async function cli(
|
|
@@ -83,6 +86,13 @@ async function cli(
|
|
|
83
86
|
let loader: Ora | null;
|
|
84
87
|
let pollCount: number = 1;
|
|
85
88
|
|
|
89
|
+
// when the json option is added, only output the json result.
|
|
90
|
+
// Disabling console output takes care of the spinners and other extra info
|
|
91
|
+
const jsonOutput = cliOptions.json;
|
|
92
|
+
if (jsonOutput) {
|
|
93
|
+
process.env.QUIET = true;
|
|
94
|
+
}
|
|
95
|
+
|
|
86
96
|
// Setup different scan() event handlers
|
|
87
97
|
const onStart = () => {
|
|
88
98
|
loader = startSpinner('Starting Aikido Security release scan');
|
|
@@ -107,6 +117,20 @@ async function cli(
|
|
|
107
117
|
};
|
|
108
118
|
|
|
109
119
|
const onScanComplete = (pollResult: any) => {
|
|
120
|
+
if (jsonOutput) {
|
|
121
|
+
outputJson({
|
|
122
|
+
gate_passed: pollResult.gate_passed === true,
|
|
123
|
+
issue_breakdown: pollResult.issue_breakdown,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
if (pollResult.gate_passed !== true) {
|
|
127
|
+
// Don't process.exit() here: with piped stdout (CI) the JSON write
|
|
128
|
+
// may still be queued and a forced exit can truncate it.
|
|
129
|
+
process.exitCode = 10;
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
110
134
|
if (pollResult.gate_passed === true) {
|
|
111
135
|
loader?.succeed('Scan completed, no open issues found');
|
|
112
136
|
if (pollResult.diff_url) {
|
|
@@ -245,8 +269,8 @@ export const scan = async ({
|
|
|
245
269
|
const parseCliOptions = (userCliOptions: TScanUserCliOptions) => {
|
|
246
270
|
// Version provided to the API corresponds with the version in package.json
|
|
247
271
|
// of the cli client
|
|
248
|
-
const apiOptions: TScanApiOptions = { version: '1.0.
|
|
249
|
-
const cliOptions: TScanCliOptions = { pollInterval: 10 };
|
|
272
|
+
const apiOptions: TScanApiOptions = { version: '1.0.18' };
|
|
273
|
+
const cliOptions: TScanCliOptions = { pollInterval: 10, json: false };
|
|
250
274
|
|
|
251
275
|
if (userCliOptions.pullRequestTitle) {
|
|
252
276
|
apiOptions.pull_request_metadata = {
|
|
@@ -292,6 +316,9 @@ const parseCliOptions = (userCliOptions: TScanUserCliOptions) => {
|
|
|
292
316
|
} else if (userCliOptions.pollInterval) {
|
|
293
317
|
cliOptions.pollInterval = userCliOptions.pollInterval;
|
|
294
318
|
}
|
|
319
|
+
if (userCliOptions.json) {
|
|
320
|
+
cliOptions.json = true;
|
|
321
|
+
}
|
|
295
322
|
|
|
296
323
|
return { apiOptions, cliOptions };
|
|
297
324
|
};
|
|
@@ -363,6 +390,10 @@ export const cliSetup = (program: Command) =>
|
|
|
363
390
|
'--sla-mode',
|
|
364
391
|
'Let Aikido only fail only on open issues that have gone out of SLA.'
|
|
365
392
|
)
|
|
393
|
+
.option(
|
|
394
|
+
'--json',
|
|
395
|
+
'Output the scan result as JSON (gate_passed and issue_breakdown)'
|
|
396
|
+
)
|
|
366
397
|
.description('Run a release scan of an Aikido repo.')
|
|
367
398
|
.action(cli);
|
|
368
399
|
|
package/src/index.ts
CHANGED
|
@@ -20,7 +20,7 @@ program
|
|
|
20
20
|
.description(
|
|
21
21
|
'CLI api client to easily integrate the Aikido public CI API into custom deploy scripts'
|
|
22
22
|
)
|
|
23
|
-
.version('1.0.
|
|
23
|
+
.version('1.0.18');
|
|
24
24
|
|
|
25
25
|
// Load in all app commands and set them up in the `program` instance
|
|
26
26
|
apiKey.cliSetup(program);
|
package/src/output.ts
CHANGED
|
@@ -10,6 +10,13 @@ export const outputLog = (message: string): void => {
|
|
|
10
10
|
}
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
+
// Output a machine readable result
|
|
14
|
+
// This ignores the global "quiet" option: it is the output that was asked
|
|
15
|
+
// for, not human readable progress information
|
|
16
|
+
export const outputJson = (data: unknown): void => {
|
|
17
|
+
console.log(JSON.stringify(data, null, 2));
|
|
18
|
+
};
|
|
19
|
+
|
|
13
20
|
// Output some message, var, .. if --debug was provided
|
|
14
21
|
export const outputDebug = (variable: any): void => {
|
|
15
22
|
if (process.env.CLI_DEBUG == true || process.env.CLI_DEBUG == 'true') {
|