@cyclonedx/cdxgen 12.4.0 → 12.4.2
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 +6 -4
- package/bin/cdxgen.js +32 -11
- package/bin/convert.js +12 -8
- package/bin/evinse.js +15 -0
- package/bin/hbom.js +13 -8
- package/bin/repl.js +14 -10
- package/bin/validate.js +10 -13
- package/bin/verify.js +7 -29
- package/data/cyclonedx-2.0-bundled.schema.json +7182 -0
- package/lib/audit/index.js +2 -1
- package/lib/cli/index.js +77 -16
- package/lib/cli/index.poku.js +197 -0
- package/lib/evinser/evinser.js +118 -3
- package/lib/helpers/bomUtils.js +155 -1
- package/lib/helpers/bomUtils.poku.js +79 -1
- package/lib/helpers/cbomutils.js +162 -2
- package/lib/helpers/cbomutils.poku.js +100 -0
- package/lib/helpers/ciParsers/githubActions.js +15 -3
- package/lib/helpers/ciParsers/githubActions.poku.js +52 -0
- package/lib/helpers/dosai.js +433 -0
- package/lib/helpers/dosai.poku.js +302 -0
- package/lib/helpers/dosaiParsers.js +103 -0
- package/lib/helpers/plugins.js +17 -16
- package/lib/helpers/protobom.js +53 -0
- package/lib/helpers/protobom.poku.js +44 -1
- package/lib/helpers/protobomLoader.js +43 -0
- package/lib/helpers/protobomLoader.poku.js +31 -0
- package/lib/helpers/utils.js +130 -1
- package/lib/helpers/utils.poku.js +295 -0
- package/lib/server/server.js +2 -1
- package/lib/stages/postgen/annotator.js +2 -1
- package/lib/stages/postgen/annotator.poku.js +28 -0
- package/lib/stages/postgen/postgen.js +219 -12
- package/lib/stages/postgen/postgen.poku.js +163 -0
- package/lib/validator/bomValidator.js +90 -38
- package/lib/validator/bomValidator.poku.js +90 -0
- package/lib/validator/complianceRules.js +4 -2
- package/lib/validator/index.poku.js +14 -0
- package/package.json +12 -12
- package/types/bin/repl.d.ts +1 -1
- package/types/bin/repl.d.ts.map +1 -1
- package/types/lib/audit/index.d.ts.map +1 -1
- package/types/lib/cli/index.d.ts.map +1 -1
- package/types/lib/evinser/evinser.d.ts +15 -0
- package/types/lib/evinser/evinser.d.ts.map +1 -1
- package/types/lib/helpers/bomUtils.d.ts +8 -0
- package/types/lib/helpers/bomUtils.d.ts.map +1 -1
- package/types/lib/helpers/cbomutils.d.ts +1 -0
- package/types/lib/helpers/cbomutils.d.ts.map +1 -1
- package/types/lib/helpers/ciParsers/githubActions.d.ts.map +1 -1
- package/types/lib/helpers/dosai.d.ts +24 -0
- package/types/lib/helpers/dosai.d.ts.map +1 -0
- package/types/lib/helpers/dosaiParsers.d.ts +8 -0
- package/types/lib/helpers/dosaiParsers.d.ts.map +1 -0
- package/types/lib/helpers/hbomAnalysis.d.ts +14 -0
- package/types/lib/helpers/hbomAnalysis.d.ts.map +1 -1
- package/types/lib/helpers/hostTopology.d.ts.map +1 -1
- package/types/lib/helpers/plugins.d.ts.map +1 -1
- package/types/lib/helpers/protobom.d.ts +2 -0
- package/types/lib/helpers/protobom.d.ts.map +1 -1
- package/types/lib/helpers/protobomLoader.d.ts +17 -0
- package/types/lib/helpers/protobomLoader.d.ts.map +1 -0
- package/types/lib/helpers/utils.d.ts.map +1 -1
- package/types/lib/server/server.d.ts.map +1 -1
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
- package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
- package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
- package/types/lib/third-party/arborist/lib/node.d.ts +23 -0
- package/types/lib/third-party/arborist/lib/node.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
- package/types/lib/validator/complianceRules.d.ts.map +1 -1
package/lib/helpers/utils.js
CHANGED
|
@@ -58,6 +58,13 @@ import Arborist from "../third-party/arborist/lib/index.js";
|
|
|
58
58
|
import { analyzeSuspiciousJsFile } from "./analyzer.js";
|
|
59
59
|
import { DEFAULT_HBOM_AUDIT_CATEGORIES } from "./auditCategories.js";
|
|
60
60
|
import { parseWorkflowFile } from "./ciParsers/githubActions.js";
|
|
61
|
+
import {
|
|
62
|
+
addDosaiSetValue,
|
|
63
|
+
buildDosaiPurlAliasMap,
|
|
64
|
+
dosaiSourceLocation,
|
|
65
|
+
dosaiSourceLocationFromNode,
|
|
66
|
+
resolveDosaiComponentPurl,
|
|
67
|
+
} from "./dosaiParsers.js";
|
|
61
68
|
import { extractPackageInfoFromHintPath } from "./dotnetutils.js";
|
|
62
69
|
import {
|
|
63
70
|
createOccurrenceEvidence,
|
|
@@ -1286,6 +1293,8 @@ export function safeSpawnSync(command, args, options) {
|
|
|
1286
1293
|
options = {
|
|
1287
1294
|
...options,
|
|
1288
1295
|
};
|
|
1296
|
+
}
|
|
1297
|
+
if (options.cdxgenActivity) {
|
|
1289
1298
|
delete options.cdxgenActivity;
|
|
1290
1299
|
}
|
|
1291
1300
|
// Inject maxBuffer
|
|
@@ -1753,6 +1762,10 @@ export const PROJECT_TYPE_ALIASES = {
|
|
|
1753
1762
|
"dotnet-framework47",
|
|
1754
1763
|
"dotnet-framework48",
|
|
1755
1764
|
"vb",
|
|
1765
|
+
"vbnet",
|
|
1766
|
+
"visualbasic",
|
|
1767
|
+
"f#",
|
|
1768
|
+
"fs",
|
|
1756
1769
|
"fsharp",
|
|
1757
1770
|
"twincat",
|
|
1758
1771
|
"csproj",
|
|
@@ -21739,6 +21752,42 @@ export async function getNugetMetadata(pkgList, dependencies = undefined) {
|
|
|
21739
21752
|
};
|
|
21740
21753
|
}
|
|
21741
21754
|
|
|
21755
|
+
function addDotnetIdentityMethod(apkg, value) {
|
|
21756
|
+
if (!value) {
|
|
21757
|
+
return;
|
|
21758
|
+
}
|
|
21759
|
+
apkg.evidence = apkg.evidence || {};
|
|
21760
|
+
const identityList = Array.isArray(apkg.evidence.identity)
|
|
21761
|
+
? apkg.evidence.identity
|
|
21762
|
+
: undefined;
|
|
21763
|
+
let identity = identityList
|
|
21764
|
+
? identityList.find((entry) => entry?.field === "purl") ||
|
|
21765
|
+
identityList.find((entry) => !entry?.field)
|
|
21766
|
+
: apkg.evidence.identity;
|
|
21767
|
+
if (!identity) {
|
|
21768
|
+
identity = { field: "purl", confidence: 1, methods: [] };
|
|
21769
|
+
if (identityList) {
|
|
21770
|
+
identityList.push(identity);
|
|
21771
|
+
}
|
|
21772
|
+
}
|
|
21773
|
+
identity.field ??= "purl";
|
|
21774
|
+
identity.confidence ??= 1;
|
|
21775
|
+
identity.methods ??= [];
|
|
21776
|
+
if (
|
|
21777
|
+
!identity.methods.some(
|
|
21778
|
+
(method) =>
|
|
21779
|
+
method.technique === "source-code-analysis" && method.value === value,
|
|
21780
|
+
)
|
|
21781
|
+
) {
|
|
21782
|
+
identity.methods.push({
|
|
21783
|
+
technique: "source-code-analysis",
|
|
21784
|
+
confidence: 1,
|
|
21785
|
+
value,
|
|
21786
|
+
});
|
|
21787
|
+
}
|
|
21788
|
+
apkg.evidence.identity = identityList || identity;
|
|
21789
|
+
}
|
|
21790
|
+
|
|
21742
21791
|
/**
|
|
21743
21792
|
* Enrich .NET package components with occurrence evidence and imported module/method
|
|
21744
21793
|
* information from a dosai dependency slices file.
|
|
@@ -21762,6 +21811,7 @@ export function addEvidenceForDotnet(pkgList, slicesFile) {
|
|
|
21762
21811
|
const purlLocationMap = {};
|
|
21763
21812
|
const purlModulesMap = {};
|
|
21764
21813
|
const purlMethodsMap = {};
|
|
21814
|
+
const purlAliasMap = buildDosaiPurlAliasMap(pkgList);
|
|
21765
21815
|
for (const apkg of pkgList) {
|
|
21766
21816
|
if (apkg.properties && Array.isArray(apkg.properties)) {
|
|
21767
21817
|
apkg.properties
|
|
@@ -21778,13 +21828,33 @@ export function addEvidenceForDotnet(pkgList, slicesFile) {
|
|
|
21778
21828
|
});
|
|
21779
21829
|
}
|
|
21780
21830
|
}
|
|
21781
|
-
|
|
21831
|
+
let slicesData;
|
|
21832
|
+
try {
|
|
21833
|
+
slicesData = JSON.parse(readFileSync(slicesFile, "utf-8"));
|
|
21834
|
+
} catch (_err) {
|
|
21835
|
+
return pkgList;
|
|
21836
|
+
}
|
|
21782
21837
|
if (slicesData && Object.keys(slicesData)) {
|
|
21783
21838
|
thoughtLog(
|
|
21784
21839
|
"Let's thoroughly inspect the dependency slice to identify where and how the components are used.",
|
|
21785
21840
|
);
|
|
21786
21841
|
if (slicesData.Dependencies) {
|
|
21787
21842
|
for (const adep of slicesData.Dependencies) {
|
|
21843
|
+
if (adep.Purl) {
|
|
21844
|
+
const modPurl = resolveDosaiComponentPurl(adep.Purl, purlAliasMap);
|
|
21845
|
+
if (modPurl) {
|
|
21846
|
+
addDosaiSetValue(
|
|
21847
|
+
purlLocationMap,
|
|
21848
|
+
modPurl,
|
|
21849
|
+
dosaiSourceLocation(adep),
|
|
21850
|
+
);
|
|
21851
|
+
addDosaiSetValue(
|
|
21852
|
+
purlModulesMap,
|
|
21853
|
+
modPurl,
|
|
21854
|
+
adep.Name || adep.Namespace,
|
|
21855
|
+
);
|
|
21856
|
+
}
|
|
21857
|
+
}
|
|
21788
21858
|
// Case 1: Dependencies slice has the .dll file
|
|
21789
21859
|
if (adep.Module?.endsWith(".dll") && pkgFilePurlMap[adep.Module]) {
|
|
21790
21860
|
const modPurl = pkgFilePurlMap[adep.Module];
|
|
@@ -21810,6 +21880,64 @@ export function addEvidenceForDotnet(pkgList, slicesFile) {
|
|
|
21810
21880
|
}
|
|
21811
21881
|
}
|
|
21812
21882
|
}
|
|
21883
|
+
if (slicesData.PackageReachability) {
|
|
21884
|
+
const graphEdges = Object.fromEntries(
|
|
21885
|
+
(slicesData.CallGraph?.Edges || []).map((edge) => [edge.Id, edge]),
|
|
21886
|
+
);
|
|
21887
|
+
const graphNodes = Object.fromEntries(
|
|
21888
|
+
(slicesData.CallGraph?.Nodes || []).map((node) => [node.Id, node]),
|
|
21889
|
+
);
|
|
21890
|
+
for (const reachability of slicesData.PackageReachability) {
|
|
21891
|
+
const modPurl = resolveDosaiComponentPurl(
|
|
21892
|
+
reachability.Purl,
|
|
21893
|
+
purlAliasMap,
|
|
21894
|
+
);
|
|
21895
|
+
if (!modPurl) {
|
|
21896
|
+
continue;
|
|
21897
|
+
}
|
|
21898
|
+
let hasExplicitSourceLocations = false;
|
|
21899
|
+
for (const sourceLocation of reachability.SourceLocations || []) {
|
|
21900
|
+
const location = dosaiSourceLocation(sourceLocation);
|
|
21901
|
+
addDosaiSetValue(purlLocationMap, modPurl, location);
|
|
21902
|
+
hasExplicitSourceLocations ||= Boolean(location);
|
|
21903
|
+
}
|
|
21904
|
+
for (const edgeId of reachability.EdgeIds || []) {
|
|
21905
|
+
const edge = graphEdges[edgeId];
|
|
21906
|
+
if (!hasExplicitSourceLocations) {
|
|
21907
|
+
addDosaiSetValue(
|
|
21908
|
+
purlLocationMap,
|
|
21909
|
+
modPurl,
|
|
21910
|
+
dosaiSourceLocation(edge),
|
|
21911
|
+
);
|
|
21912
|
+
}
|
|
21913
|
+
addDosaiSetValue(
|
|
21914
|
+
purlMethodsMap,
|
|
21915
|
+
modPurl,
|
|
21916
|
+
edge?.CalledMethodName || edge?.TargetName,
|
|
21917
|
+
);
|
|
21918
|
+
}
|
|
21919
|
+
for (const nodeId of reachability.NodeIds || []) {
|
|
21920
|
+
const node = graphNodes[nodeId];
|
|
21921
|
+
if (!hasExplicitSourceLocations) {
|
|
21922
|
+
addDosaiSetValue(
|
|
21923
|
+
purlLocationMap,
|
|
21924
|
+
modPurl,
|
|
21925
|
+
dosaiSourceLocationFromNode(node),
|
|
21926
|
+
);
|
|
21927
|
+
}
|
|
21928
|
+
addDosaiSetValue(
|
|
21929
|
+
purlModulesMap,
|
|
21930
|
+
modPurl,
|
|
21931
|
+
node?.ClassName || node?.Module,
|
|
21932
|
+
);
|
|
21933
|
+
addDosaiSetValue(
|
|
21934
|
+
purlMethodsMap,
|
|
21935
|
+
modPurl,
|
|
21936
|
+
node?.Name || node?.Identity?.MethodName,
|
|
21937
|
+
);
|
|
21938
|
+
}
|
|
21939
|
+
}
|
|
21940
|
+
}
|
|
21813
21941
|
if (slicesData.MethodCalls) {
|
|
21814
21942
|
for (const amethodCall of slicesData.MethodCalls) {
|
|
21815
21943
|
if (
|
|
@@ -21857,6 +21985,7 @@ export function addEvidenceForDotnet(pkgList, slicesFile) {
|
|
|
21857
21985
|
apkg.evidence.occurrences = locationOccurrences.map((l) =>
|
|
21858
21986
|
parseOccurrenceEvidenceLocation(l),
|
|
21859
21987
|
);
|
|
21988
|
+
addDotnetIdentityMethod(apkg, locationOccurrences[0]);
|
|
21860
21989
|
// Set the package scope
|
|
21861
21990
|
apkg.scope = "required";
|
|
21862
21991
|
}
|
|
@@ -54,6 +54,7 @@ import {
|
|
|
54
54
|
isPartialTree,
|
|
55
55
|
isValidIriReference,
|
|
56
56
|
mapConanPkgRefToPurlStringAndNameAndVersion,
|
|
57
|
+
PROJECT_TYPE_ALIASES,
|
|
57
58
|
parseBazelActionGraph,
|
|
58
59
|
parseBazelBuild,
|
|
59
60
|
parseBazelSkyframe,
|
|
@@ -4327,6 +4328,296 @@ it("addEvidenceForDotnet() initializes evidence before adding occurrences", () =
|
|
|
4327
4328
|
}
|
|
4328
4329
|
});
|
|
4329
4330
|
|
|
4331
|
+
it("addEvidenceForDotnet() ignores unreadable dosai JSON", () => {
|
|
4332
|
+
const tempDir = mkdtempSync(path.join(tmpdir(), "cdxgen-dotnet-bad-json-"));
|
|
4333
|
+
const slicesFile = path.join(tempDir, "dosai.json");
|
|
4334
|
+
try {
|
|
4335
|
+
writeFileSync(slicesFile, "");
|
|
4336
|
+
const inputPkgList = [
|
|
4337
|
+
{
|
|
4338
|
+
name: "Example",
|
|
4339
|
+
purl: "pkg:nuget/Example@1.0.0",
|
|
4340
|
+
properties: [{ name: "PackageFiles", value: "Example.dll" }],
|
|
4341
|
+
},
|
|
4342
|
+
];
|
|
4343
|
+
const pkgList = addEvidenceForDotnet(inputPkgList, slicesFile);
|
|
4344
|
+
|
|
4345
|
+
assert.strictEqual(pkgList, inputPkgList);
|
|
4346
|
+
assert.strictEqual(pkgList[0].evidence, undefined);
|
|
4347
|
+
assert.strictEqual(pkgList[0].scope, undefined);
|
|
4348
|
+
} finally {
|
|
4349
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
4350
|
+
}
|
|
4351
|
+
});
|
|
4352
|
+
|
|
4353
|
+
it("addEvidenceForDotnet() consumes dosai v3 PackageReachability", () => {
|
|
4354
|
+
const tempDir = mkdtempSync(
|
|
4355
|
+
path.join(tmpdir(), "cdxgen-dotnet-reachability-"),
|
|
4356
|
+
);
|
|
4357
|
+
const slicesFile = path.join(tempDir, "dosai.json");
|
|
4358
|
+
try {
|
|
4359
|
+
writeFileSync(
|
|
4360
|
+
slicesFile,
|
|
4361
|
+
JSON.stringify({
|
|
4362
|
+
CallGraph: {
|
|
4363
|
+
Edges: [
|
|
4364
|
+
{
|
|
4365
|
+
Id: "e1",
|
|
4366
|
+
FileName: "Controllers/EpisodesController.cs",
|
|
4367
|
+
LineNumber: 17,
|
|
4368
|
+
CalledMethodName: "System.Text.Json.JsonSerializer.Deserialize",
|
|
4369
|
+
},
|
|
4370
|
+
],
|
|
4371
|
+
Nodes: [
|
|
4372
|
+
{
|
|
4373
|
+
Id: "n1",
|
|
4374
|
+
FileName: "System.Text.Json.dll",
|
|
4375
|
+
LineNumber: 0,
|
|
4376
|
+
ClassName: "JsonSerializer",
|
|
4377
|
+
Name: "Deserialize",
|
|
4378
|
+
},
|
|
4379
|
+
],
|
|
4380
|
+
},
|
|
4381
|
+
PackageReachability: [
|
|
4382
|
+
{
|
|
4383
|
+
Purl: "pkg:nuget/System.Text.Json",
|
|
4384
|
+
EdgeIds: ["e1"],
|
|
4385
|
+
NodeIds: ["n1"],
|
|
4386
|
+
SourceLocations: [
|
|
4387
|
+
{
|
|
4388
|
+
Path: "Controllers/Parser.cs",
|
|
4389
|
+
FileName: "Parser.cs",
|
|
4390
|
+
LineNumber: 42,
|
|
4391
|
+
ColumnNumber: 13,
|
|
4392
|
+
Kind: "CallGraphEdge",
|
|
4393
|
+
},
|
|
4394
|
+
{
|
|
4395
|
+
Path: "System.Text.Json.dll",
|
|
4396
|
+
FileName: "System.Text.Json.dll",
|
|
4397
|
+
LineNumber: 1,
|
|
4398
|
+
Kind: "CallGraphNode",
|
|
4399
|
+
},
|
|
4400
|
+
],
|
|
4401
|
+
},
|
|
4402
|
+
],
|
|
4403
|
+
}),
|
|
4404
|
+
);
|
|
4405
|
+
const pkgList = addEvidenceForDotnet(
|
|
4406
|
+
[
|
|
4407
|
+
{
|
|
4408
|
+
name: "System.Text.Json",
|
|
4409
|
+
purl: "pkg:nuget/System.Text.Json@10.0.0",
|
|
4410
|
+
properties: [],
|
|
4411
|
+
},
|
|
4412
|
+
],
|
|
4413
|
+
slicesFile,
|
|
4414
|
+
);
|
|
4415
|
+
|
|
4416
|
+
assert.deepStrictEqual(pkgList[0].evidence?.occurrences, [
|
|
4417
|
+
{
|
|
4418
|
+
location: "Controllers/Parser.cs",
|
|
4419
|
+
line: 42,
|
|
4420
|
+
},
|
|
4421
|
+
]);
|
|
4422
|
+
assert.ok(
|
|
4423
|
+
pkgList[0].evidence.identity.methods.some(
|
|
4424
|
+
(method) =>
|
|
4425
|
+
method.technique === "source-code-analysis" &&
|
|
4426
|
+
method.value === "Controllers/Parser.cs#42",
|
|
4427
|
+
),
|
|
4428
|
+
);
|
|
4429
|
+
assert.ok(
|
|
4430
|
+
pkgList[0].properties.some(
|
|
4431
|
+
(property) =>
|
|
4432
|
+
property.name === "CalledMethods" &&
|
|
4433
|
+
property.value.includes(
|
|
4434
|
+
"System.Text.Json.JsonSerializer.Deserialize",
|
|
4435
|
+
),
|
|
4436
|
+
),
|
|
4437
|
+
);
|
|
4438
|
+
} finally {
|
|
4439
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
4440
|
+
}
|
|
4441
|
+
});
|
|
4442
|
+
|
|
4443
|
+
it("addEvidenceForDotnet() keeps PackageReachability fallback evidence source-only", () => {
|
|
4444
|
+
const tempDir = mkdtempSync(
|
|
4445
|
+
path.join(tmpdir(), "cdxgen-dotnet-source-fallback-"),
|
|
4446
|
+
);
|
|
4447
|
+
const slicesFile = path.join(tempDir, "dosai.json");
|
|
4448
|
+
try {
|
|
4449
|
+
writeFileSync(
|
|
4450
|
+
slicesFile,
|
|
4451
|
+
JSON.stringify({
|
|
4452
|
+
CallGraph: {
|
|
4453
|
+
Edges: [
|
|
4454
|
+
{
|
|
4455
|
+
Id: "e1",
|
|
4456
|
+
FileName: "System.Text.Json.dll",
|
|
4457
|
+
LineNumber: 12,
|
|
4458
|
+
CalledMethodName: "System.Text.Json.JsonSerializer.Deserialize",
|
|
4459
|
+
CallLocation: {
|
|
4460
|
+
FileName: "Program.fs",
|
|
4461
|
+
LineNumber: 8,
|
|
4462
|
+
},
|
|
4463
|
+
},
|
|
4464
|
+
{
|
|
4465
|
+
Id: "e2",
|
|
4466
|
+
FileName: "Controllers/EpisodesController.cs",
|
|
4467
|
+
LineNumber: 17,
|
|
4468
|
+
CalledMethodName: "System.Text.Json.JsonSerializer.Serialize",
|
|
4469
|
+
},
|
|
4470
|
+
],
|
|
4471
|
+
},
|
|
4472
|
+
PackageReachability: [
|
|
4473
|
+
{
|
|
4474
|
+
Purl: "pkg:nuget/System.Text.Json",
|
|
4475
|
+
EdgeIds: ["e1", "e2"],
|
|
4476
|
+
},
|
|
4477
|
+
],
|
|
4478
|
+
}),
|
|
4479
|
+
);
|
|
4480
|
+
const pkgList = addEvidenceForDotnet(
|
|
4481
|
+
[
|
|
4482
|
+
{
|
|
4483
|
+
name: "System.Text.Json",
|
|
4484
|
+
purl: "pkg:nuget/System.Text.Json@10.0.0",
|
|
4485
|
+
properties: [],
|
|
4486
|
+
},
|
|
4487
|
+
],
|
|
4488
|
+
slicesFile,
|
|
4489
|
+
);
|
|
4490
|
+
|
|
4491
|
+
assert.deepStrictEqual(pkgList[0].evidence?.occurrences, [
|
|
4492
|
+
{
|
|
4493
|
+
location: "Controllers/EpisodesController.cs",
|
|
4494
|
+
line: 17,
|
|
4495
|
+
},
|
|
4496
|
+
{
|
|
4497
|
+
location: "Program.fs",
|
|
4498
|
+
line: 8,
|
|
4499
|
+
},
|
|
4500
|
+
]);
|
|
4501
|
+
assert.ok(!JSON.stringify(pkgList[0].evidence).includes(".dll"));
|
|
4502
|
+
} finally {
|
|
4503
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
4504
|
+
}
|
|
4505
|
+
});
|
|
4506
|
+
|
|
4507
|
+
it("addEvidenceForDotnet() preserves additional identity entries", () => {
|
|
4508
|
+
const tempDir = mkdtempSync(
|
|
4509
|
+
path.join(tmpdir(), "cdxgen-dotnet-identity-array-"),
|
|
4510
|
+
);
|
|
4511
|
+
const slicesFile = path.join(tempDir, "dosai.json");
|
|
4512
|
+
try {
|
|
4513
|
+
writeFileSync(
|
|
4514
|
+
slicesFile,
|
|
4515
|
+
JSON.stringify({
|
|
4516
|
+
Dependencies: [
|
|
4517
|
+
{
|
|
4518
|
+
Path: "Program.cs",
|
|
4519
|
+
FileName: "Program.cs",
|
|
4520
|
+
Name: "System.Text.Json",
|
|
4521
|
+
Purl: "pkg:nuget/System.Text.Json",
|
|
4522
|
+
LineNumber: 12,
|
|
4523
|
+
},
|
|
4524
|
+
],
|
|
4525
|
+
}),
|
|
4526
|
+
);
|
|
4527
|
+
const pkgList = addEvidenceForDotnet(
|
|
4528
|
+
[
|
|
4529
|
+
{
|
|
4530
|
+
name: "System.Text.Json",
|
|
4531
|
+
purl: "pkg:nuget/System.Text.Json@10.0.0",
|
|
4532
|
+
evidence: {
|
|
4533
|
+
identity: [
|
|
4534
|
+
{
|
|
4535
|
+
field: "name",
|
|
4536
|
+
confidence: 0.8,
|
|
4537
|
+
methods: [
|
|
4538
|
+
{ technique: "filename", value: "packages.lock.json" },
|
|
4539
|
+
],
|
|
4540
|
+
},
|
|
4541
|
+
{
|
|
4542
|
+
field: "purl",
|
|
4543
|
+
confidence: 1,
|
|
4544
|
+
methods: [
|
|
4545
|
+
{
|
|
4546
|
+
technique: "manifest-analysis",
|
|
4547
|
+
value: "packages.lock.json",
|
|
4548
|
+
},
|
|
4549
|
+
],
|
|
4550
|
+
},
|
|
4551
|
+
],
|
|
4552
|
+
},
|
|
4553
|
+
properties: [],
|
|
4554
|
+
},
|
|
4555
|
+
],
|
|
4556
|
+
slicesFile,
|
|
4557
|
+
);
|
|
4558
|
+
|
|
4559
|
+
assert.strictEqual(pkgList[0].evidence.identity.length, 2);
|
|
4560
|
+
assert.strictEqual(pkgList[0].evidence.identity[0].field, "name");
|
|
4561
|
+
assert.ok(
|
|
4562
|
+
pkgList[0].evidence.identity[1].methods.some(
|
|
4563
|
+
(method) =>
|
|
4564
|
+
method.technique === "source-code-analysis" &&
|
|
4565
|
+
method.value === "Program.cs#12",
|
|
4566
|
+
),
|
|
4567
|
+
);
|
|
4568
|
+
} finally {
|
|
4569
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
4570
|
+
}
|
|
4571
|
+
});
|
|
4572
|
+
|
|
4573
|
+
it("addEvidenceForDotnet() consumes dosai Dependencies with purls", () => {
|
|
4574
|
+
const tempDir = mkdtempSync(path.join(tmpdir(), "cdxgen-dotnet-vb-deps-"));
|
|
4575
|
+
const slicesFile = path.join(tempDir, "dosai.json");
|
|
4576
|
+
try {
|
|
4577
|
+
writeFileSync(
|
|
4578
|
+
slicesFile,
|
|
4579
|
+
JSON.stringify({
|
|
4580
|
+
Dependencies: [
|
|
4581
|
+
{
|
|
4582
|
+
Path: "Program.vb",
|
|
4583
|
+
FileName: "Program.vb",
|
|
4584
|
+
Name: "Newtonsoft.Json",
|
|
4585
|
+
Purl: "pkg:nuget/Newtonsoft.Json@13.0.3",
|
|
4586
|
+
LineNumber: 4,
|
|
4587
|
+
ColumnNumber: 9,
|
|
4588
|
+
},
|
|
4589
|
+
],
|
|
4590
|
+
}),
|
|
4591
|
+
);
|
|
4592
|
+
const pkgList = addEvidenceForDotnet(
|
|
4593
|
+
[
|
|
4594
|
+
{
|
|
4595
|
+
name: "Newtonsoft.Json",
|
|
4596
|
+
purl: "pkg:nuget/Newtonsoft.Json@13.0.3",
|
|
4597
|
+
properties: [],
|
|
4598
|
+
},
|
|
4599
|
+
],
|
|
4600
|
+
slicesFile,
|
|
4601
|
+
);
|
|
4602
|
+
|
|
4603
|
+
assert.deepStrictEqual(pkgList[0].evidence?.occurrences, [
|
|
4604
|
+
{
|
|
4605
|
+
location: "Program.vb",
|
|
4606
|
+
line: 4,
|
|
4607
|
+
},
|
|
4608
|
+
]);
|
|
4609
|
+
assert.ok(
|
|
4610
|
+
pkgList[0].properties.some(
|
|
4611
|
+
(property) =>
|
|
4612
|
+
property.name === "ImportedModules" &&
|
|
4613
|
+
property.value.includes("Newtonsoft.Json"),
|
|
4614
|
+
),
|
|
4615
|
+
);
|
|
4616
|
+
} finally {
|
|
4617
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
4618
|
+
}
|
|
4619
|
+
});
|
|
4620
|
+
|
|
4330
4621
|
it("parse packages.lock.json", () => {
|
|
4331
4622
|
assert.deepStrictEqual(parseCsPkgLockData(null), {
|
|
4332
4623
|
dependenciesList: [],
|
|
@@ -10229,6 +10520,10 @@ it("parseMakeDFile tests", () => {
|
|
|
10229
10520
|
});
|
|
10230
10521
|
|
|
10231
10522
|
it("hasAnyProjectType tests", () => {
|
|
10523
|
+
for (const language of ["vb", "vbnet", "visualbasic", "f#", "fs", "fsharp"]) {
|
|
10524
|
+
assert.ok(PROJECT_TYPE_ALIASES.csharp.includes(language));
|
|
10525
|
+
}
|
|
10526
|
+
|
|
10232
10527
|
assert.deepStrictEqual(
|
|
10233
10528
|
hasAnyProjectType(["docker"], {
|
|
10234
10529
|
projectType: [],
|
package/lib/server/server.js
CHANGED
|
@@ -8,6 +8,7 @@ import compression from "compression";
|
|
|
8
8
|
import connect from "connect";
|
|
9
9
|
|
|
10
10
|
import { createBom, submitBom } from "../cli/index.js";
|
|
11
|
+
import { isCycloneDxBom } from "../helpers/bomUtils.js";
|
|
11
12
|
import { normalizeOutputFormats } from "../helpers/exportUtils.js";
|
|
12
13
|
import {
|
|
13
14
|
cleanupSourceDir,
|
|
@@ -467,7 +468,7 @@ const start = (options) => {
|
|
|
467
468
|
let responseBomJson = bomNSData.bomJson;
|
|
468
469
|
if (
|
|
469
470
|
requestedFormats.includes("spdx") &&
|
|
470
|
-
bomNSData?.bomJson
|
|
471
|
+
isCycloneDxBom(bomNSData?.bomJson)
|
|
471
472
|
) {
|
|
472
473
|
responseBomJson = convertCycloneDxToSpdx(bomNSData.bomJson, reqOptions);
|
|
473
474
|
}
|
|
@@ -23,6 +23,7 @@ function humanifyTimestamp(timestamp) {
|
|
|
23
23
|
month: "long",
|
|
24
24
|
day: "numeric",
|
|
25
25
|
weekday: "long",
|
|
26
|
+
timeZone: "UTC",
|
|
26
27
|
});
|
|
27
28
|
}
|
|
28
29
|
|
|
@@ -123,7 +124,7 @@ function getGitHubWorkflowStats(components) {
|
|
|
123
124
|
stats.continueOnError++;
|
|
124
125
|
}
|
|
125
126
|
if (propMap["cdx:github:job:runner"]) {
|
|
126
|
-
propMap["cdx:github:job:runner"]
|
|
127
|
+
String(propMap["cdx:github:job:runner"])
|
|
127
128
|
.split(",")
|
|
128
129
|
.filter((r) => r.includes("$"))
|
|
129
130
|
.forEach((r) => {
|
|
@@ -432,3 +432,31 @@ it("recognizes HBOMs and summarizes hardware-specific metadata", () => {
|
|
|
432
432
|
);
|
|
433
433
|
assert.match(summary, /Identifier policy is 'redacted-by-default'\./u);
|
|
434
434
|
});
|
|
435
|
+
|
|
436
|
+
it("handles non-string GitHub runner properties while summarizing metadata", () => {
|
|
437
|
+
const summary = textualMetadata({
|
|
438
|
+
bomFormat: "CycloneDX",
|
|
439
|
+
specVersion: "1.7",
|
|
440
|
+
metadata: {
|
|
441
|
+
timestamp: "2026-01-01T00:00:00Z",
|
|
442
|
+
tools: { components: [{ name: "cdxgen" }] },
|
|
443
|
+
component: { name: "workflow", type: "application" },
|
|
444
|
+
},
|
|
445
|
+
components: [
|
|
446
|
+
{
|
|
447
|
+
name: "checkout",
|
|
448
|
+
purl: "pkg:github/actions/checkout@v4",
|
|
449
|
+
properties: [
|
|
450
|
+
{ name: "cdx:github:workflow:name", value: "CI" },
|
|
451
|
+
{ name: "cdx:github:job:name", value: "build" },
|
|
452
|
+
{
|
|
453
|
+
name: "cdx:github:job:runner",
|
|
454
|
+
value: { group: "ubuntu-runners", labels: "ubuntu-latest" },
|
|
455
|
+
},
|
|
456
|
+
],
|
|
457
|
+
},
|
|
458
|
+
],
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
assert.match(summary, /GitHub Action references/u);
|
|
462
|
+
});
|