@cyclonedx/cdxgen 9.11.6 → 10.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 +6 -5
- package/analyzer.js +1 -0
- package/bin/cdxgen.js +140 -142
- package/bin/repl.js +5 -5
- package/bin/verify.js +1 -1
- package/binary.js +19 -13
- package/cbomutils.js +39 -0
- package/cbomutils.test.js +8 -0
- package/data/README.md +1 -0
- package/data/cbomosdb-queries.json +68 -0
- package/data/cosdb-queries.json +1 -1
- package/display.js +2 -2
- package/docker.js +4 -2
- package/envcontext.js +302 -0
- package/envcontext.test.js +31 -0
- package/evinser.js +9 -8
- package/index.js +229 -486
- package/package.json +7 -8
- package/protobom.test.js +1 -1
- package/server.js +2 -1
- package/utils.js +212 -159
- package/utils.test.js +37 -32
- package/validator.js +5 -4
package/README.md
CHANGED
|
@@ -130,8 +130,7 @@ import { createBom, submitBom } from "npm:@cyclonedx/cdxgen@^9.0.1";
|
|
|
130
130
|
```text
|
|
131
131
|
$ cdxgen -h
|
|
132
132
|
Options:
|
|
133
|
-
-o, --output Output file
|
|
134
|
-
json
|
|
133
|
+
-o, --output Output file. Default bom.json
|
|
135
134
|
-t, --type Project type
|
|
136
135
|
-r, --recurse Recurse mode suitable for mono-repos. Defaults to
|
|
137
136
|
true. Pass --no-recurse to disable.
|
|
@@ -188,6 +187,8 @@ Options:
|
|
|
188
187
|
c.
|
|
189
188
|
[choices: "appsec", "research", "operational", "threat-modeling", "license-com
|
|
190
189
|
pliance", "generic"] [default: "generic"]
|
|
190
|
+
--include-formulation Generate formulation section using git metadata.
|
|
191
|
+
[boolean] [default: false]
|
|
191
192
|
--auto-compositions Automatically set compositions when the BOM was f
|
|
192
193
|
iltered. Defaults to true
|
|
193
194
|
[boolean] [default: true]
|
|
@@ -383,7 +384,7 @@ cdxgen can retain the dependency tree under the `dependencies` attribute for a s
|
|
|
383
384
|
| SBOM_SIGN_ALGORITHM | Signature algorithm. Some valid values are RS256, RS384, RS512, PS256, PS384, PS512, ES256 etc |
|
|
384
385
|
| SBOM_SIGN_PRIVATE_KEY | Private key to use for signing |
|
|
385
386
|
| SBOM_SIGN_PUBLIC_KEY | Optional. Public key to include in the SBOM signature |
|
|
386
|
-
| CDX_MAVEN_PLUGIN | CycloneDX Maven plugin to use. Default "org.cyclonedx:cyclonedx-maven-plugin:2.7.
|
|
387
|
+
| CDX_MAVEN_PLUGIN | CycloneDX Maven plugin to use. Default "org.cyclonedx:cyclonedx-maven-plugin:2.7.11" |
|
|
387
388
|
| CDX_MAVEN_GOAL | CycloneDX Maven plugin goal to use. Default makeAggregateBom. Other options: makeBom, makePackageBom |
|
|
388
389
|
| CDX_MAVEN_INCLUDE_TEST_SCOPE | Whether test scoped dependencies should be included from Maven projects, Default: true |
|
|
389
390
|
| ASTGEN_IGNORE_DIRS | Comma separated list of directories to ignore while analyzing using babel. The environment variable is also used by atom and astgen. |
|
|
@@ -510,7 +511,7 @@ Permission to modify and redistribute is granted under the terms of the Apache 2
|
|
|
510
511
|
|
|
511
512
|
## Integration as library
|
|
512
513
|
|
|
513
|
-
cdxgen is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) and could be imported and used with both deno and Node.js >=
|
|
514
|
+
cdxgen is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) and could be imported and used with both deno and Node.js >= 20
|
|
514
515
|
|
|
515
516
|
Minimal example:
|
|
516
517
|
|
|
@@ -522,7 +523,7 @@ See the [Deno Readme](./contrib/deno/README.md) for detailed instructions.
|
|
|
522
523
|
|
|
523
524
|
```javascript
|
|
524
525
|
import { createBom, submitBom } from "@cyclonedx/cdxgen";
|
|
525
|
-
// bomNSData would contain bomJson
|
|
526
|
+
// bomNSData would contain bomJson
|
|
526
527
|
const bomNSData = await createBom(filePath, options);
|
|
527
528
|
// Submission to dependency track server
|
|
528
529
|
const dbody = await submitBom(args, bomNSData.bomJson);
|
package/analyzer.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { parse } from "@babel/parser";
|
|
2
2
|
import traverse from "@babel/traverse";
|
|
3
3
|
import { join } from "node:path";
|
|
4
|
+
import process from "node:process";
|
|
4
5
|
import { readdirSync, lstatSync, readFileSync } from "node:fs";
|
|
5
6
|
import { basename, resolve, isAbsolute, relative } from "node:path";
|
|
6
7
|
|
package/bin/cdxgen.js
CHANGED
|
@@ -7,7 +7,7 @@ import { tmpdir } from "node:os";
|
|
|
7
7
|
import { basename, dirname, join, resolve } from "node:path";
|
|
8
8
|
import jws from "jws";
|
|
9
9
|
import crypto from "node:crypto";
|
|
10
|
-
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { fileURLToPath, URL } from "node:url";
|
|
11
11
|
import globalAgent from "global-agent";
|
|
12
12
|
import process from "node:process";
|
|
13
13
|
import {
|
|
@@ -56,7 +56,7 @@ const args = yargs(hideBin(process.argv))
|
|
|
56
56
|
.env("CDXGEN")
|
|
57
57
|
.option("output", {
|
|
58
58
|
alias: "o",
|
|
59
|
-
description: "Output file
|
|
59
|
+
description: "Output file. Default bom.json",
|
|
60
60
|
default: "bom.json"
|
|
61
61
|
})
|
|
62
62
|
.option("evinse-output", {
|
|
@@ -231,6 +231,17 @@ const args = yargs(hideBin(process.argv))
|
|
|
231
231
|
default: "bom.cdx",
|
|
232
232
|
hidden: true
|
|
233
233
|
})
|
|
234
|
+
.option("include-formulation", {
|
|
235
|
+
type: "boolean",
|
|
236
|
+
default: false,
|
|
237
|
+
description: "Generate formulation section using git metadata."
|
|
238
|
+
})
|
|
239
|
+
.option("include-crypto", {
|
|
240
|
+
type: "boolean",
|
|
241
|
+
default: false,
|
|
242
|
+
description: "Include crypto libraries found under formulation.",
|
|
243
|
+
hidden: true
|
|
244
|
+
})
|
|
234
245
|
.completion("completion", "Generate bash/zsh completion")
|
|
235
246
|
.array("filter")
|
|
236
247
|
.array("only")
|
|
@@ -295,7 +306,7 @@ const applyProfile = (options) => {
|
|
|
295
306
|
case "research":
|
|
296
307
|
options.deep = true;
|
|
297
308
|
options.evidence = true;
|
|
298
|
-
process.env.CDX_MAVEN_INCLUDE_TEST_SCOPE = true;
|
|
309
|
+
process.env.CDX_MAVEN_INCLUDE_TEST_SCOPE = "true";
|
|
299
310
|
process.env.ASTGEN_IGNORE_DIRS = "";
|
|
300
311
|
process.env.ASTGEN_IGNORE_FILE_PATTERN = "";
|
|
301
312
|
break;
|
|
@@ -305,7 +316,7 @@ const applyProfile = (options) => {
|
|
|
305
316
|
case "threat-modeling": // unused
|
|
306
317
|
break;
|
|
307
318
|
case "license-compliance":
|
|
308
|
-
process.env.FETCH_LICENSE = true;
|
|
319
|
+
process.env.FETCH_LICENSE = "true";
|
|
309
320
|
break;
|
|
310
321
|
default:
|
|
311
322
|
break;
|
|
@@ -382,162 +393,149 @@ const checkPermissions = (filePath) => {
|
|
|
382
393
|
options.output &&
|
|
383
394
|
(typeof options.output === "string" || options.output instanceof String)
|
|
384
395
|
) {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
396
|
+
const jsonFile = options.output;
|
|
397
|
+
// Create bom json file
|
|
398
|
+
if (bomNSData.bomJson) {
|
|
399
|
+
let jsonPayload = undefined;
|
|
400
|
+
if (
|
|
401
|
+
typeof bomNSData.bomJson === "string" ||
|
|
402
|
+
bomNSData.bomJson instanceof String
|
|
403
|
+
) {
|
|
404
|
+
fs.writeFileSync(jsonFile, bomNSData.bomJson);
|
|
405
|
+
jsonPayload = bomNSData.bomJson;
|
|
406
|
+
} else {
|
|
407
|
+
jsonPayload = JSON.stringify(bomNSData.bomJson, null, 2);
|
|
408
|
+
fs.writeFileSync(jsonFile, jsonPayload);
|
|
409
|
+
}
|
|
410
|
+
if (
|
|
411
|
+
jsonPayload &&
|
|
412
|
+
(options.generateKeyAndSign ||
|
|
413
|
+
(process.env.SBOM_SIGN_ALGORITHM &&
|
|
414
|
+
process.env.SBOM_SIGN_ALGORITHM !== "none" &&
|
|
415
|
+
process.env.SBOM_SIGN_PRIVATE_KEY &&
|
|
416
|
+
fs.existsSync(process.env.SBOM_SIGN_PRIVATE_KEY)))
|
|
417
|
+
) {
|
|
418
|
+
let alg = process.env.SBOM_SIGN_ALGORITHM || "RS512";
|
|
419
|
+
if (alg.includes("none")) {
|
|
420
|
+
alg = "RS512";
|
|
401
421
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
privateKeyFile
|
|
441
|
-
);
|
|
442
|
-
privateKeyToUse = privateKey;
|
|
422
|
+
let privateKeyToUse = undefined;
|
|
423
|
+
let jwkPublicKey = undefined;
|
|
424
|
+
let publicKeyFile = undefined;
|
|
425
|
+
if (options.generateKeyAndSign) {
|
|
426
|
+
const jdirName = dirname(jsonFile);
|
|
427
|
+
publicKeyFile = join(jdirName, "public.key");
|
|
428
|
+
const privateKeyFile = join(jdirName, "private.key");
|
|
429
|
+
const { privateKey, publicKey } = crypto.generateKeyPairSync("rsa", {
|
|
430
|
+
modulusLength: 4096,
|
|
431
|
+
publicKeyEncoding: {
|
|
432
|
+
type: "spki",
|
|
433
|
+
format: "pem"
|
|
434
|
+
},
|
|
435
|
+
privateKeyEncoding: {
|
|
436
|
+
type: "pkcs8",
|
|
437
|
+
format: "pem"
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
fs.writeFileSync(publicKeyFile, publicKey);
|
|
441
|
+
fs.writeFileSync(privateKeyFile, privateKey);
|
|
442
|
+
console.log(
|
|
443
|
+
"Created public/private key pairs for testing purposes",
|
|
444
|
+
publicKeyFile,
|
|
445
|
+
privateKeyFile
|
|
446
|
+
);
|
|
447
|
+
privateKeyToUse = privateKey;
|
|
448
|
+
jwkPublicKey = crypto
|
|
449
|
+
.createPublicKey(publicKey)
|
|
450
|
+
.export({ format: "jwk" });
|
|
451
|
+
} else {
|
|
452
|
+
privateKeyToUse = fs.readFileSync(
|
|
453
|
+
process.env.SBOM_SIGN_PRIVATE_KEY,
|
|
454
|
+
"utf8"
|
|
455
|
+
);
|
|
456
|
+
if (
|
|
457
|
+
process.env.SBOM_SIGN_PUBLIC_KEY &&
|
|
458
|
+
fs.existsSync(process.env.SBOM_SIGN_PUBLIC_KEY)
|
|
459
|
+
) {
|
|
443
460
|
jwkPublicKey = crypto
|
|
444
|
-
.createPublicKey(
|
|
461
|
+
.createPublicKey(
|
|
462
|
+
fs.readFileSync(process.env.SBOM_SIGN_PUBLIC_KEY, "utf8")
|
|
463
|
+
)
|
|
445
464
|
.export({ format: "jwk" });
|
|
446
|
-
} else {
|
|
447
|
-
privateKeyToUse = fs.readFileSync(
|
|
448
|
-
process.env.SBOM_SIGN_PRIVATE_KEY,
|
|
449
|
-
"utf8"
|
|
450
|
-
);
|
|
451
|
-
if (
|
|
452
|
-
process.env.SBOM_SIGN_PUBLIC_KEY &&
|
|
453
|
-
fs.existsSync(process.env.SBOM_SIGN_PUBLIC_KEY)
|
|
454
|
-
) {
|
|
455
|
-
jwkPublicKey = crypto
|
|
456
|
-
.createPublicKey(
|
|
457
|
-
fs.readFileSync(process.env.SBOM_SIGN_PUBLIC_KEY, "utf8")
|
|
458
|
-
)
|
|
459
|
-
.export({ format: "jwk" });
|
|
460
|
-
}
|
|
461
465
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
payload: comp,
|
|
470
|
-
privateKey: privateKeyToUse
|
|
471
|
-
});
|
|
472
|
-
const compSignatureBlock = {
|
|
473
|
-
algorithm: alg,
|
|
474
|
-
value: compSignature
|
|
475
|
-
};
|
|
476
|
-
if (jwkPublicKey) {
|
|
477
|
-
compSignatureBlock.publicKey = jwkPublicKey;
|
|
478
|
-
}
|
|
479
|
-
comp.signature = compSignatureBlock;
|
|
480
|
-
}
|
|
481
|
-
const signature = jws.sign({
|
|
466
|
+
}
|
|
467
|
+
try {
|
|
468
|
+
// Sign the individual components
|
|
469
|
+
// Let's leave the services unsigned for now since it might require additional cleansing
|
|
470
|
+
const bomJsonUnsignedObj = JSON.parse(jsonPayload);
|
|
471
|
+
for (const comp of bomJsonUnsignedObj.components) {
|
|
472
|
+
const compSignature = jws.sign({
|
|
482
473
|
header: { alg },
|
|
483
|
-
payload:
|
|
474
|
+
payload: comp,
|
|
484
475
|
privateKey: privateKeyToUse
|
|
485
476
|
});
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
477
|
+
const compSignatureBlock = {
|
|
478
|
+
algorithm: alg,
|
|
479
|
+
value: compSignature
|
|
480
|
+
};
|
|
481
|
+
if (jwkPublicKey) {
|
|
482
|
+
compSignatureBlock.publicKey = jwkPublicKey;
|
|
483
|
+
}
|
|
484
|
+
comp.signature = compSignatureBlock;
|
|
485
|
+
}
|
|
486
|
+
const signature = jws.sign({
|
|
487
|
+
header: { alg },
|
|
488
|
+
payload: JSON.stringify(bomJsonUnsignedObj, null, 2),
|
|
489
|
+
privateKey: privateKeyToUse
|
|
490
|
+
});
|
|
491
|
+
if (signature) {
|
|
492
|
+
const signatureBlock = {
|
|
493
|
+
algorithm: alg,
|
|
494
|
+
value: signature
|
|
495
|
+
};
|
|
496
|
+
if (jwkPublicKey) {
|
|
497
|
+
signatureBlock.publicKey = jwkPublicKey;
|
|
498
|
+
}
|
|
499
|
+
bomJsonUnsignedObj.signature = signatureBlock;
|
|
500
|
+
fs.writeFileSync(
|
|
501
|
+
jsonFile,
|
|
502
|
+
JSON.stringify(bomJsonUnsignedObj, null, 2)
|
|
503
|
+
);
|
|
504
|
+
if (publicKeyFile) {
|
|
505
|
+
// Verifying this signature
|
|
506
|
+
const signatureVerification = jws.verify(
|
|
507
|
+
signature,
|
|
508
|
+
alg,
|
|
509
|
+
fs.readFileSync(publicKeyFile, "utf8")
|
|
498
510
|
);
|
|
499
|
-
if (
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
alg
|
|
504
|
-
|
|
511
|
+
if (signatureVerification) {
|
|
512
|
+
console.log(
|
|
513
|
+
"SBOM signature is verifiable with the public key and the algorithm",
|
|
514
|
+
publicKeyFile,
|
|
515
|
+
alg
|
|
516
|
+
);
|
|
517
|
+
} else {
|
|
518
|
+
console.log("SBOM signature verification was unsuccessful");
|
|
519
|
+
console.log(
|
|
520
|
+
"Check if the public key was exported in PEM format"
|
|
505
521
|
);
|
|
506
|
-
if (signatureVerification) {
|
|
507
|
-
console.log(
|
|
508
|
-
"SBOM signature is verifiable with the public key and the algorithm",
|
|
509
|
-
publicKeyFile,
|
|
510
|
-
alg
|
|
511
|
-
);
|
|
512
|
-
} else {
|
|
513
|
-
console.log("SBOM signature verification was unsuccessful");
|
|
514
|
-
console.log(
|
|
515
|
-
"Check if the public key was exported in PEM format"
|
|
516
|
-
);
|
|
517
|
-
}
|
|
518
522
|
}
|
|
519
523
|
}
|
|
520
|
-
} catch (ex) {
|
|
521
|
-
console.log("SBOM signing was unsuccessful", ex);
|
|
522
|
-
console.log("Check if the private key was exported in PEM format");
|
|
523
524
|
}
|
|
525
|
+
} catch (ex) {
|
|
526
|
+
console.log("SBOM signing was unsuccessful", ex);
|
|
527
|
+
console.log("Check if the private key was exported in PEM format");
|
|
524
528
|
}
|
|
525
529
|
}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
if (bomNSData.nsMapping && Object.keys(bomNSData.nsMapping).length) {
|
|
532
|
-
const nsFile = jsonFile + ".map";
|
|
533
|
-
fs.writeFileSync(nsFile, JSON.stringify(bomNSData.nsMapping));
|
|
534
|
-
}
|
|
530
|
+
}
|
|
531
|
+
// bom ns mapping
|
|
532
|
+
if (bomNSData.nsMapping && Object.keys(bomNSData.nsMapping).length) {
|
|
533
|
+
const nsFile = jsonFile + ".map";
|
|
534
|
+
fs.writeFileSync(nsFile, JSON.stringify(bomNSData.nsMapping));
|
|
535
535
|
}
|
|
536
536
|
} else if (!options.print) {
|
|
537
537
|
if (bomNSData.bomJson) {
|
|
538
538
|
console.log(JSON.stringify(bomNSData.bomJson, null, 2));
|
|
539
|
-
} else if (bomNSData.bomXml) {
|
|
540
|
-
console.log(Buffer.from(bomNSData.bomXml).toString());
|
|
541
539
|
} else {
|
|
542
540
|
console.log("Unable to produce BOM for", filePath);
|
|
543
541
|
console.log("Try running the command with -t <type> or -r argument");
|
package/bin/repl.js
CHANGED
|
@@ -155,7 +155,7 @@ cdxgenRepl.defineCommand("search", {
|
|
|
155
155
|
searchStr = `components[group ~> /${searchStr}/i or name ~> /${searchStr}/i or description ~> /${searchStr}/i or publisher ~> /${searchStr}/i or purl ~> /${searchStr}/i]`;
|
|
156
156
|
}
|
|
157
157
|
const expression = jsonata(searchStr);
|
|
158
|
-
|
|
158
|
+
const components = await expression.evaluate(sbom);
|
|
159
159
|
if (!components) {
|
|
160
160
|
console.log("No results found!");
|
|
161
161
|
} else {
|
|
@@ -187,7 +187,7 @@ cdxgenRepl.defineCommand("sort", {
|
|
|
187
187
|
sortStr = `components^(${sortStr})`;
|
|
188
188
|
}
|
|
189
189
|
const expression = jsonata(sortStr);
|
|
190
|
-
|
|
190
|
+
const components = await expression.evaluate(sbom);
|
|
191
191
|
if (!components) {
|
|
192
192
|
console.log("No results found!");
|
|
193
193
|
} else {
|
|
@@ -354,14 +354,14 @@ cdxgenRepl.defineCommand("occurrences", {
|
|
|
354
354
|
});
|
|
355
355
|
cdxgenRepl.defineCommand("discord", {
|
|
356
356
|
help: "display the discord invite link for support",
|
|
357
|
-
|
|
357
|
+
action() {
|
|
358
358
|
console.log("Head to https://discord.gg/pF4BYWEJcS for support");
|
|
359
359
|
this.displayPrompt();
|
|
360
360
|
}
|
|
361
361
|
});
|
|
362
362
|
cdxgenRepl.defineCommand("sponsor", {
|
|
363
363
|
help: "display the sponsorship link to fund this project",
|
|
364
|
-
|
|
364
|
+
action() {
|
|
365
365
|
console.log(
|
|
366
366
|
"Hey, thanks a lot for considering! https://github.com/sponsors/prabhu"
|
|
367
367
|
);
|
|
@@ -434,7 +434,7 @@ cdxgenRepl.defineCommand("osinfocategories", {
|
|
|
434
434
|
const expression = jsonata(
|
|
435
435
|
'$distinct(components.properties[name="cdx:osquery:category"].value)'
|
|
436
436
|
);
|
|
437
|
-
|
|
437
|
+
const catgories = await expression.evaluate(sbom);
|
|
438
438
|
if (!catgories) {
|
|
439
439
|
console.log(
|
|
440
440
|
"Unable to retrieve the os info categories. Only OBOMs generated by cdxgen are supported by this tool."
|
package/bin/verify.js
CHANGED
|
@@ -5,7 +5,7 @@ import { hideBin } from "yargs/helpers";
|
|
|
5
5
|
import fs from "node:fs";
|
|
6
6
|
import jws from "jws";
|
|
7
7
|
import process from "node:process";
|
|
8
|
-
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { fileURLToPath, URL } from "node:url";
|
|
9
9
|
import { dirname, join } from "node:path";
|
|
10
10
|
|
|
11
11
|
let url = import.meta.url;
|
package/binary.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { platform as _platform, arch as _arch, tmpdir, homedir } from "node:os";
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
import { Buffer } from "node:buffer";
|
|
2
4
|
import {
|
|
3
5
|
existsSync,
|
|
4
6
|
mkdirSync,
|
|
@@ -11,7 +13,7 @@ import { spawnSync } from "node:child_process";
|
|
|
11
13
|
import { PackageURL } from "packageurl-js";
|
|
12
14
|
import { DEBUG_MODE, TIMEOUT_MS, findLicenseId } from "./utils.js";
|
|
13
15
|
|
|
14
|
-
import { fileURLToPath } from "node:url";
|
|
16
|
+
import { fileURLToPath, URL } from "node:url";
|
|
15
17
|
|
|
16
18
|
let url = import.meta.url;
|
|
17
19
|
if (!url.startsWith("file://")) {
|
|
@@ -108,16 +110,18 @@ if (!CDXGEN_PLUGINS_DIR) {
|
|
|
108
110
|
}
|
|
109
111
|
}
|
|
110
112
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
113
|
+
if (globalNodePath) {
|
|
114
|
+
const globalPlugins = join(
|
|
115
|
+
globalNodePath,
|
|
116
|
+
"@cyclonedx",
|
|
117
|
+
"cdxgen-plugins-bin" + pluginsBinSuffix,
|
|
118
|
+
"plugins"
|
|
119
|
+
);
|
|
120
|
+
if (existsSync(globalPlugins)) {
|
|
121
|
+
CDXGEN_PLUGINS_DIR = globalPlugins;
|
|
122
|
+
if (DEBUG_MODE) {
|
|
123
|
+
console.log("Found global plugins", CDXGEN_PLUGINS_DIR);
|
|
124
|
+
}
|
|
121
125
|
}
|
|
122
126
|
}
|
|
123
127
|
}
|
|
@@ -398,7 +402,7 @@ export const getOSPackages = (src) => {
|
|
|
398
402
|
}
|
|
399
403
|
let distro_codename = osReleaseData["VERSION_CODENAME"] || "";
|
|
400
404
|
let distro_id = osReleaseData["ID"] || "";
|
|
401
|
-
|
|
405
|
+
const distro_id_like = osReleaseData["ID_LIKE"] || "";
|
|
402
406
|
let purl_type = "rpm";
|
|
403
407
|
switch (distro_id) {
|
|
404
408
|
case "debian":
|
|
@@ -464,6 +468,7 @@ export const getOSPackages = (src) => {
|
|
|
464
468
|
comp.group = group;
|
|
465
469
|
purlObj.namespace = group;
|
|
466
470
|
}
|
|
471
|
+
purlObj.qualifiers = purlObj.qualifiers || {};
|
|
467
472
|
if (distro_id && distro_id.length) {
|
|
468
473
|
purlObj.qualifiers["distro"] = distro_id;
|
|
469
474
|
}
|
|
@@ -647,6 +652,7 @@ const retrieveDependencies = (tmpDependencies, origBomRef, comp) => {
|
|
|
647
652
|
const tmpPurl = PackageURL.fromString(d.replace("none", compPurl.type));
|
|
648
653
|
tmpPurl.type = compPurl.type;
|
|
649
654
|
tmpPurl.namespace = compPurl.namespace;
|
|
655
|
+
tmpPurl.qualifiers = tmpPurl.qualifiers || {};
|
|
650
656
|
if (compPurl.qualifiers) {
|
|
651
657
|
if (compPurl.qualifiers.distro_name) {
|
|
652
658
|
tmpPurl.qualifiers.distro_name = compPurl.qualifiers.distro_name;
|
|
@@ -682,7 +688,7 @@ export const executeOsQuery = (query) => {
|
|
|
682
688
|
timeout: 60 * 1000
|
|
683
689
|
});
|
|
684
690
|
if (result.status !== 0 || result.error) {
|
|
685
|
-
if (DEBUG_MODE && result.
|
|
691
|
+
if (DEBUG_MODE && result.stderr) {
|
|
686
692
|
console.error(result.stdout, result.stderr);
|
|
687
693
|
}
|
|
688
694
|
}
|
package/cbomutils.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { convertOSQueryResults, dirNameStr } from "./utils.js";
|
|
3
|
+
import { executeOsQuery } from "./binary.js";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
const cbomosDbQueries = JSON.parse(
|
|
6
|
+
readFileSync(join(dirNameStr, "data", "cbomosdb-queries.json"), "utf-8")
|
|
7
|
+
);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Method to collect crypto and ssl libraries from the OS.
|
|
11
|
+
*
|
|
12
|
+
* @param {Object} options
|
|
13
|
+
* @returns osPkgsList Array of OS crypto packages
|
|
14
|
+
*/
|
|
15
|
+
export const collectOSCryptoLibs = (options) => {
|
|
16
|
+
let osPkgsList = [];
|
|
17
|
+
for (const queryCategory of Object.keys(cbomosDbQueries)) {
|
|
18
|
+
const queryObj = cbomosDbQueries[queryCategory];
|
|
19
|
+
const results = executeOsQuery(queryObj.query);
|
|
20
|
+
const dlist = convertOSQueryResults(
|
|
21
|
+
queryCategory,
|
|
22
|
+
queryObj,
|
|
23
|
+
results,
|
|
24
|
+
false
|
|
25
|
+
);
|
|
26
|
+
if (dlist && dlist.length) {
|
|
27
|
+
osPkgsList = osPkgsList.concat(dlist);
|
|
28
|
+
// Should we downgrade from cryptographic-asset to data for < 1.6 spec
|
|
29
|
+
if (options && options.specVersion && options.specVersion < 1.6) {
|
|
30
|
+
for (const apkg of osPkgsList) {
|
|
31
|
+
if (apkg.type === "cryptographic-asset") {
|
|
32
|
+
apkg.type = "data";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return osPkgsList;
|
|
39
|
+
};
|
package/data/README.md
CHANGED
|
@@ -7,6 +7,7 @@ Contents of data directory and their purpose.
|
|
|
7
7
|
| bom-1.4.schema.json | CycloneDX 1.4 jsonschema for validation |
|
|
8
8
|
| bom-1.5.schema.json | CycloneDX 1.5 jsonschema for validation |
|
|
9
9
|
| cosdb-queries.json | osquery useful for identifying OS packages for C |
|
|
10
|
+
| cbomosdb-queries.json | osquery for identifying ssl packages in OS |
|
|
10
11
|
| jsf-0.82.schema.json | jsonschema for validation |
|
|
11
12
|
| known-licenses.json | Hard coded list to correct any license id. Not maintained. |
|
|
12
13
|
| lic-mapping.json | Hard coded list to match a license id based on name |
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"deb_packages": {
|
|
3
|
+
"query": "select * from deb_packages where name like '%ssl%' OR name like '%crypto%';",
|
|
4
|
+
"description": "Retrieves all the installed DEB packages in the target Linux system.",
|
|
5
|
+
"purlType": "deb",
|
|
6
|
+
"componentType": "library"
|
|
7
|
+
},
|
|
8
|
+
"portage_packages": {
|
|
9
|
+
"query": "select * from portage_packages where package like '%ssl%' OR package like '%crypto%';",
|
|
10
|
+
"description": "Retrieves all the installed packages on the target Linux system.",
|
|
11
|
+
"purlType": "ebuild",
|
|
12
|
+
"componentType": "library"
|
|
13
|
+
},
|
|
14
|
+
"rpm_packages": {
|
|
15
|
+
"query": "select * from rpm_packages where name like '%ssl%' OR name like '%crypto%';",
|
|
16
|
+
"description": "Retrieves all the installed RPM packages in the target Linux system.",
|
|
17
|
+
"purlType": "rpm",
|
|
18
|
+
"componentType": "library"
|
|
19
|
+
},
|
|
20
|
+
"python_packages": {
|
|
21
|
+
"query": "select * from python_packages where name like '%ssl%' OR name like '%crypto%';",
|
|
22
|
+
"description": "Python packages installed on system.",
|
|
23
|
+
"purlType": "pypi",
|
|
24
|
+
"componentType": "library"
|
|
25
|
+
},
|
|
26
|
+
"windows_programs": {
|
|
27
|
+
"query": "select * from programs where name like '%ssl%' OR name like '%crypto%';",
|
|
28
|
+
"description": "Retrieves the list of products as they are installed by Windows Installer in the target Windows system.",
|
|
29
|
+
"purlType": "swid",
|
|
30
|
+
"componentType": "application"
|
|
31
|
+
},
|
|
32
|
+
"homebrew_packages": {
|
|
33
|
+
"query": "SELECT * FROM homebrew_packages where name like '%ssl%' OR name like '%crypto%';",
|
|
34
|
+
"description": "List all homebrew_packages.",
|
|
35
|
+
"purlType": "swid",
|
|
36
|
+
"componentType": "library"
|
|
37
|
+
},
|
|
38
|
+
"authorized_keys": {
|
|
39
|
+
"query": "SELECT * FROM authorized_keys;",
|
|
40
|
+
"description": "List of authorized keys.",
|
|
41
|
+
"purlType": "generic",
|
|
42
|
+
"componentType": "cryptographic-asset"
|
|
43
|
+
},
|
|
44
|
+
"certificates": {
|
|
45
|
+
"query": "SELECT * FROM certificates;",
|
|
46
|
+
"description": "List of certificates.",
|
|
47
|
+
"purlType": "generic",
|
|
48
|
+
"componentType": "cryptographic-asset"
|
|
49
|
+
},
|
|
50
|
+
"keychain_items": {
|
|
51
|
+
"query": "SELECT * FROM keychain_items;",
|
|
52
|
+
"description": "Generic details about keychain items.",
|
|
53
|
+
"purlType": "generic",
|
|
54
|
+
"componentType": "data"
|
|
55
|
+
},
|
|
56
|
+
"kernel_keys": {
|
|
57
|
+
"query": "SELECT * FROM kernel_keys;",
|
|
58
|
+
"description": "List of security data, authentication keys and encryption keys.",
|
|
59
|
+
"purlType": "generic",
|
|
60
|
+
"componentType": "cryptographic-asset"
|
|
61
|
+
},
|
|
62
|
+
"yum_sources": {
|
|
63
|
+
"query": "SELECT * FROM yum_sources;",
|
|
64
|
+
"description": "Current list of Yum repositories or software channels.",
|
|
65
|
+
"purlType": "generic",
|
|
66
|
+
"componentType": "data"
|
|
67
|
+
}
|
|
68
|
+
}
|