@aikidosec/ci-api-client 1.0.0 → 1.0.2

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.
@@ -1,184 +1,184 @@
1
- import { Argument, Command, InvalidArgumentError, Option } from 'commander';
2
- import {
3
- TScanApiOptions,
4
- TUploadApiOptions,
5
- TUploadPayloadType,
6
- TUploadResult,
7
- pollScanStatus,
8
- startScan,
9
- uploadCustomScanResult,
10
- } from '../aikidoApi.js';
11
- import { getApiKey } from '../configuration.js';
12
- import {
13
- outputError,
14
- outputHttpError,
15
- outputLog,
16
- startSpinner,
17
- } from '../output.js';
18
- import chalk from 'chalk';
19
- import { Ora } from 'ora';
20
- import { readFile } from 'fs';
21
- import { promisify } from 'util';
22
-
23
- type TUploadArguments = {
24
- data: TUploadApiOptions;
25
- onUploadComplete?: (startResult: TUploadResult) => void | null;
26
- onUploadFail?: (error: any) => void | null;
27
- };
28
-
29
- type TUploadCliOptions = {
30
- payloadFile?: string;
31
- payload?: string;
32
- };
33
-
34
- type TUploadUserCliOptions = {
35
- scanId?: string | number;
36
- repositoryId: string | number;
37
- containerImage?: string;
38
- type: TUploadPayloadType;
39
- file?: string;
40
- payload?: string;
41
- };
42
-
43
- async function cli(options: TUploadUserCliOptions, command: string) {
44
- const apiKey = getApiKey();
45
-
46
- if (!apiKey) {
47
- outputError('Please set an api key using: aikido-cli apikey <key>');
48
- }
49
-
50
- // Process command options and group them into apiOptions hash
51
- const { apiOptions, cliOptions } = await parseCliOptions(options);
52
-
53
- let loader: Ora | null = startSpinner('Uploading custom scan');
54
-
55
- const onUploadComplete = (result: TUploadResult) => {
56
- if (result.success === 1) {
57
- loader?.succeed('Upload completed');
58
- } else {
59
- loader?.fail(
60
- 'Upload failed, please verify your payload data and try again'
61
- );
62
- process.exit(10);
63
- }
64
- };
65
-
66
- const onUploadFail = (error: any) => {
67
- loader?.fail();
68
- outputHttpError(error);
69
-
70
- process.exit(1);
71
- };
72
-
73
- await upload({
74
- data: apiOptions,
75
- onUploadComplete,
76
- onUploadFail,
77
- });
78
- }
79
-
80
- export const upload = async ({
81
- data,
82
- onUploadComplete,
83
- onUploadFail,
84
- }: TUploadArguments): Promise<void> => {
85
- try {
86
- const result = await uploadCustomScanResult(data);
87
- onUploadComplete?.(result);
88
- } catch (error) {
89
- onUploadFail?.(error);
90
- return;
91
- }
92
- };
93
-
94
- type TParseCliResult = {
95
- apiOptions: TUploadApiOptions;
96
- cliOptions: TUploadCliOptions;
97
- };
98
-
99
- const parseCliOptions = async (
100
- userCliOptions: TUploadUserCliOptions
101
- ): Promise<TParseCliResult> => {
102
- const apiOptions: TUploadApiOptions = {
103
- repository_id: userCliOptions.repositoryId,
104
- container_image_name: userCliOptions.containerImage ?? undefined,
105
- payload_type: userCliOptions.type,
106
- payload: '',
107
- };
108
-
109
- const cliOptions: TUploadCliOptions = {
110
- payloadFile: userCliOptions.file,
111
- payload: userCliOptions.payload,
112
- };
113
-
114
- // Additional validation
115
- if (userCliOptions.type == TUploadPayloadType.JsonSbom) {
116
- if (
117
- userCliOptions.containerImage === undefined ||
118
- userCliOptions.containerImage.trim() === ''
119
- ) {
120
- outputError(
121
- 'Please provide a valid container image name with --container-image'
122
- );
123
- }
124
- }
125
-
126
- if (userCliOptions.file) {
127
- // Read '--payloadFile' if available...
128
- try {
129
- const data: string = await promisify(readFile)(
130
- userCliOptions.file,
131
- 'utf8'
132
- );
133
- apiOptions.payload = data;
134
- } catch (error) {
135
- outputError('Cannot read file provided with --payloadFile, aborting');
136
- process.exit(1);
137
- }
138
- } else if (cliOptions.payload?.trim() != '') {
139
- // ... no payloadFile provided, use the --payload information
140
- apiOptions.payload = cliOptions.payload!;
141
- } else {
142
- // No payload available? Abort
143
- outputError('No payload provided, aborting');
144
- process.exit(1);
145
- }
146
-
147
- return { apiOptions, cliOptions };
148
- };
149
-
150
- // Make an option required
151
- const requiredOption = (option: Option): Option => {
152
- option.required = true;
153
- return option;
154
- };
155
-
156
- export const cliSetup = (program: Command) =>
157
- program
158
- .command('upload')
159
- .option('-s, --scan-id <scanid>', 'Set the scan id')
160
- .requiredOption(
161
- '-r, --repository-id <repositoryid>',
162
- 'Set the repository id'
163
- )
164
- .option(
165
- '-i, --container-image <container-image-name>',
166
- 'Set the container image name. '
167
- )
168
- .addOption(
169
- requiredOption(
170
- new Option(
171
- '-t, --type <type>',
172
- 'Set the provided payload type. Available options are: checkov, json-sbom'
173
- ).choices(['checkov', 'json-sbom'])
174
- )
175
- )
176
- .option(
177
- '-f, --file <payloadFile>',
178
- 'Specify the payload file. The cli command wil read the file and use the contents as payload data.'
179
- )
180
- .option('-p, --payload <payload>', 'Set the payload')
181
- .description('Upload a custom scan result.')
182
- .action(cli);
183
-
184
- export default { cli, cliSetup };
1
+ import { Argument, Command, InvalidArgumentError, Option } from 'commander';
2
+ import {
3
+ TScanApiOptions,
4
+ TUploadApiOptions,
5
+ TUploadPayloadType,
6
+ TUploadResult,
7
+ pollScanStatus,
8
+ startScan,
9
+ uploadCustomScanResult,
10
+ } from '../aikidoApi.js';
11
+ import { getApiKey } from '../configuration.js';
12
+ import {
13
+ outputError,
14
+ outputHttpError,
15
+ outputLog,
16
+ startSpinner,
17
+ } from '../output.js';
18
+ import chalk from 'chalk';
19
+ import { Ora } from 'ora';
20
+ import { readFile } from 'fs';
21
+ import { promisify } from 'util';
22
+
23
+ type TUploadArguments = {
24
+ data: TUploadApiOptions;
25
+ onUploadComplete?: (startResult: TUploadResult) => void | null;
26
+ onUploadFail?: (error: any) => void | null;
27
+ };
28
+
29
+ type TUploadCliOptions = {
30
+ payloadFile?: string;
31
+ payload?: string;
32
+ };
33
+
34
+ type TUploadUserCliOptions = {
35
+ scanId?: string | number;
36
+ repositoryId: string | number;
37
+ containerImage?: string;
38
+ type: TUploadPayloadType;
39
+ file?: string;
40
+ payload?: string;
41
+ };
42
+
43
+ async function cli(options: TUploadUserCliOptions, command: string) {
44
+ const apiKey = getApiKey();
45
+
46
+ if (!apiKey) {
47
+ outputError('Please set an api key using: aikido-cli apikey <key>');
48
+ }
49
+
50
+ // Process command options and group them into apiOptions hash
51
+ const { apiOptions, cliOptions } = await parseCliOptions(options);
52
+
53
+ let loader: Ora | null = startSpinner('Uploading custom scan');
54
+
55
+ const onUploadComplete = (result: TUploadResult) => {
56
+ if (result.success === 1) {
57
+ loader?.succeed('Upload completed');
58
+ } else {
59
+ loader?.fail(
60
+ 'Upload failed, please verify your payload data and try again'
61
+ );
62
+ process.exit(10);
63
+ }
64
+ };
65
+
66
+ const onUploadFail = (error: any) => {
67
+ loader?.fail();
68
+ outputHttpError(error);
69
+
70
+ process.exit(1);
71
+ };
72
+
73
+ await upload({
74
+ data: apiOptions,
75
+ onUploadComplete,
76
+ onUploadFail,
77
+ });
78
+ }
79
+
80
+ export const upload = async ({
81
+ data,
82
+ onUploadComplete,
83
+ onUploadFail,
84
+ }: TUploadArguments): Promise<void> => {
85
+ try {
86
+ const result = await uploadCustomScanResult(data);
87
+ onUploadComplete?.(result);
88
+ } catch (error) {
89
+ onUploadFail?.(error);
90
+ return;
91
+ }
92
+ };
93
+
94
+ type TParseCliResult = {
95
+ apiOptions: TUploadApiOptions;
96
+ cliOptions: TUploadCliOptions;
97
+ };
98
+
99
+ const parseCliOptions = async (
100
+ userCliOptions: TUploadUserCliOptions
101
+ ): Promise<TParseCliResult> => {
102
+ const apiOptions: TUploadApiOptions = {
103
+ repository_id: userCliOptions.repositoryId,
104
+ container_image_name: userCliOptions.containerImage ?? undefined,
105
+ payload_type: userCliOptions.type,
106
+ payload: '',
107
+ };
108
+
109
+ const cliOptions: TUploadCliOptions = {
110
+ payloadFile: userCliOptions.file,
111
+ payload: userCliOptions.payload,
112
+ };
113
+
114
+ // Additional validation
115
+ if (userCliOptions.type == TUploadPayloadType.JsonSbom) {
116
+ if (
117
+ userCliOptions.containerImage === undefined ||
118
+ userCliOptions.containerImage.trim() === ''
119
+ ) {
120
+ outputError(
121
+ 'Please provide a valid container image name with --container-image'
122
+ );
123
+ }
124
+ }
125
+
126
+ if (userCliOptions.file) {
127
+ // Read '--payloadFile' if available...
128
+ try {
129
+ const data: string = await promisify(readFile)(
130
+ userCliOptions.file,
131
+ 'utf8'
132
+ );
133
+ apiOptions.payload = data;
134
+ } catch (error) {
135
+ outputError('Cannot read file provided with --payloadFile, aborting');
136
+ process.exit(1);
137
+ }
138
+ } else if (cliOptions.payload?.trim() != '') {
139
+ // ... no payloadFile provided, use the --payload information
140
+ apiOptions.payload = cliOptions.payload!;
141
+ } else {
142
+ // No payload available? Abort
143
+ outputError('No payload provided, aborting');
144
+ process.exit(1);
145
+ }
146
+
147
+ return { apiOptions, cliOptions };
148
+ };
149
+
150
+ // Make an option required
151
+ const requiredOption = (option: Option): Option => {
152
+ option.required = true;
153
+ return option;
154
+ };
155
+
156
+ export const cliSetup = (program: Command) =>
157
+ program
158
+ .command('upload')
159
+ .option('-s, --scan-id <scanid>', 'Set the scan id')
160
+ .requiredOption(
161
+ '-r, --repository-id <repositoryid>',
162
+ 'Set the repository id'
163
+ )
164
+ .option(
165
+ '-i, --container-image <container-image-name>',
166
+ 'Set the container image name. '
167
+ )
168
+ .addOption(
169
+ requiredOption(
170
+ new Option(
171
+ '-t, --type <type>',
172
+ 'Set the provided payload type. Available options are: checkov, json-sbom'
173
+ ).choices(['checkov', 'json-sbom'])
174
+ )
175
+ )
176
+ .option(
177
+ '-f, --file <payloadFile>',
178
+ 'Specify the payload file. The cli command wil read the file and use the contents as payload data.'
179
+ )
180
+ .option('-p, --payload <payload>', 'Set the payload')
181
+ .description('Upload a custom scan result.')
182
+ .action(cli);
183
+
184
+ export default { cli, cliSetup };
@@ -1,21 +1,21 @@
1
- import Configstore from 'configstore';
2
- import { program } from './index.js';
3
-
4
- // Load a new ConfigSture by using the commander program name, which is
5
- // set in the main index.js file
6
- export const getConfigStore = () => {
7
- return new Configstore(program.name());
8
- };
9
-
10
- // Get the apikey set through the cli option or the
11
- // one stored in the configuration
12
- export const getApiKey = () => {
13
- return program.getOptionValue('apikey')
14
- ? program.getOptionValue('apikey')
15
- : getConfigStore().get('apikey');
16
- };
17
-
18
- // Set the global apiKey
19
- export const setApiKey = (newApiKey: string) => {
20
- return getConfigStore().set('apikey', newApiKey);
21
- };
1
+ import Configstore from 'configstore';
2
+ import { program } from './index.js';
3
+
4
+ // Load a new ConfigSture by using the commander program name, which is
5
+ // set in the main index.js file
6
+ export const getConfigStore = () => {
7
+ return new Configstore(program.name());
8
+ };
9
+
10
+ // Get the apikey set through the cli option or the
11
+ // one stored in the configuration
12
+ export const getApiKey = () => {
13
+ return program.getOptionValue('apikey')
14
+ ? program.getOptionValue('apikey')
15
+ : getConfigStore().get('apikey');
16
+ };
17
+
18
+ // Set the global apiKey
19
+ export const setApiKey = (newApiKey: string) => {
20
+ return getConfigStore().set('apikey', newApiKey);
21
+ };
package/src/index.ts CHANGED
@@ -1,74 +1,64 @@
1
- #!/usr/bin/env node
2
- import chalk from 'chalk';
3
- import { Command } from 'commander';
4
- import dotenv from 'dotenv';
5
- import { readFileSync } from 'fs';
6
- import { dirname, join } from 'path';
7
- import { fileURLToPath } from 'url';
8
- import apiKey from './commands/apiKey.js';
9
- import scan from './commands/scan.js';
10
- import upload from './commands/upload.js';
11
-
12
- // Determine current file location and path to
13
- const __filename = fileURLToPath(import.meta.url);
14
- const __dirname = dirname(__filename);
15
- const __rootdirname = dirname(__dirname);
16
-
17
- // Load all .env configuration variables and auto-inject them into process.env
18
- dotenv.config(
19
- process.env.NODE_ENV ? { path: `.env.${process.env.NODE_ENV}` } : {}
20
- );
21
-
22
- // Set commander instance info from package.json
23
- const pkgJsonPath = join('/', __rootdirname, 'package.json');
24
- export const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf8'));
25
-
26
- // Start up commander.js Command instance
27
- export const program = new Command();
28
-
29
- program
30
- .name(pkgJson.name)
31
- .description(pkgJson.description)
32
- .version(pkgJson.version);
33
-
34
- // Load in all app commands and set them up in the `program` instance
35
- apiKey.cliSetup(program);
36
- scan.cliSetup(program);
37
- upload.cliSetup(program);
38
-
39
- // Override global process.env type
40
- declare global {
41
- namespace NodeJS {
42
- interface ProcessEnv {
43
- [key: string]: any; // used any here
44
- QUIET?: boolean;
45
- CLI_DEBUG?: boolean | string;
46
- }
47
- }
48
- }
49
-
50
- // Add global option "--quiet"
51
- program
52
- .option('-q, --quiet', 'Disable console output when executing commands')
53
- .on('option:quiet', function () {
54
- process.env.QUIET = true;
55
- });
56
-
57
- // Add global option "--debug"
58
- program
59
- .option('--debug', 'Add additional debug information to command output')
60
- .on('option:debug', function () {
61
- process.env.CLI_DEBUG = true;
62
- });
63
-
64
- // Add global option "--apikey"
65
- program.option(
66
- '--apikey <apikey>',
67
- 'Use a cli apikey instead of reading the key from the configuration'
68
- );
69
-
70
- program.configureOutput({
71
- outputError: (str, write) => write(chalk.red(str)),
72
- });
73
-
74
- program.parse();
1
+ #!/usr/bin/env node
2
+ import chalk from 'chalk';
3
+ import { Command } from 'commander';
4
+ import dotenv from 'dotenv';
5
+ import apiKey from './commands/apiKey.js';
6
+ import scan from './commands/scan.js';
7
+ import upload from './commands/upload.js';
8
+
9
+ // Load all .env configuration variables and auto-inject them into process.env
10
+ dotenv.config(
11
+ process.env.NODE_ENV ? { path: `.env.${process.env.NODE_ENV}` } : {}
12
+ );
13
+
14
+ // Start up commander.js Command instance
15
+ export const program = new Command();
16
+
17
+ program
18
+ .name('Aikido API Client')
19
+ .description(
20
+ 'CLI api client to easily integrate the Aikido public CI API into custom deploy scripts'
21
+ )
22
+ .version('1.0.1');
23
+
24
+ // Load in all app commands and set them up in the `program` instance
25
+ apiKey.cliSetup(program);
26
+ scan.cliSetup(program);
27
+ upload.cliSetup(program);
28
+
29
+ // Override global process.env type
30
+ declare global {
31
+ namespace NodeJS {
32
+ interface ProcessEnv {
33
+ [key: string]: any; // used any here
34
+ QUIET?: boolean;
35
+ CLI_DEBUG?: boolean | string;
36
+ }
37
+ }
38
+ }
39
+
40
+ // Add global option "--quiet"
41
+ program
42
+ .option('-q, --quiet', 'Disable console output when executing commands')
43
+ .on('option:quiet', function () {
44
+ process.env.QUIET = true;
45
+ });
46
+
47
+ // Add global option "--debug"
48
+ program
49
+ .option('--debug', 'Add additional debug information to command output')
50
+ .on('option:debug', function () {
51
+ process.env.CLI_DEBUG = true;
52
+ });
53
+
54
+ // Add global option "--apikey"
55
+ program.option(
56
+ '--apikey <apikey>',
57
+ 'Use a cli apikey instead of reading the key from the configuration'
58
+ );
59
+
60
+ program.configureOutput({
61
+ outputError: (str, write) => write(chalk.red(str)),
62
+ });
63
+
64
+ program.parse();