@cyclonedx/cdxgen 12.3.3 → 12.4.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.
Files changed (157) hide show
  1. package/README.md +64 -22
  2. package/bin/audit.js +21 -7
  3. package/bin/cdxgen.js +238 -116
  4. package/bin/convert.js +28 -13
  5. package/bin/hbom.js +490 -0
  6. package/bin/repl.js +580 -29
  7. package/bin/validate.js +34 -4
  8. package/bin/verify.js +40 -5
  9. package/data/README.md +298 -25
  10. package/data/component-tags.json +6 -0
  11. package/data/crypto-oid.json +16 -0
  12. package/data/predictive-audit-allowlist.json +11 -0
  13. package/data/queries-darwin.json +12 -1
  14. package/data/queries-win.json +7 -1
  15. package/data/queries.json +39 -2
  16. package/data/rules/ai-agent-governance.yaml +16 -0
  17. package/data/rules/asar-archives.yaml +150 -0
  18. package/data/rules/chrome-extensions.yaml +8 -0
  19. package/data/rules/ci-permissions.yaml +42 -18
  20. package/data/rules/container-risk.yaml +14 -7
  21. package/data/rules/dependency-sources.yaml +11 -0
  22. package/data/rules/hbom-compliance.yaml +325 -0
  23. package/data/rules/hbom-performance.yaml +307 -0
  24. package/data/rules/hbom-security.yaml +248 -0
  25. package/data/rules/host-topology.yaml +165 -0
  26. package/data/rules/mcp-servers.yaml +18 -3
  27. package/data/rules/obom-runtime.yaml +907 -22
  28. package/data/rules/package-integrity.yaml +14 -0
  29. package/data/rules/rootfs-hardening.yaml +179 -0
  30. package/data/rules/vscode-extensions.yaml +9 -0
  31. package/lib/audit/index.js +209 -8
  32. package/lib/audit/index.poku.js +332 -0
  33. package/lib/audit/reporters.js +222 -0
  34. package/lib/audit/targets.js +146 -1
  35. package/lib/audit/targets.poku.js +186 -0
  36. package/lib/cli/asar.poku.js +328 -0
  37. package/lib/cli/index.js +506 -88
  38. package/lib/cli/index.poku.js +1352 -212
  39. package/lib/evinser/evinser.js +14 -9
  40. package/lib/helpers/analyzer.js +1406 -29
  41. package/lib/helpers/analyzer.poku.js +342 -0
  42. package/lib/helpers/analyzerScope.js +712 -0
  43. package/lib/helpers/asarutils.js +1556 -0
  44. package/lib/helpers/asarutils.poku.js +443 -0
  45. package/lib/helpers/auditCategories.js +12 -0
  46. package/lib/helpers/auditCategories.poku.js +32 -0
  47. package/lib/helpers/cbomutils.js +271 -1
  48. package/lib/helpers/cbomutils.poku.js +248 -5
  49. package/lib/helpers/display.js +291 -1
  50. package/lib/helpers/display.poku.js +149 -0
  51. package/lib/helpers/evidenceUtils.js +58 -0
  52. package/lib/helpers/evidenceUtils.poku.js +54 -0
  53. package/lib/helpers/exportUtils.js +9 -0
  54. package/lib/helpers/gtfobins.js +142 -8
  55. package/lib/helpers/gtfobins.poku.js +24 -1
  56. package/lib/helpers/hbom.js +710 -0
  57. package/lib/helpers/hbom.poku.js +496 -0
  58. package/lib/helpers/hbomAnalysis.js +268 -0
  59. package/lib/helpers/hbomAnalysis.poku.js +249 -0
  60. package/lib/helpers/hbomLoader.js +35 -0
  61. package/lib/helpers/hostTopology.js +803 -0
  62. package/lib/helpers/hostTopology.poku.js +363 -0
  63. package/lib/helpers/inventoryStats.js +69 -0
  64. package/lib/helpers/inventoryStats.poku.js +86 -0
  65. package/lib/helpers/lolbas.js +19 -1
  66. package/lib/helpers/lolbas.poku.js +23 -0
  67. package/lib/helpers/osqueryTransform.js +47 -0
  68. package/lib/helpers/osqueryTransform.poku.js +47 -0
  69. package/lib/helpers/plugins.js +349 -0
  70. package/lib/helpers/plugins.poku.js +57 -0
  71. package/lib/helpers/protobom.js +156 -45
  72. package/lib/helpers/protobom.poku.js +140 -5
  73. package/lib/helpers/remote/dependency-track.js +36 -3
  74. package/lib/helpers/remote/dependency-track.poku.js +44 -0
  75. package/lib/helpers/source.js +24 -0
  76. package/lib/helpers/source.poku.js +32 -0
  77. package/lib/helpers/utils.js +1438 -93
  78. package/lib/helpers/utils.poku.js +846 -4
  79. package/lib/managers/binary.e2e.poku.js +367 -0
  80. package/lib/managers/binary.js +2293 -353
  81. package/lib/managers/binary.poku.js +1699 -1
  82. package/lib/managers/docker.js +201 -79
  83. package/lib/managers/docker.poku.js +337 -12
  84. package/lib/server/server.js +2 -27
  85. package/lib/stages/postgen/annotator.js +38 -0
  86. package/lib/stages/postgen/annotator.poku.js +107 -1
  87. package/lib/stages/postgen/auditBom.js +121 -18
  88. package/lib/stages/postgen/auditBom.poku.js +1366 -31
  89. package/lib/stages/postgen/hostTopologyAudit.poku.js +186 -0
  90. package/lib/stages/postgen/postgen.js +192 -1
  91. package/lib/stages/postgen/postgen.poku.js +321 -0
  92. package/lib/stages/postgen/ruleEngine.js +116 -0
  93. package/lib/stages/pregen/envAudit.js +14 -3
  94. package/package.json +23 -21
  95. package/types/bin/hbom.d.ts +3 -0
  96. package/types/bin/hbom.d.ts.map +1 -0
  97. package/types/bin/repl.d.ts.map +1 -1
  98. package/types/lib/audit/index.d.ts +44 -0
  99. package/types/lib/audit/index.d.ts.map +1 -1
  100. package/types/lib/audit/reporters.d.ts +16 -0
  101. package/types/lib/audit/reporters.d.ts.map +1 -1
  102. package/types/lib/audit/targets.d.ts.map +1 -1
  103. package/types/lib/cli/index.d.ts +16 -0
  104. package/types/lib/cli/index.d.ts.map +1 -1
  105. package/types/lib/evinser/evinser.d.ts +4 -0
  106. package/types/lib/evinser/evinser.d.ts.map +1 -1
  107. package/types/lib/helpers/analyzer.d.ts +33 -0
  108. package/types/lib/helpers/analyzer.d.ts.map +1 -1
  109. package/types/lib/helpers/analyzerScope.d.ts +11 -0
  110. package/types/lib/helpers/analyzerScope.d.ts.map +1 -0
  111. package/types/lib/helpers/asarutils.d.ts +34 -0
  112. package/types/lib/helpers/asarutils.d.ts.map +1 -0
  113. package/types/lib/helpers/auditCategories.d.ts +5 -0
  114. package/types/lib/helpers/auditCategories.d.ts.map +1 -1
  115. package/types/lib/helpers/cbomutils.d.ts +3 -2
  116. package/types/lib/helpers/cbomutils.d.ts.map +1 -1
  117. package/types/lib/helpers/display.d.ts.map +1 -1
  118. package/types/lib/helpers/evidenceUtils.d.ts +8 -0
  119. package/types/lib/helpers/evidenceUtils.d.ts.map +1 -0
  120. package/types/lib/helpers/exportUtils.d.ts.map +1 -1
  121. package/types/lib/helpers/gtfobins.d.ts +8 -0
  122. package/types/lib/helpers/gtfobins.d.ts.map +1 -1
  123. package/types/lib/helpers/hbom.d.ts +49 -0
  124. package/types/lib/helpers/hbom.d.ts.map +1 -0
  125. package/types/lib/helpers/hbomAnalysis.d.ts +62 -0
  126. package/types/lib/helpers/hbomAnalysis.d.ts.map +1 -0
  127. package/types/lib/helpers/hbomLoader.d.ts +7 -0
  128. package/types/lib/helpers/hbomLoader.d.ts.map +1 -0
  129. package/types/lib/helpers/hostTopology.d.ts +12 -0
  130. package/types/lib/helpers/hostTopology.d.ts.map +1 -0
  131. package/types/lib/helpers/inventoryStats.d.ts +11 -0
  132. package/types/lib/helpers/inventoryStats.d.ts.map +1 -0
  133. package/types/lib/helpers/lolbas.d.ts.map +1 -1
  134. package/types/lib/helpers/osqueryTransform.d.ts +3 -0
  135. package/types/lib/helpers/osqueryTransform.d.ts.map +1 -1
  136. package/types/lib/helpers/plugins.d.ts +58 -0
  137. package/types/lib/helpers/plugins.d.ts.map +1 -0
  138. package/types/lib/helpers/protobom.d.ts +3 -4
  139. package/types/lib/helpers/protobom.d.ts.map +1 -1
  140. package/types/lib/helpers/remote/dependency-track.d.ts +10 -3
  141. package/types/lib/helpers/remote/dependency-track.d.ts.map +1 -1
  142. package/types/lib/helpers/source.d.ts.map +1 -1
  143. package/types/lib/helpers/utils.d.ts +45 -8
  144. package/types/lib/helpers/utils.d.ts.map +1 -1
  145. package/types/lib/managers/binary.d.ts +5 -0
  146. package/types/lib/managers/binary.d.ts.map +1 -1
  147. package/types/lib/managers/docker.d.ts.map +1 -1
  148. package/types/lib/server/server.d.ts +2 -1
  149. package/types/lib/server/server.d.ts.map +1 -1
  150. package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
  151. package/types/lib/stages/postgen/auditBom.d.ts +26 -1
  152. package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
  153. package/types/lib/stages/postgen/postgen.d.ts +2 -1
  154. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  155. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  156. package/types/lib/stages/pregen/envAudit.d.ts.map +1 -1
  157. package/data/spdx-model-v3.0.1.jsonld +0 -15999
