@cyclonedx/cdxgen 12.2.1 → 12.3.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 +239 -90
- package/bin/audit.js +191 -0
- package/bin/cdxgen.js +513 -167
- package/bin/convert.js +99 -0
- package/bin/evinse.js +23 -0
- package/bin/repl.js +339 -8
- package/bin/sign.js +8 -0
- package/bin/validate.js +8 -0
- package/bin/verify.js +8 -0
- package/data/container-knowledge-index.json +125 -0
- package/data/gtfobins-index.json +6296 -0
- package/data/lolbas-index.json +150 -0
- package/data/queries-darwin.json +63 -3
- package/data/queries-win.json +45 -3
- package/data/queries.json +74 -2
- package/data/rules/chrome-extensions.yaml +240 -0
- package/data/rules/ci-permissions.yaml +478 -18
- package/data/rules/container-risk.yaml +270 -0
- package/data/rules/obom-runtime.yaml +891 -0
- package/data/rules/package-integrity.yaml +49 -0
- package/data/spdx-export.schema.json +6794 -0
- package/data/spdx-model-v3.0.1.jsonld +15999 -0
- package/lib/audit/index.js +1924 -0
- package/lib/audit/index.poku.js +1488 -0
- package/lib/audit/progress.js +137 -0
- package/lib/audit/progress.poku.js +188 -0
- package/lib/audit/reporters.js +618 -0
- package/lib/audit/scoring.js +310 -0
- package/lib/audit/scoring.poku.js +341 -0
- package/lib/audit/targets.js +260 -0
- package/lib/audit/targets.poku.js +331 -0
- package/lib/cli/index.js +154 -11
- package/lib/cli/index.poku.js +251 -0
- package/lib/helpers/analyzer.js +446 -2
- package/lib/helpers/analyzer.poku.js +72 -1
- package/lib/helpers/annotationFormatter.js +49 -0
- package/lib/helpers/annotationFormatter.poku.js +44 -0
- package/lib/helpers/bomUtils.js +36 -0
- package/lib/helpers/bomUtils.poku.js +51 -0
- package/lib/helpers/caxa.js +2 -2
- package/lib/helpers/chromextutils.js +1153 -0
- package/lib/helpers/chromextutils.poku.js +493 -0
- package/lib/helpers/ciParsers/githubActions.js +1632 -45
- package/lib/helpers/ciParsers/githubActions.poku.js +853 -1
- package/lib/helpers/containerRisk.js +186 -0
- package/lib/helpers/containerRisk.poku.js +52 -0
- package/lib/helpers/display.js +241 -59
- package/lib/helpers/display.poku.js +162 -2
- package/lib/helpers/exportUtils.js +123 -0
- package/lib/helpers/exportUtils.poku.js +60 -0
- package/lib/helpers/formulationParsers.js +69 -0
- package/lib/helpers/formulationParsers.poku.js +44 -0
- package/lib/helpers/gtfobins.js +189 -0
- package/lib/helpers/gtfobins.poku.js +49 -0
- package/lib/helpers/lolbas.js +267 -0
- package/lib/helpers/lolbas.poku.js +39 -0
- package/lib/helpers/osqueryTransform.js +84 -0
- package/lib/helpers/osqueryTransform.poku.js +49 -0
- package/lib/helpers/provenanceUtils.js +193 -0
- package/lib/helpers/provenanceUtils.poku.js +145 -0
- package/lib/helpers/pylockutils.js +281 -0
- package/lib/helpers/pylockutils.poku.js +48 -0
- package/lib/helpers/registryProvenance.js +793 -0
- package/lib/helpers/registryProvenance.poku.js +452 -0
- package/lib/helpers/source.js +1267 -0
- package/lib/helpers/source.poku.js +771 -0
- package/lib/helpers/spdxUtils.js +97 -0
- package/lib/helpers/spdxUtils.poku.js +70 -0
- package/lib/helpers/unicodeScan.js +147 -0
- package/lib/helpers/unicodeScan.poku.js +45 -0
- package/lib/helpers/utils.js +700 -128
- package/lib/helpers/utils.poku.js +877 -80
- package/lib/managers/binary.js +29 -5
- package/lib/managers/docker.js +179 -52
- package/lib/managers/docker.poku.js +327 -28
- package/lib/managers/oci.js +107 -23
- package/lib/managers/oci.poku.js +132 -0
- package/lib/server/openapi.yaml +17 -0
- package/lib/server/server.js +225 -336
- package/lib/server/server.poku.js +16 -10
- package/lib/stages/postgen/annotator.js +7 -0
- package/lib/stages/postgen/annotator.poku.js +40 -0
- package/lib/stages/postgen/auditBom.js +19 -3
- package/lib/stages/postgen/auditBom.poku.js +1729 -67
- package/lib/stages/postgen/postgen.js +40 -0
- package/lib/stages/postgen/postgen.poku.js +47 -0
- package/lib/stages/postgen/ruleEngine.js +80 -2
- package/lib/stages/postgen/spdxConverter.js +796 -0
- package/lib/stages/postgen/spdxConverter.poku.js +341 -0
- package/lib/validator/bomValidator.js +232 -0
- package/lib/validator/bomValidator.poku.js +70 -0
- package/lib/validator/complianceRules.js +70 -7
- package/lib/validator/complianceRules.poku.js +30 -0
- package/lib/validator/reporters/annotations.js +2 -2
- package/lib/validator/reporters/console.js +11 -0
- package/lib/validator/reporters.poku.js +13 -0
- package/package.json +10 -7
- package/types/bin/audit.d.ts +3 -0
- package/types/bin/audit.d.ts.map +1 -0
- package/types/bin/convert.d.ts +3 -0
- package/types/bin/convert.d.ts.map +1 -0
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts +115 -0
- package/types/lib/audit/index.d.ts.map +1 -0
- package/types/lib/audit/progress.d.ts +27 -0
- package/types/lib/audit/progress.d.ts.map +1 -0
- package/types/lib/audit/reporters.d.ts +35 -0
- package/types/lib/audit/reporters.d.ts.map +1 -0
- package/types/lib/audit/scoring.d.ts +35 -0
- package/types/lib/audit/scoring.d.ts.map +1 -0
- package/types/lib/audit/targets.d.ts +63 -0
- package/types/lib/audit/targets.d.ts.map +1 -0
- package/types/lib/cli/index.d.ts +8 -0
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/helpers/analyzer.d.ts +13 -0
- package/types/lib/helpers/analyzer.d.ts.map +1 -1
- package/types/lib/helpers/annotationFormatter.d.ts +23 -0
- package/types/lib/helpers/annotationFormatter.d.ts.map +1 -0
- package/types/lib/helpers/bomUtils.d.ts +5 -0
- package/types/lib/helpers/bomUtils.d.ts.map +1 -0
- package/types/lib/helpers/chromextutils.d.ts +97 -0
- package/types/lib/helpers/chromextutils.d.ts.map +1 -0
- package/types/lib/helpers/ciParsers/githubActions.d.ts +3 -8
- package/types/lib/helpers/ciParsers/githubActions.d.ts.map +1 -1
- package/types/lib/helpers/containerRisk.d.ts +17 -0
- package/types/lib/helpers/containerRisk.d.ts.map +1 -0
- package/types/lib/helpers/display.d.ts +4 -1
- package/types/lib/helpers/display.d.ts.map +1 -1
- package/types/lib/helpers/exportUtils.d.ts +40 -0
- package/types/lib/helpers/exportUtils.d.ts.map +1 -0
- package/types/lib/helpers/formulationParsers.d.ts.map +1 -1
- package/types/lib/helpers/gtfobins.d.ts +17 -0
- package/types/lib/helpers/gtfobins.d.ts.map +1 -0
- package/types/lib/helpers/lolbas.d.ts +16 -0
- package/types/lib/helpers/lolbas.d.ts.map +1 -0
- package/types/lib/helpers/osqueryTransform.d.ts +7 -0
- package/types/lib/helpers/osqueryTransform.d.ts.map +1 -0
- package/types/lib/helpers/provenanceUtils.d.ts +90 -0
- package/types/lib/helpers/provenanceUtils.d.ts.map +1 -0
- package/types/lib/helpers/pylockutils.d.ts +51 -0
- package/types/lib/helpers/pylockutils.d.ts.map +1 -0
- package/types/lib/helpers/registryProvenance.d.ts +17 -0
- package/types/lib/helpers/registryProvenance.d.ts.map +1 -0
- package/types/lib/helpers/source.d.ts +141 -0
- package/types/lib/helpers/source.d.ts.map +1 -0
- package/types/lib/helpers/spdxUtils.d.ts +2 -0
- package/types/lib/helpers/spdxUtils.d.ts.map +1 -0
- package/types/lib/helpers/unicodeScan.d.ts +46 -0
- package/types/lib/helpers/unicodeScan.d.ts.map +1 -0
- package/types/lib/helpers/utils.d.ts +29 -11
- package/types/lib/helpers/utils.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/server/server.d.ts +0 -36
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
- package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/postgen/spdxConverter.d.ts +11 -0
- package/types/lib/stages/postgen/spdxConverter.d.ts.map +1 -0
- package/types/lib/validator/bomValidator.d.ts +1 -0
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/complianceRules.d.ts.map +1 -1
- package/types/lib/validator/reporters/console.d.ts.map +1 -1
- package/types/bin/dependencies.d.ts +0 -3
- package/types/bin/dependencies.d.ts.map +0 -1
- package/types/bin/licenses.d.ts +0 -3
- package/types/bin/licenses.d.ts.map +0 -1
package/lib/cli/index.js
CHANGED
|
@@ -24,9 +24,16 @@ import { parse as loadYaml } from "yaml";
|
|
|
24
24
|
|
|
25
25
|
import { findJSImportsExports } from "../helpers/analyzer.js";
|
|
26
26
|
import { parseCaxaMetadata } from "../helpers/caxa.js";
|
|
27
|
+
import {
|
|
28
|
+
CHROME_EXTENSION_PURL_TYPE,
|
|
29
|
+
collectChromeExtensionsFromPath,
|
|
30
|
+
collectInstalledChromeExtensions,
|
|
31
|
+
discoverChromiumExtensionDirs,
|
|
32
|
+
} from "../helpers/chromextutils.js";
|
|
27
33
|
import { mergeDependencies, trimComponents } from "../helpers/depsUtils.js";
|
|
28
34
|
import { GIT_COMMAND } from "../helpers/envcontext.js";
|
|
29
35
|
import { thoughtLog } from "../helpers/logger.js";
|
|
36
|
+
import { isPyLockFile } from "../helpers/pylockutils.js";
|
|
30
37
|
import {
|
|
31
38
|
buildDependencyTrackBomPayload,
|
|
32
39
|
getDependencyTrackBomUrl,
|
|
@@ -3019,7 +3026,7 @@ export async function createNodejsBom(path, options) {
|
|
|
3019
3026
|
if (!wpkgJsonFiles?.length) {
|
|
3020
3027
|
if (!workspaceWarningShown) {
|
|
3021
3028
|
workspaceWarningShown = true;
|
|
3022
|
-
console.
|
|
3029
|
+
console.warn(
|
|
3023
3030
|
`Unable to find any package.json files belonging to the workspace '${awp}' referred in ${f}. To improve SBOM precision, run cdxgen from the directory containing the complete source code.`,
|
|
3024
3031
|
);
|
|
3025
3032
|
}
|
|
@@ -3109,7 +3116,7 @@ export async function createNodejsBom(path, options) {
|
|
|
3109
3116
|
}
|
|
3110
3117
|
if (!Object.keys(parentComponent).length) {
|
|
3111
3118
|
if (safeExistsSync(packageJsonF)) {
|
|
3112
|
-
const pcs = await parsePkgJson(packageJsonF, true);
|
|
3119
|
+
const pcs = await parsePkgJson(packageJsonF, true, true);
|
|
3113
3120
|
if (pcs.length && Object.keys(pcs[0]).length) {
|
|
3114
3121
|
parentComponent = { ...pcs[0] };
|
|
3115
3122
|
parentComponent.type = "application";
|
|
@@ -3195,7 +3202,7 @@ export async function createNodejsBom(path, options) {
|
|
|
3195
3202
|
const basePath = dirname(f);
|
|
3196
3203
|
const packageJsonF = join(basePath, "package.json");
|
|
3197
3204
|
if (safeExistsSync(packageJsonF)) {
|
|
3198
|
-
const pcs = await parsePkgJson(packageJsonF, true);
|
|
3205
|
+
const pcs = await parsePkgJson(packageJsonF, true, true);
|
|
3199
3206
|
if (pcs.length && Object.keys(pcs[0]).length) {
|
|
3200
3207
|
tmpParentComponent = { ...pcs[0] };
|
|
3201
3208
|
tmpParentComponent.type = "application";
|
|
@@ -3365,7 +3372,7 @@ export async function createNodejsBom(path, options) {
|
|
|
3365
3372
|
if (!wpkgJsonFiles?.length) {
|
|
3366
3373
|
if (!workspaceWarningShown) {
|
|
3367
3374
|
workspaceWarningShown = true;
|
|
3368
|
-
console.
|
|
3375
|
+
console.warn(
|
|
3369
3376
|
`Unable to find any package.json files belonging to the workspace '${awp}' referred in ${packageJsonFile}. To improve SBOM precision, run cdxgen from the directory containing the complete source code.`,
|
|
3370
3377
|
);
|
|
3371
3378
|
}
|
|
@@ -3419,7 +3426,7 @@ export async function createNodejsBom(path, options) {
|
|
|
3419
3426
|
// Determine the parent component
|
|
3420
3427
|
const packageJsonF = join(basePath, "package.json");
|
|
3421
3428
|
if (safeExistsSync(packageJsonF)) {
|
|
3422
|
-
const pcs = await parsePkgJson(packageJsonF, true);
|
|
3429
|
+
const pcs = await parsePkgJson(packageJsonF, true, true);
|
|
3423
3430
|
if (pcs.length && Object.keys(pcs[0]).length) {
|
|
3424
3431
|
const tmpParentComponent = { ...pcs[0] };
|
|
3425
3432
|
tmpParentComponent.type = "application";
|
|
@@ -3528,7 +3535,7 @@ export async function createNodejsBom(path, options) {
|
|
|
3528
3535
|
}
|
|
3529
3536
|
if (!parentComponent || !Object.keys(parentComponent).length) {
|
|
3530
3537
|
if (safeExistsSync(join(path, "package.json"))) {
|
|
3531
|
-
const pcs = await parsePkgJson(join(path, "package.json"), true);
|
|
3538
|
+
const pcs = await parsePkgJson(join(path, "package.json"), true, true);
|
|
3532
3539
|
if (pcs.length && Object.keys(pcs[0]).length) {
|
|
3533
3540
|
parentComponent = { ...pcs[0] };
|
|
3534
3541
|
parentComponent.type = "application";
|
|
@@ -3796,6 +3803,14 @@ export async function createPythonBom(path, options) {
|
|
|
3796
3803
|
if (uvLockFiles?.length) {
|
|
3797
3804
|
poetryFiles = poetryFiles.concat(uvLockFiles);
|
|
3798
3805
|
}
|
|
3806
|
+
const pyLockFiles = getAllFiles(
|
|
3807
|
+
path,
|
|
3808
|
+
`${options.multiProject ? "**/" : ""}pylock*.toml`,
|
|
3809
|
+
options,
|
|
3810
|
+
)?.filter((f) => isPyLockFile(f));
|
|
3811
|
+
if (pyLockFiles?.length) {
|
|
3812
|
+
poetryFiles = poetryFiles.concat(pyLockFiles);
|
|
3813
|
+
}
|
|
3799
3814
|
let reqFiles = getAllFiles(
|
|
3800
3815
|
path,
|
|
3801
3816
|
`${options.multiProject ? "**/" : ""}*requirements*.txt`,
|
|
@@ -3861,7 +3876,9 @@ export async function createPythonBom(path, options) {
|
|
|
3861
3876
|
}
|
|
3862
3877
|
// When we identify uv lock files, do not parse requirements files
|
|
3863
3878
|
const requirementsMode =
|
|
3864
|
-
(reqFiles?.length || reqDirFiles?.length) &&
|
|
3879
|
+
(reqFiles?.length || reqDirFiles?.length) &&
|
|
3880
|
+
!uvLockFiles.length &&
|
|
3881
|
+
!pyLockFiles?.length;
|
|
3865
3882
|
const poetryMode = poetryFiles?.length;
|
|
3866
3883
|
|
|
3867
3884
|
// TODO: Support for nested directories
|
|
@@ -3880,6 +3897,12 @@ export async function createPythonBom(path, options) {
|
|
|
3880
3897
|
if (retMap?.workspaceWarningShown) {
|
|
3881
3898
|
options.failOnError && process.exit(1);
|
|
3882
3899
|
}
|
|
3900
|
+
if (retMap?.pyLockProperties?.length) {
|
|
3901
|
+
parentComponent.properties = parentComponent.properties || [];
|
|
3902
|
+
parentComponent.properties = parentComponent.properties.concat(
|
|
3903
|
+
retMap.pyLockProperties,
|
|
3904
|
+
);
|
|
3905
|
+
}
|
|
3883
3906
|
if (retMap.pkgList?.length) {
|
|
3884
3907
|
pkgList = pkgList.concat(retMap.pkgList);
|
|
3885
3908
|
pkgList = trimComponents(pkgList);
|
|
@@ -3899,7 +3922,11 @@ export async function createPythonBom(path, options) {
|
|
|
3899
3922
|
parentComponent,
|
|
3900
3923
|
);
|
|
3901
3924
|
}
|
|
3902
|
-
if (
|
|
3925
|
+
if (
|
|
3926
|
+
(options.deep || !dependencies.length) &&
|
|
3927
|
+
!f.endsWith("uv.lock") &&
|
|
3928
|
+
!isPyLockFile(f)
|
|
3929
|
+
) {
|
|
3903
3930
|
if (options.installDeps) {
|
|
3904
3931
|
retMap = await getPipFrozenTree(
|
|
3905
3932
|
basePath,
|
|
@@ -5692,15 +5719,27 @@ export async function createCocoaBom(path, options) {
|
|
|
5692
5719
|
for (const podFile of cocoaFiles) {
|
|
5693
5720
|
const projectPath = dirname(podFile);
|
|
5694
5721
|
const lockFile = `${podFile}.lock`;
|
|
5695
|
-
|
|
5722
|
+
let missingLockWarningShown = false;
|
|
5723
|
+
if (!safeExistsSync(lockFile)) {
|
|
5696
5724
|
if (options.installDeps) {
|
|
5697
5725
|
executePodCommand(["install"], projectPath, options);
|
|
5698
5726
|
} else {
|
|
5699
5727
|
console.log(
|
|
5700
5728
|
"No 'Podfile.lock' found and '--no-install-deps' is set -- A Podfile.lock is needed to parse dependencies!",
|
|
5701
5729
|
);
|
|
5730
|
+
missingLockWarningShown = true;
|
|
5702
5731
|
options.failOnError && process.exit(1);
|
|
5703
5732
|
}
|
|
5733
|
+
} else if (options.deep && options.installDeps) {
|
|
5734
|
+
executePodCommand(["install"], projectPath, options);
|
|
5735
|
+
}
|
|
5736
|
+
if (!safeExistsSync(lockFile)) {
|
|
5737
|
+
if (!missingLockWarningShown) {
|
|
5738
|
+
console.log(
|
|
5739
|
+
`No 'Podfile.lock' found for ${projectPath}. Skipping CocoaPods dependency parsing for this project.`,
|
|
5740
|
+
);
|
|
5741
|
+
}
|
|
5742
|
+
continue;
|
|
5704
5743
|
}
|
|
5705
5744
|
const parentComponent = await buildObjectForCocoaPod(
|
|
5706
5745
|
{
|
|
@@ -7288,6 +7327,61 @@ export async function createVscodeExtensionBom(path, options) {
|
|
|
7288
7327
|
});
|
|
7289
7328
|
}
|
|
7290
7329
|
|
|
7330
|
+
/**
|
|
7331
|
+
* Function to create BOM for installed Chrome and Chromium-based browser extensions.
|
|
7332
|
+
*
|
|
7333
|
+
* @param {string} path to the project path or a directly provided extension path
|
|
7334
|
+
* @param {Object} options Parse options from the cli
|
|
7335
|
+
* @returns {Promise<Object>} Promise resolving to BOM object
|
|
7336
|
+
*/
|
|
7337
|
+
export async function createChromeExtensionBom(path, options) {
|
|
7338
|
+
let dependencies = [];
|
|
7339
|
+
let sourcePaths = [];
|
|
7340
|
+
const directResult = collectChromeExtensionsFromPath(path);
|
|
7341
|
+
const chromeDirs = discoverChromiumExtensionDirs();
|
|
7342
|
+
let pkgList = directResult.components || [];
|
|
7343
|
+
if (directResult.extensionDirs?.length) {
|
|
7344
|
+
sourcePaths = directResult.extensionDirs.slice();
|
|
7345
|
+
for (const extDir of directResult.extensionDirs) {
|
|
7346
|
+
const deepResult = await analyzeExtensionDir(extDir, options);
|
|
7347
|
+
if (deepResult.pkgList.length) {
|
|
7348
|
+
pkgList = pkgList.concat(deepResult.pkgList);
|
|
7349
|
+
}
|
|
7350
|
+
if (deepResult.dependencies.length) {
|
|
7351
|
+
dependencies = mergeDependencies(dependencies, deepResult.dependencies);
|
|
7352
|
+
}
|
|
7353
|
+
}
|
|
7354
|
+
}
|
|
7355
|
+
if (pkgList.length && DEBUG_MODE) {
|
|
7356
|
+
thoughtLog(
|
|
7357
|
+
`Found ${pkgList.length} component(s) from direct Chrome extension path scan`,
|
|
7358
|
+
);
|
|
7359
|
+
}
|
|
7360
|
+
if (chromeDirs.length) {
|
|
7361
|
+
if (DEBUG_MODE) {
|
|
7362
|
+
thoughtLog(
|
|
7363
|
+
`Discovered Chromium extension directories: ${chromeDirs.map((d) => `${d.browser} (${d.channel}): ${d.dir}`).join(", ")}`,
|
|
7364
|
+
);
|
|
7365
|
+
}
|
|
7366
|
+
if (!pkgList.length) {
|
|
7367
|
+
pkgList = collectInstalledChromeExtensions(chromeDirs);
|
|
7368
|
+
sourcePaths = chromeDirs.map((d) => d.dir);
|
|
7369
|
+
}
|
|
7370
|
+
if (DEBUG_MODE && pkgList.length && !directResult.components?.length) {
|
|
7371
|
+
thoughtLog(
|
|
7372
|
+
`Found ${pkgList.length} Chrome/Chromium extension(s) from ${chromeDirs.length} browser location(s)`,
|
|
7373
|
+
);
|
|
7374
|
+
}
|
|
7375
|
+
}
|
|
7376
|
+
pkgList = trimComponents(pkgList);
|
|
7377
|
+
return buildBomNSData(options, pkgList, CHROME_EXTENSION_PURL_TYPE, {
|
|
7378
|
+
src: path,
|
|
7379
|
+
filename: sourcePaths.join(", "),
|
|
7380
|
+
nsMapping: {},
|
|
7381
|
+
dependencies,
|
|
7382
|
+
});
|
|
7383
|
+
}
|
|
7384
|
+
|
|
7291
7385
|
/**
|
|
7292
7386
|
* Analyze an extracted extension directory for bundled dependencies.
|
|
7293
7387
|
* Looks for npm lock files, node_modules, package.json files, minified JS,
|
|
@@ -8208,6 +8302,46 @@ export async function createMultiXBom(pathList, options) {
|
|
|
8208
8302
|
}
|
|
8209
8303
|
}
|
|
8210
8304
|
}
|
|
8305
|
+
if (hasAnyProjectType(["chrome-extension"], options)) {
|
|
8306
|
+
let isExplicitChromeExtensionPath = false;
|
|
8307
|
+
if (safeExistsSync(path)) {
|
|
8308
|
+
if (basename(path) === "manifest.json") {
|
|
8309
|
+
isExplicitChromeExtensionPath = true;
|
|
8310
|
+
} else {
|
|
8311
|
+
try {
|
|
8312
|
+
isExplicitChromeExtensionPath =
|
|
8313
|
+
statSync(path).isDirectory() &&
|
|
8314
|
+
safeExistsSync(join(path, "manifest.json"));
|
|
8315
|
+
} catch (_err) {
|
|
8316
|
+
isExplicitChromeExtensionPath = false;
|
|
8317
|
+
}
|
|
8318
|
+
}
|
|
8319
|
+
}
|
|
8320
|
+
if (isExplicitChromeExtensionPath || !options.__didScanChromeExtensions) {
|
|
8321
|
+
if (!isExplicitChromeExtensionPath) {
|
|
8322
|
+
options.__didScanChromeExtensions = true;
|
|
8323
|
+
}
|
|
8324
|
+
bomData = await createChromeExtensionBom(path, options);
|
|
8325
|
+
if (bomData?.bomJson?.components?.length) {
|
|
8326
|
+
if (DEBUG_MODE) {
|
|
8327
|
+
console.log(
|
|
8328
|
+
`Found ${bomData.bomJson.components.length} Chrome extension(s) on this host`,
|
|
8329
|
+
);
|
|
8330
|
+
}
|
|
8331
|
+
components = components.concat(bomData.bomJson.components);
|
|
8332
|
+
dependencies = mergeDependencies(
|
|
8333
|
+
dependencies,
|
|
8334
|
+
bomData.bomJson.dependencies,
|
|
8335
|
+
);
|
|
8336
|
+
if (
|
|
8337
|
+
bomData.parentComponent &&
|
|
8338
|
+
Object.keys(bomData.parentComponent).length
|
|
8339
|
+
) {
|
|
8340
|
+
parentSubComponents.push(bomData.parentComponent);
|
|
8341
|
+
}
|
|
8342
|
+
}
|
|
8343
|
+
}
|
|
8344
|
+
}
|
|
8211
8345
|
// Collect any crypto keys
|
|
8212
8346
|
if (options.specVersion >= 1.6 && options.includeCrypto) {
|
|
8213
8347
|
if (!hasAnyProjectType(["oci"], options, false)) {
|
|
@@ -8362,10 +8496,16 @@ export async function createXBom(path, options) {
|
|
|
8362
8496
|
// python
|
|
8363
8497
|
const pipenvMode = safeExistsSync(join(path, "Pipfile"));
|
|
8364
8498
|
const poetryMode = safeExistsSync(join(path, "poetry.lock"));
|
|
8499
|
+
const pyLockFiles = getAllFiles(
|
|
8500
|
+
path,
|
|
8501
|
+
`${options.multiProject ? "**/" : ""}pylock*.toml`,
|
|
8502
|
+
options,
|
|
8503
|
+
).filter((f) => isPyLockFile(f));
|
|
8504
|
+
const pyLockMode = pyLockFiles.length > 0;
|
|
8365
8505
|
const pyProjectMode =
|
|
8366
|
-
!poetryMode && safeExistsSync(join(path, "pyproject.toml"));
|
|
8506
|
+
!poetryMode && !pyLockMode && safeExistsSync(join(path, "pyproject.toml"));
|
|
8367
8507
|
const setupPyMode = safeExistsSync(join(path, "setup.py"));
|
|
8368
|
-
if (pipenvMode || poetryMode || pyProjectMode || setupPyMode) {
|
|
8508
|
+
if (pipenvMode || poetryMode || pyLockMode || pyProjectMode || setupPyMode) {
|
|
8369
8509
|
return await createPythonBom(path, options);
|
|
8370
8510
|
}
|
|
8371
8511
|
const reqFiles = getAllFiles(
|
|
@@ -8927,6 +9067,9 @@ export async function createBom(path, options) {
|
|
|
8927
9067
|
if (PROJECT_TYPE_ALIASES["vscode-extension"].includes(projectType[0])) {
|
|
8928
9068
|
return await createVscodeExtensionBom(path, options);
|
|
8929
9069
|
}
|
|
9070
|
+
if (PROJECT_TYPE_ALIASES["chrome-extension"].includes(projectType[0])) {
|
|
9071
|
+
return await createChromeExtensionBom(path, options);
|
|
9072
|
+
}
|
|
8930
9073
|
switch (projectType[0]) {
|
|
8931
9074
|
case "jar":
|
|
8932
9075
|
return createJarBom(path, options);
|
package/lib/cli/index.poku.js
CHANGED
|
@@ -1,7 +1,30 @@
|
|
|
1
|
+
import {
|
|
2
|
+
mkdirSync,
|
|
3
|
+
mkdtempSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
rmSync,
|
|
6
|
+
writeFileSync,
|
|
7
|
+
} from "node:fs";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { dirname, join } from "node:path";
|
|
10
|
+
import process from "node:process";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
|
|
1
13
|
import esmock from "esmock";
|
|
2
14
|
import { assert, describe, it } from "poku";
|
|
3
15
|
import sinon from "sinon";
|
|
4
16
|
|
|
17
|
+
import { createChromeExtensionBom } from "./index.js";
|
|
18
|
+
|
|
19
|
+
const fixtureDir = join(
|
|
20
|
+
dirname(fileURLToPath(import.meta.url)),
|
|
21
|
+
"..",
|
|
22
|
+
"..",
|
|
23
|
+
"test",
|
|
24
|
+
"data",
|
|
25
|
+
"chrome-extensions",
|
|
26
|
+
);
|
|
27
|
+
|
|
5
28
|
describe("CLI tests", () => {
|
|
6
29
|
describe("submitBom()", () => {
|
|
7
30
|
it("should successfully report the SBOM with given project id, name, version and a single tag", async () => {
|
|
@@ -238,4 +261,232 @@ describe("CLI tests", () => {
|
|
|
238
261
|
sinon.assert.notCalled(gotStub);
|
|
239
262
|
});
|
|
240
263
|
});
|
|
264
|
+
|
|
265
|
+
describe("createCocoaBom()", () => {
|
|
266
|
+
it("should skip missing Podfile.lock when failOnError is false", async () => {
|
|
267
|
+
const { createCocoaBom } = await import("./index.js");
|
|
268
|
+
const tempDir = mkdtempSync(join(tmpdir(), "cdxgen-cocoa-"));
|
|
269
|
+
const podFile = join(tempDir, "Podfile");
|
|
270
|
+
writeFileSync(
|
|
271
|
+
podFile,
|
|
272
|
+
"platform :ios, '14.0'\n\ntarget 'TestApp' do\nend\n",
|
|
273
|
+
"utf-8",
|
|
274
|
+
);
|
|
275
|
+
const consoleLogStub = sinon.stub(console, "log");
|
|
276
|
+
try {
|
|
277
|
+
const bomData = await createCocoaBom(tempDir, {
|
|
278
|
+
deep: false,
|
|
279
|
+
failOnError: false,
|
|
280
|
+
installDeps: false,
|
|
281
|
+
multiProject: false,
|
|
282
|
+
});
|
|
283
|
+
assert.equal(bomData, undefined);
|
|
284
|
+
sinon.assert.calledWithMatch(
|
|
285
|
+
consoleLogStub,
|
|
286
|
+
sinon.match("No 'Podfile.lock' found"),
|
|
287
|
+
);
|
|
288
|
+
} finally {
|
|
289
|
+
consoleLogStub.restore();
|
|
290
|
+
rmSync(tempDir, { force: true, recursive: true });
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it("should not warn or exit for deep mode when Podfile.lock exists", async () => {
|
|
295
|
+
const { createCocoaBom } = await import("./index.js");
|
|
296
|
+
const tempDir = mkdtempSync(join(tmpdir(), "cdxgen-cocoa-deep-"));
|
|
297
|
+
const podFile = join(tempDir, "Podfile");
|
|
298
|
+
const lockFile = join(tempDir, "Podfile.lock");
|
|
299
|
+
writeFileSync(
|
|
300
|
+
podFile,
|
|
301
|
+
"platform :ios, '14.0'\n\ntarget 'TestApp' do\nend\n",
|
|
302
|
+
"utf-8",
|
|
303
|
+
);
|
|
304
|
+
writeFileSync(lockFile, "PODS: []\nDEPENDENCIES: []\n", "utf-8");
|
|
305
|
+
const processExitStub = sinon.stub(process, "exit");
|
|
306
|
+
try {
|
|
307
|
+
await createCocoaBom(tempDir, {
|
|
308
|
+
deep: true,
|
|
309
|
+
failOnError: true,
|
|
310
|
+
installDeps: false,
|
|
311
|
+
multiProject: false,
|
|
312
|
+
});
|
|
313
|
+
sinon.assert.notCalled(processExitStub);
|
|
314
|
+
} finally {
|
|
315
|
+
processExitStub.restore();
|
|
316
|
+
rmSync(tempDir, { force: true, recursive: true });
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
describe("createChromeExtensionBom()", () => {
|
|
322
|
+
it("should catalog a directly provided extension and its node dependencies", async () => {
|
|
323
|
+
const tempRoot = mkdtempSync(join(tmpdir(), "cdxgen-chrome-ext-cli-"));
|
|
324
|
+
const extensionId = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
|
|
325
|
+
const extensionIdDir = join(tempRoot, extensionId);
|
|
326
|
+
const extensionVersionDir = join(extensionIdDir, "1.2.3");
|
|
327
|
+
try {
|
|
328
|
+
mkdirSync(extensionVersionDir, { recursive: true });
|
|
329
|
+
writeFileSync(
|
|
330
|
+
join(extensionVersionDir, "manifest.json"),
|
|
331
|
+
JSON.stringify({
|
|
332
|
+
manifest_version: 3,
|
|
333
|
+
name: "CLI Test Extension",
|
|
334
|
+
description: "Direct path test",
|
|
335
|
+
version: "1.2.3",
|
|
336
|
+
}),
|
|
337
|
+
"utf-8",
|
|
338
|
+
);
|
|
339
|
+
writeFileSync(
|
|
340
|
+
join(extensionVersionDir, "package.json"),
|
|
341
|
+
JSON.stringify({
|
|
342
|
+
name: "chrome-extension-cli-test",
|
|
343
|
+
version: "1.2.3",
|
|
344
|
+
dependencies: {
|
|
345
|
+
"left-pad": "1.3.0",
|
|
346
|
+
},
|
|
347
|
+
}),
|
|
348
|
+
"utf-8",
|
|
349
|
+
);
|
|
350
|
+
writeFileSync(
|
|
351
|
+
join(extensionVersionDir, "package-lock.json"),
|
|
352
|
+
JSON.stringify({
|
|
353
|
+
name: "chrome-extension-cli-test",
|
|
354
|
+
version: "1.2.3",
|
|
355
|
+
lockfileVersion: 3,
|
|
356
|
+
requires: true,
|
|
357
|
+
packages: {
|
|
358
|
+
"": {
|
|
359
|
+
name: "chrome-extension-cli-test",
|
|
360
|
+
version: "1.2.3",
|
|
361
|
+
dependencies: {
|
|
362
|
+
"left-pad": "1.3.0",
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
"node_modules/left-pad": {
|
|
366
|
+
version: "1.3.0",
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
}),
|
|
370
|
+
"utf-8",
|
|
371
|
+
);
|
|
372
|
+
const bomData = await createChromeExtensionBom(extensionIdDir, {
|
|
373
|
+
projectType: ["chrome-extension"],
|
|
374
|
+
multiProject: false,
|
|
375
|
+
});
|
|
376
|
+
const components = bomData?.bomJson?.components || [];
|
|
377
|
+
assert.ok(
|
|
378
|
+
components.some(
|
|
379
|
+
(component) =>
|
|
380
|
+
component.purl === `pkg:chrome-extension/${extensionId}@1.2.3`,
|
|
381
|
+
),
|
|
382
|
+
);
|
|
383
|
+
assert.ok(
|
|
384
|
+
components.some(
|
|
385
|
+
(component) =>
|
|
386
|
+
component.name === "left-pad" &&
|
|
387
|
+
component.purl?.startsWith("pkg:npm/left-pad@1.3.0"),
|
|
388
|
+
),
|
|
389
|
+
);
|
|
390
|
+
} finally {
|
|
391
|
+
rmSync(tempRoot, { recursive: true, force: true });
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it("should parse an AI-targeted community extension manifest from direct version path", async () => {
|
|
396
|
+
const tempRoot = mkdtempSync(join(tmpdir(), "cdxgen-chrome-ext-cli-ai-"));
|
|
397
|
+
const extensionId = "llllllllllllllllllllllllllllllll";
|
|
398
|
+
const extensionVersion = "1.0.0";
|
|
399
|
+
const extensionVersionDir = join(tempRoot, extensionId, extensionVersion);
|
|
400
|
+
try {
|
|
401
|
+
mkdirSync(extensionVersionDir, { recursive: true });
|
|
402
|
+
writeFileSync(
|
|
403
|
+
join(extensionVersionDir, "manifest.json"),
|
|
404
|
+
readFileSync(
|
|
405
|
+
join(fixtureDir, "chrome-copilottts-manifest.json"),
|
|
406
|
+
"utf-8",
|
|
407
|
+
),
|
|
408
|
+
"utf-8",
|
|
409
|
+
);
|
|
410
|
+
const bomData = await createChromeExtensionBom(extensionVersionDir, {
|
|
411
|
+
projectType: ["chrome-extension"],
|
|
412
|
+
multiProject: false,
|
|
413
|
+
});
|
|
414
|
+
const extensionComponent = (bomData?.bomJson?.components || []).find(
|
|
415
|
+
(component) =>
|
|
416
|
+
component.purl ===
|
|
417
|
+
`pkg:chrome-extension/${extensionId}@${extensionVersion}`,
|
|
418
|
+
);
|
|
419
|
+
assert.ok(extensionComponent, "expected direct extension component");
|
|
420
|
+
const properties = extensionComponent.properties || [];
|
|
421
|
+
assert.ok(
|
|
422
|
+
properties.some(
|
|
423
|
+
(prop) =>
|
|
424
|
+
prop.name === "cdx:chrome-extension:permissions" &&
|
|
425
|
+
prop.value.includes("scripting"),
|
|
426
|
+
),
|
|
427
|
+
);
|
|
428
|
+
assert.ok(
|
|
429
|
+
properties.some(
|
|
430
|
+
(prop) =>
|
|
431
|
+
prop.name === "cdx:chrome-extension:capability:codeInjection" &&
|
|
432
|
+
prop.value === "true",
|
|
433
|
+
),
|
|
434
|
+
);
|
|
435
|
+
assert.ok(
|
|
436
|
+
properties.some(
|
|
437
|
+
(prop) =>
|
|
438
|
+
prop.name === "cdx:chrome-extension:hostPermissions" &&
|
|
439
|
+
prop.value.includes("https://github.com/copilot/tasks/*"),
|
|
440
|
+
),
|
|
441
|
+
);
|
|
442
|
+
} finally {
|
|
443
|
+
rmSync(tempRoot, { recursive: true, force: true });
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
describe("createMultiXBom()", () => {
|
|
449
|
+
it("should scan installed chrome extensions only once across multiple non-extension paths", async () => {
|
|
450
|
+
const tempRoot = mkdtempSync(join(tmpdir(), "cdxgen-chrome-ext-multi-"));
|
|
451
|
+
const pathA = join(tempRoot, "project-a");
|
|
452
|
+
const pathB = join(tempRoot, "project-b");
|
|
453
|
+
mkdirSync(pathA, { recursive: true });
|
|
454
|
+
mkdirSync(pathB, { recursive: true });
|
|
455
|
+
const collectInstalledChromeExtensions = sinon.stub().returns([
|
|
456
|
+
{
|
|
457
|
+
type: "application",
|
|
458
|
+
name: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
459
|
+
version: "1.0.0",
|
|
460
|
+
purl: "pkg:chrome-extension/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@1.0.0",
|
|
461
|
+
"bom-ref":
|
|
462
|
+
"pkg:chrome-extension/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@1.0.0",
|
|
463
|
+
},
|
|
464
|
+
]);
|
|
465
|
+
try {
|
|
466
|
+
const { createMultiXBom } = await esmock("./index.js", {
|
|
467
|
+
"../helpers/chromextutils.js": {
|
|
468
|
+
CHROME_EXTENSION_PURL_TYPE: "chrome-extension",
|
|
469
|
+
collectChromeExtensionsFromPath: sinon
|
|
470
|
+
.stub()
|
|
471
|
+
.returns({ components: [], extensionDirs: [] }),
|
|
472
|
+
collectInstalledChromeExtensions,
|
|
473
|
+
discoverChromiumExtensionDirs: sinon.stub().returns([
|
|
474
|
+
{
|
|
475
|
+
browser: "Google Chrome",
|
|
476
|
+
channel: "stable",
|
|
477
|
+
dir: join(tempRoot, "fake-browser-dir"),
|
|
478
|
+
},
|
|
479
|
+
]),
|
|
480
|
+
},
|
|
481
|
+
});
|
|
482
|
+
await createMultiXBom([pathA, pathB], {
|
|
483
|
+
projectType: ["chrome-extension"],
|
|
484
|
+
multiProject: true,
|
|
485
|
+
});
|
|
486
|
+
sinon.assert.calledOnce(collectInstalledChromeExtensions);
|
|
487
|
+
} finally {
|
|
488
|
+
rmSync(tempRoot, { recursive: true, force: true });
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
});
|
|
241
492
|
});
|