@soos-io/soos-sbom 0.1.3 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  SOOS is an independent software security company, located in Winooski, VT USA, building security software for your team. [SOOS, Software security, simplified](https://soos.io).
4
4
 
5
- Use SOOS to scan your software for [vulnerabilities](https://app.soos.io/research/vulnerabilities) and [open source license](https://app.soos.io/research/licenses) issues with [SOOS Core SCA](https://soos.io/sca-product). [Generate SBOMs](https://kb.soos.io/help/generating-a-software-bill-of-materials-sbom). Govern your open source dependencies. Run the [SOOS DAST vulnerability scanner](https://soos.io/dast-product) against your web apps or APIs.
5
+ Use SOOS to scan your software for [vulnerabilities](https://app.soos.io/research/vulnerabilities) and [open source license](https://app.soos.io/research/licenses) issues with [SOOS Core SCA](https://soos.io/products/sca). [Generate SBOMs](https://kb.soos.io/help/soos-reports-for-export). Govern your open source dependencies. Run the [SOOS DAST vulnerability scanner](https://soos.io/products/dast) against your web apps or APIs.
6
6
 
7
7
  [Demo SOOS](https://app.soos.io/demo) or [Register for a Free Trial](https://app.soos.io/register).
8
8
 
@@ -12,28 +12,34 @@ If you maintain an Open Source project, sign up for the Free as in Beer [SOOS Co
12
12
  - [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)
13
13
 
14
14
  ## Installation
15
+
16
+ ### Globally
15
17
  run `npm i -g @soos-io/soos-sbom@latest`
18
+ Then Run `soos-sbom` from any terminal and add the parameters you want.
16
19
 
17
- ## Usage
18
- Run `soos-sbom` from any terminal and add the parameters you want.
20
+ ### Locally
21
+ run `npm install --prefix ./soos @soos-io/soos-sbom`
22
+ Then run from the same terminal `node ./soos/node_modules/@soos-io/soos-sbom/bin/index.js`
19
23
 
20
24
  ## Parameters
21
25
 
22
26
 
23
27
  | Argument | Default | Description |
24
28
  | ----------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
25
- | `--apiKey` | `getEnvVariable(CONSTANTS.SOOS.API_KEY_ENV_VAR)` | SOOS API Key - get yours from [SOOS Integration](https://app.soos.io/integrate/sbom). |
26
- | `--apiURL` | `"https://api.soos.io/api/"` | SOOS API URL - Intended for internal use only, do not modify. |
29
+ | `--apiKey` | N/A | SSOOS API Key - get yours from [SOOS Integration](https://app.soos.io/integrate/sbom). Uses `SOOS_API_KEY` env value if present. |
30
+ | `--apiURL` | N/A | SOOS API URL - Intended for internal use only, do not modify. |
27
31
  | `--appVersion` | N/A | App Version - Intended for internal use only. |
28
32
  | `--branchName` | `null` | The name of the branch from the SCM System. |
29
33
  | `--branchURI` | `null` | The URI to the branch from the SCM System. |
30
34
  | `--buildURI` | `null` | URI to CI build info. |
31
35
  | `--buildVersion` | `null` | Version of application build artifacts. |
32
- | `--clientId` | `getEnvVariable(CONSTANTS.SOOS.CLIENT_ID_ENV_VAR)` | SOOS Client ID - get yours from [SOOS Integration](https://app.soos.io/integrate/sbom). |
36
+ | `--clientId` | N/A | SSOOS Client ID - get yours from [SOOS Integration](https://app.soos.io/integrate/sbom). Uses `SOOS_API_CLIENT` env value if present. |
33
37
  | `--commitHash` | `null` | The commit hash value from the SCM System. |
34
38
  | `--integrationName` | N/A | Integration Name - Intended for internal use only. |
35
39
  | `--integrationType` | N/A | Integration Type - Intended for internal use only. |
36
- | `--logLevel` | `LogLevel.INFO` | Minimum level to show logs: PASS, IGNORE, INFO, WARN, or FAIL. |
40
+ | `--logLevel` | `INFO` | Minimum level to show logs: PASS, IGNORE, INFO, WARN, or FAIL. |
41
+
42
+ | `--onFailure` | `continue_on_failure` | Action to perform when the scan fails. Options: fail_the_build, continue_on_failure. |
37
43
  | `--operatingEnvironment`| `null` | Set Operating environment for information purposes only. |
38
44
  | `--otherOptions` | `null` | Other Options to pass to syft. |
39
45
  | `--projectName` | N/A | Project Name - this is what will be displayed in the SOOS app. |
@@ -0,0 +1,3 @@
1
+ export declare const SOOS_SBOM_CONSTANTS: {
2
+ FileRegex: RegExp;
3
+ };
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SOOS_SBOM_CONSTANTS = void 0;
4
+ exports.SOOS_SBOM_CONSTANTS = {
5
+ FileRegex: /\.(cdx|spdx)\.json$/,
6
+ };
package/bin/index.js CHANGED
@@ -4,208 +4,122 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const tslib_1 = require("tslib");
5
5
  const api_client_1 = require("@soos-io/api-client");
6
6
  const utilities_1 = require("@soos-io/api-client/dist/utilities");
7
- const argparse_1 = require("argparse");
8
7
  const FileSystem = tslib_1.__importStar(require("fs"));
9
8
  const Path = tslib_1.__importStar(require("path"));
10
- const form_data_1 = tslib_1.__importDefault(require("form-data"));
11
- const constants_1 = require("./utils/constants");
12
9
  const process_1 = require("process");
13
- const SOOSAnalysisApiClient_1 = tslib_1.__importDefault(require("@soos-io/api-client/dist/api/SOOSAnalysisApiClient"));
10
+ const AnalysisArgumentParser_1 = tslib_1.__importDefault(require("@soos-io/api-client/dist/services/AnalysisArgumentParser"));
11
+ const package_json_1 = require("../package.json");
12
+ const AnalysisService_1 = tslib_1.__importDefault(require("@soos-io/api-client/dist/services/AnalysisService"));
13
+ const constants_1 = require("./constants");
14
14
  class SOOSSBOMAnalysis {
15
15
  constructor(args) {
16
16
  this.args = args;
17
17
  }
18
18
  static parseArgs() {
19
- const parser = new argparse_1.ArgumentParser({ description: "SOOS SBOM" });
20
- parser.add_argument("--apiKey", {
21
- help: "SOOS API Key - get yours from https://app.soos.io/integrate/sbom",
22
- default: (0, utilities_1.getEnvVariable)(constants_1.CONSTANTS.SOOS.API_KEY_ENV_VAR),
23
- required: false,
24
- });
25
- parser.add_argument("--apiURL", {
26
- help: "SOOS API URL - Intended for internal use only, do not modify.",
27
- default: "https://api.soos.io/api/",
28
- required: false,
29
- type: (value) => {
30
- return (0, utilities_1.ensureNonEmptyValue)(value, "apiURL");
31
- },
32
- });
33
- parser.add_argument("--appVersion", {
34
- help: "App Version - Intended for internal use only.",
35
- required: false,
36
- });
37
- parser.add_argument("--branchName", {
38
- help: "The name of the branch from the SCM System.",
39
- default: null,
40
- required: false,
41
- });
42
- parser.add_argument("--branchURI", {
43
- help: "The URI to the branch from the SCM System.",
44
- default: null,
45
- required: false,
46
- });
47
- parser.add_argument("--buildURI", {
48
- help: "URI to CI build info.",
49
- default: null,
50
- required: false,
51
- });
52
- parser.add_argument("--buildVersion", {
53
- help: "Version of application build artifacts.",
54
- default: null,
55
- required: false,
56
- });
57
- parser.add_argument("--clientId", {
58
- help: "SOOS Client ID - get yours from https://app.soos.io/integrate/sbom",
59
- default: (0, utilities_1.getEnvVariable)(constants_1.CONSTANTS.SOOS.CLIENT_ID_ENV_VAR),
60
- required: false,
61
- });
62
- parser.add_argument("--commitHash", {
63
- help: "The commit hash value from the SCM System.",
64
- default: null,
65
- required: false,
66
- });
67
- parser.add_argument("--integrationName", {
68
- help: "Integration Name - Intended for internal use only.",
69
- required: false,
70
- });
71
- parser.add_argument("--integrationType", {
72
- help: "Integration Type - Intended for internal use only.",
73
- required: false,
74
- default: constants_1.CONSTANTS.SOOS.DEFAULT_INTEGRATION_TYPE,
75
- });
76
- parser.add_argument("--logLevel", {
77
- help: "Minimum level to show logs: PASS, IGNORE, INFO, WARN or FAIL.",
78
- default: api_client_1.LogLevel.INFO,
79
- required: false,
80
- type: (value) => {
81
- return (0, utilities_1.ensureEnumValue)(api_client_1.LogLevel, value);
82
- },
83
- });
84
- parser.add_argument("--operatingEnvironment", {
85
- help: "Set Operating environment for information purposes only.",
86
- default: null,
87
- required: false,
88
- });
89
- parser.add_argument("--projectName", {
90
- help: "Project Name - this is what will be displayed in the SOOS app.",
91
- required: true,
92
- type: (value) => {
93
- return (0, utilities_1.ensureNonEmptyValue)(value, "projectName");
94
- },
95
- });
96
- parser.add_argument("--scriptVersion", {
97
- help: "Script Version - Intended for internal use only.",
98
- required: false,
99
- });
100
- parser.add_argument("--verbose", {
101
- help: "Enable verbose logging.",
102
- action: "store_true",
103
- default: false,
104
- required: false,
105
- });
106
- parser.add_argument("sbomPath", {
19
+ const analysisArgumentParser = AnalysisArgumentParser_1.default.create(api_client_1.ScanType.SBOM);
20
+ analysisArgumentParser.addBaseScanArguments(api_client_1.IntegrationName.SoosSbom, api_client_1.IntegrationType.Script, package_json_1.version);
21
+ analysisArgumentParser.argumentParser.add_argument("sbomPath", {
107
22
  help: "The SBOM File to scan, it could be the location of the file or the file itself. When location is specified only the first file found will be scanned.",
108
23
  });
109
24
  api_client_1.soosLogger.info("Parsing arguments");
110
- return parser.parse_args();
25
+ return analysisArgumentParser.parseArguments();
111
26
  }
112
27
  async runAnalysis() {
28
+ const scanType = api_client_1.ScanType.SBOM;
29
+ const soosAnalysisService = AnalysisService_1.default.create(this.args.apiKey, this.args.apiURL);
113
30
  let projectHash;
114
31
  let branchHash;
115
32
  let analysisId;
116
- const filePath = await this.findSbomFilePath();
117
- const soosAnalysisApiClient = new SOOSAnalysisApiClient_1.default(this.args.apiKey, this.args.apiURL);
33
+ let scanStatusUrl;
34
+ const sbomFilePath = await this.findSbomFilePath();
118
35
  try {
119
- api_client_1.soosLogger.info("Starting SOOS SBOM Analysis");
120
- api_client_1.soosLogger.info(`Creating scan for project '${this.args.projectName}'...`);
121
- api_client_1.soosLogger.info(`Branch Name: ${this.args.branchName}`);
122
- const result = await soosAnalysisApiClient.createScan({
36
+ const result = await soosAnalysisService.setupScan({
123
37
  clientId: this.args.clientId,
124
38
  projectName: this.args.projectName,
39
+ branchName: this.args.branchName,
125
40
  commitHash: this.args.commitHash,
126
- branch: this.args.branchName,
127
41
  buildVersion: this.args.buildVersion,
128
- buildUri: this.args.buildUri,
129
- branchUri: this.args.branchUri,
130
- integrationType: this.args.integrationType,
42
+ buildUri: this.args.buildURI,
43
+ branchUri: this.args.branchURI,
131
44
  operatingEnvironment: this.args.operatingEnvironment,
132
45
  integrationName: this.args.integrationName,
46
+ integrationType: this.args.integrationType,
133
47
  appVersion: this.args.appVersion,
134
- scriptVersion: null,
135
- contributingDeveloperAudit: undefined,
136
- scanType: api_client_1.ScanType.SBOM,
137
- toolName: null,
138
- toolVersion: null,
48
+ scriptVersion: this.args.scriptVersion,
49
+ contributingDeveloperAudit: !this.args.contributingDeveloperId ||
50
+ !this.args.contributingDeveloperSource ||
51
+ !this.args.contributingDeveloperSourceName
52
+ ? []
53
+ : [
54
+ {
55
+ contributingDeveloperId: this.args.contributingDeveloperId,
56
+ source: this.args.contributingDeveloperSource,
57
+ sourceName: this.args.contributingDeveloperSourceName,
58
+ },
59
+ ],
60
+ scanType,
139
61
  });
140
62
  projectHash = result.projectHash;
141
63
  branchHash = result.branchHash;
142
64
  analysisId = result.analysisId;
143
- api_client_1.soosLogger.info(`Project Hash: ${projectHash}`);
144
- api_client_1.soosLogger.info(`Branch Hash: ${branchHash}`);
145
- api_client_1.soosLogger.info(`Scan Id: ${analysisId}`);
146
- api_client_1.soosLogger.info("Scan created successfully.");
65
+ scanStatusUrl = result.scanStatusUrl;
147
66
  api_client_1.soosLogger.logLineSeparator();
148
- api_client_1.soosLogger.info("Uploading SBOM File");
149
- const formData = await this.getSbomAsFormData(filePath);
150
- const uploadManifestFilesResponse = await soosAnalysisApiClient.uploadManifestFiles({
67
+ api_client_1.soosLogger.info("Uploading SBOM File...");
68
+ const formData = await soosAnalysisService.getAnalysisFilesAsFormData([sbomFilePath], this.args.sbomPath);
69
+ const manifestUploadResponse = await soosAnalysisService.analysisApiClient.uploadManifestFiles({
151
70
  clientId: this.args.clientId,
152
71
  projectHash,
153
72
  branchHash,
154
73
  analysisId,
155
74
  manifestFiles: formData,
75
+ hasMoreThanMaximumManifests: false,
156
76
  });
157
- api_client_1.soosLogger.info(` SBOM Files: \n`, ` ${uploadManifestFilesResponse.message} \n`, uploadManifestFilesResponse.manifests
158
- ?.map((m) => ` ${m.name}: ${m.statusMessage}`)
159
- .join("\n"));
77
+ api_client_1.soosLogger.info(` SBOM Files: \n`, ` ${manifestUploadResponse.message} \n`, manifestUploadResponse.manifests?.map((m) => ` ${m.name}: ${m.statusMessage}`).join("\n"));
160
78
  api_client_1.soosLogger.logLineSeparator();
161
- api_client_1.soosLogger.info("Starting SBOM Analysis scan");
162
- await soosAnalysisApiClient.startScan({
79
+ await soosAnalysisService.startScan({
163
80
  clientId: this.args.clientId,
164
81
  projectHash,
165
- analysisId: analysisId,
82
+ analysisId,
83
+ scanType,
84
+ scanUrl: result.scanUrl,
85
+ });
86
+ const scanStatus = await soosAnalysisService.waitForScanToFinish({
87
+ scanStatusUrl: result.scanStatusUrl,
88
+ scanUrl: result.scanUrl,
89
+ scanType,
166
90
  });
167
- api_client_1.soosLogger.info(`Analysis scan started successfully, to see the results visit: ${result.scanUrl}`);
91
+ const exitCode = (0, utilities_1.getAnalysisExitCode)(scanStatus, this.args.integrationName, this.args.onFailure);
92
+ api_client_1.soosLogger.debug(`Exiting with code ${exitCode}`);
93
+ (0, process_1.exit)(exitCode);
168
94
  }
169
95
  catch (error) {
170
- if (projectHash && branchHash && analysisId)
171
- await soosAnalysisApiClient.updateScanStatus({
96
+ if (projectHash && branchHash && analysisId) {
97
+ await soosAnalysisService.updateScanStatus({
172
98
  clientId: this.args.clientId,
173
99
  projectHash,
174
100
  branchHash,
175
- scanType: api_client_1.ScanType.SBOM,
176
- scanId: analysisId,
101
+ scanType,
102
+ analysisId: analysisId,
177
103
  status: api_client_1.ScanStatus.Error,
178
- message: `Error while performing scan.`,
104
+ message: "Error while performing scan.",
105
+ scanStatusUrl,
179
106
  });
107
+ }
180
108
  api_client_1.soosLogger.error(error);
181
109
  (0, process_1.exit)(1);
182
110
  }
183
111
  }
184
- async getSbomAsFormData(filePath) {
185
- try {
186
- const fileReadStream = FileSystem.createReadStream(filePath, {
187
- encoding: api_client_1.SOOS_CONSTANTS.FileUploads.Encoding,
188
- });
189
- const formData = new form_data_1.default();
190
- formData.append("file", fileReadStream);
191
- return formData;
192
- }
193
- catch (error) {
194
- api_client_1.soosLogger.error(`Error on getSbomAsFormData: ${error}`);
195
- throw error;
196
- }
197
- }
198
112
  async findSbomFilePath() {
199
113
  const sbomPathStat = await FileSystem.statSync(this.args.sbomPath);
200
114
  if (sbomPathStat.isDirectory()) {
201
115
  const files = await FileSystem.promises.readdir(this.args.sbomPath);
202
- const sbomFile = files.find((file) => constants_1.CONSTANTS.SBOM.FILE_REGEX.test(file));
116
+ const sbomFile = files.find((file) => constants_1.SOOS_SBOM_CONSTANTS.FileRegex.test(file));
203
117
  if (!sbomFile) {
204
118
  throw new Error("No SBOM file found in the directory.");
205
119
  }
206
120
  return Path.join(this.args.sbomPath, sbomFile);
207
121
  }
208
- if (!constants_1.CONSTANTS.SBOM.FILE_REGEX.test(this.args.sbomPath)) {
122
+ if (!constants_1.SOOS_SBOM_CONSTANTS.FileRegex.test(this.args.sbomPath)) {
209
123
  throw new Error("The file does not match the required SBOM pattern.");
210
124
  }
211
125
  return this.args.sbomPath;
@@ -219,8 +133,6 @@ class SOOSSBOMAnalysis {
219
133
  api_client_1.soosLogger.setVerbose(args.verbose);
220
134
  api_client_1.soosLogger.info("Configuration read");
221
135
  api_client_1.soosLogger.verboseDebug(JSON.stringify((0, utilities_1.obfuscateProperties)(args, ["apiKey"]), null, 2));
222
- (0, utilities_1.ensureNonEmptyValue)(args.clientId, "clientId");
223
- (0, utilities_1.ensureNonEmptyValue)(args.apiKey, "apiKey");
224
136
  api_client_1.soosLogger.logLineSeparator();
225
137
  const soosSBOMAnalysis = new SOOSSBOMAnalysis(args);
226
138
  await soosSBOMAnalysis.runAnalysis();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@soos-io/soos-sbom",
3
- "version": "0.1.3",
4
- "description": "SOOS wrapper script to upload sboms.",
3
+ "version": "0.1.7",
4
+ "description": "SOOS wrapper script to upload SBOMs.",
5
5
  "main": "bin/index.js",
6
6
  "scripts": {
7
7
  "setup:install": "npm install",
@@ -15,21 +15,29 @@
15
15
  "typecheck": "tsc --noEmit",
16
16
  "check": "npm run format && npm run typecheck && npm outdated"
17
17
  },
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/soos-io/soos-sbom.git"
21
+ },
22
+ "author": "SOOS",
23
+ "license": "MIT",
24
+ "bugs": {
25
+ "url": "https://github.com/soos-io/soos-sbom/issues"
26
+ },
27
+ "homepage": "https://github.com/soos-io/soos-sbom#readme",
18
28
  "dependencies": {
19
- "@soos-io/api-client": "0.1.8",
29
+ "@soos-io/api-client": "0.2.18",
20
30
  "argparse": "^2.0.1",
31
+ "glob": "^10.3.10",
21
32
  "tslib": "^2.6.2"
22
33
  },
23
34
  "devDependencies": {
24
- "@types/argparse": "^2.0.11",
25
- "@types/glob": "^8.1.0",
26
- "@types/node": "^20.6.3",
27
- "prettier": "^2.8.8",
28
- "typescript": "^5.2.2"
35
+ "@types/argparse": "^2.0.14",
36
+ "@types/node": "^20.10.5",
37
+ "prettier": "^3.1.1",
38
+ "typescript": "^5.3.3"
29
39
  },
30
40
  "bin": {
31
41
  "soos-sbom": "bin/index.js"
32
- },
33
- "author": "SOOS",
34
- "license": "MIT"
42
+ }
35
43
  }
@@ -1,10 +0,0 @@
1
- export declare const CONSTANTS: {
2
- SBOM: {
3
- FILE_REGEX: RegExp;
4
- };
5
- SOOS: {
6
- API_KEY_ENV_VAR: string;
7
- CLIENT_ID_ENV_VAR: string;
8
- DEFAULT_INTEGRATION_TYPE: string;
9
- };
10
- };
@@ -1,13 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CONSTANTS = void 0;
4
- exports.CONSTANTS = {
5
- SBOM: {
6
- FILE_REGEX: /\.(cdx|spdx)\.json$/,
7
- },
8
- SOOS: {
9
- API_KEY_ENV_VAR: "SOOS_API_KEY",
10
- CLIENT_ID_ENV_VAR: "SOOS_CLIENT_ID",
11
- DEFAULT_INTEGRATION_TYPE: "Script",
12
- },
13
- };