@@ -3,27 +3,46 @@ import { join } from "node:path";
3
3
 
4
4
  import { assert, it } from "poku";
5
5
 
6
- import { readBinary, writeBinary } from "./protobom.js";
6
+ import { isProtoBomFile, readBinary, writeBinary } from "./protobom.js";
7
7
  import { getTmpDir } from "./utils.js";
8
8
 
9
- const tempDir = mkdtempSync(join(getTmpDir(), "bin-tests-"));
10
9
  const testBom = JSON.parse(
11
10
  readFileSync("./test/data/bom-java.json", { encoding: "utf-8" }),
12
11
  );
12
+ const cbomFixture = JSON.parse(
13
+ readFileSync("./test/data/bom-cbom-js-fixture.json", { encoding: "utf-8" }),
14
+ );
15
+
16
+ const createTempDir = () => mkdtempSync(join(getTmpDir(), "bin-tests-"));
17
+
18
+ const cleanupTempDir = (tempDir) => {
19
+ if (tempDir?.startsWith(getTmpDir()) && rmSync) {
20
+ rmSync(tempDir, { recursive: true, force: true });
21
+ }
22
+ };
13
23
 
14
24
  it("proto binary tests", () => {
25
+ const tempDir = createTempDir();
15
26
  const binFile = join(tempDir, "test.cdx.bin");
16
27
  writeBinary({}, binFile);
17
28
  assert.deepStrictEqual(existsSync(binFile), true);
18
29
  writeBinary(testBom, binFile);
19
30
  assert.deepStrictEqual(existsSync(binFile), true);
31
+ assert.equal(isProtoBomFile(binFile), true);
32
+ assert.equal(isProtoBomFile("test.proto"), true);
33
+ assert.equal(isProtoBomFile("bom.json"), false);
20
34
  let bomObject = readBinary(binFile);
21
35
  assert.ok(bomObject);
22
36
  assert.deepStrictEqual(
23
37
  bomObject.serialNumber,
24
38
  "urn:uuid:cc8b5a04-2698-4375-b04c-cedfa4317fee",
25
39
  );
40
+ assert.deepStrictEqual(bomObject.bomFormat, "CycloneDX");
26
41
  assert.deepStrictEqual(bomObject.specVersion, "1.5");
42
+ assert.equal(
43
+ bomObject.metadata.component.type.startsWith("CLASSIFICATION_"),
44
+ false,
45
+ );
27
46
  bomObject = readBinary(binFile, false, 1.5);
28
47
  assert.ok(bomObject);
29
48
  assert.deepStrictEqual(
@@ -31,7 +50,123 @@ it("proto binary tests", () => {
31
50
  "urn:uuid:cc8b5a04-2698-4375-b04c-cedfa4317fee",
32
51
  );
33
52
  assert.deepStrictEqual(bomObject.specVersion, "1.5");
34
- if (tempDir?.startsWith(getTmpDir()) && rmSync) {
35
- rmSync(tempDir, { recursive: true, force: true });
36
- }
53
+ const modernBinFile = join(tempDir, "test-1.7.cdx");
54
+ writeBinary(
55
+ {
56
+ bomFormat: "CycloneDX",
57
+ metadata: {
58
+ component: {
59
+ name: "cdxgen",
60
+ type: "application",
61
+ },
62
+ },
63
+ serialNumber: "urn:uuid:11111111-1111-1111-1111-111111111111",
64
+ specVersion: "1.7",
65
+ version: 1,
66
+ },
67
+ modernBinFile,
68
+ );
69
+ const modernBomObject = readBinary(modernBinFile);
70
+ assert.ok(modernBomObject);
71
+ assert.deepStrictEqual(modernBomObject.bomFormat, "CycloneDX");
72
+ assert.deepStrictEqual(modernBomObject.specVersion, "1.7");
73
+ assert.deepStrictEqual(
74
+ modernBomObject.metadata.component.type,
75
+ "application",
76
+ );
77
+ assert.deepStrictEqual(modernBomObject.metadata.component.name, "cdxgen");
78
+ cleanupTempDir(tempDir);
79
+ });
80
+
81
+ it("keeps canonical definitions and declarations as objects during proto round-trip", () => {
82
+ const tempDir = createTempDir();
83
+ const binFile = join(tempDir, "standard-sections.cdx");
84
+ writeBinary(
85
+ {
86
+ bomFormat: "CycloneDX",
87
+ declarations: {
88
+ affirmation: {
89
+ statement: "verified",
90
+ },
91
+ claims: [
92
+ {
93
+ predicate: "meets-control",
94
+ target: "pkg:npm/demo-app@1.0.0",
95
+ },
96
+ ],
97
+ },
98
+ definitions: {
99
+ standards: [
100
+ {
101
+ name: "ASVS",
102
+ requirements: [
103
+ {
104
+ identifier: "V1.1",
105
+ title: "Authenticate requests",
106
+ },
107
+ ],
108
+ version: "5.0",
109
+ },
110
+ ],
111
+ },
112
+ metadata: {
113
+ component: {
114
+ name: "demo-app",
115
+ type: "application",
116
+ version: "1.0.0",
117
+ },
118
+ },
119
+ serialNumber: "urn:uuid:22222222-2222-2222-2222-222222222222",
120
+ specVersion: "1.7",
121
+ version: 1,
122
+ },
123
+ binFile,
124
+ );
125
+
126
+ const bomObject = readBinary(binFile);
127
+ assert.ok(bomObject);
128
+ assert.equal(Array.isArray(bomObject.definitions), false);
129
+ assert.equal(Array.isArray(bomObject.declarations), false);
130
+ assert.equal(bomObject.definitions.standards[0].name, "ASVS");
131
+ assert.equal(
132
+ bomObject.definitions.standards[0].requirements[0].identifier,
133
+ "V1.1",
134
+ );
135
+ assert.equal(bomObject.declarations.claims[0].predicate, "meets-control");
136
+ assert.equal(bomObject.declarations.affirmation.statement, "verified");
137
+ cleanupTempDir(tempDir);
138
+ });
139
+
140
+ it("round-trips real CBOM fixture data with cryptographic assets intact", () => {
141
+ const tempDir = createTempDir();
142
+ const binFile = join(tempDir, "cbom-fixture.cdx");
143
+ writeBinary(cbomFixture, binFile);
144
+
145
+ const bomObject = readBinary(binFile);
146
+ const cryptoComponents = (bomObject.components || []).filter(
147
+ (component) => component.type === "cryptographic-asset",
148
+ );
149
+
150
+ assert.ok(bomObject);
151
+ assert.equal(bomObject.specVersion, "1.7");
152
+ assert.ok(cryptoComponents.length >= 3);
153
+ assert.equal(
154
+ cryptoComponents.some(
155
+ (component) => component.cryptoProperties?.assetType === "algorithm",
156
+ ),
157
+ true,
158
+ );
159
+ assert.equal(
160
+ cryptoComponents.some((component) => component.purl !== undefined),
161
+ false,
162
+ );
163
+ assert.equal(
164
+ cryptoComponents.some(
165
+ (component) =>
166
+ component.name === "sha-512" &&
167
+ component.cryptoProperties?.oid === "2.16.840.1.101.3.4.2.3",
168
+ ),
169
+ true,
170
+ );
171
+ cleanupTempDir(tempDir);
37
172
  });
@@ -1,13 +1,46 @@
1
1
  import { Buffer } from "node:buffer";
2
2
 
3
+ import { hasDangerousUnicode } from "../utils.js";
4
+
5
+ /**
6
+ * Returns the Dependency-Track BOM API URL as a sanitized URL object.
7
+ *
8
+ * @param {string} serverUrl Dependency-Track server URL
9
+ * @returns {URL | undefined} API URL to submit BOM payload
10
+ */
11
+ export function getDependencyTrackBomApiUrl(serverUrl) {
12
+ const rawServerUrl = `${serverUrl || ""}`.trim();
13
+ if (!rawServerUrl || hasDangerousUnicode(rawServerUrl)) {
14
+ return undefined;
15
+ }
16
+ let parsedUrl;
17
+ try {
18
+ parsedUrl = new URL(rawServerUrl);
19
+ } catch {
20
+ return undefined;
21
+ }
22
+ if (!["http:", "https:"].includes(parsedUrl.protocol)) {
23
+ return undefined;
24
+ }
25
+ if (!parsedUrl.hostname || hasDangerousUnicode(parsedUrl.hostname)) {
26
+ return undefined;
27
+ }
28
+ parsedUrl.username = "";
29
+ parsedUrl.password = "";
30
+ parsedUrl.search = "";
31
+ parsedUrl.hash = "";
32
+ parsedUrl.pathname = `${parsedUrl.pathname.replace(/\/+$/, "")}/api/v1/bom`;
33
+ return parsedUrl;
34
+ }
35
+
3
36
  /**
4
- * Returns the Dependency-Track BOM API URL.
37
+ * Returns the Dependency-Track BOM API URL string.
5
38
  *
6
39
  * @param {string} serverUrl Dependency-Track server URL
7
- * @returns {string} API URL to submit BOM payload
40
+ * @returns {string | undefined} API URL to submit BOM payload
8
41
  */
9
42
  export function getDependencyTrackBomUrl(serverUrl) {
10
- return `${serverUrl.replace(/\/$/, "")}/api/v1/bom`;
43
+ return getDependencyTrackBomApiUrl(serverUrl)?.toString();
11
44
  }
12
45
 
13
46
  /**
@@ -2,6 +2,7 @@ import { assert, describe, it } from "poku";
2
2
 
3
3
  import {
4
4
  buildDependencyTrackBomPayload,
5
+ getDependencyTrackBomApiUrl,
5
6
  getDependencyTrackBomUrl,
6
7
  } from "./dependency-track.js";
7
8
 
@@ -17,6 +18,49 @@ describe("Dependency-Track helper tests", () => {
17
18
  );
18
19
  });
19
20
 
21
+ it("removes credentials, query strings, and fragments from the submission URL", () => {
22
+ assert.strictEqual(
23
+ getDependencyTrackBomUrl(
24
+ "https://user:pass@dtrack.example.com/base/?token=secret#frag",
25
+ ),
26
+ "https://dtrack.example.com/base/api/v1/bom",
27
+ );
28
+ });
29
+
30
+ it("returns a sanitized URL object for Dependency-Track requests", () => {
31
+ const apiUrl = getDependencyTrackBomApiUrl(
32
+ "https://user:pass@dtrack.example.com/base/?token=secret#frag",
33
+ );
34
+ assert.ok(apiUrl instanceof URL);
35
+ assert.strictEqual(apiUrl?.hostname, "dtrack.example.com");
36
+ assert.strictEqual(apiUrl?.pathname, "/base/api/v1/bom");
37
+ assert.strictEqual(apiUrl?.username, "");
38
+ assert.strictEqual(apiUrl?.password, "");
39
+ assert.strictEqual(apiUrl?.search, "");
40
+ assert.strictEqual(apiUrl?.hash, "");
41
+ });
42
+
43
+ it("rejects malformed or unsupported submission URLs", () => {
44
+ assert.strictEqual(
45
+ getDependencyTrackBomUrl("file:///tmp/dtrack"),
46
+ undefined,
47
+ );
48
+ assert.strictEqual(
49
+ getDependencyTrackBomApiUrl("file:///tmp/dtrack"),
50
+ undefined,
51
+ );
52
+ assert.strictEqual(
53
+ getDependencyTrackBomUrl("javascript:alert(1)"),
54
+ undefined,
55
+ );
56
+ assert.strictEqual(
57
+ getDependencyTrackBomApiUrl("javascript:alert(1)"),
58
+ undefined,
59
+ );
60
+ assert.strictEqual(getDependencyTrackBomUrl("not a url"), undefined);
61
+ assert.strictEqual(getDependencyTrackBomApiUrl("not a url"), undefined);
62
+ });
63
+
20
64
  it("builds payload with parentUUID and tags", () => {
21
65
  const payload = buildDependencyTrackBomPayload(
22
66
  {
@@ -77,6 +77,21 @@ function isSafeGitRefName(refName) {
77
77
  return /^[A-Za-z0-9._/@+-]+$/.test(refName);
78
78
  }
79
79
 
80
+ function inferGitOperation(args = []) {
81
+ for (let index = 0; index < args.length; index += 1) {
82
+ const arg = args[index];
83
+ if (arg === "-c" || arg === "--config-env") {
84
+ index += 1;
85
+ continue;
86
+ }
87
+ if (typeof arg === "string" && arg.startsWith("-")) {
88
+ continue;
89
+ }
90
+ return arg || "command";
91
+ }
92
+ return "command";
93
+ }
94
+
80
95
  /**
81
96
  * Execute git with hardened defaults.
82
97
  *
@@ -86,6 +101,7 @@ function isSafeGitRefName(refName) {
86
101
  * @returns {Object} spawn result
87
102
  */
88
103
  export function hardenedGitCommand(args, options = {}) {
104
+ const gitOperation = inferGitOperation(args);
89
105
  const gitAllowProtocol = getGitAllowProtocol();
90
106
  const envConfigs = {
91
107
  GIT_CONFIG_COUNT: "2",
@@ -109,6 +125,14 @@ export function hardenedGitCommand(args, options = {}) {
109
125
  GIT_ALLOW_PROTOCOL: gitAllowProtocol,
110
126
  };
111
127
  return safeSpawnSync("git", args, {
128
+ cdxgenActivity: {
129
+ blockedReason: `Dry run mode blocks git ${gitOperation} operations.`,
130
+ gitOperation,
131
+ kind: `git-${gitOperation}`,
132
+ metadata: {
133
+ capability: "git-operation",
134
+ },
135
+ },
112
136
  shell: false,
113
137
  cwd: options.cwd,
114
138
  env,
@@ -45,6 +45,38 @@ describe("source helper purl resolution", () => {
45
45
  assert.strictEqual(recordActivity.firstCall.args[0].status, "blocked");
46
46
  });
47
47
 
48
+ it("hardenedGitCommand() tags git operations with the specific subcommand", async () => {
49
+ const safeSpawnSync = sinon
50
+ .stub()
51
+ .returns({ status: 0, stdout: "", stderr: "" });
52
+ const { hardenedGitCommand } = await esmock("./source.js", {
53
+ "./utils.js": {
54
+ cdxgenAgent: { get: sinon.stub() },
55
+ DEBUG_MODE: false,
56
+ fetchPomXmlAsJson: sinon.stub(),
57
+ getTmpDir: sinon.stub().returns(os.tmpdir()),
58
+ hasDangerousUnicode: sinon.stub().returns(false),
59
+ isDryRun: false,
60
+ isSecureMode: false,
61
+ isValidDriveRoot: sinon.stub().returns(true),
62
+ isWin: false,
63
+ safeMkdtempSync: sinon.stub(),
64
+ safeRmSync: sinon.stub(),
65
+ safeSpawnSync,
66
+ },
67
+ });
68
+
69
+ hardenedGitCommand(["fetch", "--tags"], { cwd: "/tmp/repo" });
70
+
71
+ sinon.assert.calledWithMatch(safeSpawnSync, "git", ["fetch", "--tags"], {
72
+ cdxgenActivity: sinon.match({
73
+ gitOperation: "fetch",
74
+ kind: "git-fetch",
75
+ }),
76
+ cwd: "/tmp/repo",
77
+ });
78
+ });
79
+
48
80
  it("resolves npm purl to repository URL", async () => {
49
81
  const getStub = sinon.stub().resolves({
50
82
  body: {