@soos-io/soos-sbom 0.1.0-pre.1
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 +8 -0
- package/bin/index.d.ts +2 -0
- package/bin/index.js +248 -0
- package/bin/utils/constants.d.ts +9 -0
- package/bin/utils/constants.js +12 -0
- package/package.json +36 -0
package/README.md
ADDED
package/bin/index.d.ts
ADDED
package/bin/index.js
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const api_client_1 = require("@soos-io/api-client");
|
|
6
|
+
const utilities_1 = require("@soos-io/api-client/dist/utilities");
|
|
7
|
+
const argparse_1 = require("argparse");
|
|
8
|
+
const FileSystem = tslib_1.__importStar(require("fs"));
|
|
9
|
+
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
|
+
const process_1 = require("process");
|
|
13
|
+
const SOOSAnalysisApiClient_1 = tslib_1.__importDefault(require("@soos-io/api-client/dist/api/SOOSAnalysisApiClient"));
|
|
14
|
+
class SOOSSBOMAnalysis {
|
|
15
|
+
constructor(args) {
|
|
16
|
+
this.args = args;
|
|
17
|
+
}
|
|
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
|
+
});
|
|
30
|
+
parser.add_argument("--appVersion", {
|
|
31
|
+
help: "App Version - Intended for internal use only.",
|
|
32
|
+
required: false,
|
|
33
|
+
});
|
|
34
|
+
parser.add_argument("--branchName", {
|
|
35
|
+
help: "The name of the branch from the SCM System.",
|
|
36
|
+
default: null,
|
|
37
|
+
required: false,
|
|
38
|
+
});
|
|
39
|
+
parser.add_argument("--branchURI", {
|
|
40
|
+
help: "The URI to the branch from the SCM System.",
|
|
41
|
+
default: null,
|
|
42
|
+
required: false,
|
|
43
|
+
});
|
|
44
|
+
parser.add_argument("--buildURI", {
|
|
45
|
+
help: "URI to CI build info.",
|
|
46
|
+
default: null,
|
|
47
|
+
required: false,
|
|
48
|
+
});
|
|
49
|
+
parser.add_argument("--buildVersion", {
|
|
50
|
+
help: "Version of application build artifacts.",
|
|
51
|
+
default: null,
|
|
52
|
+
required: false,
|
|
53
|
+
});
|
|
54
|
+
parser.add_argument("--clientId", {
|
|
55
|
+
help: "SOOS Client ID - get yours from https://app.soos.io/integrate/sbom",
|
|
56
|
+
default: (0, utilities_1.getEnvVariable)(constants_1.CONSTANTS.SOOS.CLIENT_ID_ENV_VAR),
|
|
57
|
+
required: false,
|
|
58
|
+
});
|
|
59
|
+
parser.add_argument("--commitHash", {
|
|
60
|
+
help: "The commit hash value from the SCM System.",
|
|
61
|
+
default: null,
|
|
62
|
+
required: false,
|
|
63
|
+
});
|
|
64
|
+
parser.add_argument("--integrationName", {
|
|
65
|
+
help: "Integration Name - Intended for internal use only.",
|
|
66
|
+
required: false,
|
|
67
|
+
});
|
|
68
|
+
parser.add_argument("--integrationType", {
|
|
69
|
+
help: "Integration Type - Intended for internal use only.",
|
|
70
|
+
required: false,
|
|
71
|
+
});
|
|
72
|
+
parser.add_argument("--logLevel", {
|
|
73
|
+
help: "Minimum level to show logs: PASS, IGNORE, INFO, WARN or FAIL.",
|
|
74
|
+
default: api_client_1.LogLevel.INFO,
|
|
75
|
+
required: false,
|
|
76
|
+
type: (value) => {
|
|
77
|
+
const upperCaseValue = value.toUpperCase();
|
|
78
|
+
if (upperCaseValue in api_client_1.LogLevel) {
|
|
79
|
+
return api_client_1.LogLevel[upperCaseValue];
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
throw new Error(`Invalid log level: ${value}`);
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
parser.add_argument("--onFailure", {
|
|
87
|
+
help: "Action to perform when the scan fails. Options: fail_the_build, continue_on_failure.",
|
|
88
|
+
default: "continue_on_failure",
|
|
89
|
+
required: false,
|
|
90
|
+
});
|
|
91
|
+
parser.add_argument("--operatingEnvironment", {
|
|
92
|
+
help: "Set Operating environment for information purposes only.",
|
|
93
|
+
default: null,
|
|
94
|
+
required: false,
|
|
95
|
+
});
|
|
96
|
+
parser.add_argument("--otherOptions", {
|
|
97
|
+
help: "Other Options to pass to syft.",
|
|
98
|
+
default: null,
|
|
99
|
+
required: false,
|
|
100
|
+
});
|
|
101
|
+
parser.add_argument("--projectName", {
|
|
102
|
+
help: "Project Name - this is what will be displayed in the SOOS app.",
|
|
103
|
+
required: true,
|
|
104
|
+
});
|
|
105
|
+
parser.add_argument("--scriptVersion", {
|
|
106
|
+
help: "Script Version - Intended for internal use only.",
|
|
107
|
+
required: false,
|
|
108
|
+
});
|
|
109
|
+
parser.add_argument("--verbose", {
|
|
110
|
+
help: "Enable verbose logging.",
|
|
111
|
+
action: "store_true",
|
|
112
|
+
default: false,
|
|
113
|
+
required: false,
|
|
114
|
+
});
|
|
115
|
+
parser.add_argument("sbomPath", {
|
|
116
|
+
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.",
|
|
117
|
+
});
|
|
118
|
+
api_client_1.soosLogger.info("Parsing arguments");
|
|
119
|
+
return parser.parse_args();
|
|
120
|
+
}
|
|
121
|
+
runAnalysis() {
|
|
122
|
+
var _a;
|
|
123
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
124
|
+
let projectHash;
|
|
125
|
+
let branchHash;
|
|
126
|
+
let analysisId;
|
|
127
|
+
const filePath = yield this.findSbomFilePath();
|
|
128
|
+
const soosAnalysisApiClient = new SOOSAnalysisApiClient_1.default(this.args.apiKey, this.args.apiURL);
|
|
129
|
+
try {
|
|
130
|
+
api_client_1.soosLogger.info("Starting SOOS SBOM Analysis");
|
|
131
|
+
api_client_1.soosLogger.info(`Creating scan for project '${this.args.projectName}'...`);
|
|
132
|
+
api_client_1.soosLogger.info(`Branch Name: ${this.args.branchName}`);
|
|
133
|
+
const result = yield soosAnalysisApiClient.createScan({
|
|
134
|
+
clientId: this.args.clientId,
|
|
135
|
+
projectName: this.args.projectName,
|
|
136
|
+
commitHash: this.args.commitHash,
|
|
137
|
+
branch: this.args.branchName,
|
|
138
|
+
buildVersion: this.args.buildVersion,
|
|
139
|
+
buildUri: this.args.buildUri,
|
|
140
|
+
branchUri: this.args.branchUri,
|
|
141
|
+
integrationType: this.args.integrationType,
|
|
142
|
+
operatingEnvironment: this.args.operatingEnvironment,
|
|
143
|
+
integrationName: this.args.integrationName,
|
|
144
|
+
appVersion: this.args.appVersion,
|
|
145
|
+
scriptVersion: null,
|
|
146
|
+
contributingDeveloperAudit: undefined,
|
|
147
|
+
scanType: api_client_1.ScanType.SBOM,
|
|
148
|
+
toolName: null,
|
|
149
|
+
toolVersion: null,
|
|
150
|
+
});
|
|
151
|
+
projectHash = result.projectHash;
|
|
152
|
+
branchHash = result.branchHash;
|
|
153
|
+
analysisId = result.analysisId;
|
|
154
|
+
api_client_1.soosLogger.info(`Project Hash: ${projectHash}`);
|
|
155
|
+
api_client_1.soosLogger.info(`Branch Hash: ${branchHash}`);
|
|
156
|
+
api_client_1.soosLogger.info(`Scan Id: ${analysisId}`);
|
|
157
|
+
api_client_1.soosLogger.info("Scan created successfully.");
|
|
158
|
+
api_client_1.soosLogger.logLineSeparator();
|
|
159
|
+
api_client_1.soosLogger.info("Uploading SBOM File");
|
|
160
|
+
const formData = yield this.getSbomAsFormData(filePath);
|
|
161
|
+
const uploadManifestFilesResponse = yield soosAnalysisApiClient.uploadManifestFiles({
|
|
162
|
+
clientId: this.args.clientId,
|
|
163
|
+
projectHash,
|
|
164
|
+
branchHash,
|
|
165
|
+
analysisId,
|
|
166
|
+
manifestFiles: formData,
|
|
167
|
+
});
|
|
168
|
+
api_client_1.soosLogger.info(` SBOM Files: \n`, ` ${uploadManifestFilesResponse.message} \n`, (_a = uploadManifestFilesResponse.manifests) === null || _a === void 0 ? void 0 : _a.map((m) => ` ${m.name}: ${m.statusMessage}`).join("\n"));
|
|
169
|
+
api_client_1.soosLogger.logLineSeparator();
|
|
170
|
+
api_client_1.soosLogger.info("Starting SBOM Analysis scan");
|
|
171
|
+
yield soosAnalysisApiClient.startScan({
|
|
172
|
+
clientId: this.args.clientId,
|
|
173
|
+
projectHash,
|
|
174
|
+
analysisId: analysisId,
|
|
175
|
+
});
|
|
176
|
+
api_client_1.soosLogger.info(`Analysis scan started successfully, to see the results visit: ${result.scanUrl}`);
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
if (projectHash && branchHash && analysisId)
|
|
180
|
+
yield soosAnalysisApiClient.updateScanStatus({
|
|
181
|
+
clientId: this.args.clientId,
|
|
182
|
+
projectHash,
|
|
183
|
+
branchHash,
|
|
184
|
+
scanType: api_client_1.ScanType.SBOM,
|
|
185
|
+
scanId: analysisId,
|
|
186
|
+
status: api_client_1.ScanStatus.Error,
|
|
187
|
+
message: `Error while performing scan.`,
|
|
188
|
+
});
|
|
189
|
+
api_client_1.soosLogger.error(error);
|
|
190
|
+
(0, process_1.exit)(1);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
getSbomAsFormData(filePath) {
|
|
195
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
196
|
+
try {
|
|
197
|
+
const fileReadStream = FileSystem.createReadStream(filePath, {
|
|
198
|
+
encoding: api_client_1.SOOS_CONSTANTS.FileUploads.Encoding,
|
|
199
|
+
});
|
|
200
|
+
const formData = new form_data_1.default();
|
|
201
|
+
formData.append("file", fileReadStream);
|
|
202
|
+
return formData;
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
api_client_1.soosLogger.error(`Error on getSbomAsFormData: ${error}`);
|
|
206
|
+
throw error;
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
findSbomFilePath() {
|
|
211
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
212
|
+
const sbomPathStat = yield FileSystem.statSync(this.args.sbomPath);
|
|
213
|
+
if (sbomPathStat.isDirectory()) {
|
|
214
|
+
const files = yield FileSystem.promises.readdir(this.args.sbomPath);
|
|
215
|
+
const sbomFile = files.find((file) => constants_1.CONSTANTS.SBOM.FILE_REGEX.test(file));
|
|
216
|
+
if (!sbomFile) {
|
|
217
|
+
throw new Error("No SBOM file found in the directory.");
|
|
218
|
+
}
|
|
219
|
+
return Path.join(this.args.sbomPath, sbomFile);
|
|
220
|
+
}
|
|
221
|
+
if (!constants_1.CONSTANTS.SBOM.FILE_REGEX.test(this.args.sbomPath)) {
|
|
222
|
+
throw new Error("The file does not match the required SBOM pattern.");
|
|
223
|
+
}
|
|
224
|
+
return this.args.sbomPath;
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
static createAndRun() {
|
|
228
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
229
|
+
api_client_1.soosLogger.info("Starting SOOS SBOM Analysis");
|
|
230
|
+
api_client_1.soosLogger.logLineSeparator();
|
|
231
|
+
try {
|
|
232
|
+
const args = this.parseArgs();
|
|
233
|
+
api_client_1.soosLogger.setMinLogLevel(args.logLevel);
|
|
234
|
+
api_client_1.soosLogger.setVerbose(args.verbose);
|
|
235
|
+
api_client_1.soosLogger.info("Configuration read");
|
|
236
|
+
api_client_1.soosLogger.verboseDebug(JSON.stringify((0, utilities_1.obfuscateProperties)(args, ["apiKey"]), null, 2));
|
|
237
|
+
api_client_1.soosLogger.logLineSeparator();
|
|
238
|
+
const soosSBOMAnalysis = new SOOSSBOMAnalysis(args);
|
|
239
|
+
yield soosSBOMAnalysis.runAnalysis();
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
api_client_1.soosLogger.error(`Error on createAndRun: ${error}`);
|
|
243
|
+
(0, process_1.exit)(1);
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
SOOSSBOMAnalysis.createAndRun();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CONSTANTS = void 0;
|
|
4
|
+
exports.CONSTANTS = {
|
|
5
|
+
SOOS: {
|
|
6
|
+
API_KEY_ENV_VAR: "SOOS_API_KEY",
|
|
7
|
+
CLIENT_ID_ENV_VAR: "SOOS_CLIENT_ID",
|
|
8
|
+
},
|
|
9
|
+
SBOM: {
|
|
10
|
+
FILE_REGEX: /\.(cdx|spdx)\.json$/,
|
|
11
|
+
},
|
|
12
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@soos-io/soos-sbom",
|
|
3
|
+
"version": "0.1.0-pre.1",
|
|
4
|
+
"description": "SOOS wrapper script to upload sboms.",
|
|
5
|
+
"main": "bin/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"setup:install": "npm install",
|
|
8
|
+
"setup:clean-install": "npm ci",
|
|
9
|
+
"setup:update": "npx npm-check -u",
|
|
10
|
+
"setup:clean": "npx rimraf node_modules && npx rimraf package-lock.json",
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"build:clean": "npx rimraf build",
|
|
13
|
+
"format": "prettier ./src --check",
|
|
14
|
+
"format:fix": "prettier ./src --write",
|
|
15
|
+
"typecheck": "tsc --noEmit",
|
|
16
|
+
"check": "npm run format && npm run typecheck && npm outdated"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@soos-io/api-client": "0.1.5",
|
|
20
|
+
"argparse": "^2.0.1",
|
|
21
|
+
"axios": "^0.27.2",
|
|
22
|
+
"tslib": "^2.6.2"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/argparse": "^2.0.11",
|
|
26
|
+
"@types/glob": "^8.1.0",
|
|
27
|
+
"@types/node": "^20.6.3",
|
|
28
|
+
"prettier": "^2.8.8",
|
|
29
|
+
"typescript": "^5.2.2"
|
|
30
|
+
},
|
|
31
|
+
"bin": {
|
|
32
|
+
"soos-sbom": "bin/index.js"
|
|
33
|
+
},
|
|
34
|
+
"author": "SOOS",
|
|
35
|
+
"license": "MIT"
|
|
36
|
+
}
|