@cyclonedx/cdxgen 12.4.1 → 12.4.3
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/bin/evinse.js +15 -0
- package/lib/cli/index.js +60 -9
- package/lib/cli/index.poku.js +161 -0
- package/lib/evinser/evinser.js +118 -3
- 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/display.js +12 -6
- package/lib/helpers/display.poku.js +38 -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/utils.js +198 -1
- package/lib/helpers/utils.poku.js +352 -0
- package/lib/stages/postgen/annotator.js +2 -1
- package/lib/stages/postgen/annotator.poku.js +28 -0
- package/package.json +12 -12
- 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 +1 -3
- 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/display.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/utils.d.ts.map +1 -1
- package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
- package/types/lib/validator/bomValidator.d.ts.map +1 -1
package/bin/evinse.js
CHANGED
|
@@ -51,15 +51,30 @@ const args = yargs(hideBin(process.argv))
|
|
|
51
51
|
"py",
|
|
52
52
|
"python",
|
|
53
53
|
"android",
|
|
54
|
+
"csharp",
|
|
55
|
+
"cs",
|
|
54
56
|
"c",
|
|
55
57
|
"cpp",
|
|
58
|
+
"dotnet",
|
|
56
59
|
"php",
|
|
57
60
|
"swift",
|
|
58
61
|
"ios",
|
|
59
62
|
"ruby",
|
|
60
63
|
"scala",
|
|
64
|
+
"vb",
|
|
65
|
+
"vbnet",
|
|
66
|
+
"visualbasic",
|
|
67
|
+
"f#",
|
|
68
|
+
"fs",
|
|
69
|
+
"fsharp",
|
|
61
70
|
],
|
|
62
71
|
})
|
|
72
|
+
.option("profile", {
|
|
73
|
+
description:
|
|
74
|
+
"Evidence profile. The research profile enables dosai data-flow and crypto analysis for .NET projects.",
|
|
75
|
+
default: "generic",
|
|
76
|
+
choices: ["generic", "research"],
|
|
77
|
+
})
|
|
63
78
|
.option("db-path", {
|
|
64
79
|
description: "Atom slices DB path. Unused",
|
|
65
80
|
default: undefined,
|
package/lib/cli/index.js
CHANGED
|
@@ -46,7 +46,10 @@ import {
|
|
|
46
46
|
toCycloneDxSpecVersionString,
|
|
47
47
|
} from "../helpers/bomUtils.js";
|
|
48
48
|
import { parseCaxaMetadata } from "../helpers/caxa.js";
|
|
49
|
-
import {
|
|
49
|
+
import {
|
|
50
|
+
collectDosaiCryptoComponents,
|
|
51
|
+
collectSourceCryptoComponents,
|
|
52
|
+
} from "../helpers/cbomutils.js";
|
|
50
53
|
import {
|
|
51
54
|
CHROME_EXTENSION_PURL_TYPE,
|
|
52
55
|
collectChromeExtensionsFromPath,
|
|
@@ -58,6 +61,13 @@ import {
|
|
|
58
61
|
mergeServices,
|
|
59
62
|
trimComponents,
|
|
60
63
|
} from "../helpers/depsUtils.js";
|
|
64
|
+
import {
|
|
65
|
+
collectDosaiServicesFromMethods,
|
|
66
|
+
createDosaiMethodsSlice,
|
|
67
|
+
isDosaiDotnetLanguage,
|
|
68
|
+
normalizeDosaiServiceMap,
|
|
69
|
+
readDosaiJsonFile,
|
|
70
|
+
} from "../helpers/dosai.js";
|
|
61
71
|
import { GIT_COMMAND } from "../helpers/envcontext.js";
|
|
62
72
|
import {
|
|
63
73
|
createHbomDocument,
|
|
@@ -246,7 +256,6 @@ import {
|
|
|
246
256
|
enrichOSComponentsWithTrustData,
|
|
247
257
|
executeOsQuery,
|
|
248
258
|
getBinaryBom,
|
|
249
|
-
getDotnetSlices,
|
|
250
259
|
getOSPackages,
|
|
251
260
|
getPluginToolComponents,
|
|
252
261
|
} from "../managers/binary.js";
|
|
@@ -400,6 +409,27 @@ const hasExplicitProjectTypeSelection = (options, baseProjectType) => {
|
|
|
400
409
|
);
|
|
401
410
|
};
|
|
402
411
|
|
|
412
|
+
const hasDotnetProjectIndicators = (src, options = {}) => {
|
|
413
|
+
return Boolean(
|
|
414
|
+
getAllFiles(src, "**/*.{csproj,fsproj,vbproj,sln}", options)?.length,
|
|
415
|
+
);
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
const shouldCollectDosaiCrypto = (src, options = {}) => {
|
|
419
|
+
const projectTypes = Array.isArray(options.projectType)
|
|
420
|
+
? options.projectType
|
|
421
|
+
: options.projectType
|
|
422
|
+
? [options.projectType]
|
|
423
|
+
: [];
|
|
424
|
+
if (projectTypes.some((projectType) => isDosaiDotnetLanguage(projectType))) {
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
if (!projectTypes.length || projectTypes.includes("universal")) {
|
|
428
|
+
return hasDotnetProjectIndicators(src, options);
|
|
429
|
+
}
|
|
430
|
+
return false;
|
|
431
|
+
};
|
|
432
|
+
|
|
403
433
|
const determineParentComponent = (options) => {
|
|
404
434
|
let parentComponent;
|
|
405
435
|
if (options.parentComponent && Object.keys(options.parentComponent).length) {
|
|
@@ -1749,7 +1779,7 @@ export async function createJavaBom(path, options) {
|
|
|
1749
1779
|
console.log(`Executing '${mavenCmd}' in`, basePath);
|
|
1750
1780
|
result = safeSpawnSync(mavenCmd, mvnArgs, {
|
|
1751
1781
|
cwd: basePath,
|
|
1752
|
-
shell:
|
|
1782
|
+
shell: isWin,
|
|
1753
1783
|
});
|
|
1754
1784
|
// Check if the cyclonedx plugin created the required bom.json file
|
|
1755
1785
|
// Sometimes the plugin fails silently for complex maven projects
|
|
@@ -1807,7 +1837,7 @@ export async function createJavaBom(path, options) {
|
|
|
1807
1837
|
}
|
|
1808
1838
|
result = safeSpawnSync("mvn", findParentComponentArgs, {
|
|
1809
1839
|
cwd: basePath,
|
|
1810
|
-
shell:
|
|
1840
|
+
shell: isWin,
|
|
1811
1841
|
});
|
|
1812
1842
|
if (result.status === 0) {
|
|
1813
1843
|
if (safeExistsSync(tempMvnParentTree)) {
|
|
@@ -1840,7 +1870,7 @@ export async function createJavaBom(path, options) {
|
|
|
1840
1870
|
mvnTreeArgs,
|
|
1841
1871
|
{
|
|
1842
1872
|
cwd: basePath,
|
|
1843
|
-
shell:
|
|
1873
|
+
shell: isWin,
|
|
1844
1874
|
},
|
|
1845
1875
|
);
|
|
1846
1876
|
if (result.status !== 0 || result.error) {
|
|
@@ -2385,7 +2415,7 @@ export async function createJavaBom(path, options) {
|
|
|
2385
2415
|
console.log("Executing", BAZEL_CMD, "in", basePath);
|
|
2386
2416
|
let result = safeSpawnSync(BAZEL_CMD, bArgs, {
|
|
2387
2417
|
cwd: basePath,
|
|
2388
|
-
shell:
|
|
2418
|
+
shell: isWin,
|
|
2389
2419
|
});
|
|
2390
2420
|
if (result.status !== 0 || result.error) {
|
|
2391
2421
|
if (result.stderr) {
|
|
@@ -7742,6 +7772,7 @@ export async function createCsharpBom(path, options) {
|
|
|
7742
7772
|
}
|
|
7743
7773
|
}
|
|
7744
7774
|
const pkgNameVersions = {};
|
|
7775
|
+
let services = [];
|
|
7745
7776
|
if (csProjFiles.length) {
|
|
7746
7777
|
manifestFiles = manifestFiles.concat(csProjFiles);
|
|
7747
7778
|
// Parsing csproj is quite error-prone. Some project files may not have versions specified
|
|
@@ -7803,21 +7834,30 @@ export async function createCsharpBom(path, options) {
|
|
|
7803
7834
|
// Perform deep analysis using dosai
|
|
7804
7835
|
if (options.deep) {
|
|
7805
7836
|
const slicesFile = resolve(
|
|
7806
|
-
|
|
7837
|
+
options.depsSlicesFile
|
|
7838
|
+
? join(path, options.depsSlicesFile)
|
|
7839
|
+
: join(getTmpDir(), "dosai.json"),
|
|
7807
7840
|
);
|
|
7808
7841
|
// Create the slices file if it doesn't exist
|
|
7809
7842
|
if (!safeExistsSync(slicesFile)) {
|
|
7810
7843
|
thoughtLog(
|
|
7811
7844
|
"Alright, the next step is to invoke the dosai command to identify evidence of occurrences for various components.",
|
|
7812
7845
|
);
|
|
7813
|
-
const sliceResult =
|
|
7846
|
+
const sliceResult = createDosaiMethodsSlice(
|
|
7847
|
+
resolve(path),
|
|
7848
|
+
resolve(slicesFile),
|
|
7849
|
+
options,
|
|
7850
|
+
);
|
|
7814
7851
|
if (!sliceResult && DEBUG_MODE) {
|
|
7815
7852
|
console.log(
|
|
7816
7853
|
"Slicing with dosai was unsuccessful. Check the errors reported in the logs above.",
|
|
7817
7854
|
);
|
|
7818
7855
|
}
|
|
7819
7856
|
}
|
|
7820
|
-
pkgList = addEvidenceForDotnet(pkgList, slicesFile
|
|
7857
|
+
pkgList = addEvidenceForDotnet(pkgList, slicesFile);
|
|
7858
|
+
const methodsSlice = readDosaiJsonFile(slicesFile);
|
|
7859
|
+
const servicesMap = collectDosaiServicesFromMethods(methodsSlice, {});
|
|
7860
|
+
services = normalizeDosaiServiceMap(servicesMap);
|
|
7821
7861
|
}
|
|
7822
7862
|
}
|
|
7823
7863
|
// Parent dependency tree
|
|
@@ -7843,6 +7883,8 @@ export async function createCsharpBom(path, options) {
|
|
|
7843
7883
|
filename: manifestFiles.join(", "),
|
|
7844
7884
|
dependencies,
|
|
7845
7885
|
parentComponent,
|
|
7886
|
+
services,
|
|
7887
|
+
tools: options.deep ? getPluginToolComponents(["dosai"]) : [],
|
|
7846
7888
|
});
|
|
7847
7889
|
}
|
|
7848
7890
|
|
|
@@ -8354,6 +8396,15 @@ export async function createCryptoCertsBom(path, options) {
|
|
|
8354
8396
|
if (sourceCryptoComponents.length) {
|
|
8355
8397
|
pkgList.push(...sourceCryptoComponents);
|
|
8356
8398
|
}
|
|
8399
|
+
if (shouldCollectDosaiCrypto(path, options)) {
|
|
8400
|
+
const dosaiCryptoComponents = await collectDosaiCryptoComponents(
|
|
8401
|
+
path,
|
|
8402
|
+
options,
|
|
8403
|
+
);
|
|
8404
|
+
if (dosaiCryptoComponents.length) {
|
|
8405
|
+
pkgList.push(...dosaiCryptoComponents);
|
|
8406
|
+
}
|
|
8407
|
+
}
|
|
8357
8408
|
return {
|
|
8358
8409
|
bomJson: {
|
|
8359
8410
|
components: pkgList,
|
package/lib/cli/index.poku.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { execFileSync, spawnSync } from "node:child_process";
|
|
2
2
|
import {
|
|
3
|
+
chmodSync,
|
|
3
4
|
copyFileSync,
|
|
4
5
|
existsSync,
|
|
5
6
|
mkdirSync,
|
|
@@ -29,6 +30,7 @@ import { postProcess } from "../stages/postgen/postgen.js";
|
|
|
29
30
|
import {
|
|
30
31
|
createBom,
|
|
31
32
|
createChromeExtensionBom,
|
|
33
|
+
createJavaBom,
|
|
32
34
|
createNodejsBom,
|
|
33
35
|
createPHPBom,
|
|
34
36
|
createPythonBom,
|
|
@@ -286,6 +288,165 @@ describe("CLI tests", () => {
|
|
|
286
288
|
);
|
|
287
289
|
assert.strictEqual(components[0].type, "data");
|
|
288
290
|
});
|
|
291
|
+
|
|
292
|
+
it("does not invoke dosai crypto analysis for non-.NET CBOM scans", async () => {
|
|
293
|
+
const tempDir = mkdtempSync(join(tmpdir(), "cdxgen-cbom-non-dotnet-"));
|
|
294
|
+
const collectDosaiCryptoComponents = sinon.stub().resolves([]);
|
|
295
|
+
try {
|
|
296
|
+
const { createCryptoCertsBom } = await esmock("./index.js", {
|
|
297
|
+
"../helpers/cbomutils.js": {
|
|
298
|
+
collectDosaiCryptoComponents,
|
|
299
|
+
collectSourceCryptoComponents: sinon.stub().resolves([]),
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
await createCryptoCertsBom(tempDir, {
|
|
304
|
+
projectType: ["js"],
|
|
305
|
+
specVersion: 1.7,
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
sinon.assert.notCalled(collectDosaiCryptoComponents);
|
|
309
|
+
} finally {
|
|
310
|
+
rmSync(tempDir, { force: true, recursive: true });
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it("invokes dosai crypto analysis for explicit .NET CBOM scans", async () => {
|
|
315
|
+
const tempDir = mkdtempSync(join(tmpdir(), "cdxgen-cbom-dotnet-"));
|
|
316
|
+
const collectDosaiCryptoComponents = sinon.stub().resolves([
|
|
317
|
+
{
|
|
318
|
+
name: "sha-256",
|
|
319
|
+
type: "cryptographic-asset",
|
|
320
|
+
"bom-ref": "crypto/algorithm/sha-256@2.16.840.1.101.3.4.2.1",
|
|
321
|
+
cryptoProperties: {
|
|
322
|
+
assetType: "algorithm",
|
|
323
|
+
oid: "2.16.840.1.101.3.4.2.1",
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
]);
|
|
327
|
+
try {
|
|
328
|
+
const { createCryptoCertsBom } = await esmock("./index.js", {
|
|
329
|
+
"../helpers/cbomutils.js": {
|
|
330
|
+
collectDosaiCryptoComponents,
|
|
331
|
+
collectSourceCryptoComponents: sinon.stub().resolves([]),
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
const bomData = await createCryptoCertsBom(tempDir, {
|
|
336
|
+
projectType: ["dotnet"],
|
|
337
|
+
specVersion: 1.7,
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
sinon.assert.calledOnce(collectDosaiCryptoComponents);
|
|
341
|
+
assert.strictEqual(bomData.bomJson.components[0].name, "sha-256");
|
|
342
|
+
} finally {
|
|
343
|
+
rmSync(tempDir, { force: true, recursive: true });
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it("invokes dosai crypto analysis when universal scans contain .NET project files", async () => {
|
|
348
|
+
const tempDir = mkdtempSync(
|
|
349
|
+
join(tmpdir(), "cdxgen-cbom-dotnet-indicator-"),
|
|
350
|
+
);
|
|
351
|
+
const collectDosaiCryptoComponents = sinon.stub().resolves([]);
|
|
352
|
+
try {
|
|
353
|
+
writeFileSync(join(tempDir, "app.csproj"), "<Project />");
|
|
354
|
+
const { createCryptoCertsBom } = await esmock("./index.js", {
|
|
355
|
+
"../helpers/cbomutils.js": {
|
|
356
|
+
collectDosaiCryptoComponents,
|
|
357
|
+
collectSourceCryptoComponents: sinon.stub().resolves([]),
|
|
358
|
+
},
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
await createCryptoCertsBom(tempDir, {
|
|
362
|
+
projectType: ["universal"],
|
|
363
|
+
specVersion: 1.7,
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
sinon.assert.calledOnce(collectDosaiCryptoComponents);
|
|
367
|
+
} finally {
|
|
368
|
+
rmSync(tempDir, { force: true, recursive: true });
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
it("does not interpret shell metacharacters in Maven module paths", async () => {
|
|
373
|
+
if (process.platform === "win32") {
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
const tempDir = mkdtempSync(join(tmpdir(), "cdxgen-maven-shell-"));
|
|
377
|
+
const fakeBinDir = join(tempDir, "bin");
|
|
378
|
+
const repoDir = join(tempDir, "repo");
|
|
379
|
+
const markerFile = join(tmpdir(), "CDXGEN_GITURL_E2E_MARKER_TEST");
|
|
380
|
+
const shellIfs = "$" + "{IFS}";
|
|
381
|
+
const maliciousDirName = `evil;cd${shellIfs}..;cd${shellIfs}..;printf${shellIfs}CDXGEN_MAVEN_GIT_URL_E2E_SHELL_INJECTION>CDXGEN_GITURL_E2E_MARKER_TEST;#`;
|
|
382
|
+
const maliciousModuleDir = join(repoDir, maliciousDirName);
|
|
383
|
+
const originalPath = process.env.PATH;
|
|
384
|
+
const originalMvnCmd = process.env.MVN_CMD;
|
|
385
|
+
const originalMavenCmd = process.env.MAVEN_CMD;
|
|
386
|
+
const originalMvnArgs = process.env.MVN_ARGS;
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
rmSync(markerFile, { force: true });
|
|
390
|
+
mkdirSync(fakeBinDir, { recursive: true });
|
|
391
|
+
mkdirSync(maliciousModuleDir, { recursive: true });
|
|
392
|
+
writeFileSync(
|
|
393
|
+
join(maliciousModuleDir, "pom.xml"),
|
|
394
|
+
"<project><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>evil</artifactId><version>1.0.0</version></project>",
|
|
395
|
+
);
|
|
396
|
+
writeFileSync(join(maliciousModuleDir, "settings.xml"), "<settings />");
|
|
397
|
+
const fakeMvn = join(fakeBinDir, "mvn");
|
|
398
|
+
writeFileSync(
|
|
399
|
+
fakeMvn,
|
|
400
|
+
`#!/bin/sh
|
|
401
|
+
for arg do
|
|
402
|
+
case "$arg" in
|
|
403
|
+
-DoutputFile=*)
|
|
404
|
+
output="\${arg#-DoutputFile=}"
|
|
405
|
+
mkdir -p "$(dirname "$output")"
|
|
406
|
+
printf 'org.example:evil:jar:1.0.0:compile\\n' > "$output"
|
|
407
|
+
;;
|
|
408
|
+
esac
|
|
409
|
+
done
|
|
410
|
+
`,
|
|
411
|
+
);
|
|
412
|
+
chmodSync(fakeMvn, 0o755);
|
|
413
|
+
process.env.PATH = `${fakeBinDir}${process.env.PATH ? `:${process.env.PATH}` : ""}`;
|
|
414
|
+
delete process.env.MVN_CMD;
|
|
415
|
+
delete process.env.MAVEN_CMD;
|
|
416
|
+
delete process.env.MVN_ARGS;
|
|
417
|
+
|
|
418
|
+
await createJavaBom(repoDir, {
|
|
419
|
+
multiProject: true,
|
|
420
|
+
projectType: ["java"],
|
|
421
|
+
specVersion: 1.6,
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
assert.strictEqual(existsSync(markerFile), false);
|
|
425
|
+
} finally {
|
|
426
|
+
if (originalPath === undefined) {
|
|
427
|
+
delete process.env.PATH;
|
|
428
|
+
} else {
|
|
429
|
+
process.env.PATH = originalPath;
|
|
430
|
+
}
|
|
431
|
+
if (originalMvnCmd === undefined) {
|
|
432
|
+
delete process.env.MVN_CMD;
|
|
433
|
+
} else {
|
|
434
|
+
process.env.MVN_CMD = originalMvnCmd;
|
|
435
|
+
}
|
|
436
|
+
if (originalMavenCmd === undefined) {
|
|
437
|
+
delete process.env.MAVEN_CMD;
|
|
438
|
+
} else {
|
|
439
|
+
process.env.MAVEN_CMD = originalMavenCmd;
|
|
440
|
+
}
|
|
441
|
+
if (originalMvnArgs === undefined) {
|
|
442
|
+
delete process.env.MVN_ARGS;
|
|
443
|
+
} else {
|
|
444
|
+
process.env.MVN_ARGS = originalMvnArgs;
|
|
445
|
+
}
|
|
446
|
+
rmSync(markerFile, { force: true });
|
|
447
|
+
rmSync(tempDir, { force: true, recursive: true });
|
|
448
|
+
}
|
|
449
|
+
});
|
|
289
450
|
});
|
|
290
451
|
|
|
291
452
|
describe("distribution filters", () => {
|
package/lib/evinser/evinser.js
CHANGED
|
@@ -4,7 +4,19 @@ import process from "node:process";
|
|
|
4
4
|
|
|
5
5
|
import { PackageURL } from "packageurl-js";
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
collectDosaiCryptoComponents,
|
|
9
|
+
findCryptoAlgos,
|
|
10
|
+
} from "../helpers/cbomutils.js";
|
|
11
|
+
import { mergeServices } from "../helpers/depsUtils.js";
|
|
12
|
+
import {
|
|
13
|
+
collectDosaiDataFlowFrames,
|
|
14
|
+
collectDosaiPurlEvidence,
|
|
15
|
+
collectDosaiServicesFromMethods,
|
|
16
|
+
createDosaiDataFlowSlice,
|
|
17
|
+
createDosaiMethodsSlice,
|
|
18
|
+
isDosaiDotnetLanguage,
|
|
19
|
+
} from "../helpers/dosai.js";
|
|
8
20
|
import { parseOccurrenceEvidenceLocation } from "../helpers/evidenceUtils.js";
|
|
9
21
|
import {
|
|
10
22
|
collectGradleDependencies,
|
|
@@ -230,6 +242,8 @@ export async function createSlice(
|
|
|
230
242
|
language = "python";
|
|
231
243
|
} else if (PROJECT_TYPE_ALIASES.scala.includes(language)) {
|
|
232
244
|
language = "scala";
|
|
245
|
+
} else if (isDosaiDotnetLanguage(language)) {
|
|
246
|
+
language = "csharp";
|
|
233
247
|
}
|
|
234
248
|
if (
|
|
235
249
|
PROJECT_TYPE_ALIASES.swift.includes(language) &&
|
|
@@ -270,6 +284,33 @@ export async function createSlice(
|
|
|
270
284
|
}
|
|
271
285
|
return { tempDir: sliceOutputDir, tempDirOwned, slicesFile };
|
|
272
286
|
}
|
|
287
|
+
if (isDosaiDotnetLanguage(language)) {
|
|
288
|
+
console.log(
|
|
289
|
+
`Creating ${sliceType} slice for ${resolve(filePath)} using dosai. Please wait ...`,
|
|
290
|
+
);
|
|
291
|
+
const sliceResult =
|
|
292
|
+
sliceType === "data-flow"
|
|
293
|
+
? createDosaiDataFlowSlice(
|
|
294
|
+
resolve(filePath),
|
|
295
|
+
resolve(slicesFile),
|
|
296
|
+
options,
|
|
297
|
+
)
|
|
298
|
+
: createDosaiMethodsSlice(
|
|
299
|
+
resolve(filePath),
|
|
300
|
+
resolve(slicesFile),
|
|
301
|
+
options,
|
|
302
|
+
);
|
|
303
|
+
if (!sliceResult) {
|
|
304
|
+
console.warn(
|
|
305
|
+
`Unable to generate ${sliceType} slice using dosai. Check if this is a supported .NET project.`,
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
tempDir: sliceOutputDir,
|
|
310
|
+
tempDirOwned,
|
|
311
|
+
slicesFile,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
273
314
|
console.log(
|
|
274
315
|
`Creating ${sliceType} slice for ${resolve(filePath)}. Please wait ...`,
|
|
275
316
|
);
|
|
@@ -393,6 +434,9 @@ export function purlToLanguage(purl, filePath) {
|
|
|
393
434
|
case "gem":
|
|
394
435
|
language = "ruby";
|
|
395
436
|
break;
|
|
437
|
+
case "nuget":
|
|
438
|
+
language = "csharp";
|
|
439
|
+
break;
|
|
396
440
|
case "generic":
|
|
397
441
|
language = "c";
|
|
398
442
|
}
|
|
@@ -474,6 +518,77 @@ export async function analyzeProject(dbObjMap, options) {
|
|
|
474
518
|
// Load any existing purl-location information from the sbom.
|
|
475
519
|
// For eg: cdxgen populates this information for javascript projects
|
|
476
520
|
let { purlLocationMap, purlImportsMap } = initFromSbom(components, language);
|
|
521
|
+
if (isDosaiDotnetLanguage(language)) {
|
|
522
|
+
if (options.profile === "research") {
|
|
523
|
+
options.withDataFlow = true;
|
|
524
|
+
options.includeCrypto = true;
|
|
525
|
+
}
|
|
526
|
+
if (
|
|
527
|
+
options.usagesSlicesFile &&
|
|
528
|
+
usableSlicesFile(options.usagesSlicesFile)
|
|
529
|
+
) {
|
|
530
|
+
usageSlice = JSON.parse(
|
|
531
|
+
fs.readFileSync(options.usagesSlicesFile, "utf-8"),
|
|
532
|
+
);
|
|
533
|
+
usagesSlicesFile = options.usagesSlicesFile;
|
|
534
|
+
} else {
|
|
535
|
+
retMap = await createSlice(language, dirPath, "usages", options);
|
|
536
|
+
if (retMap?.slicesFile && safeExistsSync(retMap.slicesFile)) {
|
|
537
|
+
usageSlice = JSON.parse(fs.readFileSync(retMap.slicesFile, "utf-8"));
|
|
538
|
+
usagesSlicesFile = retMap.slicesFile;
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
if (usageSlice && Object.keys(usageSlice).length) {
|
|
542
|
+
const dosaiEvidence = collectDosaiPurlEvidence(usageSlice, components);
|
|
543
|
+
for (const [purl, locations] of Object.entries(
|
|
544
|
+
dosaiEvidence.purlLocationMap,
|
|
545
|
+
)) {
|
|
546
|
+
purlLocationMap[purl] ??= new Set();
|
|
547
|
+
for (const location of locations) {
|
|
548
|
+
purlLocationMap[purl].add(location);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
servicesMap = collectDosaiServicesFromMethods(usageSlice, servicesMap);
|
|
552
|
+
userDefinedTypesMap = {};
|
|
553
|
+
}
|
|
554
|
+
if (options.withDataFlow) {
|
|
555
|
+
if (
|
|
556
|
+
options.dataFlowSlicesFile &&
|
|
557
|
+
safeExistsSync(options.dataFlowSlicesFile)
|
|
558
|
+
) {
|
|
559
|
+
dataFlowSlicesFile = options.dataFlowSlicesFile;
|
|
560
|
+
dataFlowSlice = JSON.parse(
|
|
561
|
+
fs.readFileSync(options.dataFlowSlicesFile, "utf-8"),
|
|
562
|
+
);
|
|
563
|
+
} else {
|
|
564
|
+
retMap = await createSlice(language, dirPath, "data-flow", options);
|
|
565
|
+
if (retMap?.slicesFile && safeExistsSync(retMap.slicesFile)) {
|
|
566
|
+
dataFlowSlicesFile = retMap.slicesFile;
|
|
567
|
+
dataFlowSlice = JSON.parse(
|
|
568
|
+
fs.readFileSync(retMap.slicesFile, "utf-8"),
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
if (dataFlowSlice && Object.keys(dataFlowSlice).length) {
|
|
573
|
+
dataFlowFrames = collectDosaiDataFlowFrames(dataFlowSlice, components);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
if (options.includeCrypto) {
|
|
577
|
+
cryptoComponents = await collectDosaiCryptoComponents(dirPath, options);
|
|
578
|
+
}
|
|
579
|
+
return {
|
|
580
|
+
usagesSlicesFile,
|
|
581
|
+
dataFlowSlicesFile,
|
|
582
|
+
purlLocationMap,
|
|
583
|
+
servicesMap,
|
|
584
|
+
dataFlowFrames,
|
|
585
|
+
tempDir: retMap?.tempDir,
|
|
586
|
+
tempDirOwned: retMap?.tempDirOwned,
|
|
587
|
+
userDefinedTypesMap,
|
|
588
|
+
cryptoComponents,
|
|
589
|
+
cryptoGeneratePurls,
|
|
590
|
+
};
|
|
591
|
+
}
|
|
477
592
|
// Do reachables first so that usages slicing can reuse the atom file
|
|
478
593
|
// We need reachables slicing even when trying to infer crypto packages
|
|
479
594
|
if (options.withReachables || options.includeCrypto) {
|
|
@@ -1472,8 +1587,8 @@ export function createEvinseFile(sliceArtefacts, options) {
|
|
|
1472
1587
|
properties: servicesMap[serviceName].properties,
|
|
1473
1588
|
});
|
|
1474
1589
|
}
|
|
1475
|
-
// Add to existing services
|
|
1476
|
-
bomJson.services = (bomJson.services || []
|
|
1590
|
+
// Add to existing services while preserving CycloneDX uniqueItems validity
|
|
1591
|
+
bomJson.services = mergeServices(bomJson.services || [], services);
|
|
1477
1592
|
servicesPresent = true;
|
|
1478
1593
|
}
|
|
1479
1594
|
// Add the crypto components to the components list
|