@cdxgen/cdxgen 13.6.0 → 13.7.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 +15 -8
- package/bin/audit.js +7 -4
- package/bin/cdxgen.js +256 -37
- package/bin/convert.js +146 -22
- package/bin/evinse.js +25 -3
- package/bin/repl.js +47 -3
- package/bin/sign.js +211 -34
- package/bin/tracebom.js +18 -6
- package/bin/validate.js +18 -3
- package/bin/verify.js +113 -5
- package/data/behavior-taxonomy.schema.json +1491 -0
- package/data/cyclonedx-2.0-bundled.schema.json +18606 -5528
- package/data/cyclonedx-2.0-cryptography-defs.schema.json +857 -0
- package/data/pqc-migration.json +239 -0
- package/data/remediations.json +52 -0
- package/data/rules/cbom-security.yaml +4 -3
- package/data/rules/package-integrity.yaml +28 -0
- package/lib/audit/index.js +25 -3
- package/lib/cli/bomAssembly.js +80 -1
- package/lib/cli/cliOptions.js +121 -17
- package/lib/cli/index.js +609 -898
- package/lib/cli/jsBom.js +3 -0
- package/lib/cli/jvmBom.js +365 -40
- package/lib/cli/managedBom.js +19 -3
- package/lib/cli/nativeBom.js +10 -0
- package/lib/core/env.js +26 -0
- package/lib/core/fs.js +126 -3
- package/lib/core/globPatterns.js +218 -0
- package/lib/core/processTree.js +99 -12
- package/lib/core/projectConfig.js +332 -25
- package/lib/core/toolRegistry.js +15 -1
- package/lib/ecosystems/denoutils.js +2 -2
- package/lib/ecosystems/golem.js +3 -8
- package/lib/ecosystems/gradleutils.js +1082 -6
- package/lib/ecosystems/jsEvidence.js +90 -38
- package/lib/ecosystems/kosi.js +3 -5
- package/lib/ecosystems/npmVersionResolution.js +376 -0
- package/lib/ecosystems/parsers-python.js +89 -28
- package/lib/ecosystems/parsers-rust.js +117 -15
- package/lib/ecosystems/remote/huggingface.js +22 -0
- package/lib/ecosystems/rusi.js +2 -4
- package/lib/ecosystems/scalasem.js +2 -1
- package/lib/evinser/evinser.js +196 -34
- package/lib/evinser/scalasem.js +5 -1
- package/lib/helpers/agenticToolutils.js +112 -1
- package/lib/helpers/bomSigner.js +43 -9
- package/lib/helpers/jssSigner.js +1276 -0
- package/lib/inventory/agentBlueprint.js +193 -0
- package/lib/inventory/agentToolNames.js +145 -0
- package/lib/inventory/aiCollector.js +153 -7
- package/lib/inventory/aiInventory.js +44 -8
- package/lib/inventory/aiProviders.js +324 -0
- package/lib/inventory/bomUtils.js +8 -1
- package/lib/inventory/ciParsers/githubActionsLock.js +821 -0
- package/lib/inventory/citations.js +31 -2
- package/lib/inventory/communityAiConfigParser.js +978 -80
- package/lib/inventory/cyclonedx20.js +152 -0
- package/lib/inventory/cyclonedx20Common.js +286 -0
- package/lib/inventory/cyclonedx20Derived.js +159 -0
- package/lib/inventory/cyclonedx20Parties.js +552 -0
- package/lib/inventory/cyclonedx20Read.js +768 -0
- package/lib/inventory/cyclonedx20ReadCommon.js +374 -0
- package/lib/inventory/cyclonedx20ReadComponents.js +692 -0
- package/lib/inventory/cyclonedx20ReadContext.js +380 -0
- package/lib/inventory/cyclonedx20ReadEvidence.js +466 -0
- package/lib/inventory/cyclonedx20ReadSections.js +599 -0
- package/lib/inventory/cyclonedx20Render.js +766 -0
- package/lib/inventory/cyclonedx20RenderComponents.js +716 -0
- package/lib/inventory/cyclonedx20RenderDefinitions.js +405 -0
- package/lib/inventory/cyclonedx20RenderEvidence.js +726 -0
- package/lib/inventory/cyclonedx20RenderHardware.js +203 -0
- package/lib/inventory/cyclonedx20RenderPatents.js +174 -0
- package/lib/inventory/cyclonedx20RenderRefIds.js +314 -0
- package/lib/inventory/cyclonedx20RenderSections.js +499 -0
- package/lib/inventory/cyclonedx20Tables.js +594 -0
- package/lib/inventory/deps.js +106 -13
- package/lib/inventory/dosai.js +236 -1
- package/lib/inventory/dosaiParsers.js +3 -6
- package/lib/inventory/dotnetRuntime.js +164 -14
- package/lib/inventory/ecosystemFormulationParsers.js +979 -0
- package/lib/inventory/envcontext.js +132 -38
- package/lib/inventory/evidenceUtils.js +2 -1
- package/lib/inventory/formulationParsers.js +95 -13
- package/lib/inventory/hbom.js +45 -6
- package/lib/inventory/hbomLoader.js +166 -0
- package/lib/inventory/mcpConfigParser.js +5 -0
- package/lib/inventory/occurrenceKey.js +90 -0
- package/lib/inventory/pqcMigration.js +426 -0
- package/lib/inventory/rustFormulationParser.js +21 -10
- package/lib/inventory/source.js +65 -6
- package/lib/parsers/huggingfaceManifest.js +9 -1
- package/lib/parsers/huggingfaceUtils.js +18 -0
- package/lib/parsers/kiroPermissions.js +166 -0
- package/lib/server/server.js +36 -34
- package/lib/stages/postgen/postgen.js +41 -1
- package/lib/stages/postgen/specVersionCompat.js +76 -0
- package/lib/validator/bomValidator.js +8 -1
- package/lib/validator/complianceEngine.js +30 -3
- package/lib/validator/complianceRules.js +1416 -4
- package/lib/validator/generated/validate-2.0.mjs +57554 -13851
- package/lib/validator/index.js +48 -7
- package/lib/validator/reporters/json.js +6 -0
- package/package.json +9 -9
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts +6 -2
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +16 -0
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +6 -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/jsBom.d.ts +2 -1
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts +15 -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/boundedFile.d.ts +30 -0
- package/types/lib/core/boundedFile.d.ts.map +1 -1
- package/types/lib/core/env.d.ts +10 -0
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts +4 -0
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/globPatterns.d.ts +70 -0
- package/types/lib/core/globPatterns.d.ts.map +1 -0
- package/types/lib/core/paths.d.ts +9 -0
- package/types/lib/core/paths.d.ts.map +1 -1
- package/types/lib/core/processTree.d.ts +16 -5
- package/types/lib/core/processTree.d.ts.map +1 -1
- package/types/lib/core/projectConfig.d.ts +47 -8
- package/types/lib/core/projectConfig.d.ts.map +1 -1
- package/types/lib/core/toolRegistry.d.ts.map +1 -1
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +248 -8
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/jsEvidence.d.ts +14 -1
- package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts.map +1 -1
- package/types/lib/ecosystems/npmVersionResolution.d.ts +119 -0
- package/types/lib/ecosystems/npmVersionResolution.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
- package/types/lib/ecosystems/pythonutils.d.ts +1 -1
- package/types/lib/ecosystems/remote/huggingface.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/ecosystems/scalasem.d.ts +58 -16
- package/types/lib/ecosystems/scalasem.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +61 -9
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/scalasem.d.ts.map +1 -1
- package/types/lib/helpers/agenticToolutils.d.ts +53 -19
- package/types/lib/helpers/agenticToolutils.d.ts.map +1 -1
- package/types/lib/helpers/bomSigner.d.ts +20 -3
- package/types/lib/helpers/bomSigner.d.ts.map +1 -1
- package/types/lib/helpers/jssSigner.d.ts +288 -0
- package/types/lib/helpers/jssSigner.d.ts.map +1 -0
- package/types/lib/helpers/vsixutils.d.ts +303 -19
- package/types/lib/helpers/vsixutils.d.ts.map +1 -1
- package/types/lib/inventory/agentBlueprint.d.ts +70 -0
- package/types/lib/inventory/agentBlueprint.d.ts.map +1 -0
- package/types/lib/inventory/agentToolNames.d.ts +51 -0
- package/types/lib/inventory/agentToolNames.d.ts.map +1 -0
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/aiInventory.d.ts.map +1 -1
- package/types/lib/inventory/aiProviders.d.ts +85 -0
- package/types/lib/inventory/aiProviders.d.ts.map +1 -0
- package/types/lib/inventory/bomUtils.d.ts.map +1 -1
- package/types/lib/inventory/ciParsers/githubActionsLock.d.ts +101 -0
- package/types/lib/inventory/ciParsers/githubActionsLock.d.ts.map +1 -0
- package/types/lib/inventory/citations.d.ts +5 -0
- package/types/lib/inventory/citations.d.ts.map +1 -1
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/cyclonedx20.d.ts +99 -0
- package/types/lib/inventory/cyclonedx20.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Common.d.ts +102 -0
- package/types/lib/inventory/cyclonedx20Common.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Derived.d.ts +74 -0
- package/types/lib/inventory/cyclonedx20Derived.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Parties.d.ts +91 -0
- package/types/lib/inventory/cyclonedx20Parties.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Read.d.ts +51 -0
- package/types/lib/inventory/cyclonedx20Read.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadCommon.d.ts +69 -0
- package/types/lib/inventory/cyclonedx20ReadCommon.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadComponents.d.ts +77 -0
- package/types/lib/inventory/cyclonedx20ReadComponents.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadContext.d.ts +118 -0
- package/types/lib/inventory/cyclonedx20ReadContext.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts +40 -0
- package/types/lib/inventory/cyclonedx20ReadEvidence.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20ReadSections.d.ts +45 -0
- package/types/lib/inventory/cyclonedx20ReadSections.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Render.d.ts +53 -0
- package/types/lib/inventory/cyclonedx20Render.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderComponents.d.ts +73 -0
- package/types/lib/inventory/cyclonedx20RenderComponents.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts +29 -0
- package/types/lib/inventory/cyclonedx20RenderDefinitions.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts +110 -0
- package/types/lib/inventory/cyclonedx20RenderEvidence.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderHardware.d.ts +42 -0
- package/types/lib/inventory/cyclonedx20RenderHardware.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderPatents.d.ts +26 -0
- package/types/lib/inventory/cyclonedx20RenderPatents.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts +55 -0
- package/types/lib/inventory/cyclonedx20RenderRefIds.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20RenderSections.d.ts +45 -0
- package/types/lib/inventory/cyclonedx20RenderSections.d.ts.map +1 -0
- package/types/lib/inventory/cyclonedx20Tables.d.ts +134 -0
- package/types/lib/inventory/cyclonedx20Tables.d.ts.map +1 -0
- package/types/lib/inventory/deps.d.ts +35 -2
- package/types/lib/inventory/deps.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +19 -0
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dosaiParsers.d.ts.map +1 -1
- package/types/lib/inventory/dotnetRuntime.d.ts +51 -4
- package/types/lib/inventory/dotnetRuntime.d.ts.map +1 -1
- package/types/lib/inventory/ecosystemFormulationParsers.d.ts +73 -0
- package/types/lib/inventory/ecosystemFormulationParsers.d.ts.map +1 -0
- package/types/lib/inventory/envcontext.d.ts +22 -6
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/formulationParsers.d.ts.map +1 -1
- package/types/lib/inventory/hbom.d.ts +11 -0
- package/types/lib/inventory/hbom.d.ts.map +1 -1
- package/types/lib/inventory/hbomLoader.d.ts +54 -0
- package/types/lib/inventory/hbomLoader.d.ts.map +1 -1
- package/types/lib/inventory/jvmLocalRepos.d.ts +5 -1
- package/types/lib/inventory/jvmLocalRepos.d.ts.map +1 -1
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/occurrenceKey.d.ts +52 -0
- package/types/lib/inventory/occurrenceKey.d.ts.map +1 -0
- package/types/lib/inventory/pqcMigration.d.ts +84 -0
- package/types/lib/inventory/pqcMigration.d.ts.map +1 -0
- package/types/lib/inventory/rustFormulationParser.d.ts +7 -2
- package/types/lib/inventory/rustFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/scalaCoords.d.ts.map +1 -1
- package/types/lib/inventory/scalaDedupe.d.ts +43 -0
- package/types/lib/inventory/scalaDedupe.d.ts.map +1 -0
- package/types/lib/inventory/source.d.ts +15 -0
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/parsers/huggingfaceManifest.d.ts +10 -1
- package/types/lib/parsers/huggingfaceManifest.d.ts.map +1 -1
- package/types/lib/parsers/huggingfaceUtils.d.ts +12 -0
- package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
- package/types/lib/parsers/kiroPermissions.d.ts +62 -0
- package/types/lib/parsers/kiroPermissions.d.ts.map +1 -0
- package/types/lib/parsers/vscodeExtensionId.d.ts +48 -0
- package/types/lib/parsers/vscodeExtensionId.d.ts.map +1 -0
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts +7 -0
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/stages/postgen/specVersionCompat.d.ts +25 -0
- package/types/lib/stages/postgen/specVersionCompat.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts +5 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/complianceEngine.d.ts +4 -4
- package/types/lib/validator/complianceEngine.d.ts.map +1 -1
- package/types/lib/validator/complianceRules.d.ts +14 -2
- package/types/lib/validator/complianceRules.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +10 -0
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
package/lib/inventory/deps.js
CHANGED
|
@@ -13,9 +13,11 @@ import {
|
|
|
13
13
|
getTmpDir,
|
|
14
14
|
multiChecksumFile,
|
|
15
15
|
safeExistsSync,
|
|
16
|
+
safeMkdirSync,
|
|
16
17
|
safeMkdtempSync,
|
|
17
18
|
safeRmSync,
|
|
18
19
|
safeSpawnSync,
|
|
20
|
+
safeWriteSync,
|
|
19
21
|
} from "../core/fs.js";
|
|
20
22
|
import { isWin } from "../core/paths.js";
|
|
21
23
|
import { xml2js } from "../parsers/xml.js";
|
|
@@ -34,6 +36,10 @@ import { findLicenseId, spdxLicenses } from "./spdx.js";
|
|
|
34
36
|
|
|
35
37
|
const jarNSMapping_cache = new Map();
|
|
36
38
|
|
|
39
|
+
// The largest classes.jar on Google's Maven repository is a few tens of MiB,
|
|
40
|
+
// so this bound only stops an archive that claims an implausible size.
|
|
41
|
+
const MAX_AAR_INNER_JAR_BYTES = 256 * 1024 * 1024;
|
|
42
|
+
|
|
37
43
|
/**
|
|
38
44
|
* Whether a jar holds sources or javadoc rather than classes.
|
|
39
45
|
*
|
|
@@ -193,9 +199,10 @@ export async function collectMvnDependencies(
|
|
|
193
199
|
}
|
|
194
200
|
|
|
195
201
|
/**
|
|
196
|
-
* Method to collect class names from all jars in a
|
|
202
|
+
* Method to collect class names from all jars and Android libraries in a
|
|
203
|
+
* directory
|
|
197
204
|
*
|
|
198
|
-
* @param {string} jarPath Path containing jars
|
|
205
|
+
* @param {string} jarPath Path containing jars, or a single jar or aar file
|
|
199
206
|
* @param {object} pomPathMap Map containing jar to pom names. Required to successfully parse gradle cache.
|
|
200
207
|
*
|
|
201
208
|
* @return object containing jar name and class list
|
|
@@ -213,20 +220,21 @@ export async function collectJarNS(jarPath, pomPathMap = {}) {
|
|
|
213
220
|
)}`;
|
|
214
221
|
}
|
|
215
222
|
// Parse jar files to get class names
|
|
216
|
-
const jarFiles =
|
|
223
|
+
const jarFiles = /\.(jar|aar)$/i.test(jarPath)
|
|
217
224
|
? [jarPath]
|
|
218
|
-
: getAllFiles(jarPath, "**/*.jar");
|
|
225
|
+
: getAllFiles(jarPath, "**/*.{jar,aar}");
|
|
219
226
|
if (jarFiles?.length) {
|
|
220
227
|
for (const jf of jarFiles) {
|
|
221
228
|
const jarName = basename(jf);
|
|
229
|
+
const isAar = jarName.toLowerCase().endsWith(".aar");
|
|
222
230
|
// Sources and javadoc jars hold no runtime classes. Reading them would
|
|
223
231
|
// attach their hashes to the main artifact's purl.
|
|
224
232
|
if (isDocumentationJar(jarName)) {
|
|
225
233
|
continue;
|
|
226
234
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
235
|
+
// Only the extension is replaced, never a match earlier in the path.
|
|
236
|
+
const toPom = (file) => `${file.slice(0, -".jar".length)}.pom`;
|
|
237
|
+
let pomname = pomPathMap[toPom(jarName)] || toPom(jf);
|
|
230
238
|
let pomData;
|
|
231
239
|
let purl;
|
|
232
240
|
let manifestLicenses;
|
|
@@ -235,7 +243,12 @@ export async function collectJarNS(jarPath, pomPathMap = {}) {
|
|
|
235
243
|
let searchDir = dirname(jf);
|
|
236
244
|
// in case of gradle, there would be hash directory that is different for jar vs pom
|
|
237
245
|
// so we need to start search from a level up
|
|
238
|
-
if (
|
|
246
|
+
if (
|
|
247
|
+
searchDir.includes(join(".gradle", "caches")) ||
|
|
248
|
+
// A copied cache, or one reached through GRADLE_CACHE_DIR, keeps
|
|
249
|
+
// the files-2.1 directory without the .gradle parent.
|
|
250
|
+
searchDir.split(/[\\/]/).includes("files-2.1")
|
|
251
|
+
) {
|
|
239
252
|
searchDir = join(searchDir, "..");
|
|
240
253
|
}
|
|
241
254
|
const pomSearch = getAllFiles(searchDir, "**/*.pom");
|
|
@@ -262,7 +275,9 @@ export async function collectJarNS(jarPath, pomPathMap = {}) {
|
|
|
262
275
|
}
|
|
263
276
|
} else {
|
|
264
277
|
const coordinates = inferMavenCoordinatesFromPath(jf);
|
|
265
|
-
|
|
278
|
+
// An aar gets the same type=jar purl the Gradle dependency tree gives
|
|
279
|
+
// it, so the two entries merge into one component.
|
|
280
|
+
if (coordinates && ["jar", "aar"].includes(coordinates.extension)) {
|
|
266
281
|
purl = jarPurl(
|
|
267
282
|
coordinates.group,
|
|
268
283
|
coordinates.name,
|
|
@@ -274,8 +289,9 @@ export async function collectJarNS(jarPath, pomPathMap = {}) {
|
|
|
274
289
|
// descriptor, so its metadata and manifest licence are read here,
|
|
275
290
|
// before any remote lookup. Only a jar whose path names its
|
|
276
291
|
// coordinates is read: they choose its descriptor, and an entry
|
|
277
|
-
// without a purl never becomes a component.
|
|
278
|
-
|
|
292
|
+
// without a purl never becomes a component. An aar keeps no
|
|
293
|
+
// descriptor of its own; its POM sits beside it in the cache.
|
|
294
|
+
if (purl && !isAar) {
|
|
279
295
|
const embedded = await readEmbeddedJarMetadata(jf, coordinates);
|
|
280
296
|
if (embedded?.pomXml) {
|
|
281
297
|
try {
|
|
@@ -300,7 +316,9 @@ export async function collectJarNS(jarPath, pomPathMap = {}) {
|
|
|
300
316
|
console.log(`Parsing ${jf}`);
|
|
301
317
|
}
|
|
302
318
|
const [nsList, hashValues] = await Promise.all([
|
|
303
|
-
|
|
319
|
+
// An aar is hashed as published, over the aar file itself; its
|
|
320
|
+
// classes come from the jars it holds.
|
|
321
|
+
isAar ? getAarClasses(jf) : getJarClasses(jf),
|
|
304
322
|
multiChecksumFile(["md5", "sha1", "sha256", "sha512"], jf).catch(
|
|
305
323
|
() => undefined,
|
|
306
324
|
),
|
|
@@ -336,7 +354,7 @@ export async function collectJarNS(jarPath, pomPathMap = {}) {
|
|
|
336
354
|
}
|
|
337
355
|
} else {
|
|
338
356
|
console.log(
|
|
339
|
-
`${jarPath} did not contain any jars. Try building the project to improve the BOM precision.`,
|
|
357
|
+
`${jarPath} did not contain any jars or aar files. Try building the project to improve the BOM precision.`,
|
|
340
358
|
);
|
|
341
359
|
}
|
|
342
360
|
return jarNSMapping;
|
|
@@ -1136,6 +1154,81 @@ export async function getJarClasses(jarFile) {
|
|
|
1136
1154
|
return retList;
|
|
1137
1155
|
}
|
|
1138
1156
|
|
|
1157
|
+
/**
|
|
1158
|
+
* Method to get the classes of an Android library. An `.aar` keeps its code
|
|
1159
|
+
* in `classes.jar` at its root and any number of further jars under `libs/`,
|
|
1160
|
+
* each of which is read in turn.
|
|
1161
|
+
*
|
|
1162
|
+
* Each inner jar is unpacked to a temporary file, so in dry run mode, which
|
|
1163
|
+
* blocks those writes, an aar yields no classes. An inner jar that declares
|
|
1164
|
+
* more than `maxInnerJarBytes` uncompressed is skipped without being
|
|
1165
|
+
* decompressed, because the whole entry is held in memory while it is copied.
|
|
1166
|
+
*
|
|
1167
|
+
* @param {string} aarFile Aar file to read
|
|
1168
|
+
* @param {Object} [opts] Options
|
|
1169
|
+
* @param {number} [opts.maxInnerJarBytes] Uncompressed size bound for each
|
|
1170
|
+
* inner jar. Defaults to 256 MiB.
|
|
1171
|
+
*
|
|
1172
|
+
* @returns {Promise<string[]>} Sorted class names of every jar inside
|
|
1173
|
+
*/
|
|
1174
|
+
export async function getAarClasses(
|
|
1175
|
+
aarFile,
|
|
1176
|
+
{ maxInnerJarBytes = MAX_AAR_INNER_JAR_BYTES } = {},
|
|
1177
|
+
) {
|
|
1178
|
+
const classes = new Set();
|
|
1179
|
+
let zip;
|
|
1180
|
+
let tempDir;
|
|
1181
|
+
try {
|
|
1182
|
+
zip = new StreamZip.async({ file: aarFile });
|
|
1183
|
+
const innerJars = Object.values(await zip.entries())
|
|
1184
|
+
.filter(
|
|
1185
|
+
(entry) =>
|
|
1186
|
+
!entry.isDirectory &&
|
|
1187
|
+
(entry.name === "classes.jar" ||
|
|
1188
|
+
(entry.name.startsWith("libs/") && entry.name.endsWith(".jar"))),
|
|
1189
|
+
)
|
|
1190
|
+
.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
1191
|
+
for (const entry of innerJars) {
|
|
1192
|
+
if (entry.size > maxInnerJarBytes) {
|
|
1193
|
+
if (DEBUG_MODE) {
|
|
1194
|
+
console.log(
|
|
1195
|
+
`Skipping ${entry.name} in ${aarFile}: ${entry.size} bytes exceeds the ${maxInnerJarBytes} byte limit.`,
|
|
1196
|
+
);
|
|
1197
|
+
}
|
|
1198
|
+
continue;
|
|
1199
|
+
}
|
|
1200
|
+
tempDir ||= safeMkdtempSync(join(getTmpDir(), "aar-classes-"));
|
|
1201
|
+
// Entry names use forward slashes on every platform. node-stream-zip
|
|
1202
|
+
// rejects names with "..", a backslash or an absolute prefix, so the
|
|
1203
|
+
// file stays inside tempDir.
|
|
1204
|
+
const innerJar = join(tempDir, ...entry.name.split("/"));
|
|
1205
|
+
safeMkdirSync(dirname(innerJar), { recursive: true });
|
|
1206
|
+
safeWriteSync(innerJar, await zip.entryData(entry.name));
|
|
1207
|
+
// The write is skipped in dry run mode or without write permission.
|
|
1208
|
+
if (!safeExistsSync(innerJar)) {
|
|
1209
|
+
continue;
|
|
1210
|
+
}
|
|
1211
|
+
for (const cls of await getJarClasses(innerJar)) {
|
|
1212
|
+
classes.add(cls);
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
} catch (err) {
|
|
1216
|
+
if (DEBUG_MODE) {
|
|
1217
|
+
console.log(`Unable to read the jars inside ${aarFile}: ${err.message}`);
|
|
1218
|
+
}
|
|
1219
|
+
} finally {
|
|
1220
|
+
if (tempDir) {
|
|
1221
|
+
safeRmSync(tempDir, { recursive: true, force: true });
|
|
1222
|
+
}
|
|
1223
|
+
try {
|
|
1224
|
+
await zip?.close();
|
|
1225
|
+
} catch (_err) {
|
|
1226
|
+
// An archive that failed to open reports its close as well.
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
return [...classes].sort();
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1139
1232
|
/**
|
|
1140
1233
|
* Recursively flatten a Python package's transitive dependencies into the
|
|
1141
1234
|
* dependencies map and flat package list.
|
package/lib/inventory/dosai.js
CHANGED
|
@@ -2,12 +2,21 @@ import { Buffer } from "node:buffer";
|
|
|
2
2
|
import {
|
|
3
3
|
closeSync,
|
|
4
4
|
openSync,
|
|
5
|
+
readdirSync,
|
|
5
6
|
readFileSync,
|
|
6
7
|
readSync,
|
|
7
8
|
realpathSync,
|
|
8
9
|
statSync,
|
|
9
10
|
} from "node:fs";
|
|
10
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
basename,
|
|
13
|
+
delimiter,
|
|
14
|
+
extname,
|
|
15
|
+
isAbsolute,
|
|
16
|
+
join,
|
|
17
|
+
relative,
|
|
18
|
+
resolve,
|
|
19
|
+
} from "node:path";
|
|
11
20
|
import process from "node:process";
|
|
12
21
|
|
|
13
22
|
import { DEBUG_MODE, readEnvironmentVariable } from "../core/activity.js";
|
|
@@ -24,12 +33,22 @@ import {
|
|
|
24
33
|
} from "../core/fs.js";
|
|
25
34
|
import { formatDuration, reapProcessTree } from "../core/processTree.js";
|
|
26
35
|
import { encodeUriReference } from "../core/propertySanitizer.js";
|
|
36
|
+
import {
|
|
37
|
+
normalizeHuggingFaceReference,
|
|
38
|
+
toHuggingFaceSupplier,
|
|
39
|
+
} from "../parsers/huggingfaceUtils.js";
|
|
27
40
|
import {
|
|
28
41
|
indexJsonObject,
|
|
29
42
|
MAX_JSON_TEXT_BYTES,
|
|
30
43
|
readJsonMember,
|
|
31
44
|
readJsonRange,
|
|
32
45
|
} from "../parsers/largeJson.js";
|
|
46
|
+
import {
|
|
47
|
+
modelCreatorEntity,
|
|
48
|
+
modelCreatorFromOrganization,
|
|
49
|
+
resolveModelCreator,
|
|
50
|
+
} from "./aiProviders.js";
|
|
51
|
+
import { isPathExcludedByGlobPatterns } from "./atomUtils.js";
|
|
33
52
|
import {
|
|
34
53
|
addDosaiSetValue,
|
|
35
54
|
buildDosaiPurlAliasMap,
|
|
@@ -712,6 +731,198 @@ export function dosaiSectionsCdxgenReads(omittedSections) {
|
|
|
712
731
|
);
|
|
713
732
|
}
|
|
714
733
|
|
|
734
|
+
// The files dosai reads from a source tree whose changes make an existing
|
|
735
|
+
// methods slice stale: the C#, VB, and F# sources it analyses, the project
|
|
736
|
+
// and MSBuild files that shape the analysis, and the Razor and WebForms
|
|
737
|
+
// templates whose call sites it maps from build output. packages.config is
|
|
738
|
+
// read by cdxgen's own dotnet analysis and holds the same package list a
|
|
739
|
+
// csproj does, so a change to it can change the slice too. Solution files are
|
|
740
|
+
// left out: dosai opens them only to budget its restore runs, and a changed
|
|
741
|
+
// solution does not change the occurrences a slice holds.
|
|
742
|
+
const DOSAI_SLICE_SOURCE_EXTENSIONS = new Set([
|
|
743
|
+
".cs",
|
|
744
|
+
".cshtml",
|
|
745
|
+
".csproj",
|
|
746
|
+
".fs",
|
|
747
|
+
".fsi",
|
|
748
|
+
".fsproj",
|
|
749
|
+
".fsx",
|
|
750
|
+
".props",
|
|
751
|
+
".razor",
|
|
752
|
+
".targets",
|
|
753
|
+
".vb",
|
|
754
|
+
".vbproj",
|
|
755
|
+
]);
|
|
756
|
+
const DOSAI_SLICE_SOURCE_FILES = new Set(["packages.config"]);
|
|
757
|
+
|
|
758
|
+
// Directories that hold nothing dosai analyses. Its own tree walk skips the
|
|
759
|
+
// build-output directories (obj for every file, bin for sources) and the
|
|
760
|
+
// package and version-control directories, so a build cannot make a slice
|
|
761
|
+
// stale and a dependency tree cannot slow the check down.
|
|
762
|
+
const DOSAI_SLICE_SOURCE_SKIP_DIRS = new Set([
|
|
763
|
+
".git",
|
|
764
|
+
"bin",
|
|
765
|
+
"node_modules",
|
|
766
|
+
"obj",
|
|
767
|
+
]);
|
|
768
|
+
|
|
769
|
+
// The tree walk is bounded so a pathological tree (a symlink farm, or entries
|
|
770
|
+
// that appear faster than the walk reads them) cannot keep a scan from
|
|
771
|
+
// finishing. A walk that reaches a bound leaves freshness unknown, and the
|
|
772
|
+
// callers regenerate the slice, which is the run a fresh scan pays anyway.
|
|
773
|
+
const DOSAI_FRESHNESS_MAX_DIRS = 50_000;
|
|
774
|
+
const DOSAI_FRESHNESS_MAX_FILES = 200_000;
|
|
775
|
+
|
|
776
|
+
/**
|
|
777
|
+
* The newest mtime among the source files dosai analyses under a directory.
|
|
778
|
+
*
|
|
779
|
+
* The walk is deterministic and bounded: directory entries are read and then
|
|
780
|
+
* visited depth-first in sorted name order, symlinked directories are not
|
|
781
|
+
* followed, and counts of visited directories and analysed files cap the
|
|
782
|
+
* work. Excluded paths are pruned rather than filtered, so nothing beneath an
|
|
783
|
+
* excluded directory is read.
|
|
784
|
+
*
|
|
785
|
+
* @param {string} projectDir Absolute directory dosai analyses
|
|
786
|
+
* @param {string[]} excludePatterns Exclude globs relative to projectDir
|
|
787
|
+
* @returns {{mtimeMs: number|undefined, complete: boolean}} The newest mtime seen (undefined when no analysed file was found), and whether the walk finished, which is false for an unreadable directory or file, or a tree past the bounds
|
|
788
|
+
*/
|
|
789
|
+
function newestDosaiSourceMtimeSync(projectDir, excludePatterns) {
|
|
790
|
+
const newest = { mtimeMs: undefined, complete: true };
|
|
791
|
+
const stack = [[projectDir, ""]];
|
|
792
|
+
let dirsVisited = 0;
|
|
793
|
+
let filesVisited = 0;
|
|
794
|
+
while (newest.complete && stack.length) {
|
|
795
|
+
if (dirsVisited++ >= DOSAI_FRESHNESS_MAX_DIRS) {
|
|
796
|
+
newest.complete = false;
|
|
797
|
+
break;
|
|
798
|
+
}
|
|
799
|
+
const [dir, relDir] = stack.pop();
|
|
800
|
+
let entries;
|
|
801
|
+
try {
|
|
802
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
803
|
+
} catch (_err) {
|
|
804
|
+
newest.complete = false;
|
|
805
|
+
break;
|
|
806
|
+
}
|
|
807
|
+
entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
808
|
+
for (const entry of entries) {
|
|
809
|
+
const relPath = relDir ? `${relDir}/${entry.name}` : entry.name;
|
|
810
|
+
if (entry.isDirectory()) {
|
|
811
|
+
if (
|
|
812
|
+
!DOSAI_SLICE_SOURCE_SKIP_DIRS.has(entry.name.toLowerCase()) &&
|
|
813
|
+
!(
|
|
814
|
+
excludePatterns.length &&
|
|
815
|
+
isPathExcludedByGlobPatterns(relPath, excludePatterns)
|
|
816
|
+
)
|
|
817
|
+
) {
|
|
818
|
+
stack.push([join(dir, entry.name), relPath]);
|
|
819
|
+
}
|
|
820
|
+
continue;
|
|
821
|
+
}
|
|
822
|
+
if (
|
|
823
|
+
!DOSAI_SLICE_SOURCE_EXTENSIONS.has(extname(entry.name).toLowerCase()) &&
|
|
824
|
+
!DOSAI_SLICE_SOURCE_FILES.has(entry.name.toLowerCase())
|
|
825
|
+
) {
|
|
826
|
+
continue;
|
|
827
|
+
}
|
|
828
|
+
if (filesVisited++ >= DOSAI_FRESHNESS_MAX_FILES) {
|
|
829
|
+
newest.complete = false;
|
|
830
|
+
break;
|
|
831
|
+
}
|
|
832
|
+
try {
|
|
833
|
+
// statSync follows a symlink to the source it points at, which is
|
|
834
|
+
// the file dosai reads.
|
|
835
|
+
const mtimeMs = statSync(join(dir, entry.name)).mtimeMs;
|
|
836
|
+
if (newest.mtimeMs === undefined || mtimeMs > newest.mtimeMs) {
|
|
837
|
+
newest.mtimeMs = mtimeMs;
|
|
838
|
+
}
|
|
839
|
+
} catch (_err) {
|
|
840
|
+
newest.complete = false;
|
|
841
|
+
break;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
return newest;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
/**
|
|
849
|
+
* The cdxgen exclude patterns that apply under a directory, as globs relative
|
|
850
|
+
* to it.
|
|
851
|
+
*
|
|
852
|
+
* The patterns are matched the way the atom evidence filter matches
|
|
853
|
+
* `options.exclude`: a relative pattern matches at any depth, and an absolute
|
|
854
|
+
* pattern is anchored to the scanned directory, which for the walk means the
|
|
855
|
+
* patterns that sit beneath it are rebased to relative ones. Patterns that
|
|
856
|
+
* fall outside the directory are dropped.
|
|
857
|
+
*
|
|
858
|
+
* @param {string[]} excludes cdxgen exclude globs
|
|
859
|
+
* @param {string} projectDir Absolute directory dosai analyses
|
|
860
|
+
* @returns {string[]} Exclude globs relative to projectDir
|
|
861
|
+
*/
|
|
862
|
+
function dosaiSourceExcludePatterns(excludes, projectDir) {
|
|
863
|
+
const rebased = [];
|
|
864
|
+
if (!Array.isArray(excludes)) {
|
|
865
|
+
return rebased;
|
|
866
|
+
}
|
|
867
|
+
for (const pattern of excludes) {
|
|
868
|
+
if (typeof pattern !== "string" || !pattern.trim()) {
|
|
869
|
+
continue;
|
|
870
|
+
}
|
|
871
|
+
// resolve() leaves a relative pattern where it was and turns an absolute
|
|
872
|
+
// one into a path the relative() call drops the scanned directory from.
|
|
873
|
+
const underRoot = resolve(projectDir, pattern);
|
|
874
|
+
const rel = relative(projectDir, underRoot);
|
|
875
|
+
if (rel && !rel.startsWith("..") && !isAbsolute(rel)) {
|
|
876
|
+
rebased.push(rel.replaceAll("\\", "/"));
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
return rebased;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Whether an existing dosai methods slice is newer than the sources it
|
|
884
|
+
* describes.
|
|
885
|
+
*
|
|
886
|
+
* A slice written by an earlier scan holds the occurrences of the sources as
|
|
887
|
+
* they were then, so a slice that predates an edit must be regenerated rather
|
|
888
|
+
* than reused: reusing one keeps occurrences of call sites that no longer
|
|
889
|
+
* exist (issue 4675). The slice is fresh only when its mtime is not older
|
|
890
|
+
* than the newest mtime among the files dosai analyses under the directory.
|
|
891
|
+
* A missing slice, a slice whose mtime cannot be read, a source that cannot
|
|
892
|
+
* be stat-ed, and a tree that exceeds the walk bounds all count as stale: the
|
|
893
|
+
* callers regenerate the slice, which is the run a fresh scan pays anyway.
|
|
894
|
+
*
|
|
895
|
+
* @param {string} slicesFile Candidate dosai methods slice
|
|
896
|
+
* @param {string} projectDir Directory dosai analyses
|
|
897
|
+
* @param {Object} [options] Scan options; options.exclude prunes the walk
|
|
898
|
+
* @returns {boolean} True when the slice is newer than every source it describes
|
|
899
|
+
*/
|
|
900
|
+
export function dosaiSlicesFileIsFresh(slicesFile, projectDir, options = {}) {
|
|
901
|
+
if (!slicesFile || !projectDir || !safeExistsSync(slicesFile)) {
|
|
902
|
+
return false;
|
|
903
|
+
}
|
|
904
|
+
let sliceMtimeMs;
|
|
905
|
+
try {
|
|
906
|
+
sliceMtimeMs = statSync(slicesFile).mtimeMs;
|
|
907
|
+
} catch (_err) {
|
|
908
|
+
return false;
|
|
909
|
+
}
|
|
910
|
+
const excludePatterns = dosaiSourceExcludePatterns(
|
|
911
|
+
options.exclude,
|
|
912
|
+
resolve(projectDir),
|
|
913
|
+
);
|
|
914
|
+
const newest = newestDosaiSourceMtimeSync(
|
|
915
|
+
resolve(projectDir),
|
|
916
|
+
excludePatterns,
|
|
917
|
+
);
|
|
918
|
+
if (!newest.complete) {
|
|
919
|
+
return false;
|
|
920
|
+
}
|
|
921
|
+
// A directory with no file dosai analyses holds nothing that can go stale,
|
|
922
|
+
// so the slice counts as fresh.
|
|
923
|
+
return newest.mtimeMs === undefined || sliceMtimeMs >= newest.mtimeMs;
|
|
924
|
+
}
|
|
925
|
+
|
|
715
926
|
// The file each report returned by readDosaiReport came from, and whether it
|
|
716
927
|
// was trimmed to what cdxgen reads. persistDosaiSemanticsReport copies the
|
|
717
928
|
// file rather than re-serialising a trimmed or oversized report.
|
|
@@ -1978,6 +2189,23 @@ export function collectDosaiAiComponents(methodsSlice, components = []) {
|
|
|
1978
2189
|
format,
|
|
1979
2190
|
}));
|
|
1980
2191
|
}
|
|
2192
|
+
const hubReference = String(ai.Purl || "").startsWith("pkg:huggingface/")
|
|
2193
|
+
? normalizeHuggingFaceReference(ai.Purl)
|
|
2194
|
+
: undefined;
|
|
2195
|
+
const hubNamespace = hubReference?.repoId?.split("/")[0];
|
|
2196
|
+
// A Hub model is credited to the namespace only when it is the
|
|
2197
|
+
// creator's own organisation, as the source collectors do. Otherwise
|
|
2198
|
+
// dosai reports the provider the code calls, which names the client
|
|
2199
|
+
// rather than the model, so the creator is resolved from the model
|
|
2200
|
+
// name.
|
|
2201
|
+
const manufacturer = modelCreatorEntity(
|
|
2202
|
+
hubNamespace
|
|
2203
|
+
? modelCreatorFromOrganization(hubNamespace)
|
|
2204
|
+
: resolveModelCreator(ai.Name, {
|
|
2205
|
+
fileProviders: ai.Provider ? [ai.Provider] : [],
|
|
2206
|
+
provider: ai.Provider,
|
|
2207
|
+
}),
|
|
2208
|
+
);
|
|
1981
2209
|
const component = {
|
|
1982
2210
|
"bom-ref": ai.Id,
|
|
1983
2211
|
type: "machine-learning-model",
|
|
@@ -1985,6 +2213,13 @@ export function collectDosaiAiComponents(methodsSlice, components = []) {
|
|
|
1985
2213
|
version: ai.Version,
|
|
1986
2214
|
purl: ai.Purl,
|
|
1987
2215
|
modelCard,
|
|
2216
|
+
// The creator is the manufacturer; a Hugging Face namespace that
|
|
2217
|
+
// published the model is the supplier. The provider stays a
|
|
2218
|
+
// property either way.
|
|
2219
|
+
manufacturer,
|
|
2220
|
+
supplier: hubNamespace
|
|
2221
|
+
? toHuggingFaceSupplier(hubNamespace)
|
|
2222
|
+
: undefined,
|
|
1988
2223
|
properties: [
|
|
1989
2224
|
{ name: "cdx:ai:provider", value: ai.Provider || "unknown" },
|
|
1990
2225
|
{ name: "cdx:ai:deployment", value: ai.Deployment || "unknown" },
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { Purl } from "@cdxgen/cdx-purl";
|
|
13
13
|
|
|
14
14
|
import { safeExistsSync } from "../core/fs.js";
|
|
15
|
+
import { occurrenceLocationKey } from "./occurrenceKey.js";
|
|
15
16
|
|
|
16
17
|
/**
|
|
17
18
|
* Build a lowercase type/namespace/name lookup key for a purl.
|
|
@@ -68,10 +69,7 @@ export function dosaiLocation(item) {
|
|
|
68
69
|
return undefined;
|
|
69
70
|
}
|
|
70
71
|
const lineNumber = location?.LineNumber || item?.LineNumber;
|
|
71
|
-
|
|
72
|
-
return `${fileName}#${lineNumber}`;
|
|
73
|
-
}
|
|
74
|
-
return fileName;
|
|
72
|
+
return occurrenceLocationKey(fileName, lineNumber);
|
|
75
73
|
}
|
|
76
74
|
|
|
77
75
|
function dosaiSourceFileName(item) {
|
|
@@ -110,8 +108,7 @@ export function dosaiCallSiteLocation(methodCall, srcPath) {
|
|
|
110
108
|
if (!file || lowerFile.endsWith(".dll") || lowerFile.endsWith(".exe")) {
|
|
111
109
|
return undefined;
|
|
112
110
|
}
|
|
113
|
-
|
|
114
|
-
return lineNumber > 0 ? `${file}#${lineNumber}` : file;
|
|
111
|
+
return occurrenceLocationKey(file, methodCall?.LineNumber);
|
|
115
112
|
}
|
|
116
113
|
|
|
117
114
|
function dosaiCallSiteFile(methodCall, srcPath) {
|