@cyclonedx/cdxgen 8.6.0 → 9.0.0
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 +51 -45
- package/analyzer.js +15 -19
- package/bin/{cdxgen → cdxgen.js} +69 -23
- package/binary.js +52 -52
- package/data/pypi-pkg-aliases.json +2 -0
- package/data/python-stdlib.json +2 -1
- package/docker.js +154 -127
- package/docker.test.js +18 -14
- package/index.js +488 -381
- package/jest.config.js +6 -180
- package/package.json +15 -14
- package/server.js +12 -12
- package/utils.js +376 -384
- package/utils.test.js +362 -319
- package/.eslintrc.js +0 -15
package/README.md
CHANGED
|
@@ -8,40 +8,39 @@ When used with plugins, cdxgen could generate an SBoM for Linux docker images an
|
|
|
8
8
|
|
|
9
9
|
## Supported languages and package format
|
|
10
10
|
|
|
11
|
-
| Language/Platform
|
|
12
|
-
|
|
|
13
|
-
| node.js
|
|
14
|
-
| java
|
|
15
|
-
| php
|
|
16
|
-
| python
|
|
17
|
-
| go
|
|
18
|
-
| ruby
|
|
19
|
-
| rust
|
|
20
|
-
| .Net
|
|
21
|
-
| dart
|
|
22
|
-
| haskell
|
|
23
|
-
| elixir
|
|
24
|
-
| c/c++
|
|
25
|
-
| clojure
|
|
26
|
-
| swift
|
|
27
|
-
| docker / oci image
|
|
28
|
-
| GitHub Actions
|
|
29
|
-
| Linux
|
|
30
|
-
| Windows
|
|
31
|
-
| Jenkins Plugins
|
|
32
|
-
| Helm Charts
|
|
33
|
-
| Skaffold
|
|
34
|
-
| kustomization
|
|
35
|
-
| Tekton tasks
|
|
36
|
-
| Kubernetes
|
|
37
|
-
| Maven Cache
|
|
38
|
-
| SBT Cache
|
|
39
|
-
| Gradle Cache
|
|
40
|
-
| Helm Index
|
|
41
|
-
| Docker compose
|
|
42
|
-
| Google CloudBuild configuration
|
|
43
|
-
| OpenAPI
|
|
44
|
-
| [Privado](https://www.privado.ai?utm_source=cyclonedx) | privado.json | Data and service information will be included. Use with universal mode. |
|
|
11
|
+
| Language/Platform | Package format | Transitive dependencies |
|
|
12
|
+
| ------------------------------- | ------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
|
13
|
+
| node.js | npm-shrinkwrap.json, package-lock.json, pnpm-lock.yaml, yarn.lock, rush.js, bower.json, .min.js | Yes except .min.js |
|
|
14
|
+
| java | maven (pom.xml [1]), gradle (build.gradle, .kts), scala (sbt), bazel | Yes unless pom.xml is manually parsed due to unavailability of maven or errors |
|
|
15
|
+
| php | composer.lock | Yes |
|
|
16
|
+
| python | pyproject.toml, setup.py, requirements.txt [2], Pipfile.lock, poetry.lock, bdist_wheel, .whl, .egg-info | Yes using the automatic pip install/freeze. When disabled, only with Pipfile.lock and poetry.lock |
|
|
17
|
+
| go | binary, go.mod, go.sum, Gopkg.lock | Yes except binary |
|
|
18
|
+
| ruby | Gemfile.lock, gemspec | Only for Gemfile.lock |
|
|
19
|
+
| rust | binary, Cargo.toml, Cargo.lock | Only for Cargo.lock |
|
|
20
|
+
| .Net | .csproj, packages.config, project.assets.json [3], packages.lock.json, .nupkg | Only for project.assets.json, packages.lock.json |
|
|
21
|
+
| dart | pubspec.lock, pubspec.yaml | Only for pubspec.lock |
|
|
22
|
+
| haskell | cabal.project.freeze | Yes |
|
|
23
|
+
| elixir | mix.lock | Yes |
|
|
24
|
+
| c/c++ | conan.lock, conanfile.txt | Yes only for conan.lock |
|
|
25
|
+
| clojure | Clojure CLI (deps.edn), Leiningen (project.clj) | Yes unless the files are parsed manually due to lack of clojure cli or leiningen command |
|
|
26
|
+
| swift | Package.resolved, Package.swift (swiftpm) | Yes |
|
|
27
|
+
| docker / oci image | All supported languages. Linux OS packages with plugins [4] | Best effort based on lock files |
|
|
28
|
+
| GitHub Actions | .github/workflows/\*.yml | N/A |
|
|
29
|
+
| Linux | All supported languages. Linux OS packages with plugins [5] | Best effort based on lock files |
|
|
30
|
+
| Windows | All supported languages. OS packages with best effort [5] | Best effort based on lock files |
|
|
31
|
+
| Jenkins Plugins | .hpi files | |
|
|
32
|
+
| Helm Charts | .yaml | N/A |
|
|
33
|
+
| Skaffold | .yaml | N/A |
|
|
34
|
+
| kustomization | .yaml | N/A |
|
|
35
|
+
| Tekton tasks | .yaml | N/A |
|
|
36
|
+
| Kubernetes | .yaml | N/A |
|
|
37
|
+
| Maven Cache | $HOME/.m2/repository/\*\*/\*.jar | N/A |
|
|
38
|
+
| SBT Cache | $HOME/.ivy2/cache/\*\*/\*.jar | N/A |
|
|
39
|
+
| Gradle Cache | $HOME/caches/modules-2/files-2.1/\*\*/\*.jar | N/A |
|
|
40
|
+
| Helm Index | $HOME/.cache/helm/repository/\*\*/\*.yaml | N/A |
|
|
41
|
+
| Docker compose | docker-compose\*.yml. Images would also be scanned. | N/A |
|
|
42
|
+
| Google CloudBuild configuration | cloudbuild.yaml | N/A |
|
|
43
|
+
| OpenAPI | openapi\*.json, openapi\*.yaml | N/A |
|
|
45
44
|
|
|
46
45
|
NOTE:
|
|
47
46
|
|
|
@@ -200,17 +199,6 @@ git clone https://github.com/cyclonedx/cdxgen.git
|
|
|
200
199
|
docker compose up
|
|
201
200
|
```
|
|
202
201
|
|
|
203
|
-
## Privado.ai support
|
|
204
|
-
|
|
205
|
-
In universal mode, cdxgen can look for any [Privado](https://www.privado.ai?utm_source=cyclonedx) scan reports and enrich the SBoM with data (flow and classification), endpoints, and leakage information. Such an SBoM would help with privacy compliance and use cases.
|
|
206
|
-
|
|
207
|
-
Invoke privado scan first to generate this report followed by an invocation of cdxgen in universal mode as shown.
|
|
208
|
-
|
|
209
|
-
```shell
|
|
210
|
-
privado scan --enable-javascript <directory>
|
|
211
|
-
cdxgen -t universal <directory> -o bom.json
|
|
212
|
-
```
|
|
213
|
-
|
|
214
202
|
## War file support
|
|
215
203
|
|
|
216
204
|
cdxgen can generate a BoM file from a given war file.
|
|
@@ -362,6 +350,24 @@ Permission to modify and redistribute is granted under the terms of the Apache 2
|
|
|
362
350
|
[license]: https://github.com/cyclonedx/cdxgen/blob/master/LICENSE
|
|
363
351
|
[cyclonedx-homepage]: https://cyclonedx.org
|
|
364
352
|
|
|
353
|
+
## Integration as library
|
|
354
|
+
|
|
355
|
+
This project is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) and requires Node.js >= 16
|
|
356
|
+
|
|
357
|
+
Minimal example:
|
|
358
|
+
|
|
359
|
+
```javascript
|
|
360
|
+
import { createBom, submitBom } from "@cyclonedx/cdxgen";
|
|
361
|
+
// bomNSData would contain bomJson, bomXml
|
|
362
|
+
const bomNSData = await createBom(filePath, options);
|
|
363
|
+
// Submission to dependency track server
|
|
364
|
+
const dbody = await submitBom(args, bomNSData.bomXml);
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
## Node.js >= 20 permission model
|
|
368
|
+
|
|
369
|
+
Refer to the [permissions document](./docs/PERMISSIONS.md)
|
|
370
|
+
|
|
365
371
|
## Contributing
|
|
366
372
|
|
|
367
373
|
Follow the usual PR process but prior to raising a PR run the following commands.
|
package/analyzer.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { parse } from "@babel/parser";
|
|
2
|
+
import babelTraverse from "@babel/traverse";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { readdirSync, statSync, readFileSync } from "fs";
|
|
5
|
+
import { basename, resolve, isAbsolute } from "path";
|
|
6
6
|
|
|
7
7
|
const IGNORE_DIRS = [
|
|
8
8
|
"node_modules",
|
|
@@ -28,7 +28,7 @@ const IGNORE_FILE_PATTERN = new RegExp(
|
|
|
28
28
|
);
|
|
29
29
|
|
|
30
30
|
const getAllFiles = (dir, extn, files, result, regex) => {
|
|
31
|
-
files = files ||
|
|
31
|
+
files = files || readdirSync(dir);
|
|
32
32
|
result = result || [];
|
|
33
33
|
regex = regex || new RegExp(`\\${extn}$`);
|
|
34
34
|
|
|
@@ -37,9 +37,9 @@ const getAllFiles = (dir, extn, files, result, regex) => {
|
|
|
37
37
|
continue;
|
|
38
38
|
}
|
|
39
39
|
let file = join(dir, files[i]);
|
|
40
|
-
if (
|
|
40
|
+
if (statSync(file).isDirectory()) {
|
|
41
41
|
// Ignore directories
|
|
42
|
-
const dirName =
|
|
42
|
+
const dirName = basename(file);
|
|
43
43
|
if (
|
|
44
44
|
dirName.startsWith(".") ||
|
|
45
45
|
IGNORE_DIRS.includes(dirName.toLowerCase())
|
|
@@ -47,7 +47,7 @@ const getAllFiles = (dir, extn, files, result, regex) => {
|
|
|
47
47
|
continue;
|
|
48
48
|
}
|
|
49
49
|
try {
|
|
50
|
-
result = getAllFiles(file, extn,
|
|
50
|
+
result = getAllFiles(file, extn, readdirSync(file), result, regex);
|
|
51
51
|
} catch (error) {
|
|
52
52
|
continue;
|
|
53
53
|
}
|
|
@@ -95,11 +95,11 @@ const setFileRef = (allImports, file, pathway) => {
|
|
|
95
95
|
// replace relative imports with full path
|
|
96
96
|
let module = pathway;
|
|
97
97
|
if (/\.\//g.test(pathway) || /\.\.\//g.test(pathway)) {
|
|
98
|
-
module =
|
|
98
|
+
module = resolve(file, "..", pathway);
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
// initialise or increase reference count for file
|
|
102
|
-
if (
|
|
102
|
+
if (Object.prototype.hasOwnProperty.call(allImports, module)) {
|
|
103
103
|
allImports[module] = allImports[module] + 1;
|
|
104
104
|
} else {
|
|
105
105
|
allImports[module] = 1;
|
|
@@ -107,9 +107,9 @@ const setFileRef = (allImports, file, pathway) => {
|
|
|
107
107
|
|
|
108
108
|
// Handle module package name
|
|
109
109
|
// Eg: zone.js/dist/zone will be referred to as zone.js in package.json
|
|
110
|
-
if (!
|
|
110
|
+
if (!isAbsolute(module) && module.includes("/")) {
|
|
111
111
|
const modPkg = module.split("/")[0];
|
|
112
|
-
if (
|
|
112
|
+
if (Object.prototype.hasOwnProperty.call(allImports, modPkg)) {
|
|
113
113
|
allImports[modPkg] = allImports[modPkg] + 1;
|
|
114
114
|
} else {
|
|
115
115
|
allImports[modPkg] = 1;
|
|
@@ -122,10 +122,7 @@ const setFileRef = (allImports, file, pathway) => {
|
|
|
122
122
|
* references for any import, require or dynamic import files.
|
|
123
123
|
*/
|
|
124
124
|
const parseFileASTTree = (file, allImports) => {
|
|
125
|
-
const ast =
|
|
126
|
-
fs.readFileSync(file, "utf-8"),
|
|
127
|
-
babelParserOptions
|
|
128
|
-
);
|
|
125
|
+
const ast = parse(readFileSync(file, "utf-8"), babelParserOptions);
|
|
129
126
|
babelTraverse(ast, {
|
|
130
127
|
// Used for all ES6 import statements
|
|
131
128
|
ImportDeclaration: (path) => {
|
|
@@ -177,7 +174,7 @@ const getAllSrcJSAndTSFiles = (src) =>
|
|
|
177
174
|
/**
|
|
178
175
|
* Where Node CLI runs from.
|
|
179
176
|
*/
|
|
180
|
-
const findJSImports = async (src) => {
|
|
177
|
+
export const findJSImports = async (src) => {
|
|
181
178
|
const allImports = {};
|
|
182
179
|
const errFiles = [];
|
|
183
180
|
try {
|
|
@@ -195,4 +192,3 @@ const findJSImports = async (src) => {
|
|
|
195
192
|
return allImports;
|
|
196
193
|
}
|
|
197
194
|
};
|
|
198
|
-
exports.findJSImports = findJSImports;
|
package/bin/{cdxgen → cdxgen.js}
RENAMED
|
@@ -1,13 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
import { createBom, submitBom } from "../index.js";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
7
|
+
import jws from "jws";
|
|
8
|
+
import crypto from "crypto";
|
|
9
|
+
import { start as _serverStart } from "../server.js";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
import globalAgent from "global-agent";
|
|
12
|
+
import { table } from "table";
|
|
13
|
+
import process from "node:process";
|
|
9
14
|
|
|
10
|
-
|
|
15
|
+
let url = import.meta.url;
|
|
16
|
+
if (!url.startsWith("file://")) {
|
|
17
|
+
url = new URL(`file://${import.meta.url}`).toString();
|
|
18
|
+
}
|
|
19
|
+
const dirName = import.meta ? dirname(fileURLToPath(url)) : __dirname;
|
|
20
|
+
|
|
21
|
+
import yargs from "yargs";
|
|
22
|
+
import { hideBin } from "yargs/helpers";
|
|
23
|
+
|
|
24
|
+
const args = yargs(hideBin(process.argv))
|
|
11
25
|
.option("output", {
|
|
12
26
|
alias: "o",
|
|
13
27
|
description: "Output file for bom.xml or bom.json. Default bom.json"
|
|
@@ -99,7 +113,7 @@ const args = require("yargs")
|
|
|
99
113
|
|
|
100
114
|
if (args.version) {
|
|
101
115
|
const packageJsonAsString = fs.readFileSync(
|
|
102
|
-
|
|
116
|
+
join(dirName, "..", "package.json"),
|
|
103
117
|
"utf-8"
|
|
104
118
|
);
|
|
105
119
|
const packageJson = JSON.parse(packageJsonAsString);
|
|
@@ -113,16 +127,15 @@ if (process.env.GLOBAL_AGENT_HTTP_PROXY || process.env.HTTP_PROXY) {
|
|
|
113
127
|
if (!process.env.GLOBAL_AGENT_ENVIRONMENT_VARIABLE_NAMESPACE) {
|
|
114
128
|
process.env.GLOBAL_AGENT_ENVIRONMENT_VARIABLE_NAMESPACE = "";
|
|
115
129
|
}
|
|
116
|
-
const globalAgent = require("global-agent");
|
|
117
130
|
globalAgent.bootstrap();
|
|
118
131
|
}
|
|
119
132
|
|
|
120
133
|
let filePath = args._[0] || ".";
|
|
121
134
|
if (!args.projectName) {
|
|
122
135
|
if (filePath !== ".") {
|
|
123
|
-
args.projectName =
|
|
136
|
+
args.projectName = basename(filePath);
|
|
124
137
|
} else {
|
|
125
|
-
args.projectName =
|
|
138
|
+
args.projectName = basename(resolve(filePath));
|
|
126
139
|
}
|
|
127
140
|
}
|
|
128
141
|
|
|
@@ -150,15 +163,51 @@ let options = {
|
|
|
150
163
|
serverPort: args.serverPort
|
|
151
164
|
};
|
|
152
165
|
|
|
166
|
+
/**
|
|
167
|
+
* Check for node >= 20 permissions
|
|
168
|
+
*
|
|
169
|
+
* @param {string} filePath File path
|
|
170
|
+
* @returns
|
|
171
|
+
*/
|
|
172
|
+
const checkPermissions = (filePath) => {
|
|
173
|
+
if (!process.permission) {
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
if (!process.permission.has("fs.read", filePath)) {
|
|
177
|
+
console.log(
|
|
178
|
+
`FileSystemRead permission required. Please invoke with the argument --allow-fs-read="${resolve(
|
|
179
|
+
filePath
|
|
180
|
+
)}"`
|
|
181
|
+
);
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
if (!process.permission.has("fs.write", tmpdir())) {
|
|
185
|
+
console.log(
|
|
186
|
+
`FileSystemWrite permission required. Please invoke with the argument --allow-fs-write="${tmpdir()}"`
|
|
187
|
+
);
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
if (!process.permission.has("child")) {
|
|
191
|
+
console.log(
|
|
192
|
+
"ChildProcess permission is missing. This is required to spawn commands for some languages. Please invoke with the argument --allow-child-process"
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
return true;
|
|
196
|
+
};
|
|
197
|
+
|
|
153
198
|
/**
|
|
154
199
|
* Method to start the bom creation process
|
|
155
200
|
*/
|
|
156
201
|
(async () => {
|
|
157
202
|
// Start SBoM server
|
|
158
203
|
if (args.server) {
|
|
159
|
-
return await
|
|
204
|
+
return await _serverStart(options);
|
|
205
|
+
}
|
|
206
|
+
// Check if cdxgen has the required permissions
|
|
207
|
+
if (!checkPermissions(filePath)) {
|
|
208
|
+
return;
|
|
160
209
|
}
|
|
161
|
-
const bomNSData = (await
|
|
210
|
+
const bomNSData = (await createBom(filePath, options)) || {};
|
|
162
211
|
if (!args.output) {
|
|
163
212
|
args.output = "bom.json";
|
|
164
213
|
args.print = true;
|
|
@@ -172,7 +221,7 @@ let options = {
|
|
|
172
221
|
} else {
|
|
173
222
|
const jsonFile = args.output.replace(".xml", ".json");
|
|
174
223
|
// Create bom json file
|
|
175
|
-
if (bomNSData.bomJson) {
|
|
224
|
+
if (!args.output.endsWith(".xml") && bomNSData.bomJson) {
|
|
176
225
|
let jsonPayload = undefined;
|
|
177
226
|
if (
|
|
178
227
|
typeof bomNSData.bomJson === "string" ||
|
|
@@ -199,9 +248,9 @@ let options = {
|
|
|
199
248
|
let privateKeyToUse = undefined;
|
|
200
249
|
let jwkPublicKey = undefined;
|
|
201
250
|
if (args.generateKeyAndSign) {
|
|
202
|
-
const
|
|
203
|
-
const publicKeyFile =
|
|
204
|
-
const privateKeyFile =
|
|
251
|
+
const jdirName = dirname(jsonFile);
|
|
252
|
+
const publicKeyFile = join(jdirName, "public.key");
|
|
253
|
+
const privateKeyFile = join(jdirName, "private.key");
|
|
205
254
|
const { privateKey, publicKey } = crypto.generateKeyPairSync(
|
|
206
255
|
"rsa",
|
|
207
256
|
{
|
|
@@ -271,9 +320,8 @@ let options = {
|
|
|
271
320
|
}
|
|
272
321
|
}
|
|
273
322
|
// Create bom xml file
|
|
274
|
-
if (bomNSData.bomXml) {
|
|
275
|
-
|
|
276
|
-
fs.writeFileSync(xmlFile, bomNSData.bomXml);
|
|
323
|
+
if (args.output.endsWith(".xml") && bomNSData.bomXml) {
|
|
324
|
+
fs.writeFileSync(args.output, bomNSData.bomXml);
|
|
277
325
|
}
|
|
278
326
|
//
|
|
279
327
|
if (bomNSData.nsMapping && Object.keys(bomNSData.nsMapping).length) {
|
|
@@ -296,8 +344,7 @@ let options = {
|
|
|
296
344
|
// Automatically submit the bom data
|
|
297
345
|
if (args.serverUrl && args.serverUrl != true && args.apiKey) {
|
|
298
346
|
try {
|
|
299
|
-
|
|
300
|
-
const dbody = await bom.submitBom(args, bomNSData.bomXml);
|
|
347
|
+
const dbody = await submitBom(args, bomNSData.bomJson);
|
|
301
348
|
console.log("Response from server", dbody);
|
|
302
349
|
} catch (err) {
|
|
303
350
|
console.log(err);
|
|
@@ -305,7 +352,6 @@ let options = {
|
|
|
305
352
|
}
|
|
306
353
|
|
|
307
354
|
if (args.print && bomNSData.bomJson && bomNSData.bomJson.components) {
|
|
308
|
-
const { table } = require("table");
|
|
309
355
|
const data = [["Group", "Name", "Version", "Scope"]];
|
|
310
356
|
for (let comp of bomNSData.bomJson.components) {
|
|
311
357
|
data.push([comp.group || "", comp.name, comp.version, comp.scope || ""]);
|
package/binary.js
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { platform as _platform, arch as _arch, tmpdir } from "node:os";
|
|
2
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
3
|
+
import { join, dirname, basename } from "node:path";
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
5
|
+
import { PackageURL } from "packageurl-js";
|
|
6
|
+
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
|
|
10
|
+
let url = import.meta.url;
|
|
11
|
+
if (!url.startsWith("file://")) {
|
|
12
|
+
url = new URL(`file://${import.meta.url}`).toString();
|
|
13
|
+
}
|
|
14
|
+
const dirName = import.meta ? path.dirname(fileURLToPath(url)) : __dirname;
|
|
15
|
+
|
|
16
|
+
const isWin = _platform() === "win32";
|
|
7
17
|
|
|
8
18
|
// Debug mode flag
|
|
9
19
|
const DEBUG_MODE =
|
|
@@ -12,14 +22,14 @@ const DEBUG_MODE =
|
|
|
12
22
|
process.env.SHIFTLEFT_LOGGING_LEVEL === "debug" ||
|
|
13
23
|
process.env.NODE_ENV === "development";
|
|
14
24
|
|
|
15
|
-
let platform =
|
|
25
|
+
let platform = _platform();
|
|
16
26
|
let extn = "";
|
|
17
27
|
if (platform == "win32") {
|
|
18
28
|
platform = "windows";
|
|
19
29
|
extn = ".exe";
|
|
20
30
|
}
|
|
21
31
|
|
|
22
|
-
let arch =
|
|
32
|
+
let arch = _arch();
|
|
23
33
|
switch (arch) {
|
|
24
34
|
case "x32":
|
|
25
35
|
arch = "386";
|
|
@@ -34,26 +44,20 @@ let CDXGEN_PLUGINS_DIR = process.env.CDXGEN_PLUGINS_DIR;
|
|
|
34
44
|
// Is there a non-empty local plugins directory
|
|
35
45
|
if (
|
|
36
46
|
!CDXGEN_PLUGINS_DIR &&
|
|
37
|
-
|
|
38
|
-
|
|
47
|
+
existsSync(join(dirName, "plugins")) &&
|
|
48
|
+
existsSync(join(dirName, "plugins", "goversion"))
|
|
39
49
|
) {
|
|
40
|
-
CDXGEN_PLUGINS_DIR =
|
|
50
|
+
CDXGEN_PLUGINS_DIR = join(dirName, "plugins");
|
|
41
51
|
}
|
|
42
52
|
// Is there a non-empty local node_modules directory
|
|
43
53
|
if (
|
|
44
54
|
!CDXGEN_PLUGINS_DIR &&
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
__dirname,
|
|
48
|
-
"node_modules",
|
|
49
|
-
"@cyclonedx",
|
|
50
|
-
"cdxgen-plugins-bin",
|
|
51
|
-
"plugins"
|
|
52
|
-
)
|
|
55
|
+
existsSync(
|
|
56
|
+
join(dirName, "node_modules", "@cyclonedx", "cdxgen-plugins-bin", "plugins")
|
|
53
57
|
) &&
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
58
|
+
existsSync(
|
|
59
|
+
join(
|
|
60
|
+
dirName,
|
|
57
61
|
"node_modules",
|
|
58
62
|
"@cyclonedx",
|
|
59
63
|
"cdxgen-plugins-bin",
|
|
@@ -62,8 +66,8 @@ if (
|
|
|
62
66
|
)
|
|
63
67
|
)
|
|
64
68
|
) {
|
|
65
|
-
CDXGEN_PLUGINS_DIR =
|
|
66
|
-
|
|
69
|
+
CDXGEN_PLUGINS_DIR = join(
|
|
70
|
+
dirName,
|
|
67
71
|
"node_modules",
|
|
68
72
|
"@cyclonedx",
|
|
69
73
|
"cdxgen-plugins-bin",
|
|
@@ -88,13 +92,13 @@ if (!CDXGEN_PLUGINS_DIR) {
|
|
|
88
92
|
}
|
|
89
93
|
}
|
|
90
94
|
}
|
|
91
|
-
const globalPlugins =
|
|
95
|
+
const globalPlugins = join(
|
|
92
96
|
globalNodePath,
|
|
93
97
|
"@cyclonedx",
|
|
94
98
|
"cdxgen-plugins-bin",
|
|
95
99
|
"plugins"
|
|
96
100
|
);
|
|
97
|
-
if (
|
|
101
|
+
if (existsSync(globalPlugins)) {
|
|
98
102
|
CDXGEN_PLUGINS_DIR = globalPlugins;
|
|
99
103
|
if (DEBUG_MODE) {
|
|
100
104
|
console.log("Found global plugins", CDXGEN_PLUGINS_DIR);
|
|
@@ -111,16 +115,16 @@ if (!CDXGEN_PLUGINS_DIR) {
|
|
|
111
115
|
CDXGEN_PLUGINS_DIR = "";
|
|
112
116
|
}
|
|
113
117
|
let GOVERSION_BIN = null;
|
|
114
|
-
if (
|
|
115
|
-
GOVERSION_BIN =
|
|
118
|
+
if (existsSync(join(CDXGEN_PLUGINS_DIR, "goversion"))) {
|
|
119
|
+
GOVERSION_BIN = join(
|
|
116
120
|
CDXGEN_PLUGINS_DIR,
|
|
117
121
|
"goversion",
|
|
118
122
|
"goversion-" + platform + "-" + arch + extn
|
|
119
123
|
);
|
|
120
124
|
}
|
|
121
125
|
let TRIVY_BIN = null;
|
|
122
|
-
if (
|
|
123
|
-
TRIVY_BIN =
|
|
126
|
+
if (existsSync(join(CDXGEN_PLUGINS_DIR, "trivy"))) {
|
|
127
|
+
TRIVY_BIN = join(
|
|
124
128
|
CDXGEN_PLUGINS_DIR,
|
|
125
129
|
"trivy",
|
|
126
130
|
"trivy-cdxgen-" + platform + "-" + arch + extn
|
|
@@ -129,8 +133,8 @@ if (fs.existsSync(path.join(CDXGEN_PLUGINS_DIR, "trivy"))) {
|
|
|
129
133
|
TRIVY_BIN = process.env.TRIVY_CMD;
|
|
130
134
|
}
|
|
131
135
|
let CARGO_AUDITABLE_BIN = null;
|
|
132
|
-
if (
|
|
133
|
-
CARGO_AUDITABLE_BIN =
|
|
136
|
+
if (existsSync(join(CDXGEN_PLUGINS_DIR, "cargo-auditable"))) {
|
|
137
|
+
CARGO_AUDITABLE_BIN = join(
|
|
134
138
|
CDXGEN_PLUGINS_DIR,
|
|
135
139
|
"cargo-auditable",
|
|
136
140
|
"cargo-auditable-cdxgen-" + platform + "-" + arch + extn
|
|
@@ -139,8 +143,8 @@ if (fs.existsSync(path.join(CDXGEN_PLUGINS_DIR, "cargo-auditable"))) {
|
|
|
139
143
|
CARGO_AUDITABLE_BIN = process.env.CARGO_AUDITABLE_CMD;
|
|
140
144
|
}
|
|
141
145
|
let OSQUERY_BIN = null;
|
|
142
|
-
if (
|
|
143
|
-
OSQUERY_BIN =
|
|
146
|
+
if (existsSync(join(CDXGEN_PLUGINS_DIR, "osquery"))) {
|
|
147
|
+
OSQUERY_BIN = join(
|
|
144
148
|
CDXGEN_PLUGINS_DIR,
|
|
145
149
|
"osquery",
|
|
146
150
|
"osqueryi-" + platform + "-" + arch + extn
|
|
@@ -213,7 +217,7 @@ const OS_DISTRO_ALIAS = {
|
|
|
213
217
|
"debian-1.1": "buzz"
|
|
214
218
|
};
|
|
215
219
|
|
|
216
|
-
const getGoBuildInfo = (src) => {
|
|
220
|
+
export const getGoBuildInfo = (src) => {
|
|
217
221
|
if (GOVERSION_BIN) {
|
|
218
222
|
let result = spawnSync(GOVERSION_BIN, [src], {
|
|
219
223
|
encoding: "utf-8"
|
|
@@ -244,9 +248,8 @@ const getGoBuildInfo = (src) => {
|
|
|
244
248
|
}
|
|
245
249
|
return undefined;
|
|
246
250
|
};
|
|
247
|
-
exports.getGoBuildInfo = getGoBuildInfo;
|
|
248
251
|
|
|
249
|
-
const getCargoAuditableInfo = (src) => {
|
|
252
|
+
export const getCargoAuditableInfo = (src) => {
|
|
250
253
|
if (CARGO_AUDITABLE_BIN) {
|
|
251
254
|
let result = spawnSync(CARGO_AUDITABLE_BIN, [src], {
|
|
252
255
|
encoding: "utf-8"
|
|
@@ -266,18 +269,17 @@ const getCargoAuditableInfo = (src) => {
|
|
|
266
269
|
}
|
|
267
270
|
return undefined;
|
|
268
271
|
};
|
|
269
|
-
exports.getCargoAuditableInfo = getCargoAuditableInfo;
|
|
270
272
|
|
|
271
|
-
const getOSPackages = (src) => {
|
|
273
|
+
export const getOSPackages = (src) => {
|
|
272
274
|
const pkgList = [];
|
|
273
275
|
const allTypes = new Set();
|
|
274
276
|
if (TRIVY_BIN) {
|
|
275
277
|
let imageType = "image";
|
|
276
|
-
if (
|
|
278
|
+
if (existsSync(src)) {
|
|
277
279
|
imageType = "rootfs";
|
|
278
280
|
}
|
|
279
|
-
let tempDir =
|
|
280
|
-
const bomJsonFile =
|
|
281
|
+
let tempDir = mkdtempSync(join(tmpdir(), "trivy-cdxgen-"));
|
|
282
|
+
const bomJsonFile = join(tempDir, "trivy-bom.json");
|
|
281
283
|
const args = [
|
|
282
284
|
imageType,
|
|
283
285
|
"--skip-db-update",
|
|
@@ -305,11 +307,11 @@ const getOSPackages = (src) => {
|
|
|
305
307
|
console.error(result.stdout, result.stderr);
|
|
306
308
|
}
|
|
307
309
|
}
|
|
308
|
-
if (
|
|
310
|
+
if (existsSync(bomJsonFile)) {
|
|
309
311
|
let tmpBom = {};
|
|
310
312
|
try {
|
|
311
313
|
tmpBom = JSON.parse(
|
|
312
|
-
|
|
314
|
+
readFileSync(bomJsonFile, {
|
|
313
315
|
encoding: "utf-8"
|
|
314
316
|
})
|
|
315
317
|
);
|
|
@@ -317,12 +319,12 @@ const getOSPackages = (src) => {
|
|
|
317
319
|
// ignore errors
|
|
318
320
|
}
|
|
319
321
|
// Clean up
|
|
320
|
-
if (tempDir && tempDir.startsWith(
|
|
322
|
+
if (tempDir && tempDir.startsWith(tmpdir())) {
|
|
321
323
|
if (DEBUG_MODE) {
|
|
322
324
|
console.log(`Cleaning up ${tempDir}`);
|
|
323
325
|
}
|
|
324
|
-
if (
|
|
325
|
-
|
|
326
|
+
if (rmSync) {
|
|
327
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
326
328
|
}
|
|
327
329
|
}
|
|
328
330
|
if (tmpBom && tmpBom.components) {
|
|
@@ -347,8 +349,8 @@ const getOSPackages = (src) => {
|
|
|
347
349
|
continue;
|
|
348
350
|
}
|
|
349
351
|
// Fix the group
|
|
350
|
-
let group =
|
|
351
|
-
let name =
|
|
352
|
+
let group = dirname(comp.name);
|
|
353
|
+
let name = basename(comp.name);
|
|
352
354
|
let purlObj = undefined;
|
|
353
355
|
let distro_codename = "";
|
|
354
356
|
if (group === ".") {
|
|
@@ -483,9 +485,8 @@ const getOSPackages = (src) => {
|
|
|
483
485
|
}
|
|
484
486
|
return { osPackages: pkgList, allTypes: Array.from(allTypes) };
|
|
485
487
|
};
|
|
486
|
-
exports.getOSPackages = getOSPackages;
|
|
487
488
|
|
|
488
|
-
const executeOsQuery = (query) => {
|
|
489
|
+
export const executeOsQuery = (query) => {
|
|
489
490
|
if (OSQUERY_BIN) {
|
|
490
491
|
if (!query.endsWith(";")) {
|
|
491
492
|
query = query + ";";
|
|
@@ -515,4 +516,3 @@ const executeOsQuery = (query) => {
|
|
|
515
516
|
}
|
|
516
517
|
return undefined;
|
|
517
518
|
};
|
|
518
|
-
exports.executeOsQuery = executeOsQuery;
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
"bb_jekyll_hook": "bitbucket_jekyll_hook",
|
|
9
9
|
"banzai": "banzai_ngs",
|
|
10
10
|
"beautifulsouptests": "beautifulsoup",
|
|
11
|
+
"blackd": "black",
|
|
12
|
+
"blib2to3": "black",
|
|
11
13
|
"biosql": "biopython",
|
|
12
14
|
"buildbotstatusshields": "buildboteightstatusshields",
|
|
13
15
|
"computedattribute": "extensionclass",
|