@aikidosec/ci-api-client 1.0.2 → 1.0.4
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/.editorconfig +18 -18
- package/.env +1 -1
- package/.prettierrc +10 -10
- package/CONTRIBUTING.md +30 -30
- package/LICENSE +21 -21
- package/README.md +80 -80
- package/RELEASING.md +26 -26
- package/lib/commands/scan.js +3 -0
- package/lib/index.js +1 -1
- package/lib/output.js +18 -24
- package/package.json +51 -51
- package/src/aikidoApi.ts +148 -148
- package/src/commands/apiKey.ts +34 -33
- package/src/commands/scan.ts +373 -370
- package/src/commands/upload.ts +184 -184
- package/src/configuration.ts +21 -21
- package/src/index.ts +64 -64
- package/src/output.ts +82 -87
- package/tsconfig.json +27 -27
package/src/aikidoApi.ts
CHANGED
|
@@ -1,148 +1,148 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
2
|
-
import { getApiKey } from './configuration.js';
|
|
3
|
-
import { outputDebug } from './output.js';
|
|
4
|
-
|
|
5
|
-
const getApiUrl = () => process.env.AIKIDO_API || 'https://app.aikido.dev';
|
|
6
|
-
const getApiHeaders = () => ({
|
|
7
|
-
'Content-Type': 'application/json',
|
|
8
|
-
'X-AIK-API-SECRET': getApiKey(),
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
// #region Scan start
|
|
12
|
-
export type TScanApiOptions = {
|
|
13
|
-
repository_id?: string | number;
|
|
14
|
-
base_commit_id?: string;
|
|
15
|
-
head_commit_id?: string;
|
|
16
|
-
branch_name?: string;
|
|
17
|
-
pull_request_metadata?: {
|
|
18
|
-
title?: string;
|
|
19
|
-
url?: string;
|
|
20
|
-
};
|
|
21
|
-
self_managed_scanners?: string[];
|
|
22
|
-
fail_on_dependency_scan?: boolean;
|
|
23
|
-
fail_on_sast_scan?: boolean;
|
|
24
|
-
fail_on_iac_scan?: boolean;
|
|
25
|
-
minimum_severity?: string;
|
|
26
|
-
version: string;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export type TStartScanResult = {
|
|
30
|
-
scan_id: number;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
// Start a scan with options (see: startScanSchema)
|
|
34
|
-
// and return the data
|
|
35
|
-
export async function startScan(
|
|
36
|
-
data: TScanApiOptions
|
|
37
|
-
): Promise<TStartScanResult> {
|
|
38
|
-
const requestUrl = `${getApiUrl()}/api/integrations/continuous_integration/scan/repository`;
|
|
39
|
-
const requestConfig = {
|
|
40
|
-
method: 'POST',
|
|
41
|
-
data,
|
|
42
|
-
headers: getApiHeaders(),
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
outputDebug(`API request: ${requestUrl}`);
|
|
46
|
-
outputDebug(requestConfig);
|
|
47
|
-
|
|
48
|
-
const resultData = (await axios(requestUrl, requestConfig)).data;
|
|
49
|
-
outputDebug(resultData);
|
|
50
|
-
|
|
51
|
-
return resultData;
|
|
52
|
-
}
|
|
53
|
-
// #endregion
|
|
54
|
-
|
|
55
|
-
// #region Scan polling
|
|
56
|
-
export type TPollIsScanningResult = {
|
|
57
|
-
all_scans_completed: boolean;
|
|
58
|
-
dependency_scan_completed: boolean;
|
|
59
|
-
sast_scan_completed: boolean;
|
|
60
|
-
iac_scan_completed: boolean;
|
|
61
|
-
sbom_scan_completed?: boolean;
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
type TPollScanCompletedOptions = {
|
|
65
|
-
gate_passed: boolean;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
type TPollScanFeatureBranchCompletedOptions = {
|
|
69
|
-
new_issues_found: number;
|
|
70
|
-
new_dependency_issues_found: number;
|
|
71
|
-
new_sast_issues_found: number;
|
|
72
|
-
new_iac_issues_found: number;
|
|
73
|
-
diff_url: string;
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
type TPollScanDefaultBranchCompletedOptions = {
|
|
77
|
-
open_issues_found: number;
|
|
78
|
-
issue_links: string[];
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
export type TPollScanFeatureBranchCompletedResult = TPollIsScanningResult &
|
|
82
|
-
TPollScanCompletedOptions &
|
|
83
|
-
TPollScanFeatureBranchCompletedOptions;
|
|
84
|
-
|
|
85
|
-
export type TPollScanCompletedDefaultBranchResult = TPollIsScanningResult &
|
|
86
|
-
TPollScanCompletedOptions &
|
|
87
|
-
TPollScanDefaultBranchCompletedOptions;
|
|
88
|
-
|
|
89
|
-
export async function pollScanStatus(
|
|
90
|
-
scanId: number
|
|
91
|
-
): Promise<
|
|
92
|
-
| TPollIsScanningResult
|
|
93
|
-
| TPollScanFeatureBranchCompletedResult
|
|
94
|
-
| TPollScanCompletedDefaultBranchResult
|
|
95
|
-
> {
|
|
96
|
-
const requestUrl = `${getApiUrl()}/api/integrations/continuous_integration/scan/repository`;
|
|
97
|
-
const requestConfig = {
|
|
98
|
-
method: 'GET',
|
|
99
|
-
params: { scan_id: scanId },
|
|
100
|
-
headers: getApiHeaders(),
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
outputDebug(`API request: ${requestUrl}`);
|
|
104
|
-
outputDebug(requestConfig);
|
|
105
|
-
|
|
106
|
-
const resultData = (await axios(requestUrl, requestConfig)).data;
|
|
107
|
-
outputDebug(resultData);
|
|
108
|
-
|
|
109
|
-
return resultData;
|
|
110
|
-
}
|
|
111
|
-
// #endregion
|
|
112
|
-
|
|
113
|
-
// #region Upload custom result
|
|
114
|
-
export enum TUploadPayloadType {
|
|
115
|
-
Checkov = 'checkov',
|
|
116
|
-
JsonSbom = 'json-sbom',
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export type TUploadApiOptions = {
|
|
120
|
-
scan_id?: number;
|
|
121
|
-
repository_id: string | number;
|
|
122
|
-
container_image_name?: string;
|
|
123
|
-
payload_type: TUploadPayloadType;
|
|
124
|
-
payload: string;
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
export type TUploadResult = {
|
|
128
|
-
success: number;
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
export async function uploadCustomScanResult(
|
|
132
|
-
data: TUploadApiOptions
|
|
133
|
-
): Promise<TUploadResult> {
|
|
134
|
-
const requestUrl = `${getApiUrl()}/api/integrations/continuous_integration/scan/custom`;
|
|
135
|
-
const requestConfig = {
|
|
136
|
-
method: 'POST',
|
|
137
|
-
data,
|
|
138
|
-
headers: getApiHeaders(),
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
outputDebug(`API request: ${requestUrl}`);
|
|
142
|
-
outputDebug(requestConfig);
|
|
143
|
-
|
|
144
|
-
const resultData = (await axios(requestUrl, requestConfig)).data;
|
|
145
|
-
outputDebug(resultData);
|
|
146
|
-
|
|
147
|
-
return resultData;
|
|
148
|
-
}
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { getApiKey } from './configuration.js';
|
|
3
|
+
import { outputDebug } from './output.js';
|
|
4
|
+
|
|
5
|
+
const getApiUrl = () => process.env.AIKIDO_API || 'https://app.aikido.dev';
|
|
6
|
+
const getApiHeaders = () => ({
|
|
7
|
+
'Content-Type': 'application/json',
|
|
8
|
+
'X-AIK-API-SECRET': getApiKey(),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
// #region Scan start
|
|
12
|
+
export type TScanApiOptions = {
|
|
13
|
+
repository_id?: string | number;
|
|
14
|
+
base_commit_id?: string;
|
|
15
|
+
head_commit_id?: string;
|
|
16
|
+
branch_name?: string;
|
|
17
|
+
pull_request_metadata?: {
|
|
18
|
+
title?: string;
|
|
19
|
+
url?: string;
|
|
20
|
+
};
|
|
21
|
+
self_managed_scanners?: string[];
|
|
22
|
+
fail_on_dependency_scan?: boolean;
|
|
23
|
+
fail_on_sast_scan?: boolean;
|
|
24
|
+
fail_on_iac_scan?: boolean;
|
|
25
|
+
minimum_severity?: string;
|
|
26
|
+
version: string;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type TStartScanResult = {
|
|
30
|
+
scan_id: number;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// Start a scan with options (see: startScanSchema)
|
|
34
|
+
// and return the data
|
|
35
|
+
export async function startScan(
|
|
36
|
+
data: TScanApiOptions
|
|
37
|
+
): Promise<TStartScanResult> {
|
|
38
|
+
const requestUrl = `${getApiUrl()}/api/integrations/continuous_integration/scan/repository`;
|
|
39
|
+
const requestConfig = {
|
|
40
|
+
method: 'POST',
|
|
41
|
+
data,
|
|
42
|
+
headers: getApiHeaders(),
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
outputDebug(`API request: ${requestUrl}`);
|
|
46
|
+
outputDebug(requestConfig);
|
|
47
|
+
|
|
48
|
+
const resultData = (await axios(requestUrl, requestConfig)).data;
|
|
49
|
+
outputDebug(resultData);
|
|
50
|
+
|
|
51
|
+
return resultData;
|
|
52
|
+
}
|
|
53
|
+
// #endregion
|
|
54
|
+
|
|
55
|
+
// #region Scan polling
|
|
56
|
+
export type TPollIsScanningResult = {
|
|
57
|
+
all_scans_completed: boolean;
|
|
58
|
+
dependency_scan_completed: boolean;
|
|
59
|
+
sast_scan_completed: boolean;
|
|
60
|
+
iac_scan_completed: boolean;
|
|
61
|
+
sbom_scan_completed?: boolean;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
type TPollScanCompletedOptions = {
|
|
65
|
+
gate_passed: boolean;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
type TPollScanFeatureBranchCompletedOptions = {
|
|
69
|
+
new_issues_found: number;
|
|
70
|
+
new_dependency_issues_found: number;
|
|
71
|
+
new_sast_issues_found: number;
|
|
72
|
+
new_iac_issues_found: number;
|
|
73
|
+
diff_url: string;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
type TPollScanDefaultBranchCompletedOptions = {
|
|
77
|
+
open_issues_found: number;
|
|
78
|
+
issue_links: string[];
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export type TPollScanFeatureBranchCompletedResult = TPollIsScanningResult &
|
|
82
|
+
TPollScanCompletedOptions &
|
|
83
|
+
TPollScanFeatureBranchCompletedOptions;
|
|
84
|
+
|
|
85
|
+
export type TPollScanCompletedDefaultBranchResult = TPollIsScanningResult &
|
|
86
|
+
TPollScanCompletedOptions &
|
|
87
|
+
TPollScanDefaultBranchCompletedOptions;
|
|
88
|
+
|
|
89
|
+
export async function pollScanStatus(
|
|
90
|
+
scanId: number
|
|
91
|
+
): Promise<
|
|
92
|
+
| TPollIsScanningResult
|
|
93
|
+
| TPollScanFeatureBranchCompletedResult
|
|
94
|
+
| TPollScanCompletedDefaultBranchResult
|
|
95
|
+
> {
|
|
96
|
+
const requestUrl = `${getApiUrl()}/api/integrations/continuous_integration/scan/repository`;
|
|
97
|
+
const requestConfig = {
|
|
98
|
+
method: 'GET',
|
|
99
|
+
params: { scan_id: scanId },
|
|
100
|
+
headers: getApiHeaders(),
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
outputDebug(`API request: ${requestUrl}`);
|
|
104
|
+
outputDebug(requestConfig);
|
|
105
|
+
|
|
106
|
+
const resultData = (await axios(requestUrl, requestConfig)).data;
|
|
107
|
+
outputDebug(resultData);
|
|
108
|
+
|
|
109
|
+
return resultData;
|
|
110
|
+
}
|
|
111
|
+
// #endregion
|
|
112
|
+
|
|
113
|
+
// #region Upload custom result
|
|
114
|
+
export enum TUploadPayloadType {
|
|
115
|
+
Checkov = 'checkov',
|
|
116
|
+
JsonSbom = 'json-sbom',
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export type TUploadApiOptions = {
|
|
120
|
+
scan_id?: number;
|
|
121
|
+
repository_id: string | number;
|
|
122
|
+
container_image_name?: string;
|
|
123
|
+
payload_type: TUploadPayloadType;
|
|
124
|
+
payload: string;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export type TUploadResult = {
|
|
128
|
+
success: number;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export async function uploadCustomScanResult(
|
|
132
|
+
data: TUploadApiOptions
|
|
133
|
+
): Promise<TUploadResult> {
|
|
134
|
+
const requestUrl = `${getApiUrl()}/api/integrations/continuous_integration/scan/custom`;
|
|
135
|
+
const requestConfig = {
|
|
136
|
+
method: 'POST',
|
|
137
|
+
data,
|
|
138
|
+
headers: getApiHeaders(),
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
outputDebug(`API request: ${requestUrl}`);
|
|
142
|
+
outputDebug(requestConfig);
|
|
143
|
+
|
|
144
|
+
const resultData = (await axios(requestUrl, requestConfig)).data;
|
|
145
|
+
outputDebug(resultData);
|
|
146
|
+
|
|
147
|
+
return resultData;
|
|
148
|
+
}
|
package/src/commands/apiKey.ts
CHANGED
|
@@ -1,33 +1,34 @@
|
|
|
1
|
-
import { Argument, Command } from 'commander';
|
|
2
|
-
import { setApiKey } from '../configuration.js';
|
|
3
|
-
import { outputError, outputLog } from '../output.js';
|
|
4
|
-
|
|
5
|
-
const API_KEY_REGEX = /^AIK_CI_[a-zA-Z0-9]{64}$/g;
|
|
6
|
-
|
|
7
|
-
function cli(apiKey: string): void {
|
|
8
|
-
const isValidApiKey =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
.
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
'
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
.
|
|
32
|
-
|
|
33
|
-
|
|
1
|
+
import { Argument, Command } from 'commander';
|
|
2
|
+
import { setApiKey } from '../configuration.js';
|
|
3
|
+
import { outputError, outputLog } from '../output.js';
|
|
4
|
+
|
|
5
|
+
const API_KEY_REGEX = /^AIK_CI_[a-zA-Z0-9]{64}$/g;
|
|
6
|
+
|
|
7
|
+
function cli(apiKey: string): void {
|
|
8
|
+
const isValidApiKey =
|
|
9
|
+
API_KEY_REGEX.test(apiKey) || apiKey.trim().length === 128;
|
|
10
|
+
if (apiKey && !isValidApiKey) {
|
|
11
|
+
outputError('That does not seem right, please verify your api key');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
setApiKey(apiKey);
|
|
15
|
+
outputLog(
|
|
16
|
+
apiKey
|
|
17
|
+
? 'Your new Aikido API key has been set'
|
|
18
|
+
: 'Your Aikido API key has been removed'
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const cliSetup = (program: Command): Command =>
|
|
23
|
+
program
|
|
24
|
+
.command('apikey')
|
|
25
|
+
.addArgument(
|
|
26
|
+
new Argument(
|
|
27
|
+
'<key>',
|
|
28
|
+
'Your Aikido public CI API key. See also: https://app.aikido.dev/settings/integrations/continuous-integration. To remove your apikey, leave the <key> argument value empty.'
|
|
29
|
+
).argOptional()
|
|
30
|
+
)
|
|
31
|
+
.description('Set (or remove) your Aikido public CI API key.')
|
|
32
|
+
.action(cli);
|
|
33
|
+
|
|
34
|
+
export default { cli, cliSetup };
|