@cdxgen/cdxgen 13.4.0 → 13.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 +7 -7
- package/bin/cdxgen-secure.js +15 -0
- package/bin/cdxgen.js +25 -5
- package/bin/evinse.js +5 -0
- package/bin/hbom.js +15 -7
- package/bin/repl.js +29 -0
- package/data/cbomosdb-queries.json +13 -1
- package/data/component-tags.json +8 -0
- package/data/queries-darwin.json +25 -1
- package/data/queries-win.json +19 -1
- package/data/queries.json +41 -0
- package/data/remediations.json +283 -1
- package/data/rules/cbom-security.yaml +235 -4
- package/data/rules/go-binaries.yaml +46 -0
- package/data/rules/golem-go.yaml +80 -0
- package/data/rules/hbom-compliance.yaml +40 -22
- package/data/rules/hbom-performance.yaml +7 -1
- package/data/rules/host-topology.yaml +1 -1
- package/data/rules/obom-runtime.yaml +31 -2
- package/data/rules/php-runtime.yaml +143 -0
- package/lib/audit/index.js +14 -0
- package/lib/cli/bomAssembly.js +28 -3
- package/lib/cli/cliOptions.js +110 -3
- package/lib/cli/index.js +73 -4
- package/lib/cli/jvmBom.js +126 -20
- package/lib/cli/managedBom.js +711 -71
- package/lib/cli/nativeBom.js +225 -28
- package/lib/core/env.js +30 -8
- package/lib/core/fs.js +28 -19
- package/lib/core/httpClient.js +74 -45
- package/lib/core/projectConfig.js +1 -0
- package/lib/core/projectTooling.js +97 -0
- package/lib/core/propertySanitizer.js +72 -0
- package/lib/ecosystems/golem.js +550 -6
- package/lib/ecosystems/gradleutils.js +30 -20
- package/lib/ecosystems/parsers-dotnet.js +117 -18
- package/lib/ecosystems/parsers-go.js +1090 -367
- package/lib/ecosystems/parsers-jvm.js +259 -17
- package/lib/ecosystems/parsers-misc.js +229 -44
- package/lib/ecosystems/parsers-python.js +11 -10
- package/lib/ecosystems/rubyutils.js +6 -3
- package/lib/ecosystems/rusi.js +10 -0
- package/lib/ecosystems/scalacliutils.js +2 -1
- package/lib/ecosystems/scalasem.js +3 -1
- package/lib/evinser/evinser.js +126 -18
- package/lib/evinser/swiftsem.js +68 -5
- package/lib/inventory/agentSkillSpec.js +17 -0
- package/lib/inventory/apkInstalledDb.js +243 -0
- package/lib/inventory/atomUtils.js +33 -0
- package/lib/inventory/auditCategories.js +3 -3
- package/lib/inventory/cacheDir.js +52 -9
- package/lib/inventory/certificates.js +2371 -0
- package/lib/inventory/communityAiConfigParser.js +4 -9
- package/lib/inventory/deps.js +6 -4
- package/lib/inventory/display.js +200 -3
- package/lib/inventory/dosai.js +500 -58
- package/lib/inventory/dynamic.js +45 -3
- package/lib/inventory/envcontext.js +168 -5
- package/lib/inventory/evidenceUtils.js +436 -23
- package/lib/inventory/formulationParsers.js +3 -2
- package/lib/inventory/goBuildInfo.js +140 -0
- package/lib/inventory/hbom.js +67 -7
- package/lib/inventory/hbomAnalysis.js +16 -0
- package/lib/inventory/hostTopology.js +21 -0
- package/lib/inventory/osPackagePurl.js +244 -0
- package/lib/inventory/osPackageResolver.js +89 -87
- package/lib/inventory/osinfo.js +79 -31
- package/lib/inventory/osqueryTransform.js +114 -5
- package/lib/inventory/phpBuildInfo.js +487 -0
- package/lib/inventory/phpHostProbe.js +641 -0
- package/lib/inventory/rootfsCerts.js +426 -0
- package/lib/inventory/source.js +207 -3
- package/lib/inventory/spdx.js +144 -0
- package/lib/inventory/traceRunner.js +5 -1
- package/lib/inventory/tracedCrypto.js +129 -0
- package/lib/managers/binary.js +529 -183
- package/lib/managers/docker.js +166 -12
- package/lib/parsers/email.js +277 -0
- package/lib/parsers/largeJson.js +34 -2
- package/lib/server/server.js +186 -1
- package/lib/stages/postgen/annotator.js +17 -1
- package/lib/stages/postgen/introspection/reflect.js +4 -1
- package/lib/stages/postgen/postgen.js +5 -5
- package/lib/stages/postgen/ruleEngine.js +58 -1
- package/lib/stages/postgen/specVersionCompat.js +30 -0
- package/lib/stages/pregen/pregen.js +117 -5
- package/lib/validator/bomValidator.js +26 -5
- package/lib/validator/generated/runtime.mjs +13 -0
- package/lib/validator/generated/validate-1.6.mjs +26 -7
- package/lib/validator/generated/validate-1.7.mjs +31 -12
- package/lib/validator/generated/validate-2.0.mjs +30 -11
- package/package.json +17 -17
- package/types/bin/cdxgen-secure.d.ts +3 -0
- package/types/bin/cdxgen-secure.d.ts.map +1 -0
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +9 -2
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +43 -0
- package/types/lib/cli/cliOptions.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts +20 -0
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/env.d.ts +18 -2
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/httpClient.d.ts.map +1 -1
- package/types/lib/core/projectConfig.d.ts.map +1 -1
- package/types/lib/core/projectTooling.d.ts +41 -0
- package/types/lib/core/projectTooling.d.ts.map +1 -0
- package/types/lib/core/propertySanitizer.d.ts +26 -0
- package/types/lib/core/propertySanitizer.d.ts.map +1 -1
- package/types/lib/ecosystems/cppEvidence.d.ts +1 -1
- package/types/lib/ecosystems/golem.d.ts +81 -1
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +10 -8
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-dotnet.d.ts +40 -4
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-go.d.ts +85 -13
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-jvm.d.ts +28 -2
- package/types/lib/ecosystems/parsers-jvm.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-misc.d.ts +18 -58
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-python.d.ts +4 -3
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/ecosystems/scalacliutils.d.ts.map +1 -1
- package/types/lib/ecosystems/scalasem.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +25 -0
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/swiftsem.d.ts +10 -0
- package/types/lib/evinser/swiftsem.d.ts.map +1 -1
- package/types/lib/inventory/agentSkillSpec.d.ts +13 -0
- package/types/lib/inventory/agentSkillSpec.d.ts.map +1 -0
- package/types/lib/inventory/apkInstalledDb.d.ts +42 -0
- package/types/lib/inventory/apkInstalledDb.d.ts.map +1 -0
- package/types/lib/inventory/atomUtils.d.ts +14 -0
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/cacheDir.d.ts +12 -4
- package/types/lib/inventory/cacheDir.d.ts.map +1 -1
- package/types/lib/inventory/certificates.d.ts +205 -0
- package/types/lib/inventory/certificates.d.ts.map +1 -0
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/deps.d.ts +2 -1
- package/types/lib/inventory/deps.d.ts.map +1 -1
- package/types/lib/inventory/display.d.ts +18 -0
- package/types/lib/inventory/display.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +145 -6
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dynamic.d.ts +6 -1
- package/types/lib/inventory/dynamic.d.ts.map +1 -1
- package/types/lib/inventory/envcontext.d.ts +40 -2
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts +29 -10
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
- package/types/lib/inventory/goBuildInfo.d.ts +10 -0
- package/types/lib/inventory/goBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/hbom.d.ts.map +1 -1
- package/types/lib/inventory/hbomAnalysis.d.ts +10 -0
- package/types/lib/inventory/hbomAnalysis.d.ts.map +1 -1
- package/types/lib/inventory/hostTopology.d.ts.map +1 -1
- package/types/lib/inventory/osPackagePurl.d.ts +99 -0
- package/types/lib/inventory/osPackagePurl.d.ts.map +1 -0
- package/types/lib/inventory/osPackageResolver.d.ts +6 -2
- package/types/lib/inventory/osPackageResolver.d.ts.map +1 -1
- package/types/lib/inventory/osinfo.d.ts +31 -6
- package/types/lib/inventory/osinfo.d.ts.map +1 -1
- package/types/lib/inventory/osqueryTransform.d.ts +51 -0
- package/types/lib/inventory/osqueryTransform.d.ts.map +1 -1
- package/types/lib/inventory/phpBuildInfo.d.ts +142 -0
- package/types/lib/inventory/phpBuildInfo.d.ts.map +1 -0
- package/types/lib/inventory/phpHostProbe.d.ts +35 -0
- package/types/lib/inventory/phpHostProbe.d.ts.map +1 -0
- package/types/lib/inventory/rootfsCerts.d.ts +27 -0
- package/types/lib/inventory/rootfsCerts.d.ts.map +1 -0
- package/types/lib/inventory/source.d.ts +28 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/spdx.d.ts +15 -0
- package/types/lib/inventory/spdx.d.ts.map +1 -1
- package/types/lib/inventory/traceRunner.d.ts +4 -1
- package/types/lib/inventory/traceRunner.d.ts.map +1 -1
- package/types/lib/inventory/tracedCrypto.d.ts +22 -0
- package/types/lib/inventory/tracedCrypto.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts +10 -0
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +12 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/parsers/email.d.ts +19 -0
- package/types/lib/parsers/email.d.ts.map +1 -0
- package/types/lib/parsers/largeJson.d.ts +13 -0
- package/types/lib/parsers/largeJson.d.ts.map +1 -1
- package/types/lib/server/server.d.ts +8 -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/introspection/reflect.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts +7 -4
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
- package/types/lib/stages/pregen/pregen.d.ts +14 -1
- package/types/lib/stages/pregen/pregen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
|
@@ -13,11 +13,16 @@ import {
|
|
|
13
13
|
import { build, Purl } from "@cdxgen/cdx-purl";
|
|
14
14
|
import { parse as _load } from "yaml";
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
DEBUG_MODE,
|
|
18
|
+
hasDangerousUnicode,
|
|
19
|
+
readEnvironmentVariable,
|
|
20
|
+
} from "../core/activity.js";
|
|
17
21
|
import { recordDegradation } from "../core/buildLedger.js";
|
|
18
22
|
import { deferFailOnError } from "../core/deferredExit.js";
|
|
19
23
|
import { shouldFetchLicense } from "../core/env.js";
|
|
20
24
|
import {
|
|
25
|
+
getTmpDir,
|
|
21
26
|
safeExistsSync,
|
|
22
27
|
safeSpawnSync,
|
|
23
28
|
safeWriteSync,
|
|
@@ -25,6 +30,10 @@ import {
|
|
|
25
30
|
} from "../core/fs.js";
|
|
26
31
|
import { thoughtLog } from "../core/logger.js";
|
|
27
32
|
import { isWin } from "../core/paths.js";
|
|
33
|
+
import {
|
|
34
|
+
isProjectToolingAllowed,
|
|
35
|
+
reportProjectToolingSkipped,
|
|
36
|
+
} from "../core/projectTooling.js";
|
|
28
37
|
import { registerRunReset } from "../core/runState.js";
|
|
29
38
|
import { mesonWrapDB } from "../core/state.js";
|
|
30
39
|
import { isGitHubUrl } from "../core/urls.js";
|
|
@@ -44,6 +53,7 @@ import {
|
|
|
44
53
|
} from "../inventory/purl.js";
|
|
45
54
|
import { getLicenses, spdxLicenses } from "../inventory/spdx.js";
|
|
46
55
|
import { cmakeFetchDependencies } from "../parsers/cmakeLists.js";
|
|
56
|
+
import { isValidIdnEmail } from "../parsers/email.js";
|
|
47
57
|
import { submodulePurlCoordinates } from "../parsers/gitmodules.js";
|
|
48
58
|
import { getDartMetadata } from "./ecosystems.js";
|
|
49
59
|
import { parsePkgJson } from "./parsers-js.js";
|
|
@@ -1555,11 +1565,146 @@ export function parseComposerJson(composerJsonFile) {
|
|
|
1555
1565
|
return { rootRequires, moduleParent };
|
|
1556
1566
|
}
|
|
1557
1567
|
|
|
1568
|
+
// Composer's platform packages stand for what the runtime provides, and none
|
|
1569
|
+
// is installable, so none has a purl or an edge in the dependency graph. These
|
|
1570
|
+
// are the ones that describe PHP: PHP and its build flavours, HHVM, and the
|
|
1571
|
+
// extensions and system libraries PHP was built with. composer,
|
|
1572
|
+
// composer-plugin-api and composer-runtime-api are platform packages too, but
|
|
1573
|
+
// they describe Composer, so they are left out.
|
|
1574
|
+
// https://github.com/composer/composer/blob/2.10.3/src/Composer/Repository/PlatformRepository.php#L39
|
|
1575
|
+
const COMPOSER_PLATFORM_NAMES = new Set([
|
|
1576
|
+
"php",
|
|
1577
|
+
"php-64bit",
|
|
1578
|
+
"php-ipv6",
|
|
1579
|
+
"php-zts",
|
|
1580
|
+
"php-debug",
|
|
1581
|
+
"hhvm",
|
|
1582
|
+
]);
|
|
1583
|
+
|
|
1584
|
+
function isComposerPlatformRequirement(name) {
|
|
1585
|
+
return (
|
|
1586
|
+
COMPOSER_PLATFORM_NAMES.has(name) ||
|
|
1587
|
+
name.startsWith("ext-") ||
|
|
1588
|
+
name.startsWith("lib-")
|
|
1589
|
+
);
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
// What the lock records about the platform is a constraint, or the version a
|
|
1593
|
+
// project told Composer to assume. It is a fact about the project's
|
|
1594
|
+
// requirements, never the version of anything that runs, so it is kept as a
|
|
1595
|
+
// property and never becomes a component.
|
|
1596
|
+
const COMPOSER_PLATFORM_SECTIONS = [
|
|
1597
|
+
["platform", "cdx:composer:platform:"],
|
|
1598
|
+
["platform-dev", "cdx:composer:platformDev:"],
|
|
1599
|
+
["platform-overrides", "cdx:composer:platformOverride:"],
|
|
1600
|
+
];
|
|
1601
|
+
const COMPOSER_REQUIRE_PREFIX = "cdx:composer:require:";
|
|
1602
|
+
const MAX_COMPOSER_PLATFORM_VALUE_LENGTH = 256;
|
|
1603
|
+
// A real lock names a few dozen platform packages per map at most; the cap
|
|
1604
|
+
// keeps a crafted one from turning each map into thousands of properties.
|
|
1605
|
+
const MAX_COMPOSER_PLATFORM_ENTRIES = 128;
|
|
1606
|
+
|
|
1607
|
+
function isComposerPlatformKey(key) {
|
|
1608
|
+
return /^[A-Za-z0-9._-]{1,100}$/.test(key);
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
/**
|
|
1612
|
+
* The value a platform entry carries as a property: a constraint or version
|
|
1613
|
+
* string, or `false`, which `config.platform` uses to disable a package:
|
|
1614
|
+
* https://github.com/composer/composer/blob/2.10.3/src/Composer/Repository/PlatformRepository.php#L125
|
|
1615
|
+
*
|
|
1616
|
+
* @param {unknown} value value from the lock
|
|
1617
|
+
* @returns {string|undefined} property value, or undefined when it is not one
|
|
1618
|
+
*/
|
|
1619
|
+
function composerPlatformValue(value) {
|
|
1620
|
+
if (value === false) {
|
|
1621
|
+
return "false";
|
|
1622
|
+
}
|
|
1623
|
+
if (typeof value !== "string") {
|
|
1624
|
+
return undefined;
|
|
1625
|
+
}
|
|
1626
|
+
const trimmed = value.trim();
|
|
1627
|
+
if (
|
|
1628
|
+
!trimmed ||
|
|
1629
|
+
trimmed.length > MAX_COMPOSER_PLATFORM_VALUE_LENGTH ||
|
|
1630
|
+
hasDangerousUnicode(trimmed) ||
|
|
1631
|
+
/[\t\n\r]/.test(trimmed)
|
|
1632
|
+
) {
|
|
1633
|
+
return undefined;
|
|
1634
|
+
}
|
|
1635
|
+
return trimmed;
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
/**
|
|
1639
|
+
* Turn a map of platform entries into properties named `<prefix><name>`, in
|
|
1640
|
+
* name order, at most MAX_COMPOSER_PLATFORM_ENTRIES of them. Over the cap the
|
|
1641
|
+
* PHP and HHVM entries are kept first, since `ext-*` sorts before `php`. An
|
|
1642
|
+
* array (Composer writes an empty section as `[]`) or any other shape holds no
|
|
1643
|
+
* entries.
|
|
1644
|
+
*
|
|
1645
|
+
* @param {unknown} entries map of requirement name to constraint
|
|
1646
|
+
* @param {string} prefix property name prefix
|
|
1647
|
+
* @param {function(string): boolean} [keep] which names to keep
|
|
1648
|
+
* @returns {{name: string, value: string}[]} properties
|
|
1649
|
+
*/
|
|
1650
|
+
function composerPlatformProperties(entries, prefix, keep = () => true) {
|
|
1651
|
+
if (!entries || typeof entries !== "object" || Array.isArray(entries)) {
|
|
1652
|
+
return [];
|
|
1653
|
+
}
|
|
1654
|
+
const kept = [];
|
|
1655
|
+
for (const key of Object.keys(entries).sort()) {
|
|
1656
|
+
const value = composerPlatformValue(entries[key]);
|
|
1657
|
+
if (value !== undefined && isComposerPlatformKey(key) && keep(key)) {
|
|
1658
|
+
kept.push({ key, value });
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
let selected = kept;
|
|
1662
|
+
if (kept.length > MAX_COMPOSER_PLATFORM_ENTRIES) {
|
|
1663
|
+
const runtimes = kept.filter(({ key }) => COMPOSER_PLATFORM_NAMES.has(key));
|
|
1664
|
+
const others = kept
|
|
1665
|
+
.filter(({ key }) => !COMPOSER_PLATFORM_NAMES.has(key))
|
|
1666
|
+
.slice(0, MAX_COMPOSER_PLATFORM_ENTRIES - runtimes.length);
|
|
1667
|
+
const chosen = new Set([...runtimes, ...others]);
|
|
1668
|
+
selected = kept.filter((entry) => chosen.has(entry));
|
|
1669
|
+
}
|
|
1670
|
+
return selected.map(({ key, value }) => ({ name: `${prefix}${key}`, value }));
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
/**
|
|
1674
|
+
* A CycloneDX `organizationalContact` from an author record in package
|
|
1675
|
+
* metadata. The schema asserts `idn-email` on the address, so one that is not
|
|
1676
|
+
* a valid address is dropped rather than failing BOM validation; an author with
|
|
1677
|
+
* neither a name nor a valid address yields nothing.
|
|
1678
|
+
*
|
|
1679
|
+
* @param {{name?: string, email?: unknown}} author Author as the manifest has it
|
|
1680
|
+
* @returns {Array<{name?: string, email?: string}>} Zero or one contact
|
|
1681
|
+
*/
|
|
1682
|
+
function contactFromAuthor(author) {
|
|
1683
|
+
const contact = {};
|
|
1684
|
+
if (typeof author?.name === "string" && author.name.trim()) {
|
|
1685
|
+
contact.name = author.name;
|
|
1686
|
+
}
|
|
1687
|
+
const email = typeof author?.email === "string" ? author.email.trim() : "";
|
|
1688
|
+
if (isValidIdnEmail(email)) {
|
|
1689
|
+
contact.email = email;
|
|
1690
|
+
}
|
|
1691
|
+
return Object.keys(contact).length ? [contact] : [];
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1558
1694
|
/**
|
|
1559
1695
|
* Parse composer lock file
|
|
1560
1696
|
*
|
|
1561
1697
|
* @param {string} pkgLockFile composer.lock file
|
|
1562
1698
|
* @param {array} rootRequires require section from composer.json
|
|
1699
|
+
* @returns {{pkgList: Object[], dependenciesList: Object[], rootList: Object[], platformProperties: {name: string, value: string}[]}|Array}
|
|
1700
|
+
* The locked packages, the dependency edges between them, the packages the
|
|
1701
|
+
* root requires, and the lock's `platform`, `platform-dev` and
|
|
1702
|
+
* `platform-overrides` entries as `cdx:composer:*` properties. The platform
|
|
1703
|
+
* entries are constraints and assumed versions, not the PHP that runs; the
|
|
1704
|
+
* caller puts them on the module component. Every locked package carries its
|
|
1705
|
+
* own `php`, `hhvm`, `ext-*` and `lib-*` requirements as
|
|
1706
|
+
* `cdx:composer:require:<name>` properties. A lock that is not valid JSON
|
|
1707
|
+
* gives an empty array.
|
|
1563
1708
|
*/
|
|
1564
1709
|
export function parseComposerLock(pkgLockFile, rootRequires) {
|
|
1565
1710
|
const pkgList = [];
|
|
@@ -1568,16 +1713,12 @@ export function parseComposerLock(pkgLockFile, rootRequires) {
|
|
|
1568
1713
|
const pkgNamePurlMap = {};
|
|
1569
1714
|
const rootList = [];
|
|
1570
1715
|
const rootRequiresMap = {};
|
|
1716
|
+
let platformProperties = [];
|
|
1571
1717
|
if (rootRequires) {
|
|
1572
1718
|
for (const rr of Object.keys(rootRequires)) {
|
|
1573
1719
|
// Skip platform requirements (php, hhvm, ext-*, lib-*) — they are never
|
|
1574
1720
|
// Composer package names and must not be used to identify root packages.
|
|
1575
|
-
if (
|
|
1576
|
-
rr === "php" ||
|
|
1577
|
-
rr === "hhvm" ||
|
|
1578
|
-
rr.startsWith("ext-") ||
|
|
1579
|
-
rr.startsWith("lib-")
|
|
1580
|
-
) {
|
|
1721
|
+
if (isComposerPlatformRequirement(rr)) {
|
|
1581
1722
|
continue;
|
|
1582
1723
|
}
|
|
1583
1724
|
rootRequiresMap[rr] = true;
|
|
@@ -1592,6 +1733,11 @@ export function parseComposerLock(pkgLockFile, rootRequires) {
|
|
|
1592
1733
|
return [];
|
|
1593
1734
|
}
|
|
1594
1735
|
if (lockData) {
|
|
1736
|
+
for (const [section, prefix] of COMPOSER_PLATFORM_SECTIONS) {
|
|
1737
|
+
platformProperties = platformProperties.concat(
|
|
1738
|
+
composerPlatformProperties(lockData[section], prefix),
|
|
1739
|
+
);
|
|
1740
|
+
}
|
|
1595
1741
|
const packages = {};
|
|
1596
1742
|
if (lockData["packages"]) {
|
|
1597
1743
|
packages["required"] = lockData["packages"];
|
|
@@ -1664,9 +1810,10 @@ export function parseComposerLock(pkgLockFile, rootRequires) {
|
|
|
1664
1810
|
apkg.distribution = { url: distUrl };
|
|
1665
1811
|
}
|
|
1666
1812
|
if (pkg?.authors?.length) {
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1813
|
+
const authors = pkg.authors.flatMap(contactFromAuthor);
|
|
1814
|
+
if (authors.length) {
|
|
1815
|
+
apkg.authors = authors;
|
|
1816
|
+
}
|
|
1670
1817
|
}
|
|
1671
1818
|
if (pkg?.keywords?.length) {
|
|
1672
1819
|
apkg.tags = pkg.keywords;
|
|
@@ -1687,6 +1834,13 @@ export function parseComposerLock(pkgLockFile, rootRequires) {
|
|
|
1687
1834
|
});
|
|
1688
1835
|
}
|
|
1689
1836
|
}
|
|
1837
|
+
apkg.properties.push(
|
|
1838
|
+
...composerPlatformProperties(
|
|
1839
|
+
pkg.require,
|
|
1840
|
+
COMPOSER_REQUIRE_PREFIX,
|
|
1841
|
+
isComposerPlatformRequirement,
|
|
1842
|
+
),
|
|
1843
|
+
);
|
|
1690
1844
|
pkgList.push(apkg);
|
|
1691
1845
|
dependenciesMap[purl] = new Set();
|
|
1692
1846
|
pkgNamePurlMap[pkg.name] = purl;
|
|
@@ -1726,6 +1880,7 @@ export function parseComposerLock(pkgLockFile, rootRequires) {
|
|
|
1726
1880
|
pkgList,
|
|
1727
1881
|
dependenciesList,
|
|
1728
1882
|
rootList,
|
|
1883
|
+
platformProperties,
|
|
1729
1884
|
};
|
|
1730
1885
|
}
|
|
1731
1886
|
|
|
@@ -3608,6 +3763,12 @@ async function fullScanCocoaPod(dependency, component, options) {
|
|
|
3608
3763
|
value: podspecLocation,
|
|
3609
3764
|
});
|
|
3610
3765
|
}
|
|
3766
|
+
// A Ruby podspec is a program that only `pod` can read, and it comes from
|
|
3767
|
+
// the scanned project or a repository its lockfile names. Without
|
|
3768
|
+
// permission to run the project's tooling, only JSON podspecs are read,
|
|
3769
|
+
// directly and without `pod`.
|
|
3770
|
+
const toolingAllowed = isProjectToolingAllowed(options);
|
|
3771
|
+
const isRemote = podspecLocation.startsWith("http");
|
|
3611
3772
|
let replacements = [];
|
|
3612
3773
|
if (
|
|
3613
3774
|
podspecLocation.endsWith(".podspec") &&
|
|
@@ -3624,34 +3785,50 @@ async function fullScanCocoaPod(dependency, component, options) {
|
|
|
3624
3785
|
"COCOA_PODSPEC_JSON_REPLACEMENTS",
|
|
3625
3786
|
).split(";");
|
|
3626
3787
|
}
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
httpResult =
|
|
3634
|
-
prefetchedResponse(cocoaPodspecPrefetch, candidate) ||
|
|
3635
|
-
(await gatedGet(candidate, { responseType: "text" }));
|
|
3636
|
-
podspecLocation = candidate;
|
|
3637
|
-
} catch (_err) {
|
|
3638
|
-
continue;
|
|
3639
|
-
}
|
|
3640
|
-
component.properties.find(
|
|
3641
|
-
({ name }) => name === "cdx:pods:podspecLocation",
|
|
3642
|
-
).value = podspecLocation;
|
|
3643
|
-
podspecLocation = `${randomUUID()}.${podspecLocation.substring(podspecLocation.lastIndexOf(".") + 1)}`;
|
|
3644
|
-
podspecContent = httpResult.body;
|
|
3645
|
-
break;
|
|
3788
|
+
let podspecContent;
|
|
3789
|
+
if (isRemote) {
|
|
3790
|
+
let httpResult;
|
|
3791
|
+
for (const candidate of podspecCandidates(podspecLocation)) {
|
|
3792
|
+
if (!toolingAllowed && !candidate.endsWith(".json")) {
|
|
3793
|
+
continue;
|
|
3646
3794
|
}
|
|
3647
|
-
|
|
3648
|
-
|
|
3795
|
+
try {
|
|
3796
|
+
httpResult =
|
|
3797
|
+
prefetchedResponse(cocoaPodspecPrefetch, candidate) ||
|
|
3798
|
+
(await gatedGet(candidate, { responseType: "text" }));
|
|
3799
|
+
podspecLocation = candidate;
|
|
3800
|
+
} catch (_err) {
|
|
3801
|
+
continue;
|
|
3802
|
+
}
|
|
3803
|
+
component.properties.find(
|
|
3804
|
+
({ name }) => name === "cdx:pods:podspecLocation",
|
|
3805
|
+
).value = podspecLocation;
|
|
3806
|
+
podspecContent = httpResult.body;
|
|
3807
|
+
break;
|
|
3649
3808
|
}
|
|
3650
3809
|
// A location none of the candidates served contributes nothing: the
|
|
3651
3810
|
// component keeps what the lockfile already said about the pod.
|
|
3652
3811
|
if (podspecContent === undefined) {
|
|
3653
3812
|
return;
|
|
3654
3813
|
}
|
|
3814
|
+
} else if (!toolingAllowed && !podspecLocation.endsWith(".json")) {
|
|
3815
|
+
reportProjectToolingSkipped(
|
|
3816
|
+
options,
|
|
3817
|
+
"cocoa.podspec.tooling-disabled",
|
|
3818
|
+
"Skipping 'pod ipc spec' for Ruby podspecs since dependency installation is disabled. Their pods keep the details from Podfile.lock.",
|
|
3819
|
+
{
|
|
3820
|
+
ecosystem: "cocoa",
|
|
3821
|
+
tool: "pod",
|
|
3822
|
+
impact: "licenses",
|
|
3823
|
+
detail:
|
|
3824
|
+
"Dependency installation is disabled, so Ruby podspecs were not evaluated and their pods lack license and author details.",
|
|
3825
|
+
},
|
|
3826
|
+
);
|
|
3827
|
+
return;
|
|
3828
|
+
} else if (replacements.length || !toolingAllowed) {
|
|
3829
|
+
podspecContent = readFileSync(podspecLocation, "utf-8");
|
|
3830
|
+
}
|
|
3831
|
+
if (podspecContent !== undefined) {
|
|
3655
3832
|
for (const replacement of replacements) {
|
|
3656
3833
|
const replacementPair = replacement.split("=");
|
|
3657
3834
|
let match = replacementPair[0].replaceAll("<NEWLINE>", "\n");
|
|
@@ -3661,18 +3838,28 @@ async function fullScanCocoaPod(dependency, component, options) {
|
|
|
3661
3838
|
const repl = replacementPair[1].replaceAll("<NEWLINE>", "\n");
|
|
3662
3839
|
podspecContent = podspecContent.replaceAll(match, repl);
|
|
3663
3840
|
}
|
|
3664
|
-
|
|
3665
|
-
|
|
3666
|
-
|
|
3841
|
+
}
|
|
3842
|
+
if (!toolingAllowed) {
|
|
3843
|
+
// Only JSON podspecs reach this point, and they need no `pod`
|
|
3844
|
+
result = { stdout: podspecContent };
|
|
3845
|
+
} else {
|
|
3846
|
+
if (podspecContent !== undefined) {
|
|
3847
|
+
// A downloaded podspec goes to the temporary directory; a local one
|
|
3848
|
+
// with replacements stays beside the original, whose relative paths
|
|
3849
|
+
// its Ruby may read.
|
|
3850
|
+
podspecLocation = join(
|
|
3851
|
+
isRemote ? getTmpDir() : dirname(podspecLocation),
|
|
3852
|
+
`${randomUUID()}.${podspecLocation.substring(podspecLocation.lastIndexOf(".") + 1)}`,
|
|
3853
|
+
);
|
|
3854
|
+
safeWriteSync(podspecLocation, podspecContent);
|
|
3855
|
+
temporaryFiles.add(podspecLocation);
|
|
3856
|
+
}
|
|
3857
|
+
result = executePodCommand(
|
|
3858
|
+
["ipc", "spec", "--silent", podspecLocation],
|
|
3859
|
+
undefined,
|
|
3860
|
+
options,
|
|
3667
3861
|
);
|
|
3668
|
-
safeWriteSync(podspecLocation, podspecContent);
|
|
3669
|
-
temporaryFiles.add(podspecLocation);
|
|
3670
3862
|
}
|
|
3671
|
-
result = executePodCommand(
|
|
3672
|
-
["ipc", "spec", "--silent", podspecLocation],
|
|
3673
|
-
undefined,
|
|
3674
|
-
options,
|
|
3675
|
-
);
|
|
3676
3863
|
} else {
|
|
3677
3864
|
let dependencyName = dependency.name;
|
|
3678
3865
|
if (dependencyName.includes("/")) {
|
|
@@ -3737,9 +3924,7 @@ async function fullScanCocoaPod(dependency, component, options) {
|
|
|
3737
3924
|
component.authors = [];
|
|
3738
3925
|
if (podspec.authors.constructor === Object) {
|
|
3739
3926
|
Object.entries(podspec.authors).forEach(([name, email]) => {
|
|
3740
|
-
|
|
3741
|
-
? component.authors.push({ name, email })
|
|
3742
|
-
: component.authors.push({ name });
|
|
3927
|
+
component.authors.push(...contactFromAuthor({ name, email }));
|
|
3743
3928
|
});
|
|
3744
3929
|
} else if (podspec.authors.constructor === Array) {
|
|
3745
3930
|
podspec.authors.forEach((name) => {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
import { basename, dirname, join, relative } from "node:path";
|
|
3
|
-
import process from "node:process";
|
|
4
3
|
import { URL } from "node:url";
|
|
5
4
|
|
|
6
5
|
import { build } from "@cdxgen/cdx-purl";
|
|
@@ -2205,28 +2204,30 @@ export function parsePixiTomlFile(pixiToml) {
|
|
|
2205
2204
|
}
|
|
2206
2205
|
|
|
2207
2206
|
/**
|
|
2208
|
-
* Method to run cli command `pixi install`
|
|
2209
|
-
*
|
|
2207
|
+
* Method to run cli command `pixi install` in the project directory.
|
|
2210
2208
|
*
|
|
2209
|
+
* @param {string} projectPath Directory containing pixi.toml
|
|
2210
|
+
* @returns {boolean} true when pixi install succeeded
|
|
2211
2211
|
*/
|
|
2212
|
-
export function generatePixiLockFile(
|
|
2213
|
-
const result = safeSpawnSync("pixi", ["install"]);
|
|
2212
|
+
export function generatePixiLockFile(projectPath) {
|
|
2213
|
+
const result = safeSpawnSync("pixi", ["install"], { cwd: projectPath });
|
|
2214
2214
|
|
|
2215
2215
|
if (result.status !== 0) {
|
|
2216
|
-
// Handle errors
|
|
2216
|
+
// Handle errors. The caller carries on without a lock file: exiting here
|
|
2217
|
+
// would also stop a long-running cdxgen server.
|
|
2217
2218
|
if (result.error && result.error.code === "ENOENT") {
|
|
2218
2219
|
console.error(
|
|
2219
2220
|
"Error: pixi command not found. Make sure pixi.js is installed globally.",
|
|
2220
2221
|
);
|
|
2221
2222
|
} else {
|
|
2222
2223
|
console.error(
|
|
2223
|
-
`Error executing pixi install: ${result.error || result.stderr
|
|
2224
|
+
`Error executing pixi install: ${result.error || result.stderr?.toString()}`,
|
|
2224
2225
|
);
|
|
2225
2226
|
}
|
|
2226
|
-
|
|
2227
|
-
} else {
|
|
2228
|
-
console.log("Dependencies installed successfully.");
|
|
2227
|
+
return false;
|
|
2229
2228
|
}
|
|
2229
|
+
console.log("Dependencies installed successfully.");
|
|
2230
|
+
return true;
|
|
2230
2231
|
}
|
|
2231
2232
|
|
|
2232
2233
|
/**
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
recordPolicyDegradationFromError,
|
|
30
30
|
} from "../inventory/fetchBatch.js";
|
|
31
31
|
import { genericPurl } from "../inventory/purl.js";
|
|
32
|
+
import { isValidIdnEmail } from "../parsers/email.js";
|
|
32
33
|
|
|
33
34
|
// FIXME. This has to get removed, once we improve the module detection one-liner.
|
|
34
35
|
// If you're a Rubyist, please help us improve this code.
|
|
@@ -1967,9 +1968,11 @@ export async function parseGemspecData(gemspecData, gemspecFile, options = {}) {
|
|
|
1967
1968
|
// `spec.email` holds the contact addresses of the gem, not of an author.
|
|
1968
1969
|
// An address can only be attributed when there is one per author, in the
|
|
1969
1970
|
// same order, or when the gem has a single author.
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
)
|
|
1971
|
+
// Only a valid `idn-email` may reach `organizationalContact.email`; the
|
|
1972
|
+
// schema asserts the format, so anything else fails BOM validation.
|
|
1973
|
+
const emails = (lists.email || [])
|
|
1974
|
+
.map((email) => `${email}`.trim())
|
|
1975
|
+
.filter(isValidIdnEmail);
|
|
1973
1976
|
pkg.authors = lists.authors.map((name, i) => {
|
|
1974
1977
|
let email;
|
|
1975
1978
|
if (emails.length === lists.authors.length) {
|
package/lib/ecosystems/rusi.js
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
reportPluginRunFailure,
|
|
15
15
|
resolvePluginSourceDir,
|
|
16
16
|
} from "../core/pluginRun.js";
|
|
17
|
+
import { isProjectToolingAllowed } from "../core/projectTooling.js";
|
|
17
18
|
import { applyAlgorithmProperties } from "../inventory/cryptoAlgorithmFamily.js";
|
|
18
19
|
import { resolvePluginBinary } from "../inventory/plugins.js";
|
|
19
20
|
|
|
@@ -821,6 +822,15 @@ export function runRusiAnalysis(src, outputFile, options = {}) {
|
|
|
821
822
|
if (!executable || !src || !outputFile) {
|
|
822
823
|
return false;
|
|
823
824
|
}
|
|
825
|
+
// rusi runs cargo and rustc on the project, which follow its
|
|
826
|
+
// `.cargo/config.toml` and `rust-toolchain.toml` and can compile its build
|
|
827
|
+
// scripts, so it waits for permission to run the project's tooling.
|
|
828
|
+
if (!isProjectToolingAllowed(options)) {
|
|
829
|
+
console.log(
|
|
830
|
+
"Skipping rusi since dependency installation is disabled. Rust evidence needs cargo to run on the project.",
|
|
831
|
+
);
|
|
832
|
+
return false;
|
|
833
|
+
}
|
|
824
834
|
|
|
825
835
|
// cdxgen may run with its working directory already inside the project
|
|
826
836
|
// while the source argument is relative to a repository root; a plain
|
|
@@ -5,6 +5,7 @@ import { DEBUG_MODE } from "../core/activity.js";
|
|
|
5
5
|
import { commandOutputText } from "../core/buildLedger.js";
|
|
6
6
|
import { getAllFiles, safeExistsSync, safeSpawnSync } from "../core/fs.js";
|
|
7
7
|
import { isWin } from "../core/paths.js";
|
|
8
|
+
import { isProjectToolingAllowed } from "../core/projectTooling.js";
|
|
8
9
|
import { noteBuildToolRateLimit } from "../inventory/buildToolRateLimit.js";
|
|
9
10
|
import { parseScalaArtifact } from "../inventory/scalaCoords.js";
|
|
10
11
|
import { completeComponent } from "./parsers-jvm.js";
|
|
@@ -283,7 +284,7 @@ export function collectScalaCliComponents(dirPath, options = {}) {
|
|
|
283
284
|
// The transitive dependencies of the declared ones come from the class
|
|
284
285
|
// path the tool itself resolves; the compile requires installs to be
|
|
285
286
|
// allowed.
|
|
286
|
-
if (options
|
|
287
|
+
if (!isProjectToolingAllowed(options)) {
|
|
287
288
|
console.log(
|
|
288
289
|
"Skipping the scala-cli class path resolution since dependency installation is disabled.",
|
|
289
290
|
);
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
import { dirNameStr } from "../core/paths.js";
|
|
19
19
|
import { resolvePluginSourceDir } from "../core/pluginRun.js";
|
|
20
20
|
import { formatDuration } from "../core/processTree.js";
|
|
21
|
+
import { isProjectToolingAllowed } from "../core/projectTooling.js";
|
|
21
22
|
import { parsetoolsFile } from "../inventory/atomUtils.js";
|
|
22
23
|
import { applyAlgorithmProperties } from "../inventory/cryptoAlgorithmFamily.js";
|
|
23
24
|
import { getJarClasses } from "../inventory/deps.js";
|
|
@@ -341,7 +342,8 @@ function runScalasem(analysisDir, options, requestedFile, v1Slice, v1File) {
|
|
|
341
342
|
const runDir = safeMkdtempSync(join(getTmpDir(), "scalasem-"));
|
|
342
343
|
const runReportFile = join(runDir, "scalasem.slices.json");
|
|
343
344
|
const args = [scalasem, analysisDir, runReportFile];
|
|
344
|
-
|
|
345
|
+
// The build runs the project's sbt definitions
|
|
346
|
+
if (!isProjectToolingAllowed(options)) {
|
|
345
347
|
args.push("--no-build");
|
|
346
348
|
}
|
|
347
349
|
if (options.scalasemIncludeTests && !options.requiredOnly) {
|