@cyclonedx/cdxgen 9.3.2 → 9.5.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 +53 -7
- package/analyzer.js +29 -4
- package/bin/cdxgen.js +22 -1
- package/bin/evinse.js +121 -0
- package/bin/repl.js +425 -0
- package/bin/verify.js +39 -0
- package/db.js +80 -0
- package/display.js +103 -1
- package/docker.js +1 -1
- package/docker.test.js +17 -5
- package/evinser.js +827 -0
- package/evinser.test.js +35 -0
- package/index.js +151 -72
- package/package.json +19 -11
- package/piptree.js +2 -2
- package/utils.js +447 -190
- package/utils.test.js +88 -18
package/utils.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { globSync } from "glob";
|
|
2
|
-
import { tmpdir, platform, freemem } from "node:os";
|
|
2
|
+
import { homedir, tmpdir, platform, freemem } from "node:os";
|
|
3
3
|
import {
|
|
4
4
|
dirname,
|
|
5
5
|
sep as _sep,
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import {
|
|
12
12
|
existsSync,
|
|
13
13
|
readFileSync,
|
|
14
|
+
lstatSync,
|
|
14
15
|
mkdtempSync,
|
|
15
16
|
rmSync,
|
|
16
17
|
copyFileSync,
|
|
@@ -26,19 +27,19 @@ let url = import.meta.url;
|
|
|
26
27
|
if (!url.startsWith("file://")) {
|
|
27
28
|
url = new URL(`file://${import.meta.url}`).toString();
|
|
28
29
|
}
|
|
29
|
-
const
|
|
30
|
+
const dirNameStr = import.meta ? dirname(fileURLToPath(url)) : __dirname;
|
|
30
31
|
|
|
31
32
|
const licenseMapping = JSON.parse(
|
|
32
|
-
readFileSync(join(
|
|
33
|
+
readFileSync(join(dirNameStr, "data", "lic-mapping.json"))
|
|
33
34
|
);
|
|
34
35
|
const vendorAliases = JSON.parse(
|
|
35
|
-
readFileSync(join(
|
|
36
|
+
readFileSync(join(dirNameStr, "data", "vendor-alias.json"))
|
|
36
37
|
);
|
|
37
38
|
const spdxLicenses = JSON.parse(
|
|
38
|
-
readFileSync(join(
|
|
39
|
+
readFileSync(join(dirNameStr, "data", "spdx-licenses.json"))
|
|
39
40
|
);
|
|
40
41
|
const knownLicenses = JSON.parse(
|
|
41
|
-
readFileSync(join(
|
|
42
|
+
readFileSync(join(dirNameStr, "data", "known-licenses.json"))
|
|
42
43
|
);
|
|
43
44
|
import { load } from "cheerio";
|
|
44
45
|
import { load as _load } from "js-yaml";
|
|
@@ -49,18 +50,19 @@ import StreamZip from "node-stream-zip";
|
|
|
49
50
|
import { parseEDNString } from "edn-data";
|
|
50
51
|
import { PackageURL } from "packageurl-js";
|
|
51
52
|
import { getTreeWithPlugin } from "./piptree.js";
|
|
53
|
+
import iconv from "iconv-lite";
|
|
52
54
|
|
|
53
|
-
const selfPJson = JSON.parse(readFileSync(join(
|
|
55
|
+
const selfPJson = JSON.parse(readFileSync(join(dirNameStr, "package.json")));
|
|
54
56
|
const _version = selfPJson.version;
|
|
55
57
|
|
|
56
58
|
// Refer to contrib/py-modules.py for a script to generate this list
|
|
57
59
|
// The script needs to be used once every few months to update this list
|
|
58
60
|
const PYTHON_STD_MODULES = JSON.parse(
|
|
59
|
-
readFileSync(join(
|
|
61
|
+
readFileSync(join(dirNameStr, "data", "python-stdlib.json"))
|
|
60
62
|
);
|
|
61
63
|
// Mapping between modules and package names
|
|
62
64
|
const PYPI_MODULE_PACKAGE_MAPPING = JSON.parse(
|
|
63
|
-
readFileSync(join(
|
|
65
|
+
readFileSync(join(dirNameStr, "data", "pypi-pkg-aliases.json"))
|
|
64
66
|
);
|
|
65
67
|
|
|
66
68
|
// Debug mode flag
|
|
@@ -168,10 +170,24 @@ export function getLicenses(pkg, format = "xml") {
|
|
|
168
170
|
} else if (l.startsWith("http")) {
|
|
169
171
|
if (!l.includes("opensource.org")) {
|
|
170
172
|
licenseContent.name = "CUSTOM";
|
|
173
|
+
} else {
|
|
174
|
+
const possibleId = l
|
|
175
|
+
.replace("http://www.opensource.org/licenses/", "")
|
|
176
|
+
.toUpperCase();
|
|
177
|
+
spdxLicenses.forEach((v) => {
|
|
178
|
+
if (v.toUpperCase() === possibleId) {
|
|
179
|
+
licenseContent.id = v;
|
|
180
|
+
}
|
|
181
|
+
});
|
|
171
182
|
}
|
|
172
183
|
if (l.includes("mit-license")) {
|
|
173
184
|
licenseContent.id = "MIT";
|
|
174
185
|
}
|
|
186
|
+
// We always need a name to avoid validation errors
|
|
187
|
+
// Issue: #469
|
|
188
|
+
if (!licenseContent.name && !licenseContent.id) {
|
|
189
|
+
licenseContent.name = "CUSTOM";
|
|
190
|
+
}
|
|
175
191
|
licenseContent.url = l;
|
|
176
192
|
} else {
|
|
177
193
|
licenseContent.name = l;
|
|
@@ -497,12 +513,16 @@ export const parsePkgJson = async (pkgJsonFile) => {
|
|
|
497
513
|
* Parse nodejs package lock file
|
|
498
514
|
*
|
|
499
515
|
* @param {string} pkgLockFile package-lock.json file
|
|
516
|
+
* @param {object} options Command line options
|
|
500
517
|
*/
|
|
501
|
-
export const parsePkgLock = async (pkgLockFile) => {
|
|
518
|
+
export const parsePkgLock = async (pkgLockFile, options = {}) => {
|
|
502
519
|
let pkgList = [];
|
|
503
520
|
const dependenciesList = [];
|
|
504
521
|
const depKeys = {};
|
|
505
522
|
let rootPkg = {};
|
|
523
|
+
if (!options) {
|
|
524
|
+
options = {};
|
|
525
|
+
}
|
|
506
526
|
if (existsSync(pkgLockFile)) {
|
|
507
527
|
const lockData = JSON.parse(readFileSync(pkgLockFile, "utf8"));
|
|
508
528
|
rootPkg.name = lockData.name || "";
|
|
@@ -510,16 +530,16 @@ export const parsePkgLock = async (pkgLockFile) => {
|
|
|
510
530
|
if (lockData.name && lockData.packages && lockData.packages[""]) {
|
|
511
531
|
// Build the initial dependency tree for the root package
|
|
512
532
|
rootPkg = {
|
|
513
|
-
group: "",
|
|
514
|
-
name: lockData.name,
|
|
515
|
-
version: lockData.version,
|
|
533
|
+
group: options.projectGroup || "",
|
|
534
|
+
name: options.projectName || lockData.name,
|
|
535
|
+
version: options.projectVersion || lockData.version,
|
|
516
536
|
type: "application",
|
|
517
537
|
"bom-ref": decodeURIComponent(
|
|
518
538
|
new PackageURL(
|
|
519
539
|
"npm",
|
|
520
|
-
"",
|
|
521
|
-
lockData.name,
|
|
522
|
-
lockData.version,
|
|
540
|
+
options.projectGroup || "",
|
|
541
|
+
options.projectName || lockData.name,
|
|
542
|
+
options.projectVersion || lockData.version,
|
|
523
543
|
null,
|
|
524
544
|
null
|
|
525
545
|
).toString()
|
|
@@ -531,10 +551,10 @@ export const parsePkgLock = async (pkgLockFile) => {
|
|
|
531
551
|
dirName = tmpA[tmpA.length - 1];
|
|
532
552
|
// v1 lock file
|
|
533
553
|
rootPkg = {
|
|
534
|
-
group: "",
|
|
535
|
-
name: lockData.name || dirName,
|
|
536
|
-
version: lockData.version || "",
|
|
537
|
-
type: "
|
|
554
|
+
group: options.projectGroup || "",
|
|
555
|
+
name: options.projectName || lockData.name || dirName,
|
|
556
|
+
version: options.projectVersion || lockData.version || "",
|
|
557
|
+
type: "npm"
|
|
538
558
|
};
|
|
539
559
|
}
|
|
540
560
|
if (rootPkg && rootPkg.name) {
|
|
@@ -625,6 +645,7 @@ export const yarnLockToIdentMap = function (lockData) {
|
|
|
625
645
|
const identMap = {};
|
|
626
646
|
let currentIdents = [];
|
|
627
647
|
lockData.split("\n").forEach((l) => {
|
|
648
|
+
l = l.replace("\r", "");
|
|
628
649
|
if (l === "\n" || l.startsWith("#")) {
|
|
629
650
|
return;
|
|
630
651
|
}
|
|
@@ -687,6 +708,7 @@ export const parseYarnLock = async function (yarnLockFile) {
|
|
|
687
708
|
const identMap = yarnLockToIdentMap(lockData);
|
|
688
709
|
let prefixAtSymbol = false;
|
|
689
710
|
lockData.split("\n").forEach((l) => {
|
|
711
|
+
l = l.replace("\r", "");
|
|
690
712
|
if (l === "\n" || l.startsWith("#")) {
|
|
691
713
|
return;
|
|
692
714
|
}
|
|
@@ -1443,7 +1465,7 @@ export const parseGradleDep = function (
|
|
|
1443
1465
|
}
|
|
1444
1466
|
let stack = [last_purl];
|
|
1445
1467
|
const depRegex =
|
|
1446
|
-
/^.*?--- +(?<
|
|
1468
|
+
/^.*?--- +(?<groupspecified>[^\s:]+) ?:(?<namespecified>[^\s:]+)(?::(?:{strictly [[]?)?(?<versionspecified>[^,\s:}]+))?(?:})?(?:[^->]* +-> +(?:(?<groupoverride>[^\s:]+):(?<nameoverride>[^\s:]+):)?(?<versionoverride>[^\s:]+))?/gm;
|
|
1447
1469
|
for (const rline of rawOutput.split("\n")) {
|
|
1448
1470
|
if (!rline) {
|
|
1449
1471
|
continue;
|
|
@@ -1461,16 +1483,9 @@ export const parseGradleDep = function (
|
|
|
1461
1483
|
rline.startsWith("\\--- ")
|
|
1462
1484
|
) {
|
|
1463
1485
|
last_level = 1;
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
stack = [last_project_purl];
|
|
1468
|
-
last_purl = last_project_purl;
|
|
1469
|
-
} else {
|
|
1470
|
-
last_project_purl = first_purl;
|
|
1471
|
-
last_purl = last_project_purl;
|
|
1472
|
-
stack = [first_purl];
|
|
1473
|
-
}
|
|
1486
|
+
last_project_purl = first_purl;
|
|
1487
|
+
last_purl = last_project_purl;
|
|
1488
|
+
stack = [first_purl];
|
|
1474
1489
|
}
|
|
1475
1490
|
if (rline.includes(" - ")) {
|
|
1476
1491
|
profileName = rline.split(" - ")[0];
|
|
@@ -1483,76 +1498,86 @@ export const parseGradleDep = function (
|
|
|
1483
1498
|
}
|
|
1484
1499
|
}
|
|
1485
1500
|
while ((match = depRegex.exec(rline))) {
|
|
1486
|
-
const [
|
|
1501
|
+
const [
|
|
1502
|
+
line,
|
|
1503
|
+
groupspecified,
|
|
1504
|
+
namespecified,
|
|
1505
|
+
versionspecified,
|
|
1506
|
+
groupoverride,
|
|
1507
|
+
nameoverride,
|
|
1508
|
+
versionoverride
|
|
1509
|
+
] = match;
|
|
1510
|
+
const group = groupoverride || groupspecified;
|
|
1511
|
+
const name = nameoverride || namespecified;
|
|
1487
1512
|
const version = versionoverride || versionspecified;
|
|
1488
|
-
const level = line.split(
|
|
1489
|
-
if (version !== undefined) {
|
|
1513
|
+
const level = line.split(groupspecified)[0].length / 5;
|
|
1514
|
+
if (version !== undefined || group === "project") {
|
|
1490
1515
|
let purlString = new PackageURL(
|
|
1491
1516
|
"maven",
|
|
1492
|
-
group,
|
|
1517
|
+
group !== "project" ? group : rootProjectGroup,
|
|
1493
1518
|
name,
|
|
1494
|
-
version,
|
|
1519
|
+
version !== undefined ? version : rootProjectVersion,
|
|
1495
1520
|
{ type: "jar" },
|
|
1496
1521
|
null
|
|
1497
1522
|
).toString();
|
|
1498
1523
|
purlString = decodeURIComponent(purlString);
|
|
1499
1524
|
keys_cache[purlString + "_" + last_purl] = true;
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
if (profileName) {
|
|
1514
|
-
adep.properties = [
|
|
1515
|
-
{
|
|
1516
|
-
name: "GradleProfileName",
|
|
1517
|
-
value: profileName
|
|
1518
|
-
}
|
|
1519
|
-
];
|
|
1520
|
-
}
|
|
1521
|
-
deps.push(adep);
|
|
1525
|
+
// Filter duplicates
|
|
1526
|
+
if (!deps_keys_cache[purlString]) {
|
|
1527
|
+
deps_keys_cache[purlString] = true;
|
|
1528
|
+
const adep = {
|
|
1529
|
+
group: group !== "project" ? group : rootProjectGroup,
|
|
1530
|
+
name: name,
|
|
1531
|
+
version: version !== undefined ? version : rootProjectVersion,
|
|
1532
|
+
qualifiers: { type: "jar" }
|
|
1533
|
+
};
|
|
1534
|
+
adep["purl"] = purlString;
|
|
1535
|
+
adep["bom-ref"] = purlString;
|
|
1536
|
+
if (scope) {
|
|
1537
|
+
adep["scope"] = scope;
|
|
1522
1538
|
}
|
|
1523
|
-
if (
|
|
1524
|
-
|
|
1539
|
+
if (profileName) {
|
|
1540
|
+
adep.properties = [
|
|
1541
|
+
{
|
|
1542
|
+
name: "GradleProfileName",
|
|
1543
|
+
value: profileName
|
|
1544
|
+
}
|
|
1545
|
+
];
|
|
1525
1546
|
}
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
}
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
}
|
|
1544
|
-
const last_stack =
|
|
1545
|
-
stack.length > 0 ? stack[stack.length - 1] : last_project_purl;
|
|
1546
|
-
const cnodes = level_trees[last_stack] || [];
|
|
1547
|
-
if (!cnodes.includes(purlString)) {
|
|
1548
|
-
cnodes.push(purlString);
|
|
1549
|
-
}
|
|
1550
|
-
level_trees[last_stack] = cnodes;
|
|
1547
|
+
deps.push(adep);
|
|
1548
|
+
}
|
|
1549
|
+
if (!level_trees[purlString]) {
|
|
1550
|
+
level_trees[purlString] = [];
|
|
1551
|
+
}
|
|
1552
|
+
if (level == 0) {
|
|
1553
|
+
stack = [first_purl];
|
|
1554
|
+
stack.push(purlString);
|
|
1555
|
+
} else if (last_purl === "") {
|
|
1556
|
+
stack.push(purlString);
|
|
1557
|
+
} else if (level > last_level) {
|
|
1558
|
+
const cnodes = level_trees[last_purl] || [];
|
|
1559
|
+
if (!cnodes.includes(purlString)) {
|
|
1560
|
+
cnodes.push(purlString);
|
|
1561
|
+
}
|
|
1562
|
+
level_trees[last_purl] = cnodes;
|
|
1563
|
+
if (stack[stack.length - 1] !== purlString) {
|
|
1551
1564
|
stack.push(purlString);
|
|
1552
1565
|
}
|
|
1553
|
-
|
|
1554
|
-
|
|
1566
|
+
} else {
|
|
1567
|
+
for (let i = level; i <= last_level; i++) {
|
|
1568
|
+
stack.pop();
|
|
1569
|
+
}
|
|
1570
|
+
const last_stack =
|
|
1571
|
+
stack.length > 0 ? stack[stack.length - 1] : last_project_purl;
|
|
1572
|
+
const cnodes = level_trees[last_stack] || [];
|
|
1573
|
+
if (!cnodes.includes(purlString)) {
|
|
1574
|
+
cnodes.push(purlString);
|
|
1575
|
+
}
|
|
1576
|
+
level_trees[last_stack] = cnodes;
|
|
1577
|
+
stack.push(purlString);
|
|
1555
1578
|
}
|
|
1579
|
+
last_level = level;
|
|
1580
|
+
last_purl = purlString;
|
|
1556
1581
|
}
|
|
1557
1582
|
}
|
|
1558
1583
|
}
|
|
@@ -1664,7 +1689,7 @@ export const parseGradleProjects = function (rawOutput) {
|
|
|
1664
1689
|
if (typeof rawOutput === "string") {
|
|
1665
1690
|
const tmpA = rawOutput.split("\n");
|
|
1666
1691
|
tmpA.forEach((l) => {
|
|
1667
|
-
l = l.replace("\r", "")
|
|
1692
|
+
l = l.replace("\r", "");
|
|
1668
1693
|
if (l.startsWith("Root project ")) {
|
|
1669
1694
|
rootProject = l
|
|
1670
1695
|
.split("Root project ")[1]
|
|
@@ -1676,7 +1701,8 @@ export const parseGradleProjects = function (rawOutput) {
|
|
|
1676
1701
|
const projName = tmpB[1].split(" ")[0].replace(/'/g, "");
|
|
1677
1702
|
// Include all projects including test projects
|
|
1678
1703
|
if (projName.startsWith(":")) {
|
|
1679
|
-
|
|
1704
|
+
// Handle the case where the project name could have a space. Eg: +--- project :app (*)
|
|
1705
|
+
projects.add(projName.split(" ")[0]);
|
|
1680
1706
|
}
|
|
1681
1707
|
}
|
|
1682
1708
|
} else if (l.includes("--- project ")) {
|
|
@@ -1684,7 +1710,7 @@ export const parseGradleProjects = function (rawOutput) {
|
|
|
1684
1710
|
if (tmpB && tmpB.length > 1) {
|
|
1685
1711
|
const projName = tmpB[1];
|
|
1686
1712
|
if (projName.startsWith(":")) {
|
|
1687
|
-
projects.add(projName);
|
|
1713
|
+
projects.add(projName.split(" ")[0]);
|
|
1688
1714
|
}
|
|
1689
1715
|
}
|
|
1690
1716
|
}
|
|
@@ -1726,7 +1752,7 @@ export const parseGradleProperties = function (rawOutput) {
|
|
|
1726
1752
|
const spStrs = tmpB[1].replace(/[[\]']/g, "").split(", ");
|
|
1727
1753
|
const tmpprojects = spStrs
|
|
1728
1754
|
.flatMap((s) => s.replace("project ", ""))
|
|
1729
|
-
.filter((s) => !["
|
|
1755
|
+
.filter((s) => ![""].includes(s.trim()));
|
|
1730
1756
|
tmpprojects.forEach(projects.add, projects);
|
|
1731
1757
|
}
|
|
1732
1758
|
}
|
|
@@ -1893,6 +1919,7 @@ export const parseKVDep = function (rawOutput) {
|
|
|
1893
1919
|
if (typeof rawOutput === "string") {
|
|
1894
1920
|
const deps = [];
|
|
1895
1921
|
rawOutput.split("\n").forEach((l) => {
|
|
1922
|
+
l = l.replace("\r", "");
|
|
1896
1923
|
const tmpA = l.split(":");
|
|
1897
1924
|
if (tmpA.length === 3) {
|
|
1898
1925
|
deps.push({
|
|
@@ -2331,6 +2358,9 @@ export const parsePyProjectToml = (tomlFile) => {
|
|
|
2331
2358
|
pkg.name = value;
|
|
2332
2359
|
break;
|
|
2333
2360
|
case "version":
|
|
2361
|
+
if (value.includes("{")) {
|
|
2362
|
+
value = "latest";
|
|
2363
|
+
}
|
|
2334
2364
|
pkg.version = value;
|
|
2335
2365
|
break;
|
|
2336
2366
|
case "authors":
|
|
@@ -2365,6 +2395,7 @@ export const parsePoetrylockData = async function (lockData, lockFile) {
|
|
|
2365
2395
|
return pkgList;
|
|
2366
2396
|
}
|
|
2367
2397
|
lockData.split("\n").forEach((l) => {
|
|
2398
|
+
l = l.replace("\r", "");
|
|
2368
2399
|
let key = null;
|
|
2369
2400
|
let value = null;
|
|
2370
2401
|
// Package section starts with this marker
|
|
@@ -2420,6 +2451,7 @@ export async function parseReqFile(reqData, fetchDepsInfo) {
|
|
|
2420
2451
|
const pkgList = [];
|
|
2421
2452
|
let compScope = undefined;
|
|
2422
2453
|
reqData.split("\n").forEach((l) => {
|
|
2454
|
+
l = l.replace("\r", "");
|
|
2423
2455
|
l = l.trim();
|
|
2424
2456
|
if (l.startsWith("Skipping line") || l.startsWith("(add")) {
|
|
2425
2457
|
return;
|
|
@@ -3146,6 +3178,7 @@ export const parseGemfileLockData = async function (gemLockData) {
|
|
|
3146
3178
|
let specsFound = false;
|
|
3147
3179
|
gemLockData.split("\n").forEach((l) => {
|
|
3148
3180
|
l = l.trim();
|
|
3181
|
+
l = l.replace("\r", "");
|
|
3149
3182
|
if (specsFound) {
|
|
3150
3183
|
const tmpA = l.split(" ");
|
|
3151
3184
|
if (tmpA && tmpA.length == 2) {
|
|
@@ -3358,6 +3391,7 @@ export const parseCargoData = async function (cargoData) {
|
|
|
3358
3391
|
cargoData.split("\n").forEach((l) => {
|
|
3359
3392
|
let key = null;
|
|
3360
3393
|
let value = null;
|
|
3394
|
+
l = l.replace("\r", "");
|
|
3361
3395
|
// Ignore version = 3 found at the top of newer lock files
|
|
3362
3396
|
if (!pkg && l.startsWith("version =")) {
|
|
3363
3397
|
return;
|
|
@@ -3402,6 +3436,7 @@ export const parseCargoAuditableData = async function (cargoData) {
|
|
|
3402
3436
|
return pkgList;
|
|
3403
3437
|
}
|
|
3404
3438
|
cargoData.split("\n").forEach((l) => {
|
|
3439
|
+
l = l.replace("\r", "");
|
|
3405
3440
|
const tmpA = l.split("\t");
|
|
3406
3441
|
if (tmpA && tmpA.length > 2) {
|
|
3407
3442
|
let group = dirname(tmpA[0].trim());
|
|
@@ -3433,6 +3468,7 @@ export const parsePubLockData = async function (pubLockData) {
|
|
|
3433
3468
|
pubLockData.split("\n").forEach((l) => {
|
|
3434
3469
|
let key = null;
|
|
3435
3470
|
let value = null;
|
|
3471
|
+
l = l.replace("\r", "");
|
|
3436
3472
|
if (!pkg && (l.startsWith("sdks:") || !l.startsWith(" "))) {
|
|
3437
3473
|
return;
|
|
3438
3474
|
}
|
|
@@ -3848,6 +3884,7 @@ export const parseCabalData = function (cabalData) {
|
|
|
3848
3884
|
if (!l.includes(" ==")) {
|
|
3849
3885
|
return;
|
|
3850
3886
|
}
|
|
3887
|
+
l = l.replace("\r", "");
|
|
3851
3888
|
if (l.includes(" ==")) {
|
|
3852
3889
|
const tmpA = l.split(" ==");
|
|
3853
3890
|
const name = tmpA[0]
|
|
@@ -3875,6 +3912,7 @@ export const parseMixLockData = function (mixData) {
|
|
|
3875
3912
|
if (!l.includes(":hex")) {
|
|
3876
3913
|
return;
|
|
3877
3914
|
}
|
|
3915
|
+
l = l.replace("\r", "");
|
|
3878
3916
|
if (l.includes(":hex")) {
|
|
3879
3917
|
const tmpA = l.split(",");
|
|
3880
3918
|
if (tmpA.length > 3) {
|
|
@@ -4099,13 +4137,19 @@ export const parseEdnData = function (rawEdnData) {
|
|
|
4099
4137
|
};
|
|
4100
4138
|
|
|
4101
4139
|
export const parseNupkg = async function (nupkgFile) {
|
|
4102
|
-
const pkgList = [];
|
|
4103
|
-
const pkg = { group: "" };
|
|
4104
4140
|
let nuspecData = await readZipEntry(nupkgFile, ".nuspec");
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
nuspecData = nuspecData.slice(1);
|
|
4141
|
+
if (!nuspecData) {
|
|
4142
|
+
return [];
|
|
4108
4143
|
}
|
|
4144
|
+
if (nuspecData.charCodeAt(0) === 65533) {
|
|
4145
|
+
nuspecData = await readZipEntry(nupkgFile, ".nuspec", "ucs2");
|
|
4146
|
+
}
|
|
4147
|
+
return await parseNuspecData(nupkgFile, nuspecData);
|
|
4148
|
+
};
|
|
4149
|
+
|
|
4150
|
+
export const parseNuspecData = async function (nupkgFile, nuspecData) {
|
|
4151
|
+
const pkgList = [];
|
|
4152
|
+
const pkg = { group: "" };
|
|
4109
4153
|
let npkg = undefined;
|
|
4110
4154
|
try {
|
|
4111
4155
|
npkg = xml2js(nuspecData, {
|
|
@@ -4117,9 +4161,13 @@ export const parseNupkg = async function (nupkgFile) {
|
|
|
4117
4161
|
commentKey: "value"
|
|
4118
4162
|
}).package;
|
|
4119
4163
|
} catch (e) {
|
|
4120
|
-
// If we are parsing with invalid encoding unicode replacement character is used
|
|
4164
|
+
// If we are parsing with invalid encoding, unicode replacement character is used
|
|
4121
4165
|
if (nuspecData.charCodeAt(0) === 65533) {
|
|
4122
4166
|
console.log(`Unable to parse ${nupkgFile} in utf-8 mode`);
|
|
4167
|
+
} else {
|
|
4168
|
+
console.log(
|
|
4169
|
+
"Unable to parse this package. Tried utf-8 and ucs2 encoding."
|
|
4170
|
+
);
|
|
4123
4171
|
}
|
|
4124
4172
|
}
|
|
4125
4173
|
if (!npkg) {
|
|
@@ -4832,48 +4880,100 @@ export const parseSwiftResolved = (resolvedFile) => {
|
|
|
4832
4880
|
*
|
|
4833
4881
|
* @param {string} mavenCmd Maven command to use
|
|
4834
4882
|
* @param {string} basePath Path to the maven project
|
|
4883
|
+
* @param {boolean} cleanup Remove temporary directories
|
|
4884
|
+
* @param {boolean} includeCacheDir Include maven and gradle cache directories
|
|
4835
4885
|
*/
|
|
4836
|
-
export const collectMvnDependencies = function (
|
|
4837
|
-
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
mavenCmd,
|
|
4843
|
-
[
|
|
4844
|
-
"dependency:copy-dependencies",
|
|
4845
|
-
`-DoutputDirectory=${tempDir}`,
|
|
4846
|
-
"-DexcludeTransitive=true",
|
|
4847
|
-
"-DincludeScope=runtime",
|
|
4848
|
-
"-U",
|
|
4849
|
-
"-Dmdep.prependGroupId=" + (process.env.MAVEN_PREPEND_GROUP || "false"),
|
|
4850
|
-
"-Dmdep.stripVersion=" + (process.env.MAVEN_STRIP_VERSION || "false")
|
|
4851
|
-
],
|
|
4852
|
-
{ cwd: basePath, encoding: "utf-8" }
|
|
4853
|
-
);
|
|
4886
|
+
export const collectMvnDependencies = function (
|
|
4887
|
+
mavenCmd,
|
|
4888
|
+
basePath,
|
|
4889
|
+
cleanup = true,
|
|
4890
|
+
includeCacheDir = false
|
|
4891
|
+
) {
|
|
4854
4892
|
let jarNSMapping = {};
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4893
|
+
const MAVEN_CACHE_DIR =
|
|
4894
|
+
process.env.MAVEN_CACHE_DIR || join(homedir(), ".m2", "repository");
|
|
4895
|
+
const tempDir = mkdtempSync(join(tmpdir(), "mvn-deps-"));
|
|
4896
|
+
const copyArgs = [
|
|
4897
|
+
"dependency:copy-dependencies",
|
|
4898
|
+
`-DoutputDirectory=${tempDir}`,
|
|
4899
|
+
"-U",
|
|
4900
|
+
"-Dmdep.copyPom=true",
|
|
4901
|
+
"-Dmdep.useRepositoryLayout=true",
|
|
4902
|
+
"-Dmdep.includeScope=compile",
|
|
4903
|
+
"-Dmdep.prependGroupId=" + (process.env.MAVEN_PREPEND_GROUP || "false"),
|
|
4904
|
+
"-Dmdep.stripVersion=" + (process.env.MAVEN_STRIP_VERSION || "false")
|
|
4905
|
+
];
|
|
4906
|
+
if (basePath && basePath !== MAVEN_CACHE_DIR) {
|
|
4860
4907
|
console.log(
|
|
4861
|
-
|
|
4908
|
+
`Executing '${mavenCmd} dependency:copy-dependencies ${copyArgs.join(
|
|
4909
|
+
" "
|
|
4910
|
+
)}' in ${basePath}`
|
|
4862
4911
|
);
|
|
4863
|
-
|
|
4864
|
-
|
|
4912
|
+
const result = spawnSync(mavenCmd, copyArgs, {
|
|
4913
|
+
cwd: basePath,
|
|
4914
|
+
encoding: "utf-8"
|
|
4915
|
+
});
|
|
4916
|
+
if (result.status !== 0 || result.error) {
|
|
4917
|
+
console.error(result.stdout, result.stderr);
|
|
4918
|
+
console.log(
|
|
4919
|
+
"Resolve the above maven error. You can try the following remediation tips:\n"
|
|
4920
|
+
);
|
|
4921
|
+
console.log(
|
|
4922
|
+
"1. Check if the correct version of maven is installed and available in the PATH."
|
|
4923
|
+
);
|
|
4924
|
+
console.log(
|
|
4925
|
+
"2. Perform 'mvn compile package' before invoking this command. Fix any errors found during this invocation."
|
|
4926
|
+
);
|
|
4927
|
+
console.log(
|
|
4928
|
+
"3. Ensure the temporary directory is available and has sufficient disk space to copy all the artifacts."
|
|
4929
|
+
);
|
|
4930
|
+
} else {
|
|
4931
|
+
jarNSMapping = collectJarNS(tempDir);
|
|
4932
|
+
}
|
|
4933
|
+
}
|
|
4934
|
+
if (includeCacheDir || basePath === MAVEN_CACHE_DIR) {
|
|
4935
|
+
// slow operation
|
|
4936
|
+
jarNSMapping = collectJarNS(MAVEN_CACHE_DIR);
|
|
4937
|
+
}
|
|
4938
|
+
|
|
4939
|
+
// Clean up
|
|
4940
|
+
if (cleanup && tempDir && tempDir.startsWith(tmpdir()) && rmSync) {
|
|
4941
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
4942
|
+
}
|
|
4943
|
+
return jarNSMapping;
|
|
4944
|
+
};
|
|
4945
|
+
|
|
4946
|
+
export const collectGradleDependencies = (
|
|
4947
|
+
gradleCmd,
|
|
4948
|
+
basePath,
|
|
4949
|
+
cleanup = true, // eslint-disable-line no-unused-vars
|
|
4950
|
+
includeCacheDir = false // eslint-disable-line no-unused-vars
|
|
4951
|
+
) => {
|
|
4952
|
+
// HELP WANTED: We need an init script that mimics maven copy-dependencies that only collects the project specific jars and poms
|
|
4953
|
+
// Construct gradle cache directory
|
|
4954
|
+
let GRADLE_CACHE_DIR =
|
|
4955
|
+
process.env.GRADLE_CACHE_DIR ||
|
|
4956
|
+
join(homedir(), ".gradle", "caches", "modules-2", "files-2.1");
|
|
4957
|
+
if (process.env.GRADLE_USER_HOME) {
|
|
4958
|
+
GRADLE_CACHE_DIR = join(
|
|
4959
|
+
process.env.GRADLE_USER_HOME,
|
|
4960
|
+
"caches",
|
|
4961
|
+
"modules-2",
|
|
4962
|
+
"files-2.1"
|
|
4865
4963
|
);
|
|
4964
|
+
}
|
|
4965
|
+
if (DEBUG_MODE) {
|
|
4966
|
+
console.log("Collecting jars from", GRADLE_CACHE_DIR);
|
|
4866
4967
|
console.log(
|
|
4867
|
-
"
|
|
4968
|
+
"To improve performance, ensure only the project dependencies are present in this cache location."
|
|
4868
4969
|
);
|
|
4869
|
-
} else {
|
|
4870
|
-
jarNSMapping = collectJarNS(tempDir);
|
|
4871
4970
|
}
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4971
|
+
const pomPathMap = {};
|
|
4972
|
+
const pomFiles = getAllFiles(GRADLE_CACHE_DIR, "**/*.pom");
|
|
4973
|
+
for (const apom of pomFiles) {
|
|
4974
|
+
pomPathMap[basename(apom)] = apom;
|
|
4876
4975
|
}
|
|
4976
|
+
const jarNSMapping = collectJarNS(GRADLE_CACHE_DIR, pomPathMap);
|
|
4877
4977
|
return jarNSMapping;
|
|
4878
4978
|
};
|
|
4879
4979
|
|
|
@@ -4881,10 +4981,11 @@ export const collectMvnDependencies = function (mavenCmd, basePath) {
|
|
|
4881
4981
|
* Method to collect class names from all jars in a directory
|
|
4882
4982
|
*
|
|
4883
4983
|
* @param {string} jarPath Path containing jars
|
|
4984
|
+
* @param {object} pomPathMap Map containing jar to pom names. Required to successful parse gradle cache.
|
|
4884
4985
|
*
|
|
4885
4986
|
* @return object containing jar name and class list
|
|
4886
4987
|
*/
|
|
4887
|
-
export const collectJarNS = function (jarPath) {
|
|
4988
|
+
export const collectJarNS = function (jarPath, pomPathMap = {}) {
|
|
4888
4989
|
const jarNSMapping = {};
|
|
4889
4990
|
console.log(
|
|
4890
4991
|
`About to identify class names for all jars in the path ${jarPath}`
|
|
@@ -4893,31 +4994,72 @@ export const collectJarNS = function (jarPath) {
|
|
|
4893
4994
|
const jarFiles = getAllFiles(jarPath, "**/*.jar");
|
|
4894
4995
|
if (jarFiles && jarFiles.length) {
|
|
4895
4996
|
for (const jf of jarFiles) {
|
|
4896
|
-
const jarname =
|
|
4997
|
+
const jarname = jf;
|
|
4998
|
+
const pomname =
|
|
4999
|
+
pomPathMap[basename(jf).replace(".jar", ".pom")] ||
|
|
5000
|
+
jarname.replace(".jar", ".pom");
|
|
5001
|
+
let pomData = undefined;
|
|
5002
|
+
let purl = undefined;
|
|
5003
|
+
if (existsSync(pomname)) {
|
|
5004
|
+
pomData = parsePomXml(readFileSync(pomname, "utf-8"));
|
|
5005
|
+
if (pomData) {
|
|
5006
|
+
const purlObj = new PackageURL(
|
|
5007
|
+
"maven",
|
|
5008
|
+
pomData.groupId || "",
|
|
5009
|
+
pomData.artifactId,
|
|
5010
|
+
pomData.version,
|
|
5011
|
+
{ type: "jar" },
|
|
5012
|
+
null
|
|
5013
|
+
);
|
|
5014
|
+
purl = purlObj.toString();
|
|
5015
|
+
}
|
|
5016
|
+
} else if (jf.includes(join(".gradle", "caches"))) {
|
|
5017
|
+
// Let's try our best to construct a purl for gradle cache entries of the form
|
|
5018
|
+
// .gradle/caches/modules-2/files-2.1/org.xmlresolver/xmlresolver/4.2.0/f4dbdaa83d636dcac91c9003ffa7fb173173fe8d/xmlresolver-4.2.0-data.jar
|
|
5019
|
+
const tmpA = jf.split(join("files-2.1", ""));
|
|
5020
|
+
if (tmpA && tmpA.length) {
|
|
5021
|
+
let tmpJarPath = tmpA[tmpA.length - 1];
|
|
5022
|
+
// This would yield xmlresolver-4.2.0-data.jar
|
|
5023
|
+
const jarFileName = basename(tmpJarPath);
|
|
5024
|
+
let tmpDirParts = dirname(tmpJarPath).split(_sep);
|
|
5025
|
+
// This would remove the hash from the end of the directory name
|
|
5026
|
+
tmpDirParts.pop();
|
|
5027
|
+
// Retrieve the version
|
|
5028
|
+
const jarVersion = tmpDirParts.pop();
|
|
5029
|
+
// The result would form the group name
|
|
5030
|
+
const jarGroupName = tmpDirParts.join(".").replace(/^\./, "");
|
|
5031
|
+
const purlObj = new PackageURL(
|
|
5032
|
+
"maven",
|
|
5033
|
+
jarGroupName,
|
|
5034
|
+
jarFileName.replace(`-${jarVersion}`, ""),
|
|
5035
|
+
jarVersion,
|
|
5036
|
+
{ type: "jar" },
|
|
5037
|
+
null
|
|
5038
|
+
);
|
|
5039
|
+
purl = purlObj.toString();
|
|
5040
|
+
}
|
|
5041
|
+
}
|
|
4897
5042
|
if (DEBUG_MODE) {
|
|
4898
5043
|
console.log(`Executing 'jar tf ${jf}'`);
|
|
4899
5044
|
}
|
|
4900
5045
|
const jarResult = spawnSync("jar", ["-tf", jf], { encoding: "utf-8" });
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
});
|
|
4919
|
-
jarNSMapping[jarname] = nsList;
|
|
4920
|
-
}
|
|
5046
|
+
const consolelines = (jarResult.stdout || "").split("\n");
|
|
5047
|
+
const nsList = consolelines
|
|
5048
|
+
.filter((l) => {
|
|
5049
|
+
return (
|
|
5050
|
+
l.includes(".class") &&
|
|
5051
|
+
!l.includes("-INF") &&
|
|
5052
|
+
!l.includes("module-info")
|
|
5053
|
+
);
|
|
5054
|
+
})
|
|
5055
|
+
.map((e) => {
|
|
5056
|
+
return e.replace(".class", "").replace(/\/$/, "").replace(/\//g, ".");
|
|
5057
|
+
});
|
|
5058
|
+
jarNSMapping[purl || jf] = {
|
|
5059
|
+
jarFile: jf,
|
|
5060
|
+
pom: pomData,
|
|
5061
|
+
namespaces: nsList
|
|
5062
|
+
};
|
|
4921
5063
|
}
|
|
4922
5064
|
if (!jarNSMapping) {
|
|
4923
5065
|
console.log(`Unable to determine class names for the jars in ${jarPath}`);
|
|
@@ -4925,12 +5067,69 @@ export const collectJarNS = function (jarPath) {
|
|
|
4925
5067
|
} else {
|
|
4926
5068
|
console.log(`${jarPath} did not contain any jars.`);
|
|
4927
5069
|
}
|
|
4928
|
-
if (DEBUG_MODE) {
|
|
4929
|
-
console.log("JAR Namespace mapping", jarNSMapping);
|
|
4930
|
-
}
|
|
4931
5070
|
return jarNSMapping;
|
|
4932
5071
|
};
|
|
4933
5072
|
|
|
5073
|
+
export const convertJarNSToPackages = (jarNSMapping) => {
|
|
5074
|
+
let pkgList = [];
|
|
5075
|
+
for (const purl of Object.keys(jarNSMapping)) {
|
|
5076
|
+
let { jarFile, pom, namespaces } = jarNSMapping[purl];
|
|
5077
|
+
if (!pom) {
|
|
5078
|
+
pom = {};
|
|
5079
|
+
}
|
|
5080
|
+
let purlObj = undefined;
|
|
5081
|
+
try {
|
|
5082
|
+
purlObj = PackageURL.fromString(purl);
|
|
5083
|
+
} catch (e) {
|
|
5084
|
+
// ignore
|
|
5085
|
+
purlObj = {};
|
|
5086
|
+
}
|
|
5087
|
+
const name = pom.artifactId || purlObj.name;
|
|
5088
|
+
if (!name) {
|
|
5089
|
+
continue;
|
|
5090
|
+
}
|
|
5091
|
+
const apackage = {
|
|
5092
|
+
name,
|
|
5093
|
+
group: pom.groupId || purlObj.namespace || "",
|
|
5094
|
+
version: pom.version || purlObj.version,
|
|
5095
|
+
description: (pom.description || "").trim(),
|
|
5096
|
+
purl,
|
|
5097
|
+
"bom-ref": purl,
|
|
5098
|
+
evidence: {
|
|
5099
|
+
identity: {
|
|
5100
|
+
field: "purl",
|
|
5101
|
+
confidence: 0.5,
|
|
5102
|
+
methods: [
|
|
5103
|
+
{
|
|
5104
|
+
technique: "filename",
|
|
5105
|
+
confidence: 1,
|
|
5106
|
+
value: jarFile
|
|
5107
|
+
}
|
|
5108
|
+
]
|
|
5109
|
+
}
|
|
5110
|
+
},
|
|
5111
|
+
properties: [
|
|
5112
|
+
{
|
|
5113
|
+
name: "SrcFile",
|
|
5114
|
+
value: jarFile
|
|
5115
|
+
},
|
|
5116
|
+
{
|
|
5117
|
+
name: "Namespaces",
|
|
5118
|
+
value: namespaces.join("\n")
|
|
5119
|
+
}
|
|
5120
|
+
]
|
|
5121
|
+
};
|
|
5122
|
+
if (pom.url) {
|
|
5123
|
+
apackage["homepage"] = { url: pom.url };
|
|
5124
|
+
}
|
|
5125
|
+
if (pom.scm) {
|
|
5126
|
+
apackage["repository"] = { url: pom.scm };
|
|
5127
|
+
}
|
|
5128
|
+
pkgList.push(apackage);
|
|
5129
|
+
}
|
|
5130
|
+
return pkgList;
|
|
5131
|
+
};
|
|
5132
|
+
|
|
4934
5133
|
export const parsePomXml = function (pomXmlData) {
|
|
4935
5134
|
if (!pomXmlData) {
|
|
4936
5135
|
return undefined;
|
|
@@ -4969,6 +5168,7 @@ export const parseJarManifest = function (jarMetadata) {
|
|
|
4969
5168
|
return metadata;
|
|
4970
5169
|
}
|
|
4971
5170
|
jarMetadata.split("\n").forEach((l) => {
|
|
5171
|
+
l = l.replace("\r", "");
|
|
4972
5172
|
if (l.includes(": ")) {
|
|
4973
5173
|
const tmpA = l.split(": ");
|
|
4974
5174
|
if (tmpA && tmpA.length === 2) {
|
|
@@ -4999,10 +5199,19 @@ export const extractJarArchive = function (jarFile, tempDir) {
|
|
|
4999
5199
|
const fname = basename(jarFile);
|
|
5000
5200
|
let pomname = undefined;
|
|
5001
5201
|
// If there is a pom file in the same directory, try to use it
|
|
5202
|
+
let manifestname = join(dirname(jarFile), "META-INF", "MANIFEST.MF");
|
|
5203
|
+
// Issue 439: Current implementation checks for existance of a .pom file, but .pom file is not used.
|
|
5204
|
+
// Instead code expects to find META-INF/MANIFEST.MF in the same folder as a .jar file.
|
|
5205
|
+
// For now check for presence of both .pom and MANIFEST.MF files.
|
|
5002
5206
|
if (jarFile.endsWith(".jar")) {
|
|
5003
5207
|
pomname = jarFile.replace(".jar", ".pom");
|
|
5004
5208
|
}
|
|
5005
|
-
if (
|
|
5209
|
+
if (
|
|
5210
|
+
pomname &&
|
|
5211
|
+
existsSync(pomname) &&
|
|
5212
|
+
manifestname &&
|
|
5213
|
+
existsSync(manifestname)
|
|
5214
|
+
) {
|
|
5006
5215
|
tempDir = dirname(jarFile);
|
|
5007
5216
|
} else if (!existsSync(join(tempDir, fname))) {
|
|
5008
5217
|
// Only copy if the file doesn't exist
|
|
@@ -5264,10 +5473,15 @@ export const sbtPluginsPath = function (projectPath) {
|
|
|
5264
5473
|
*
|
|
5265
5474
|
* @param {string} zipFile Zip file to read
|
|
5266
5475
|
* @param {string} filePattern File pattern
|
|
5476
|
+
* @param {string} contentEncoding Encoding. Defaults to utf-8
|
|
5267
5477
|
*
|
|
5268
5478
|
* @returns File contents
|
|
5269
5479
|
*/
|
|
5270
|
-
export const readZipEntry = async function (
|
|
5480
|
+
export const readZipEntry = async function (
|
|
5481
|
+
zipFile,
|
|
5482
|
+
filePattern,
|
|
5483
|
+
contentEncoding = "utf-8"
|
|
5484
|
+
) {
|
|
5271
5485
|
let retData = undefined;
|
|
5272
5486
|
try {
|
|
5273
5487
|
const zip = new StreamZip.async({ file: zipFile });
|
|
@@ -5282,7 +5496,7 @@ export const readZipEntry = async function (zipFile, filePattern) {
|
|
|
5282
5496
|
}
|
|
5283
5497
|
if (entry.name.endsWith(filePattern)) {
|
|
5284
5498
|
const fileData = await zip.entryData(entry.name);
|
|
5285
|
-
retData = Buffer.from(fileData)
|
|
5499
|
+
retData = iconv.decode(Buffer.from(fileData), contentEncoding);
|
|
5286
5500
|
break;
|
|
5287
5501
|
}
|
|
5288
5502
|
}
|
|
@@ -5389,7 +5603,7 @@ export const getAtomCommand = () => {
|
|
|
5389
5603
|
}
|
|
5390
5604
|
const NODE_CMD = process.env.NODE_CMD || "node";
|
|
5391
5605
|
const localAtom = join(
|
|
5392
|
-
|
|
5606
|
+
dirNameStr,
|
|
5393
5607
|
"node_modules",
|
|
5394
5608
|
"@appthreat",
|
|
5395
5609
|
"atom",
|
|
@@ -5402,6 +5616,8 @@ export const getAtomCommand = () => {
|
|
|
5402
5616
|
};
|
|
5403
5617
|
|
|
5404
5618
|
export const executeAtom = (src, args) => {
|
|
5619
|
+
let cwd =
|
|
5620
|
+
existsSync(src) && lstatSync(src).isDirectory() ? src : dirname(src);
|
|
5405
5621
|
let ATOM_BIN = getAtomCommand();
|
|
5406
5622
|
if (ATOM_BIN.includes(" ")) {
|
|
5407
5623
|
const tmpA = ATOM_BIN.split(" ");
|
|
@@ -5419,7 +5635,7 @@ export const executeAtom = (src, args) => {
|
|
|
5419
5635
|
JAVA_OPTS: `-Xms${freeMemoryGB}G -Xmx${freeMemoryGB}G`
|
|
5420
5636
|
};
|
|
5421
5637
|
const result = spawnSync(ATOM_BIN, args, {
|
|
5422
|
-
cwd
|
|
5638
|
+
cwd,
|
|
5423
5639
|
encoding: "utf-8",
|
|
5424
5640
|
timeout: TIMEOUT_MS,
|
|
5425
5641
|
env
|
|
@@ -5548,7 +5764,11 @@ export const getPipFrozenTree = (basePath, reqOrSetupFile, tempVenvDir) => {
|
|
|
5548
5764
|
*
|
|
5549
5765
|
* By checking the environment variable "VIRTUAL_ENV" we decide whether to create an env or not
|
|
5550
5766
|
*/
|
|
5551
|
-
if (
|
|
5767
|
+
if (
|
|
5768
|
+
!process.env.VIRTUAL_ENV &&
|
|
5769
|
+
reqOrSetupFile &&
|
|
5770
|
+
!reqOrSetupFile.endsWith("poetry.lock")
|
|
5771
|
+
) {
|
|
5552
5772
|
result = spawnSync(PYTHON_CMD, ["-m", "venv", tempVenvDir], {
|
|
5553
5773
|
encoding: "utf-8"
|
|
5554
5774
|
});
|
|
@@ -5562,29 +5782,43 @@ export const getPipFrozenTree = (basePath, reqOrSetupFile, tempVenvDir) => {
|
|
|
5562
5782
|
}
|
|
5563
5783
|
}
|
|
5564
5784
|
} else {
|
|
5785
|
+
if (DEBUG_MODE) {
|
|
5786
|
+
console.log(join("Using virtual environment in ", tempVenvDir));
|
|
5787
|
+
}
|
|
5565
5788
|
env.VIRTUAL_ENV = tempVenvDir;
|
|
5566
5789
|
env.PATH = `${join(
|
|
5567
5790
|
tempVenvDir,
|
|
5568
|
-
platform()
|
|
5791
|
+
platform() === "win32" ? "Scripts" : "bin"
|
|
5569
5792
|
)}${_delimiter}${process.env.PATH || ""}`;
|
|
5570
5793
|
}
|
|
5571
5794
|
}
|
|
5572
5795
|
/**
|
|
5573
5796
|
* We now have a virtual environment so we can attempt to install the project and perform
|
|
5574
5797
|
* pip freeze to collect the packages that got installed.
|
|
5798
|
+
* Note that we did not create a virtual environment for poetry because poetry will do this when we run the install.
|
|
5575
5799
|
* This step is accurate but not reproducible since the resulting list could differ based on various factors
|
|
5576
5800
|
* such as the version of python, pip, os, pypi.org availability (and weather?)
|
|
5577
5801
|
*/
|
|
5578
5802
|
// Bug #388. Perform pip install in all virtualenv to make the experience consistent
|
|
5579
5803
|
if (reqOrSetupFile) {
|
|
5580
5804
|
if (reqOrSetupFile.endsWith("poetry.lock")) {
|
|
5581
|
-
let
|
|
5805
|
+
let poetryConfigArgs = [
|
|
5806
|
+
"config",
|
|
5807
|
+
"virtualenvs.options.no-setuptools",
|
|
5808
|
+
"true",
|
|
5809
|
+
"--local"
|
|
5810
|
+
];
|
|
5811
|
+
result = spawnSync("poetry", poetryConfigArgs, {
|
|
5812
|
+
cwd: basePath,
|
|
5813
|
+
encoding: "utf-8",
|
|
5814
|
+
timeout: TIMEOUT_MS
|
|
5815
|
+
});
|
|
5816
|
+
let poetryInstallArgs = ["install", "-n", "--no-root"];
|
|
5582
5817
|
// Attempt to perform poetry install
|
|
5583
|
-
result = spawnSync(
|
|
5818
|
+
result = spawnSync("poetry", poetryInstallArgs, {
|
|
5584
5819
|
cwd: basePath,
|
|
5585
5820
|
encoding: "utf-8",
|
|
5586
|
-
timeout: TIMEOUT_MS
|
|
5587
|
-
env
|
|
5821
|
+
timeout: TIMEOUT_MS
|
|
5588
5822
|
});
|
|
5589
5823
|
if (result.status !== 0 || result.error) {
|
|
5590
5824
|
if (result.stderr && result.stderr.includes("No module named poetry")) {
|
|
@@ -5597,6 +5831,9 @@ export const getPipFrozenTree = (basePath, reqOrSetupFile, tempVenvDir) => {
|
|
|
5597
5831
|
env
|
|
5598
5832
|
});
|
|
5599
5833
|
if (result.status !== 0 || result.error) {
|
|
5834
|
+
if (DEBUG_MODE && result.stderr) {
|
|
5835
|
+
console.log(result.stderr);
|
|
5836
|
+
}
|
|
5600
5837
|
console.log("poetry install has failed.");
|
|
5601
5838
|
console.log(
|
|
5602
5839
|
"1. Install the poetry command using python -m pip install poetry."
|
|
@@ -5610,11 +5847,27 @@ export const getPipFrozenTree = (basePath, reqOrSetupFile, tempVenvDir) => {
|
|
|
5610
5847
|
}
|
|
5611
5848
|
} else {
|
|
5612
5849
|
console.log(
|
|
5613
|
-
"
|
|
5850
|
+
"Poetry install has failed. Setup and activate the poetry virtual environment and re-run cdxgen."
|
|
5614
5851
|
);
|
|
5615
5852
|
}
|
|
5853
|
+
} else {
|
|
5854
|
+
let poetryEnvArgs = ["env info", "--path"];
|
|
5855
|
+
result = spawnSync("poetry", poetryEnvArgs, {
|
|
5856
|
+
cwd: basePath,
|
|
5857
|
+
encoding: "utf-8",
|
|
5858
|
+
timeout: TIMEOUT_MS,
|
|
5859
|
+
env
|
|
5860
|
+
});
|
|
5861
|
+
tempVenvDir = result.stdout.replaceAll(/[\r\n]+/g, "");
|
|
5862
|
+
if (tempVenvDir && tempVenvDir.length) {
|
|
5863
|
+
env.VIRTUAL_ENV = tempVenvDir;
|
|
5864
|
+
env.PATH = `${join(
|
|
5865
|
+
tempVenvDir,
|
|
5866
|
+
platform() === "win32" ? "Scripts" : "bin"
|
|
5867
|
+
)}${_delimiter}${process.env.PATH || ""}`;
|
|
5868
|
+
}
|
|
5616
5869
|
}
|
|
5617
|
-
} else
|
|
5870
|
+
} else {
|
|
5618
5871
|
const pipInstallArgs = [
|
|
5619
5872
|
"-m",
|
|
5620
5873
|
"pip",
|
|
@@ -5659,7 +5912,7 @@ export const getPipFrozenTree = (basePath, reqOrSetupFile, tempVenvDir) => {
|
|
|
5659
5912
|
console.log(
|
|
5660
5913
|
"Possible build errors detected. The resulting list in the SBoM would therefore be incomplete.\nTry installing any missing build tools or development libraries to improve the accuracy."
|
|
5661
5914
|
);
|
|
5662
|
-
if (platform()
|
|
5915
|
+
if (platform() === "win32") {
|
|
5663
5916
|
console.log(
|
|
5664
5917
|
"- Install the appropriate compilers and build tools on Windows by following this documentation - https://wiki.python.org/moin/WindowsCompilers"
|
|
5665
5918
|
);
|
|
@@ -5703,28 +5956,32 @@ export const getPipFrozenTree = (basePath, reqOrSetupFile, tempVenvDir) => {
|
|
|
5703
5956
|
continue;
|
|
5704
5957
|
}
|
|
5705
5958
|
const name = t.name.replace(/_/g, "-").toLowerCase();
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5959
|
+
const version = t.version;
|
|
5960
|
+
let exclude = ["pip", "setuptools", "wheel"];
|
|
5961
|
+
if (!exclude.includes(name)) {
|
|
5962
|
+
pkgList.push({
|
|
5963
|
+
name,
|
|
5964
|
+
version,
|
|
5965
|
+
evidence: {
|
|
5966
|
+
identity: {
|
|
5967
|
+
field: "purl",
|
|
5968
|
+
confidence: 1,
|
|
5969
|
+
methods: [
|
|
5970
|
+
{
|
|
5971
|
+
technique: "instrumentation",
|
|
5972
|
+
confidence: 1,
|
|
5973
|
+
value: env.VIRTUAL_ENV
|
|
5974
|
+
}
|
|
5975
|
+
]
|
|
5976
|
+
}
|
|
5720
5977
|
}
|
|
5721
|
-
}
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5978
|
+
});
|
|
5979
|
+
rootList.push({
|
|
5980
|
+
name,
|
|
5981
|
+
version
|
|
5982
|
+
});
|
|
5983
|
+
flattenDeps(dependenciesMap, pkgList, reqOrSetupFile, t);
|
|
5984
|
+
}
|
|
5728
5985
|
} // end for
|
|
5729
5986
|
for (const k of Object.keys(dependenciesMap)) {
|
|
5730
5987
|
dependenciesList.push({ ref: k, dependsOn: dependenciesMap[k] });
|