@cdxgen/cdxgen 13.1.0 → 13.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -15
- package/bin/cdxgen.js +243 -122
- package/bin/evinse.js +7 -0
- package/bin/sign.js +88 -14
- package/bin/tracebom.js +64 -4
- package/bin/validate.js +29 -7
- package/bin/verify.js +79 -39
- package/data/crypto-oid.json +88 -12
- package/data/remediations.json +47 -0
- package/data/rules/ai-bom.yaml +78 -0
- package/data/rules/cargo-rust.yaml +132 -0
- package/data/rules/mcp-servers.yaml +122 -0
- package/lib/audit/targets.js +6 -1
- package/lib/cli/bomAssembly.js +117 -15
- package/lib/cli/cliOptions.js +93 -1
- package/lib/cli/index.js +235 -5
- package/lib/cli/jsBom.js +108 -26
- package/lib/cli/jvmBom.js +68 -12
- package/lib/cli/managedBom.js +264 -41
- package/lib/cli/nativeBom.js +923 -15
- package/lib/core/activity.js +120 -2
- package/lib/core/env.js +44 -7
- package/lib/core/fs.js +174 -6
- package/lib/core/metadataProperties.js +97 -0
- package/lib/core/projectConfig.js +31 -4
- package/lib/core/userAgent.js +151 -0
- package/lib/ecosystems/bunutils.js +269 -101
- package/lib/ecosystems/cargoCache.js +364 -0
- package/lib/ecosystems/caxa.js +447 -7
- package/lib/ecosystems/cmakeResolver.js +66 -0
- package/lib/ecosystems/cppEvidence.js +104 -33
- package/lib/ecosystems/ecosystems.js +260 -99
- package/lib/ecosystems/golem.js +11 -1
- package/lib/ecosystems/gradleutils.js +185 -9
- package/lib/ecosystems/jsEvidence.js +19 -20
- package/lib/ecosystems/kosi.js +1263 -0
- package/lib/ecosystems/npmutils.js +110 -9
- package/lib/ecosystems/parsers-crystal.js +179 -0
- package/lib/ecosystems/parsers-dotnet.js +120 -49
- package/lib/ecosystems/parsers-elm.js +816 -0
- package/lib/ecosystems/parsers-gleam.js +1 -1
- package/lib/ecosystems/parsers-go.js +47 -9
- package/lib/ecosystems/parsers-js.js +171 -50
- package/lib/ecosystems/parsers-julia.js +268 -0
- package/lib/ecosystems/parsers-lua.js +218 -0
- package/lib/ecosystems/parsers-misc.js +919 -161
- package/lib/ecosystems/parsers-nim.js +221 -0
- package/lib/ecosystems/parsers-opam.js +180 -0
- package/lib/ecosystems/parsers-perl.js +277 -0
- package/lib/ecosystems/parsers-python.js +27 -15
- package/lib/ecosystems/parsers-r.js +148 -0
- package/lib/ecosystems/parsers-rebar.js +411 -0
- package/lib/ecosystems/parsers-rust.js +954 -33
- package/lib/ecosystems/parsers-spack.js +204 -0
- package/lib/ecosystems/parsers-stack.js +264 -0
- package/lib/ecosystems/parsers-terraform.js +194 -0
- package/lib/ecosystems/rubyutils.js +1426 -166
- package/lib/ecosystems/rusi.js +158 -7
- package/lib/evinser/evinser.js +547 -129
- package/lib/evinser/swiftsem.js +1914 -302
- package/lib/helpers/agenticToolutils.js +925 -0
- package/lib/helpers/bomSigner.js +652 -221
- package/lib/inventory/agentFormulationParser.js +57 -4
- package/lib/inventory/aiCollector.js +504 -10
- package/lib/inventory/aiInventory.js +12 -0
- package/lib/inventory/analyzer.js +488 -30
- package/lib/inventory/astWorker.js +6 -0
- package/lib/inventory/atomUtils.js +568 -31
- package/lib/inventory/auditCategories.js +8 -0
- package/lib/inventory/cbomutils.js +30 -0
- package/lib/inventory/cdxrs.js +110 -14
- package/lib/inventory/communityAiConfigParser.js +215 -2
- package/lib/inventory/depsUtils.js +183 -11
- package/lib/inventory/display.js +4 -5
- package/lib/inventory/dosai.js +627 -13
- package/lib/inventory/dynamic.js +39 -1
- package/lib/inventory/envcontext.js +9 -6
- package/lib/inventory/evidenceUtils.js +26 -5
- package/lib/inventory/fetchBatch.js +190 -19
- package/lib/inventory/fetchRate.js +74 -11
- package/lib/inventory/forgeEnricher.js +3 -2
- package/lib/inventory/jvmToolEnv.js +6 -5
- package/lib/inventory/mcpConfigParser.js +166 -2
- package/lib/inventory/mcpDiscovery.js +11 -0
- package/lib/inventory/mcpServerManifest.js +389 -0
- package/lib/inventory/mcpbBundle.js +419 -0
- package/lib/inventory/plugins.js +3 -0
- package/lib/inventory/protobom.js +62 -89
- package/lib/inventory/protobomLoader.js +4 -0
- package/lib/inventory/purl.js +152 -3
- package/lib/inventory/rustFormulationParser.js +1 -1
- package/lib/inventory/sbomDocument.js +2 -70
- package/lib/inventory/source.js +47 -23
- package/lib/inventory/stylePackageRefs.js +41 -0
- package/lib/inventory/svelteUtils.js +524 -0
- package/lib/inventory/traceRunner.js +210 -21
- package/lib/managers/binary.js +115 -24
- package/lib/managers/docker.js +440 -20
- package/lib/managers/oci.js +39 -1
- package/lib/parsers/cmakeLists.js +452 -0
- package/lib/parsers/huggingfaceUtils.js +15 -7
- package/lib/parsers/largeJson.js +454 -0
- package/lib/parsers/npmrc.js +19 -1
- package/lib/parsers/onnx.js +294 -0
- package/lib/parsers/safetensors.js +114 -0
- package/lib/stages/postgen/introspection/markers.js +29 -6
- package/lib/stages/postgen/postgen.js +237 -16
- package/lib/third-party/arborist/snapshots/calc-dep-flags.test.cjs +462 -0
- package/lib/validator/bomValidator.js +6 -16
- package/lib/validator/index.js +48 -10
- package/lib/validator/reporters/console.js +3 -0
- package/lib/validator/reporters/json.js +1 -0
- package/lib/validator/reporters/sarif.js +7 -0
- package/package.json +27 -27
- package/types/lib/audit/targets.d.ts.map +1 -1
- package/types/lib/cli/bomAssembly.d.ts +2 -1
- package/types/lib/cli/bomAssembly.d.ts.map +1 -1
- package/types/lib/cli/cliOptions.d.ts +33 -1
- package/types/lib/cli/cliOptions.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts +2 -2
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/cli/jsBom.d.ts +23 -0
- package/types/lib/cli/jsBom.d.ts.map +1 -1
- package/types/lib/cli/jvmBom.d.ts.map +1 -1
- package/types/lib/cli/managedBom.d.ts +33 -0
- package/types/lib/cli/managedBom.d.ts.map +1 -1
- package/types/lib/cli/nativeBom.d.ts +88 -8
- package/types/lib/cli/nativeBom.d.ts.map +1 -1
- package/types/lib/core/activity.d.ts.map +1 -1
- package/types/lib/core/env.d.ts +21 -0
- package/types/lib/core/env.d.ts.map +1 -1
- package/types/lib/core/fs.d.ts +55 -0
- package/types/lib/core/fs.d.ts.map +1 -1
- package/types/lib/core/metadataProperties.d.ts +39 -0
- package/types/lib/core/metadataProperties.d.ts.map +1 -0
- package/types/lib/core/projectConfig.d.ts +6 -2
- package/types/lib/core/projectConfig.d.ts.map +1 -1
- package/types/lib/core/userAgent.d.ts +56 -0
- package/types/lib/core/userAgent.d.ts.map +1 -0
- package/types/lib/ecosystems/bunutils.d.ts +12 -5
- package/types/lib/ecosystems/bunutils.d.ts.map +1 -1
- package/types/lib/ecosystems/cargoCache.d.ts +75 -0
- package/types/lib/ecosystems/cargoCache.d.ts.map +1 -0
- package/types/lib/ecosystems/caxa.d.ts +14 -0
- package/types/lib/ecosystems/caxa.d.ts.map +1 -1
- package/types/lib/ecosystems/cmakeResolver.d.ts +23 -0
- package/types/lib/ecosystems/cmakeResolver.d.ts.map +1 -1
- package/types/lib/ecosystems/cppEvidence.d.ts +2 -2
- package/types/lib/ecosystems/cppEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/ecosystems.d.ts +15 -1
- package/types/lib/ecosystems/ecosystems.d.ts.map +1 -1
- package/types/lib/ecosystems/golem.d.ts.map +1 -1
- package/types/lib/ecosystems/gradleutils.d.ts +40 -4
- package/types/lib/ecosystems/gradleutils.d.ts.map +1 -1
- package/types/lib/ecosystems/jsEvidence.d.ts +5 -2
- package/types/lib/ecosystems/jsEvidence.d.ts.map +1 -1
- package/types/lib/ecosystems/kosi.d.ts +86 -0
- package/types/lib/ecosystems/kosi.d.ts.map +1 -0
- package/types/lib/ecosystems/npmutils.d.ts +42 -0
- package/types/lib/ecosystems/npmutils.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-crystal.d.ts +27 -0
- package/types/lib/ecosystems/parsers-crystal.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-dotnet.d.ts +19 -0
- package/types/lib/ecosystems/parsers-dotnet.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-elm.d.ts +79 -0
- package/types/lib/ecosystems/parsers-elm.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-go.d.ts +10 -0
- package/types/lib/ecosystems/parsers-go.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-js.d.ts +10 -4
- package/types/lib/ecosystems/parsers-js.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-julia.d.ts +57 -0
- package/types/lib/ecosystems/parsers-julia.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-lua.d.ts +50 -0
- package/types/lib/ecosystems/parsers-lua.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-misc.d.ts +90 -3
- package/types/lib/ecosystems/parsers-misc.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-nim.d.ts +43 -0
- package/types/lib/ecosystems/parsers-nim.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-opam.d.ts +31 -0
- package/types/lib/ecosystems/parsers-opam.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-perl.d.ts +36 -0
- package/types/lib/ecosystems/parsers-perl.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-python.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-r.d.ts +35 -0
- package/types/lib/ecosystems/parsers-r.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-rebar.d.ts +42 -0
- package/types/lib/ecosystems/parsers-rebar.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-rust.d.ts +199 -0
- package/types/lib/ecosystems/parsers-rust.d.ts.map +1 -1
- package/types/lib/ecosystems/parsers-spack.d.ts +33 -0
- package/types/lib/ecosystems/parsers-spack.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-stack.d.ts +35 -0
- package/types/lib/ecosystems/parsers-stack.d.ts.map +1 -0
- package/types/lib/ecosystems/parsers-terraform.d.ts +34 -0
- package/types/lib/ecosystems/parsers-terraform.d.ts.map +1 -0
- package/types/lib/ecosystems/pythonutils.d.ts +1 -1
- package/types/lib/ecosystems/rubyutils.d.ts +143 -2
- package/types/lib/ecosystems/rubyutils.d.ts.map +1 -1
- package/types/lib/ecosystems/rusi.d.ts +13 -0
- package/types/lib/ecosystems/rusi.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +81 -2
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/evinser/swiftsem.d.ts +280 -15
- package/types/lib/evinser/swiftsem.d.ts.map +1 -1
- package/types/lib/helpers/agenticToolutils.d.ts +87 -0
- package/types/lib/helpers/agenticToolutils.d.ts.map +1 -0
- package/types/lib/helpers/bomSigner.d.ts +106 -13
- package/types/lib/helpers/bomSigner.d.ts.map +1 -1
- package/types/lib/inventory/agentFormulationParser.d.ts.map +1 -1
- package/types/lib/inventory/aiCollector.d.ts.map +1 -1
- package/types/lib/inventory/aiInventory.d.ts.map +1 -1
- package/types/lib/inventory/analyzer.d.ts.map +1 -1
- package/types/lib/inventory/atomUtils.d.ts +145 -3
- package/types/lib/inventory/atomUtils.d.ts.map +1 -1
- package/types/lib/inventory/auditCategories.d.ts +4 -0
- package/types/lib/inventory/auditCategories.d.ts.map +1 -1
- package/types/lib/inventory/cbomutils.d.ts.map +1 -1
- package/types/lib/inventory/cdxrs.d.ts +8 -3
- package/types/lib/inventory/cdxrs.d.ts.map +1 -1
- package/types/lib/inventory/communityAiConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/depsUtils.d.ts +15 -0
- package/types/lib/inventory/depsUtils.d.ts.map +1 -1
- package/types/lib/inventory/display.d.ts.map +1 -1
- package/types/lib/inventory/dosai.d.ts +87 -1
- package/types/lib/inventory/dosai.d.ts.map +1 -1
- package/types/lib/inventory/dynamic.d.ts.map +1 -1
- package/types/lib/inventory/envcontext.d.ts.map +1 -1
- package/types/lib/inventory/evidenceUtils.d.ts.map +1 -1
- package/types/lib/inventory/fetchBatch.d.ts +34 -0
- package/types/lib/inventory/fetchBatch.d.ts.map +1 -1
- package/types/lib/inventory/fetchRate.d.ts +9 -6
- package/types/lib/inventory/fetchRate.d.ts.map +1 -1
- package/types/lib/inventory/forgeEnricher.d.ts.map +1 -1
- package/types/lib/inventory/jvmToolEnv.d.ts +5 -4
- package/types/lib/inventory/jvmToolEnv.d.ts.map +1 -1
- package/types/lib/inventory/mcpConfigParser.d.ts.map +1 -1
- package/types/lib/inventory/mcpDiscovery.d.ts.map +1 -1
- package/types/lib/inventory/mcpServerManifest.d.ts +19 -0
- package/types/lib/inventory/mcpServerManifest.d.ts.map +1 -0
- package/types/lib/inventory/mcpbBundle.d.ts +19 -0
- package/types/lib/inventory/mcpbBundle.d.ts.map +1 -0
- package/types/lib/inventory/plugins.d.ts.map +1 -1
- package/types/lib/inventory/protobom.d.ts +18 -13
- package/types/lib/inventory/protobom.d.ts.map +1 -1
- package/types/lib/inventory/protobomLoader.d.ts.map +1 -1
- package/types/lib/inventory/purl.d.ts +71 -2
- package/types/lib/inventory/purl.d.ts.map +1 -1
- package/types/lib/inventory/sbomDocument.d.ts.map +1 -1
- package/types/lib/inventory/source.d.ts.map +1 -1
- package/types/lib/inventory/stylePackageRefs.d.ts +17 -0
- package/types/lib/inventory/stylePackageRefs.d.ts.map +1 -0
- package/types/lib/inventory/svelteUtils.d.ts +124 -0
- package/types/lib/inventory/svelteUtils.d.ts.map +1 -0
- package/types/lib/inventory/traceRunner.d.ts +47 -1
- package/types/lib/inventory/traceRunner.d.ts.map +1 -1
- package/types/lib/managers/binary.d.ts +30 -1
- package/types/lib/managers/binary.d.ts.map +1 -1
- package/types/lib/managers/docker.d.ts +29 -0
- package/types/lib/managers/docker.d.ts.map +1 -1
- package/types/lib/managers/oci.d.ts +14 -0
- package/types/lib/managers/oci.d.ts.map +1 -1
- package/types/lib/parsers/cmakeLists.d.ts +76 -0
- package/types/lib/parsers/cmakeLists.d.ts.map +1 -0
- package/types/lib/parsers/huggingfaceUtils.d.ts +8 -1
- package/types/lib/parsers/huggingfaceUtils.d.ts.map +1 -1
- package/types/lib/parsers/largeJson.d.ts +52 -0
- package/types/lib/parsers/largeJson.d.ts.map +1 -0
- package/types/lib/parsers/npmrc.d.ts +5 -0
- package/types/lib/parsers/npmrc.d.ts.map +1 -1
- package/types/lib/parsers/onnx.d.ts +28 -0
- package/types/lib/parsers/onnx.d.ts.map +1 -0
- package/types/lib/parsers/safetensors.d.ts +22 -0
- package/types/lib/parsers/safetensors.d.ts.map +1 -0
- package/types/lib/stages/postgen/introspection/markers.d.ts +3 -2
- package/types/lib/stages/postgen/introspection/markers.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts +33 -0
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/index.d.ts +10 -1
- package/types/lib/validator/index.d.ts.map +1 -1
- package/types/lib/validator/reporters/console.d.ts.map +1 -1
- package/types/lib/validator/reporters/json.d.ts.map +1 -1
- package/types/lib/validator/reporters/sarif.d.ts.map +1 -1
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
import * as toml from "smol-toml";
|
|
4
|
+
|
|
5
|
+
import { tryBuildPurl } from "../inventory/purl.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Julia project parser.
|
|
9
|
+
*
|
|
10
|
+
* Two files are consulted:
|
|
11
|
+
* - `Project.toml` — the manifest holding the project name, UUID, version,
|
|
12
|
+
* the names (and UUIDs) of its direct dependencies, and version
|
|
13
|
+
* compatibility constraints.
|
|
14
|
+
* - `Manifest.toml` — the lock file written by Pkg on resolve, pinning every
|
|
15
|
+
* package in the environment with its UUID, version, content hash, and the
|
|
16
|
+
* names of the packages it depends on.
|
|
17
|
+
*
|
|
18
|
+
* Packages are identified with the registered `julia` purl type, which carries
|
|
19
|
+
* the package UUID as a required qualifier (`pkg:julia/JSON@0.21.4?uuid=…`).
|
|
20
|
+
* The UUID is what Julia itself uses to identify packages, so keeping it in
|
|
21
|
+
* the purl lets consumers match components across differently named mirrors.
|
|
22
|
+
*
|
|
23
|
+
* Julia standard libraries ship with the language itself and carry no version
|
|
24
|
+
* in the manifest; they are emitted without a version and marked as standard
|
|
25
|
+
* libraries so they can be told apart from registry packages.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Parse a Julia environment from its `Project.toml` and optional
|
|
30
|
+
* `Manifest.toml`.
|
|
31
|
+
*
|
|
32
|
+
* @param {string} [projectTomlFile] Path to `Project.toml`, if present
|
|
33
|
+
* @param {string} [manifestTomlFile] Path to `Manifest.toml`, if present
|
|
34
|
+
* @returns {{ pkgList: object[], dependencies: object[], parentComponent: object, rootInputs?: string[] }}
|
|
35
|
+
*/
|
|
36
|
+
export function parseJuliaProject(projectTomlFile, manifestTomlFile) {
|
|
37
|
+
const project = projectTomlFile ? readTomlFile(projectTomlFile) : undefined;
|
|
38
|
+
|
|
39
|
+
const parentComponent = {};
|
|
40
|
+
const directNames = new Set(
|
|
41
|
+
project?.deps && typeof project.deps === "object"
|
|
42
|
+
? Object.keys(project.deps)
|
|
43
|
+
: [],
|
|
44
|
+
);
|
|
45
|
+
if (project?.name) {
|
|
46
|
+
const properties = [{ name: "internal:SrcFile", value: projectTomlFile }];
|
|
47
|
+
if (typeof project.uuid === "string" && project.uuid) {
|
|
48
|
+
properties.push({ name: "cdx:julia:uuid", value: project.uuid });
|
|
49
|
+
}
|
|
50
|
+
parentComponent.type = "application";
|
|
51
|
+
parentComponent.name = project.name;
|
|
52
|
+
if (typeof project.version === "string" && project.version) {
|
|
53
|
+
parentComponent.version = project.version;
|
|
54
|
+
}
|
|
55
|
+
parentComponent.description = `Julia project: ${project.name}`;
|
|
56
|
+
parentComponent.properties = properties;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let manifest;
|
|
60
|
+
if (manifestTomlFile) {
|
|
61
|
+
manifest = readTomlFile(manifestTomlFile);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Manifest packages live under `[[deps.<Name>]]` array-of-table entries,
|
|
65
|
+
// which smol-toml surfaces as a plain object keyed by package name.
|
|
66
|
+
const manifestDeps = manifest?.deps;
|
|
67
|
+
if (!manifestDeps || typeof manifestDeps !== "object") {
|
|
68
|
+
// Lockless fallback: emit the declared direct dependencies without
|
|
69
|
+
// resolved versions, using the UUIDs from Project.toml.
|
|
70
|
+
const pkgList = [...directNames].map((name) =>
|
|
71
|
+
juliaPackage(name, undefined, project?.deps?.[name], {
|
|
72
|
+
direct: true,
|
|
73
|
+
stdlib: JULIA_STDLIBS.has(name),
|
|
74
|
+
srcFile: projectTomlFile,
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
77
|
+
return {
|
|
78
|
+
pkgList,
|
|
79
|
+
dependencies: [],
|
|
80
|
+
parentComponent,
|
|
81
|
+
rootInputs: pkgList.map((p) => p["bom-ref"]),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const pkgList = [];
|
|
86
|
+
const edges = [];
|
|
87
|
+
const refByName = new Map();
|
|
88
|
+
for (const [name, raw] of Object.entries(manifestDeps)) {
|
|
89
|
+
// `[[deps.<Name>]]` is an array of tables; a name repeats only when two
|
|
90
|
+
// sources of the same package are pinned, and the first entry is the one
|
|
91
|
+
// the environment resolves first.
|
|
92
|
+
const entry = Array.isArray(raw) ? raw[0] : raw;
|
|
93
|
+
const uuid =
|
|
94
|
+
typeof entry?.uuid === "string" && entry.uuid ? entry.uuid : undefined;
|
|
95
|
+
const registryVersion =
|
|
96
|
+
typeof entry?.version === "string" && entry.version
|
|
97
|
+
? entry.version
|
|
98
|
+
: undefined;
|
|
99
|
+
// Entries without a registry version are pinned by content hash (git or
|
|
100
|
+
// local path sources); the hash is the most precise version available.
|
|
101
|
+
const gitTreeSha1 =
|
|
102
|
+
typeof entry?.["git-tree-sha1"] === "string" && entry["git-tree-sha1"]
|
|
103
|
+
? entry["git-tree-sha1"]
|
|
104
|
+
: undefined;
|
|
105
|
+
const version = registryVersion || gitTreeSha1;
|
|
106
|
+
const pkg = juliaPackage(name, version, uuid, {
|
|
107
|
+
direct: directNames.has(name),
|
|
108
|
+
stdlib: !registryVersion && JULIA_STDLIBS.has(name),
|
|
109
|
+
srcFile: manifestTomlFile,
|
|
110
|
+
});
|
|
111
|
+
pkgList.push(pkg);
|
|
112
|
+
refByName.set(name, pkg["bom-ref"]);
|
|
113
|
+
}
|
|
114
|
+
for (const [name, raw] of Object.entries(manifestDeps)) {
|
|
115
|
+
const ref = refByName.get(name);
|
|
116
|
+
if (!ref) {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const entry = Array.isArray(raw) ? raw[0] : raw;
|
|
120
|
+
const dependsOn = (Array.isArray(entry?.deps) ? entry.deps : [])
|
|
121
|
+
.map((depName) => refByName.get(depName))
|
|
122
|
+
.filter(Boolean);
|
|
123
|
+
edges.push({ ref, dependsOn: [...new Set(dependsOn)].sort() });
|
|
124
|
+
}
|
|
125
|
+
const rootInputs = [...directNames]
|
|
126
|
+
.map((name) => refByName.get(name))
|
|
127
|
+
.filter(Boolean);
|
|
128
|
+
return { pkgList, dependencies: edges, parentComponent, rootInputs };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Tell whether a `Project.toml` declares a Julia environment.
|
|
133
|
+
*
|
|
134
|
+
* Cloud Native Buildpacks and Gleam use the same file names, so the caller
|
|
135
|
+
* needs a content check before dispatching. A Julia project always carries a
|
|
136
|
+
* package `uuid`, a `[deps]` table, or a `[compat]` table.
|
|
137
|
+
*
|
|
138
|
+
* @param {string} filePath Path to the candidate `Project.toml`
|
|
139
|
+
* @returns {boolean} true when the file is a Julia project
|
|
140
|
+
*/
|
|
141
|
+
export function isJuliaProjectFile(filePath) {
|
|
142
|
+
const project = readTomlFile(filePath);
|
|
143
|
+
return Boolean(
|
|
144
|
+
project &&
|
|
145
|
+
(typeof project.uuid === "string" ||
|
|
146
|
+
(project.deps && typeof project.deps === "object") ||
|
|
147
|
+
(project.compat && typeof project.compat === "object")),
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Tell whether a `Manifest.toml` is a Julia lock file.
|
|
153
|
+
*
|
|
154
|
+
* Gleam writes a lowercase `manifest.toml` that case-insensitive filesystems
|
|
155
|
+
* surface under the same glob; a Julia manifest is recognised by its
|
|
156
|
+
* `manifest_format` marker or its `[[deps.<Name>]]` tables.
|
|
157
|
+
*
|
|
158
|
+
* @param {string} filePath Path to the candidate `Manifest.toml`
|
|
159
|
+
* @returns {boolean} true when the file is a Julia manifest
|
|
160
|
+
*/
|
|
161
|
+
export function isJuliaManifestFile(filePath) {
|
|
162
|
+
const manifest = readTomlFile(filePath);
|
|
163
|
+
return Boolean(
|
|
164
|
+
manifest &&
|
|
165
|
+
(typeof manifest.manifest_format === "string" ||
|
|
166
|
+
(manifest.deps && typeof manifest.deps === "object")),
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Build a component-like package record for a Julia dependency.
|
|
172
|
+
*
|
|
173
|
+
* @param {string} name Package name as Julia writes it
|
|
174
|
+
* @param {string|undefined} version Resolved version, if known
|
|
175
|
+
* @param {string|undefined} uuid Package UUID, if known
|
|
176
|
+
* @param {object} opts Extra context (`direct`, `stdlib`, `srcFile`)
|
|
177
|
+
* @returns {object} Package record
|
|
178
|
+
*/
|
|
179
|
+
function juliaPackage(name, version, uuid, opts) {
|
|
180
|
+
// The julia purl type requires the uuid qualifier; when it is unknown the
|
|
181
|
+
// purl is dropped entirely rather than emitted without its identity anchor.
|
|
182
|
+
const purl =
|
|
183
|
+
name && uuid
|
|
184
|
+
? tryBuildPurl({
|
|
185
|
+
type: "julia",
|
|
186
|
+
name,
|
|
187
|
+
version: version || undefined,
|
|
188
|
+
qualifiers: { uuid },
|
|
189
|
+
})
|
|
190
|
+
: null;
|
|
191
|
+
const properties = [
|
|
192
|
+
{ name: "internal:SrcFile", value: opts.srcFile },
|
|
193
|
+
{
|
|
194
|
+
name: "cdx:julia:dependency",
|
|
195
|
+
value: opts.direct ? "direct" : "transitive",
|
|
196
|
+
},
|
|
197
|
+
];
|
|
198
|
+
if (uuid) {
|
|
199
|
+
properties.push({ name: "cdx:julia:uuid", value: uuid });
|
|
200
|
+
}
|
|
201
|
+
if (opts.stdlib) {
|
|
202
|
+
properties.push({ name: "cdx:julia:stdlib", value: "true" });
|
|
203
|
+
}
|
|
204
|
+
const pkg = {
|
|
205
|
+
name,
|
|
206
|
+
...(version ? { version } : {}),
|
|
207
|
+
type: "library",
|
|
208
|
+
scope: "required",
|
|
209
|
+
properties,
|
|
210
|
+
};
|
|
211
|
+
if (purl) {
|
|
212
|
+
pkg.purl = purl;
|
|
213
|
+
pkg["bom-ref"] = decodeURIComponent(purl);
|
|
214
|
+
} else {
|
|
215
|
+
pkg["bom-ref"] = `library:${name}:${version || ""}`;
|
|
216
|
+
}
|
|
217
|
+
return pkg;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Julia standard libraries ship with the language itself. They carry no
|
|
222
|
+
* version in the manifest, so they are recognised by name against this list.
|
|
223
|
+
*
|
|
224
|
+
* @type {Set<string>}
|
|
225
|
+
*/
|
|
226
|
+
const JULIA_STDLIBS = new Set([
|
|
227
|
+
"Base",
|
|
228
|
+
"CRC32c",
|
|
229
|
+
"Dates",
|
|
230
|
+
"DelimitedFiles",
|
|
231
|
+
"Distributed",
|
|
232
|
+
"FileWatching",
|
|
233
|
+
"Future",
|
|
234
|
+
"LibGit2",
|
|
235
|
+
"Libdl",
|
|
236
|
+
"LinearAlgebra",
|
|
237
|
+
"Logging",
|
|
238
|
+
"Markdown",
|
|
239
|
+
"Mmap",
|
|
240
|
+
"Printf",
|
|
241
|
+
"Profile",
|
|
242
|
+
"Random",
|
|
243
|
+
"REPL",
|
|
244
|
+
"Serialization",
|
|
245
|
+
"SharedArrays",
|
|
246
|
+
"Sockets",
|
|
247
|
+
"SparseArrays",
|
|
248
|
+
"Statistics",
|
|
249
|
+
"TOML",
|
|
250
|
+
"Test",
|
|
251
|
+
"UUIDs",
|
|
252
|
+
"Unicode",
|
|
253
|
+
]);
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Read and parse a TOML file, warning instead of throwing on invalid input.
|
|
257
|
+
*
|
|
258
|
+
* @param {string} filePath File to read
|
|
259
|
+
* @returns {object|undefined} Parsed value
|
|
260
|
+
*/
|
|
261
|
+
function readTomlFile(filePath) {
|
|
262
|
+
try {
|
|
263
|
+
return toml.parse(readFileSync(filePath, "utf-8"));
|
|
264
|
+
} catch (error) {
|
|
265
|
+
console.warn(`Failed to parse ${filePath}: ${error.message}`);
|
|
266
|
+
return undefined;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
|
|
3
|
+
import { tryBuildPurl } from "../inventory/purl.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Lua parser for rockspecs and `luarocks.lock`.
|
|
7
|
+
*
|
|
8
|
+
* A rockspec is a Lua source file whose top level is a sequence of
|
|
9
|
+
* assignments describing one package: its name, version, source, and a
|
|
10
|
+
* `dependencies` table of constraint strings (`"lua >= 5.1"`). LuaRocks 3
|
|
11
|
+
* can also persist the resolved set to `luarocks.lock`. Both are read with a
|
|
12
|
+
* small line scanner over the shapes these files actually use, rather than a
|
|
13
|
+
* Lua interpreter.
|
|
14
|
+
*
|
|
15
|
+
* Rocks are identified with the registered `luarocks` purl type
|
|
16
|
+
* (`pkg:luarocks/<name>@<version>`). Lua versions carry a rock revision
|
|
17
|
+
* suffix (`5.4.6-1`); the full rock version is kept verbatim because the
|
|
18
|
+
* revision distinguishes distinct builds.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Parse a rockspec manifest.
|
|
23
|
+
*
|
|
24
|
+
* @param {string} rockspecFile Path to the `.rockspec` file
|
|
25
|
+
* @returns {{ pkgList: object[], parentComponent: object }}
|
|
26
|
+
*/
|
|
27
|
+
export function parseRockspecFile(rockspecFile) {
|
|
28
|
+
const fields = readLuaAssignments(rockspecFile);
|
|
29
|
+
const name = fields.get("package");
|
|
30
|
+
const version = fields.get("version");
|
|
31
|
+
const declaredDeps = fields.get("dependencies");
|
|
32
|
+
|
|
33
|
+
const parentComponent = {};
|
|
34
|
+
if (name) {
|
|
35
|
+
parentComponent.type = "application";
|
|
36
|
+
parentComponent.name = name;
|
|
37
|
+
if (version) {
|
|
38
|
+
parentComponent.version = version;
|
|
39
|
+
}
|
|
40
|
+
parentComponent.description = `Lua rockspec: ${name}`;
|
|
41
|
+
parentComponent.properties = [
|
|
42
|
+
{ name: "internal:SrcFile", value: rockspecFile },
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const directNames = new Set(
|
|
47
|
+
Array.isArray(declaredDeps) ? declaredDeps.map((dep) => dep.name) : [],
|
|
48
|
+
);
|
|
49
|
+
const pkgList = [...directNames].map((depName) =>
|
|
50
|
+
luaPackage(depName, undefined, { direct: true, srcFile: rockspecFile }),
|
|
51
|
+
);
|
|
52
|
+
return { pkgList, parentComponent };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Parse a `luarocks.lock` file.
|
|
57
|
+
*
|
|
58
|
+
* @param {string} lockFile Path to `luarocks.lock`
|
|
59
|
+
* @param {string} [rockspecFile] Path to the rockspec, to mark direct deps
|
|
60
|
+
* @returns {{ pkgList: object[], parentComponent: object }}
|
|
61
|
+
*/
|
|
62
|
+
export function parseLuaRocksLock(lockFile, rockspecFile) {
|
|
63
|
+
const fields = readLuaAssignments(lockFile);
|
|
64
|
+
const locked = fields.get("dependencies");
|
|
65
|
+
if (!Array.isArray(locked)) {
|
|
66
|
+
return { pkgList: [], parentComponent: {} };
|
|
67
|
+
}
|
|
68
|
+
const directNames = new Set();
|
|
69
|
+
if (rockspecFile) {
|
|
70
|
+
const declared = readLuaAssignments(rockspecFile).get("dependencies");
|
|
71
|
+
if (Array.isArray(declared)) {
|
|
72
|
+
for (const dep of declared) {
|
|
73
|
+
if (typeof dep?.name === "string") {
|
|
74
|
+
directNames.add(dep.name);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const pkgList = locked
|
|
80
|
+
.filter((dep) => typeof dep?.name === "string" && dep.name)
|
|
81
|
+
.map((dep) =>
|
|
82
|
+
luaPackage(dep.name, dep.version, {
|
|
83
|
+
// Without a rockspec there is no declared set to compare against, so
|
|
84
|
+
// the whole closure is reported direct rather than guessing.
|
|
85
|
+
direct: rockspecFile ? directNames.has(dep.name) : true,
|
|
86
|
+
srcFile: lockFile,
|
|
87
|
+
}),
|
|
88
|
+
);
|
|
89
|
+
return { pkgList, parentComponent: {} };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Build a component-like package record for a Lua rock.
|
|
94
|
+
*
|
|
95
|
+
* @param {string} name Rock name
|
|
96
|
+
* @param {string|undefined} version Rock version including revision suffix
|
|
97
|
+
* @param {object} opts Extra context (`direct`, `srcFile`)
|
|
98
|
+
* @returns {object} Package record
|
|
99
|
+
*/
|
|
100
|
+
function luaPackage(name, version, opts) {
|
|
101
|
+
const purl = tryBuildPurl({
|
|
102
|
+
type: "luarocks",
|
|
103
|
+
name,
|
|
104
|
+
version: version || undefined,
|
|
105
|
+
});
|
|
106
|
+
const properties = [
|
|
107
|
+
{ name: "internal:SrcFile", value: opts.srcFile },
|
|
108
|
+
{
|
|
109
|
+
name: "cdx:luarocks:dependency",
|
|
110
|
+
value: opts.direct ? "direct" : "transitive",
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
const pkg = {
|
|
114
|
+
name,
|
|
115
|
+
...(version ? { version } : {}),
|
|
116
|
+
type: "library",
|
|
117
|
+
scope: "required",
|
|
118
|
+
properties,
|
|
119
|
+
};
|
|
120
|
+
if (purl) {
|
|
121
|
+
pkg.purl = purl;
|
|
122
|
+
pkg["bom-ref"] = decodeURIComponent(purl);
|
|
123
|
+
} else {
|
|
124
|
+
pkg["bom-ref"] = `library:${name}:${version || ""}`;
|
|
125
|
+
}
|
|
126
|
+
return pkg;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Read the top-level assignments of a Lua configuration file.
|
|
131
|
+
*
|
|
132
|
+
* The files cdxgen consumes use a constrained surface: `key = value` lines
|
|
133
|
+
* where the value is a quoted string, a table of strings or
|
|
134
|
+
* `["key"] = "value"` entries, or a short list expression. Values are
|
|
135
|
+
* normalised to strings, arrays of `{name, constraint}` records, or arrays
|
|
136
|
+
* of `{key, value}` records for mixed tables.
|
|
137
|
+
*
|
|
138
|
+
* @param {string} filePath File to read
|
|
139
|
+
* @returns {Map<string, *>} Parsed top-level fields
|
|
140
|
+
*/
|
|
141
|
+
export function readLuaAssignments(filePath) {
|
|
142
|
+
const fields = new Map();
|
|
143
|
+
let text;
|
|
144
|
+
try {
|
|
145
|
+
text = readFileSync(filePath, "utf-8");
|
|
146
|
+
} catch (error) {
|
|
147
|
+
console.warn(`Failed to read ${filePath}: ${error.message}`);
|
|
148
|
+
return fields;
|
|
149
|
+
}
|
|
150
|
+
let currentTable;
|
|
151
|
+
let depth = 0;
|
|
152
|
+
for (const rawLine of text.split("\n")) {
|
|
153
|
+
const line = rawLine.trim();
|
|
154
|
+
if (!line || line.startsWith("--") || line.startsWith("#")) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (depth === 0) {
|
|
158
|
+
const assignment = line.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/u);
|
|
159
|
+
if (!assignment) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const [, key, rest] = assignment;
|
|
163
|
+
const stringValue = rest.match(/^"([^"]*)"/u);
|
|
164
|
+
if (stringValue) {
|
|
165
|
+
fields.set(key, stringValue[1]);
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (rest.startsWith("{")) {
|
|
169
|
+
currentTable = [];
|
|
170
|
+
fields.set(key, currentTable);
|
|
171
|
+
depth = countBraces(rest, 0);
|
|
172
|
+
}
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
// Inside a table: collect string items, `key = "value"` pairs, and the
|
|
176
|
+
// bracketed `["key"] = "value"` form LuaRocks uses for names that are not
|
|
177
|
+
// valid Lua identifiers.
|
|
178
|
+
depth = countBraces(line, depth);
|
|
179
|
+
const keyed = line.match(
|
|
180
|
+
/^(?:\[\s*"([^"]+)"\s*\]|([A-Za-z_][A-Za-z0-9_]*))\s*=\s*"([^"]*)"/u,
|
|
181
|
+
);
|
|
182
|
+
if (keyed && currentTable) {
|
|
183
|
+
currentTable.push({ name: keyed[1] ?? keyed[2], version: keyed[3] });
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const item = line.match(/^"([^"]+)"/u);
|
|
187
|
+
if (item && currentTable) {
|
|
188
|
+
const tokens = item[1].split(/\s+/u);
|
|
189
|
+
currentTable.push({
|
|
190
|
+
name: tokens[0],
|
|
191
|
+
constraint: tokens.slice(1).join(" "),
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
if (depth <= 0) {
|
|
195
|
+
depth = 0;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return fields;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Track table nesting depth for one line.
|
|
203
|
+
*
|
|
204
|
+
* @param {string} line Line to scan
|
|
205
|
+
* @param {number} depth Depth before the line
|
|
206
|
+
* @returns {number} Depth after the line
|
|
207
|
+
*/
|
|
208
|
+
function countBraces(line, depth) {
|
|
209
|
+
let next = depth;
|
|
210
|
+
for (const char of line) {
|
|
211
|
+
if (char === "{") {
|
|
212
|
+
next += 1;
|
|
213
|
+
} else if (char === "}") {
|
|
214
|
+
next -= 1;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return next;
|
|
218
|
+
}
|