@cyclonedx/cdxgen 12.1.2 → 12.1.3
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 +10 -8
- package/bin/cdxgen.js +1 -1
- package/lib/cli/index.js +2 -2
- package/lib/evinser/evinser.js +2 -8
- package/lib/helpers/display.js +1 -1
- package/lib/helpers/envcontext.js +10 -2
- package/lib/helpers/utils.js +421 -86
- package/lib/helpers/utils.poku.js +179 -2
- package/lib/helpers/validator.js +8 -5
- package/lib/managers/docker.getConnection.poku.js +61 -0
- package/lib/managers/docker.js +36 -23
- package/lib/parsers/iri.js +1 -2
- package/lib/server/server.js +164 -34
- package/lib/server/server.poku.js +232 -10
- package/lib/stages/postgen/annotator.js +281 -3
- package/lib/stages/postgen/postgen.js +4 -7
- package/lib/third-party/arborist/lib/diff.js +1 -1
- package/lib/third-party/arborist/lib/node.js +1 -1
- package/lib/third-party/arborist/lib/yarn-lock.js +1 -1
- package/package.json +20 -326
- package/types/bin/dependencies.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/helpers/envcontext.d.ts.map +1 -1
- package/types/lib/helpers/utils.d.ts.map +1 -1
- package/types/lib/helpers/validator.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/parsers/iri.d.ts.map +1 -1
- package/types/lib/server/server.d.ts +14 -0
- 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/postgen.d.ts.map +1 -1
- package/bin/dependencies.js +0 -131
- package/bin/licenses.js +0 -78
- package/lib/helpers/dependencies.poku.js +0 -11
- package/lib/helpers/licenses.poku.js +0 -11
package/lib/helpers/utils.js
CHANGED
|
@@ -48,6 +48,7 @@ import {
|
|
|
48
48
|
satisfies,
|
|
49
49
|
valid,
|
|
50
50
|
} from "semver";
|
|
51
|
+
import { v4 as uuidv4 } from "uuid";
|
|
51
52
|
import { xml2js } from "xml-js";
|
|
52
53
|
import { parse as _load } from "yaml";
|
|
53
54
|
|
|
@@ -160,6 +161,20 @@ export function safeSpawnSync(command, args, options) {
|
|
|
160
161
|
if (!options.timeout) {
|
|
161
162
|
options.timeout = TIMEOUT_MS;
|
|
162
163
|
}
|
|
164
|
+
// Check for -S for python invocations in secure mode
|
|
165
|
+
if (command.includes("python") && (!args?.length || args[0] !== "-S")) {
|
|
166
|
+
if (isSecureMode) {
|
|
167
|
+
console.warn(
|
|
168
|
+
"\x1b[1;35mNotice: Running python command without '-S' argument. This is a bug in cdxgen. Please report with an example repo here https://github.com/cdxgen/cdxgen/issues.\x1b[0m",
|
|
169
|
+
);
|
|
170
|
+
} else if (process.env?.CDXGEN_IN_CONTAINER === "true") {
|
|
171
|
+
console.log("Running python command without '-S' argument.");
|
|
172
|
+
} else {
|
|
173
|
+
console.warn(
|
|
174
|
+
"\x1b[1;35mNotice: Running python command without '-S' argument. Only run cdxgen in trusted directories to prevent auto-executing local scripts.\x1b[0m",
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
163
178
|
traceLog("spawn", { command, args, ...options });
|
|
164
179
|
commandsExecuted.add(command);
|
|
165
180
|
// Fix for DEP0190 warning
|
|
@@ -1033,18 +1048,10 @@ export function getKnownLicense(licenseUrl, pkg) {
|
|
|
1033
1048
|
return { id: akLic.license, name: akLic.licenseName };
|
|
1034
1049
|
}
|
|
1035
1050
|
}
|
|
1036
|
-
if (
|
|
1037
|
-
akLic.urlIncludes &&
|
|
1038
|
-
licenseUrl &&
|
|
1039
|
-
licenseUrl.includes(akLic.urlIncludes)
|
|
1040
|
-
) {
|
|
1051
|
+
if (akLic.urlIncludes && licenseUrl?.includes(akLic.urlIncludes)) {
|
|
1041
1052
|
return { id: akLic.license, name: akLic.licenseName };
|
|
1042
1053
|
}
|
|
1043
|
-
if (
|
|
1044
|
-
akLic.urlEndswith &&
|
|
1045
|
-
licenseUrl &&
|
|
1046
|
-
licenseUrl.endsWith(akLic.urlEndswith)
|
|
1047
|
-
) {
|
|
1054
|
+
if (akLic.urlEndswith && licenseUrl?.endsWith(akLic.urlEndswith)) {
|
|
1048
1055
|
return { id: akLic.license, name: akLic.licenseName };
|
|
1049
1056
|
}
|
|
1050
1057
|
}
|
|
@@ -1265,7 +1272,7 @@ export async function parsePkgJson(pkgJsonFile, simple = false) {
|
|
|
1265
1272
|
// continue regardless of error
|
|
1266
1273
|
}
|
|
1267
1274
|
}
|
|
1268
|
-
if (!simple && shouldFetchLicense() && pkgList
|
|
1275
|
+
if (!simple && shouldFetchLicense() && pkgList?.length) {
|
|
1269
1276
|
if (DEBUG_MODE) {
|
|
1270
1277
|
console.log(
|
|
1271
1278
|
`About to fetch license information for ${pkgList.length} packages in parsePkgJson`,
|
|
@@ -1786,7 +1793,7 @@ export async function parsePkgLock(pkgLockFile, options = {}) {
|
|
|
1786
1793
|
options,
|
|
1787
1794
|
));
|
|
1788
1795
|
|
|
1789
|
-
if (shouldFetchLicense() && pkgList
|
|
1796
|
+
if (shouldFetchLicense() && pkgList?.length) {
|
|
1790
1797
|
if (DEBUG_MODE) {
|
|
1791
1798
|
console.log(
|
|
1792
1799
|
`About to fetch license information for ${pkgList.length} packages in parsePkgLock`,
|
|
@@ -2419,7 +2426,7 @@ export async function parseYarnLock(
|
|
|
2419
2426
|
}
|
|
2420
2427
|
}
|
|
2421
2428
|
|
|
2422
|
-
if (shouldFetchLicense() && pkgList
|
|
2429
|
+
if (shouldFetchLicense() && pkgList?.length) {
|
|
2423
2430
|
if (DEBUG_MODE) {
|
|
2424
2431
|
console.log(
|
|
2425
2432
|
`About to fetch license information for ${pkgList.length} packages in parseYarnLock`,
|
|
@@ -2496,7 +2503,7 @@ export async function parseNodeShrinkwrap(swFile) {
|
|
|
2496
2503
|
}
|
|
2497
2504
|
}
|
|
2498
2505
|
}
|
|
2499
|
-
if (shouldFetchLicense() && pkgList
|
|
2506
|
+
if (shouldFetchLicense() && pkgList?.length) {
|
|
2500
2507
|
if (DEBUG_MODE) {
|
|
2501
2508
|
console.log(
|
|
2502
2509
|
`About to fetch license information for ${pkgList.length} packages in parseNodeShrinkwrap`,
|
|
@@ -3698,7 +3705,7 @@ export async function parsePnpmLock(
|
|
|
3698
3705
|
pkgList = await pnpmMetadata(pkgList, pnpmLock);
|
|
3699
3706
|
}
|
|
3700
3707
|
|
|
3701
|
-
if (shouldFetchLicense() && pkgList
|
|
3708
|
+
if (shouldFetchLicense() && pkgList?.length) {
|
|
3702
3709
|
if (DEBUG_MODE) {
|
|
3703
3710
|
console.log(
|
|
3704
3711
|
`About to fetch license information for ${pkgList.length} packages in parsePnpmLock`,
|
|
@@ -3759,7 +3766,7 @@ export async function parseBowerJson(bowerJsonFile) {
|
|
|
3759
3766
|
// continue regardless of error
|
|
3760
3767
|
}
|
|
3761
3768
|
}
|
|
3762
|
-
if (shouldFetchLicense() && pkgList
|
|
3769
|
+
if (shouldFetchLicense() && pkgList?.length) {
|
|
3763
3770
|
if (DEBUG_MODE) {
|
|
3764
3771
|
console.log(
|
|
3765
3772
|
`About to fetch license information for ${pkgList.length} packages in parseBowerJson`,
|
|
@@ -3857,7 +3864,7 @@ export async function parseMinJs(minJsFile) {
|
|
|
3857
3864
|
// continue regardless of error
|
|
3858
3865
|
}
|
|
3859
3866
|
}
|
|
3860
|
-
if (shouldFetchLicense() && pkgList
|
|
3867
|
+
if (shouldFetchLicense() && pkgList?.length) {
|
|
3861
3868
|
if (DEBUG_MODE) {
|
|
3862
3869
|
console.log(
|
|
3863
3870
|
`About to fetch license information for ${pkgList.length} packages in parseMinJs`,
|
|
@@ -6132,6 +6139,9 @@ export async function parsePyLockData(lockData, lockFile, pyProjectFile) {
|
|
|
6132
6139
|
).toString();
|
|
6133
6140
|
pkg.purl = purlString;
|
|
6134
6141
|
pkg["bom-ref"] = decodeURIComponent(purlString);
|
|
6142
|
+
if (parentComponent && pkg["bom-ref"] === parentComponent["bom-ref"]) {
|
|
6143
|
+
continue;
|
|
6144
|
+
}
|
|
6135
6145
|
pkg.evidence = {
|
|
6136
6146
|
identity: {
|
|
6137
6147
|
field: "purl",
|
|
@@ -6387,6 +6397,9 @@ export async function parseReqFile(reqFile, fetchDepsInfo = false) {
|
|
|
6387
6397
|
return await parseReqData(reqFile, null, fetchDepsInfo);
|
|
6388
6398
|
}
|
|
6389
6399
|
|
|
6400
|
+
const LICENSE_ID_COMMENTS_PATTERN =
|
|
6401
|
+
/^(Apache-2\.0|MIT|ISC|GPL-|LGPL-|BSD-[23]-Clause)/i;
|
|
6402
|
+
|
|
6390
6403
|
/**
|
|
6391
6404
|
* Method to parse requirements.txt file. Must only be used internally.
|
|
6392
6405
|
*
|
|
@@ -6434,6 +6447,12 @@ async function parseReqData(reqFile, reqData = null, fetchDepsInfo = false) {
|
|
|
6434
6447
|
if (!l || l.startsWith("#") || l.startsWith("-")) {
|
|
6435
6448
|
continue;
|
|
6436
6449
|
}
|
|
6450
|
+
let comment = null;
|
|
6451
|
+
const commentMatch = l.match(/\s+#(.*)$/);
|
|
6452
|
+
if (commentMatch) {
|
|
6453
|
+
comment = commentMatch[1].trim();
|
|
6454
|
+
l = l.substring(0, commentMatch.index).trim();
|
|
6455
|
+
}
|
|
6437
6456
|
const properties = reqFile
|
|
6438
6457
|
? [
|
|
6439
6458
|
{
|
|
@@ -6482,6 +6501,21 @@ async function parseReqData(reqFile, reqData = null, fetchDepsInfo = false) {
|
|
|
6482
6501
|
scope: compScope,
|
|
6483
6502
|
evidence,
|
|
6484
6503
|
};
|
|
6504
|
+
if (comment) {
|
|
6505
|
+
apkg.licenses = comment
|
|
6506
|
+
.split("/")
|
|
6507
|
+
.map((c) => {
|
|
6508
|
+
const licenseObj = {};
|
|
6509
|
+
const cId = c.trim();
|
|
6510
|
+
if (cId.match(LICENSE_ID_COMMENTS_PATTERN)) {
|
|
6511
|
+
licenseObj.id = cId;
|
|
6512
|
+
} else {
|
|
6513
|
+
return undefined;
|
|
6514
|
+
}
|
|
6515
|
+
return { license: licenseObj };
|
|
6516
|
+
})
|
|
6517
|
+
.filter((l) => l !== undefined);
|
|
6518
|
+
}
|
|
6485
6519
|
if (extras && extras.length > 0) {
|
|
6486
6520
|
properties.push({
|
|
6487
6521
|
name: "cdx:pypi:extras",
|
|
@@ -6533,6 +6567,21 @@ async function parseReqData(reqFile, reqData = null, fetchDepsInfo = false) {
|
|
|
6533
6567
|
scope: compScope,
|
|
6534
6568
|
evidence,
|
|
6535
6569
|
};
|
|
6570
|
+
if (comment) {
|
|
6571
|
+
apkg.licenses = comment
|
|
6572
|
+
.split("/")
|
|
6573
|
+
.map((c) => {
|
|
6574
|
+
const licenseObj = {};
|
|
6575
|
+
const cId = c.trim();
|
|
6576
|
+
if (cId.match(LICENSE_ID_COMMENTS_PATTERN)) {
|
|
6577
|
+
licenseObj.id = cId;
|
|
6578
|
+
} else {
|
|
6579
|
+
return undefined;
|
|
6580
|
+
}
|
|
6581
|
+
return { license: licenseObj };
|
|
6582
|
+
})
|
|
6583
|
+
.filter((l) => l !== undefined);
|
|
6584
|
+
}
|
|
6536
6585
|
if (versionSpecifiers && !versionSpecifiers.startsWith("==")) {
|
|
6537
6586
|
properties.push({
|
|
6538
6587
|
name: "cdx:pypi:versionSpecifiers",
|
|
@@ -9877,82 +9926,250 @@ export function parseGitHubWorkflowData(f) {
|
|
|
9877
9926
|
return pkgList;
|
|
9878
9927
|
}
|
|
9879
9928
|
const lines = ghwData.split("\n");
|
|
9929
|
+
// workflow-related values
|
|
9930
|
+
const workflowName = yamlObj.name || path.basename(f, path.extname(f));
|
|
9931
|
+
const workflowTriggers = yamlObj.on || yamlObj.true;
|
|
9932
|
+
const workflowPermissions = yamlObj.permissions || {};
|
|
9933
|
+
let hasWritePermissions = analyzePermissions(workflowPermissions);
|
|
9934
|
+
// GitHub of course supports strings such as "write-all" to make it easy to create supply-chain attacks.
|
|
9935
|
+
if (
|
|
9936
|
+
(typeof workflowPermissions === "string" ||
|
|
9937
|
+
workflowPermissions instanceof String) &&
|
|
9938
|
+
workflowPermissions.includes("write")
|
|
9939
|
+
) {
|
|
9940
|
+
hasWritePermissions = true;
|
|
9941
|
+
}
|
|
9942
|
+
const workflowConcurrency = yamlObj.concurrency || {};
|
|
9943
|
+
const _workflowEnv = yamlObj.env || {};
|
|
9944
|
+
const hasIdTokenWrite = workflowPermissions?.["id-token"] === "write";
|
|
9880
9945
|
for (const jobName of Object.keys(yamlObj.jobs)) {
|
|
9881
|
-
|
|
9882
|
-
|
|
9883
|
-
|
|
9884
|
-
|
|
9885
|
-
|
|
9886
|
-
|
|
9946
|
+
const job = yamlObj.jobs[jobName];
|
|
9947
|
+
if (!job.steps) {
|
|
9948
|
+
continue;
|
|
9949
|
+
}
|
|
9950
|
+
// job-related values
|
|
9951
|
+
const jobRunner = job["runs-on"] || "unknown";
|
|
9952
|
+
const jobEnvironment = job.environment?.name || job.environment || "";
|
|
9953
|
+
const jobTimeout = job["timeout-minutes"] || null;
|
|
9954
|
+
const jobPermissions = job.permissions || {};
|
|
9955
|
+
const jobServices = job.services ? Object.keys(job.services) : [];
|
|
9956
|
+
let jobNeeds = job.needs || [];
|
|
9957
|
+
if (!Array.isArray(jobNeeds)) {
|
|
9958
|
+
jobNeeds = [jobNeeds];
|
|
9959
|
+
}
|
|
9960
|
+
const _jobIf = job.if || "";
|
|
9961
|
+
const _jobStrategy = job.strategy ? JSON.stringify(job.strategy) : "";
|
|
9962
|
+
const jobHasWritePermissions = analyzePermissions(jobPermissions);
|
|
9963
|
+
for (const step of job.steps) {
|
|
9964
|
+
if (step.uses) {
|
|
9965
|
+
const tmpA = step.uses.split("@");
|
|
9966
|
+
if (tmpA.length !== 2) {
|
|
9967
|
+
continue;
|
|
9968
|
+
}
|
|
9969
|
+
const groupName = tmpA[0];
|
|
9970
|
+
let name = groupName;
|
|
9971
|
+
let group = "";
|
|
9972
|
+
const tagOrCommit = tmpA[1];
|
|
9973
|
+
let version = tagOrCommit;
|
|
9974
|
+
const tmpB = groupName.split("/");
|
|
9975
|
+
if (tmpB.length >= 2) {
|
|
9976
|
+
name = tmpB.pop();
|
|
9977
|
+
group = tmpB.join("/");
|
|
9978
|
+
} else if (tmpB.length === 1) {
|
|
9979
|
+
name = tmpB[0];
|
|
9980
|
+
group = "";
|
|
9981
|
+
}
|
|
9982
|
+
const versionPinningType = getVersionPinningType(tagOrCommit);
|
|
9983
|
+
const isShaPinned = versionPinningType === "sha";
|
|
9984
|
+
const _isTagPinned = versionPinningType === "tag";
|
|
9985
|
+
const _isBranchRef = versionPinningType === "branch";
|
|
9986
|
+
let lineNum = -1;
|
|
9987
|
+
const stepLineMatch = ghwData.indexOf(step.uses);
|
|
9988
|
+
if (stepLineMatch >= 0) {
|
|
9989
|
+
lineNum = ghwData.substring(0, stepLineMatch).split("\n").length - 1;
|
|
9990
|
+
}
|
|
9991
|
+
if (lineNum >= 0 && lines[lineNum]) {
|
|
9992
|
+
const line = lines[lineNum];
|
|
9993
|
+
const commentMatch = line.match(/#\s*v?([0-9]+(?:\.[0-9]+)*)/);
|
|
9994
|
+
if (commentMatch?.[1]) {
|
|
9995
|
+
version = commentMatch[1];
|
|
9887
9996
|
}
|
|
9888
|
-
|
|
9889
|
-
|
|
9890
|
-
|
|
9891
|
-
|
|
9892
|
-
|
|
9893
|
-
|
|
9894
|
-
if (
|
|
9895
|
-
|
|
9896
|
-
group = tmpB.join("/");
|
|
9897
|
-
} else if (tmpB.length === 1) {
|
|
9898
|
-
name = tmpB[0];
|
|
9899
|
-
group = "";
|
|
9997
|
+
}
|
|
9998
|
+
const key = `${group}-${name}-${version}`;
|
|
9999
|
+
let confidence = 0.6;
|
|
10000
|
+
if (!keys_cache[key] && name && version) {
|
|
10001
|
+
keys_cache[key] = key;
|
|
10002
|
+
let fullName = name;
|
|
10003
|
+
if (group.length) {
|
|
10004
|
+
fullName = `${group}/${name}`;
|
|
9900
10005
|
}
|
|
9901
|
-
let
|
|
9902
|
-
|
|
9903
|
-
|
|
9904
|
-
|
|
9905
|
-
|
|
10006
|
+
let purl = `pkg:github/${fullName}@${version}`;
|
|
10007
|
+
if (tagOrCommit && version !== tagOrCommit) {
|
|
10008
|
+
const qualifierDesc = tagOrCommit.startsWith("v")
|
|
10009
|
+
? "tag"
|
|
10010
|
+
: "commit";
|
|
10011
|
+
purl = `${purl}?${qualifierDesc}=${tagOrCommit}`;
|
|
10012
|
+
confidence = 0.7;
|
|
9906
10013
|
}
|
|
9907
|
-
|
|
9908
|
-
|
|
9909
|
-
|
|
9910
|
-
|
|
9911
|
-
|
|
9912
|
-
|
|
10014
|
+
const properties = [
|
|
10015
|
+
{ name: "SrcFile", value: f },
|
|
10016
|
+
{ name: "cdx:github:workflow:name", value: workflowName },
|
|
10017
|
+
{ name: "cdx:github:job:name", value: jobName },
|
|
10018
|
+
{
|
|
10019
|
+
name: "cdx:github:job:runner",
|
|
10020
|
+
value: Array.isArray(jobRunner) ? jobRunner.join(",") : jobRunner,
|
|
10021
|
+
},
|
|
10022
|
+
{ name: "cdx:github:action:uses", value: step.uses },
|
|
10023
|
+
{
|
|
10024
|
+
name: "cdx:github:action:versionPinningType",
|
|
10025
|
+
value: versionPinningType,
|
|
10026
|
+
},
|
|
10027
|
+
{
|
|
10028
|
+
name: "cdx:github:action:isShaPinned",
|
|
10029
|
+
value: isShaPinned.toString(),
|
|
10030
|
+
},
|
|
10031
|
+
];
|
|
10032
|
+
if (step.name) {
|
|
10033
|
+
properties.push({ name: "cdx:github:step:name", value: step.name });
|
|
9913
10034
|
}
|
|
9914
|
-
|
|
9915
|
-
|
|
9916
|
-
|
|
9917
|
-
|
|
9918
|
-
|
|
9919
|
-
|
|
9920
|
-
|
|
9921
|
-
|
|
9922
|
-
|
|
9923
|
-
|
|
9924
|
-
|
|
9925
|
-
|
|
9926
|
-
|
|
9927
|
-
|
|
9928
|
-
|
|
9929
|
-
|
|
9930
|
-
|
|
9931
|
-
|
|
9932
|
-
|
|
9933
|
-
|
|
10035
|
+
if (step.if) {
|
|
10036
|
+
properties.push({
|
|
10037
|
+
name: "cdx:github:step:condition",
|
|
10038
|
+
value: step.if,
|
|
10039
|
+
});
|
|
10040
|
+
}
|
|
10041
|
+
if (step["continue-on-error"]) {
|
|
10042
|
+
properties.push({
|
|
10043
|
+
name: "cdx:github:step:continueOnError",
|
|
10044
|
+
value: "true",
|
|
10045
|
+
});
|
|
10046
|
+
}
|
|
10047
|
+
if (step.timeout) {
|
|
10048
|
+
properties.push({
|
|
10049
|
+
name: "cdx:github:step:timeout",
|
|
10050
|
+
value: step.timeout.toString(),
|
|
10051
|
+
});
|
|
10052
|
+
}
|
|
10053
|
+
if (jobEnvironment) {
|
|
10054
|
+
properties.push({
|
|
10055
|
+
name: "cdx:github:job:environment",
|
|
10056
|
+
value: jobEnvironment,
|
|
10057
|
+
});
|
|
10058
|
+
}
|
|
10059
|
+
if (jobTimeout) {
|
|
10060
|
+
properties.push({
|
|
10061
|
+
name: "cdx:github:job:timeoutMinutes",
|
|
10062
|
+
value: jobTimeout.toString(),
|
|
10063
|
+
});
|
|
10064
|
+
}
|
|
10065
|
+
if (jobHasWritePermissions) {
|
|
10066
|
+
properties.push({
|
|
10067
|
+
name: "cdx:github:job:hasWritePermissions",
|
|
10068
|
+
value: "true",
|
|
10069
|
+
});
|
|
10070
|
+
}
|
|
10071
|
+
if (jobServices.length > 0) {
|
|
10072
|
+
properties.push({
|
|
10073
|
+
name: "cdx:github:job:services",
|
|
10074
|
+
value: jobServices.join(","),
|
|
10075
|
+
});
|
|
10076
|
+
}
|
|
10077
|
+
if (jobNeeds.length > 0) {
|
|
10078
|
+
properties.push({
|
|
10079
|
+
name: "cdx:github:job:needs",
|
|
10080
|
+
value: jobNeeds.join(","),
|
|
10081
|
+
});
|
|
10082
|
+
}
|
|
10083
|
+
if (hasWritePermissions) {
|
|
10084
|
+
properties.push({
|
|
10085
|
+
name: "cdx:github:workflow:hasWritePermissions",
|
|
10086
|
+
value: "true",
|
|
10087
|
+
});
|
|
10088
|
+
}
|
|
10089
|
+
if (hasIdTokenWrite) {
|
|
10090
|
+
properties.push({
|
|
10091
|
+
name: "cdx:github:workflow:hasIdTokenWrite",
|
|
10092
|
+
value: "true",
|
|
10093
|
+
});
|
|
10094
|
+
}
|
|
10095
|
+
if (workflowConcurrency?.group) {
|
|
10096
|
+
properties.push({
|
|
10097
|
+
name: "cdx:github:workflow:concurrencyGroup",
|
|
10098
|
+
value: workflowConcurrency.group,
|
|
10099
|
+
});
|
|
10100
|
+
}
|
|
10101
|
+
if (group?.startsWith("github/") || group === "actions") {
|
|
10102
|
+
properties.push({ name: "cdx:actions:isOfficial", value: "true" });
|
|
10103
|
+
}
|
|
10104
|
+
if (group?.startsWith("github/")) {
|
|
10105
|
+
properties.push({ name: "cdx:actions:isVerified", value: "true" });
|
|
10106
|
+
}
|
|
10107
|
+
if (workflowTriggers) {
|
|
10108
|
+
const triggers =
|
|
10109
|
+
typeof workflowTriggers === "string"
|
|
10110
|
+
? workflowTriggers
|
|
10111
|
+
: Object.keys(workflowTriggers).join(",");
|
|
10112
|
+
properties.push({
|
|
10113
|
+
name: "cdx:github:workflow:triggers",
|
|
10114
|
+
value: triggers,
|
|
10115
|
+
});
|
|
10116
|
+
}
|
|
10117
|
+
pkgList.push({
|
|
10118
|
+
group,
|
|
10119
|
+
name,
|
|
10120
|
+
version,
|
|
10121
|
+
purl,
|
|
10122
|
+
properties,
|
|
10123
|
+
evidence: {
|
|
10124
|
+
identity: {
|
|
10125
|
+
field: "purl",
|
|
10126
|
+
confidence,
|
|
10127
|
+
methods: [
|
|
10128
|
+
{
|
|
10129
|
+
technique: "source-code-analysis",
|
|
10130
|
+
confidence,
|
|
10131
|
+
value: f,
|
|
10132
|
+
},
|
|
10133
|
+
],
|
|
9934
10134
|
},
|
|
9935
|
-
|
|
9936
|
-
|
|
9937
|
-
|
|
9938
|
-
|
|
9939
|
-
|
|
9940
|
-
|
|
9941
|
-
|
|
10135
|
+
},
|
|
10136
|
+
});
|
|
10137
|
+
}
|
|
10138
|
+
}
|
|
10139
|
+
if (step.run) {
|
|
10140
|
+
const runLineNum = ghwData.indexOf(step.run);
|
|
10141
|
+
const runLine =
|
|
10142
|
+
runLineNum >= 0
|
|
10143
|
+
? ghwData.substring(0, runLineNum).split("\n").length
|
|
10144
|
+
: -1;
|
|
10145
|
+
const pkgCommands = extractPackageManagerCommands(step.run);
|
|
10146
|
+
for (const pkgCmd of pkgCommands) {
|
|
10147
|
+
const key = `run-${pkgCmd.name}-${pkgCmd.version || "latest"}`;
|
|
10148
|
+
if (!keys_cache[key]) {
|
|
10149
|
+
keys_cache[key] = key;
|
|
9942
10150
|
pkgList.push({
|
|
9943
|
-
group,
|
|
9944
|
-
name,
|
|
9945
|
-
version,
|
|
9946
|
-
|
|
9947
|
-
|
|
10151
|
+
group: "",
|
|
10152
|
+
name: pkgCmd.name,
|
|
10153
|
+
version: pkgCmd.version || undefined,
|
|
10154
|
+
scope: "excluded",
|
|
10155
|
+
"bom-ref": uuidv4(),
|
|
10156
|
+
description: key,
|
|
10157
|
+
properties: [
|
|
10158
|
+
{ name: "SrcFile", value: f },
|
|
10159
|
+
{ name: "cdx:github:workflow:name", value: workflowName },
|
|
10160
|
+
{ name: "cdx:github:job:name", value: jobName },
|
|
10161
|
+
{ name: "cdx:github:step:type", value: "run" },
|
|
10162
|
+
{ name: "cdx:github:step:command", value: pkgCmd.command },
|
|
10163
|
+
{ name: "cdx:github:run:line", value: runLine.toString() },
|
|
10164
|
+
],
|
|
9948
10165
|
evidence: {
|
|
9949
10166
|
identity: {
|
|
9950
10167
|
field: "purl",
|
|
9951
|
-
confidence,
|
|
10168
|
+
confidence: 0.5,
|
|
9952
10169
|
methods: [
|
|
9953
10170
|
{
|
|
9954
10171
|
technique: "source-code-analysis",
|
|
9955
|
-
confidence,
|
|
10172
|
+
confidence: 0.5,
|
|
9956
10173
|
value: f,
|
|
9957
10174
|
},
|
|
9958
10175
|
],
|
|
@@ -9967,6 +10184,103 @@ export function parseGitHubWorkflowData(f) {
|
|
|
9967
10184
|
return pkgList;
|
|
9968
10185
|
}
|
|
9969
10186
|
|
|
10187
|
+
/**
|
|
10188
|
+
* Analyze permissions for write access.
|
|
10189
|
+
*
|
|
10190
|
+
* Refer to https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#permissions
|
|
10191
|
+
*/
|
|
10192
|
+
function analyzePermissions(permissions) {
|
|
10193
|
+
if (!permissions || typeof permissions !== "object") {
|
|
10194
|
+
return false;
|
|
10195
|
+
}
|
|
10196
|
+
const writePermissions = [
|
|
10197
|
+
"actions",
|
|
10198
|
+
"artifact-metadata",
|
|
10199
|
+
"attestations",
|
|
10200
|
+
"checks",
|
|
10201
|
+
"contents",
|
|
10202
|
+
"deployments",
|
|
10203
|
+
"id-token",
|
|
10204
|
+
"models",
|
|
10205
|
+
"discussions",
|
|
10206
|
+
"packages",
|
|
10207
|
+
"pages",
|
|
10208
|
+
"actions",
|
|
10209
|
+
"deployments",
|
|
10210
|
+
"issues",
|
|
10211
|
+
"pull-requests",
|
|
10212
|
+
"security-events",
|
|
10213
|
+
"statuses",
|
|
10214
|
+
];
|
|
10215
|
+
for (const perm of writePermissions) {
|
|
10216
|
+
if (permissions[perm] === "write") {
|
|
10217
|
+
return true;
|
|
10218
|
+
}
|
|
10219
|
+
}
|
|
10220
|
+
return false;
|
|
10221
|
+
}
|
|
10222
|
+
|
|
10223
|
+
/**
|
|
10224
|
+
* Determine version pinning type for security assessment
|
|
10225
|
+
*/
|
|
10226
|
+
function getVersionPinningType(versionRef) {
|
|
10227
|
+
if (!versionRef) {
|
|
10228
|
+
return "unknown";
|
|
10229
|
+
}
|
|
10230
|
+
if (/^[a-f0-9]{40}$/.test(versionRef)) {
|
|
10231
|
+
return "sha";
|
|
10232
|
+
}
|
|
10233
|
+
if (/^[a-f0-9]{7,}$/.test(versionRef)) {
|
|
10234
|
+
return "sha";
|
|
10235
|
+
}
|
|
10236
|
+
if (
|
|
10237
|
+
versionRef === "main" ||
|
|
10238
|
+
versionRef === "master" ||
|
|
10239
|
+
versionRef.includes("/")
|
|
10240
|
+
) {
|
|
10241
|
+
return "branch";
|
|
10242
|
+
}
|
|
10243
|
+
return "tag";
|
|
10244
|
+
}
|
|
10245
|
+
|
|
10246
|
+
/**
|
|
10247
|
+
* Extract package manager commands from run steps
|
|
10248
|
+
*/
|
|
10249
|
+
function extractPackageManagerCommands(runScript) {
|
|
10250
|
+
const commands = [];
|
|
10251
|
+
if (!runScript) {
|
|
10252
|
+
return commands;
|
|
10253
|
+
}
|
|
10254
|
+
const patterns = [
|
|
10255
|
+
{ regex: /npm\s+(install|i|ci)\s+([^&\n|;]+)/g, name: "npm", type: "npm" },
|
|
10256
|
+
{
|
|
10257
|
+
regex: /yarn\s+(add|install)\s+([^&\n|;]+)/g,
|
|
10258
|
+
name: "yarn",
|
|
10259
|
+
type: "yarn",
|
|
10260
|
+
},
|
|
10261
|
+
{ regex: /pip\s+(install)\s+([^&\n|;]+)/g, name: "pip", type: "pip" },
|
|
10262
|
+
{ regex: /pip3\s+(install)\s+([^&\n|;]+)/g, name: "pip3", type: "pip" },
|
|
10263
|
+
{ regex: /gem\s+(install)\s+([^&\n|;]+)/g, name: "gem", type: "gem" },
|
|
10264
|
+
{ regex: /go\s+(get|install)\s+([^&\n|;]+)/g, name: "go", type: "go" },
|
|
10265
|
+
{
|
|
10266
|
+
regex: /cargo\s+(install|add)\s+([^&\n|;]+)/g,
|
|
10267
|
+
name: "cargo",
|
|
10268
|
+
type: "cargo",
|
|
10269
|
+
},
|
|
10270
|
+
];
|
|
10271
|
+
for (const pattern of patterns) {
|
|
10272
|
+
let match;
|
|
10273
|
+
while ((match = pattern.regex.exec(runScript)) !== null) {
|
|
10274
|
+
commands.push({
|
|
10275
|
+
name: pattern.name,
|
|
10276
|
+
command: match[0],
|
|
10277
|
+
version: null,
|
|
10278
|
+
});
|
|
10279
|
+
}
|
|
10280
|
+
}
|
|
10281
|
+
return commands;
|
|
10282
|
+
}
|
|
10283
|
+
|
|
9970
10284
|
export function parseCloudBuildData(cbwData) {
|
|
9971
10285
|
const pkgList = [];
|
|
9972
10286
|
const keys_cache = {};
|
|
@@ -10833,6 +11147,9 @@ export function parseCsPkgData(pkgData, pkgFile) {
|
|
|
10833
11147
|
export function getPropertyGroupTextNodes(propsFiles) {
|
|
10834
11148
|
const matches = {};
|
|
10835
11149
|
for (const f of propsFiles) {
|
|
11150
|
+
if (!f) {
|
|
11151
|
+
continue;
|
|
11152
|
+
}
|
|
10836
11153
|
let projects;
|
|
10837
11154
|
try {
|
|
10838
11155
|
const data = readFileSync(f, { encoding: "utf-8" });
|
|
@@ -11623,9 +11940,7 @@ export function parseCsPkgLockData(csLockData, pkgLockFile) {
|
|
|
11623
11940
|
assetData.dependencies[aversion][adep].resolved;
|
|
11624
11941
|
} else if (
|
|
11625
11942
|
aversion.includes("/") &&
|
|
11626
|
-
assetData.dependencies[aversionNoRuntime]
|
|
11627
|
-
assetData.dependencies[aversionNoRuntime][adep] &&
|
|
11628
|
-
assetData.dependencies[aversionNoRuntime][adep].resolved
|
|
11943
|
+
assetData.dependencies[aversionNoRuntime]?.[adep]?.resolved
|
|
11629
11944
|
) {
|
|
11630
11945
|
adepResolvedVersion =
|
|
11631
11946
|
assetData.dependencies[aversionNoRuntime][adep].resolved;
|
|
@@ -12695,7 +13010,7 @@ export async function collectMvnDependencies(
|
|
|
12695
13010
|
}
|
|
12696
13011
|
|
|
12697
13012
|
// Clean up
|
|
12698
|
-
if (cleanup && tempDir
|
|
13013
|
+
if (cleanup && tempDir?.startsWith(tmpdir()) && rmSync) {
|
|
12699
13014
|
rmSync(tempDir, { recursive: true, force: true });
|
|
12700
13015
|
}
|
|
12701
13016
|
return jarNSMapping;
|
|
@@ -15056,7 +15371,11 @@ export async function getPipFrozenTree(
|
|
|
15056
15371
|
thoughtLog(
|
|
15057
15372
|
"Let me create a new virtual environment for installing the packages with pip.",
|
|
15058
15373
|
);
|
|
15059
|
-
|
|
15374
|
+
const venvCreationArgs = ["-m", "venv", tempVenvDir];
|
|
15375
|
+
if (isSecureMode) {
|
|
15376
|
+
venvCreationArgs.unshift("-S");
|
|
15377
|
+
}
|
|
15378
|
+
result = safeSpawnSync(PYTHON_CMD, venvCreationArgs, {
|
|
15060
15379
|
shell: isWin,
|
|
15061
15380
|
});
|
|
15062
15381
|
if (result.status !== 0 || result.error) {
|
|
@@ -15107,12 +15426,18 @@ export async function getPipFrozenTree(
|
|
|
15107
15426
|
"true",
|
|
15108
15427
|
"--local",
|
|
15109
15428
|
];
|
|
15429
|
+
if (isSecureMode) {
|
|
15430
|
+
poetryConfigArgs.unshift("-S");
|
|
15431
|
+
}
|
|
15110
15432
|
result = safeSpawnSync(PYTHON_CMD, poetryConfigArgs, {
|
|
15111
15433
|
cwd: basePath,
|
|
15112
15434
|
shell: isWin,
|
|
15113
15435
|
});
|
|
15114
15436
|
thoughtLog("Performing poetry install");
|
|
15115
15437
|
let poetryInstallArgs = ["-m", "poetry", "install", "-n", "--no-root"];
|
|
15438
|
+
if (isSecureMode) {
|
|
15439
|
+
poetryInstallArgs.unshift("-S");
|
|
15440
|
+
}
|
|
15116
15441
|
// Attempt to perform poetry install
|
|
15117
15442
|
result = safeSpawnSync(PYTHON_CMD, poetryInstallArgs, {
|
|
15118
15443
|
cwd: basePath,
|
|
@@ -15189,6 +15514,9 @@ export async function getPipFrozenTree(
|
|
|
15189
15514
|
"install",
|
|
15190
15515
|
"--disable-pip-version-check",
|
|
15191
15516
|
];
|
|
15517
|
+
if (isSecureMode) {
|
|
15518
|
+
pipInstallArgs.unshift("-S");
|
|
15519
|
+
}
|
|
15192
15520
|
// Requirements.txt could be called with any name so best to check for not setup.py and not pyproject.toml
|
|
15193
15521
|
if (
|
|
15194
15522
|
!reqOrSetupFile.endsWith("setup.py") &&
|
|
@@ -15509,7 +15837,11 @@ export function getPipTreeForPackages(
|
|
|
15509
15837
|
};
|
|
15510
15838
|
if (!process.env.VIRTUAL_ENV && !process.env.CONDA_PREFIX) {
|
|
15511
15839
|
// Create a virtual environment
|
|
15512
|
-
|
|
15840
|
+
const venvCreationArgs = ["-m", "venv", tempVenvDir];
|
|
15841
|
+
if (isSecureMode) {
|
|
15842
|
+
venvCreationArgs.unshift("-S");
|
|
15843
|
+
}
|
|
15844
|
+
result = safeSpawnSync(PYTHON_CMD, venvCreationArgs, {
|
|
15513
15845
|
shell: isWin,
|
|
15514
15846
|
});
|
|
15515
15847
|
if (result.status !== 0 || result.error) {
|
|
@@ -15532,6 +15864,9 @@ export function getPipTreeForPackages(
|
|
|
15532
15864
|
}
|
|
15533
15865
|
const python_cmd_for_tree = get_python_command_from_env(env);
|
|
15534
15866
|
let pipInstallArgs = ["-m", "pip", "install", "--disable-pip-version-check"];
|
|
15867
|
+
if (isSecureMode) {
|
|
15868
|
+
pipInstallArgs.unshift("-S");
|
|
15869
|
+
}
|
|
15535
15870
|
// Support for passing additional arguments to pip
|
|
15536
15871
|
// Eg: --python-version 3.10 --ignore-requires-python --no-warn-conflicts
|
|
15537
15872
|
if (process?.env?.PIP_INSTALL_ARGS) {
|