@cyclonedx/cdxgen 12.2.1 → 12.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.
Files changed (170) hide show
  1. package/README.md +239 -90
  2. package/bin/audit.js +191 -0
  3. package/bin/cdxgen.js +513 -167
  4. package/bin/convert.js +99 -0
  5. package/bin/evinse.js +23 -0
  6. package/bin/repl.js +339 -8
  7. package/bin/sign.js +8 -0
  8. package/bin/validate.js +8 -0
  9. package/bin/verify.js +8 -0
  10. package/data/container-knowledge-index.json +125 -0
  11. package/data/gtfobins-index.json +6296 -0
  12. package/data/lolbas-index.json +150 -0
  13. package/data/queries-darwin.json +63 -3
  14. package/data/queries-win.json +45 -3
  15. package/data/queries.json +74 -2
  16. package/data/rules/chrome-extensions.yaml +240 -0
  17. package/data/rules/ci-permissions.yaml +478 -18
  18. package/data/rules/container-risk.yaml +270 -0
  19. package/data/rules/obom-runtime.yaml +891 -0
  20. package/data/rules/package-integrity.yaml +49 -0
  21. package/data/spdx-export.schema.json +6794 -0
  22. package/data/spdx-model-v3.0.1.jsonld +15999 -0
  23. package/lib/audit/index.js +1924 -0
  24. package/lib/audit/index.poku.js +1488 -0
  25. package/lib/audit/progress.js +137 -0
  26. package/lib/audit/progress.poku.js +188 -0
  27. package/lib/audit/reporters.js +618 -0
  28. package/lib/audit/scoring.js +310 -0
  29. package/lib/audit/scoring.poku.js +341 -0
  30. package/lib/audit/targets.js +260 -0
  31. package/lib/audit/targets.poku.js +331 -0
  32. package/lib/cli/index.js +154 -11
  33. package/lib/cli/index.poku.js +251 -0
  34. package/lib/helpers/analyzer.js +446 -2
  35. package/lib/helpers/analyzer.poku.js +72 -1
  36. package/lib/helpers/annotationFormatter.js +49 -0
  37. package/lib/helpers/annotationFormatter.poku.js +44 -0
  38. package/lib/helpers/bomUtils.js +36 -0
  39. package/lib/helpers/bomUtils.poku.js +51 -0
  40. package/lib/helpers/caxa.js +2 -2
  41. package/lib/helpers/chromextutils.js +1153 -0
  42. package/lib/helpers/chromextutils.poku.js +493 -0
  43. package/lib/helpers/ciParsers/githubActions.js +1632 -45
  44. package/lib/helpers/ciParsers/githubActions.poku.js +853 -1
  45. package/lib/helpers/containerRisk.js +186 -0
  46. package/lib/helpers/containerRisk.poku.js +52 -0
  47. package/lib/helpers/display.js +241 -59
  48. package/lib/helpers/display.poku.js +162 -2
  49. package/lib/helpers/exportUtils.js +123 -0
  50. package/lib/helpers/exportUtils.poku.js +60 -0
  51. package/lib/helpers/formulationParsers.js +69 -0
  52. package/lib/helpers/formulationParsers.poku.js +44 -0
  53. package/lib/helpers/gtfobins.js +189 -0
  54. package/lib/helpers/gtfobins.poku.js +49 -0
  55. package/lib/helpers/lolbas.js +267 -0
  56. package/lib/helpers/lolbas.poku.js +39 -0
  57. package/lib/helpers/osqueryTransform.js +84 -0
  58. package/lib/helpers/osqueryTransform.poku.js +49 -0
  59. package/lib/helpers/provenanceUtils.js +193 -0
  60. package/lib/helpers/provenanceUtils.poku.js +145 -0
  61. package/lib/helpers/pylockutils.js +281 -0
  62. package/lib/helpers/pylockutils.poku.js +48 -0
  63. package/lib/helpers/registryProvenance.js +793 -0
  64. package/lib/helpers/registryProvenance.poku.js +452 -0
  65. package/lib/helpers/source.js +1267 -0
  66. package/lib/helpers/source.poku.js +771 -0
  67. package/lib/helpers/spdxUtils.js +97 -0
  68. package/lib/helpers/spdxUtils.poku.js +70 -0
  69. package/lib/helpers/unicodeScan.js +147 -0
  70. package/lib/helpers/unicodeScan.poku.js +45 -0
  71. package/lib/helpers/utils.js +700 -128
  72. package/lib/helpers/utils.poku.js +877 -80
  73. package/lib/managers/binary.js +29 -5
  74. package/lib/managers/docker.js +179 -52
  75. package/lib/managers/docker.poku.js +327 -28
  76. package/lib/managers/oci.js +107 -23
  77. package/lib/managers/oci.poku.js +132 -0
  78. package/lib/server/openapi.yaml +17 -0
  79. package/lib/server/server.js +225 -336
  80. package/lib/server/server.poku.js +16 -10
  81. package/lib/stages/postgen/annotator.js +7 -0
  82. package/lib/stages/postgen/annotator.poku.js +40 -0
  83. package/lib/stages/postgen/auditBom.js +19 -3
  84. package/lib/stages/postgen/auditBom.poku.js +1729 -67
  85. package/lib/stages/postgen/postgen.js +40 -0
  86. package/lib/stages/postgen/postgen.poku.js +47 -0
  87. package/lib/stages/postgen/ruleEngine.js +80 -2
  88. package/lib/stages/postgen/spdxConverter.js +796 -0
  89. package/lib/stages/postgen/spdxConverter.poku.js +341 -0
  90. package/lib/validator/bomValidator.js +232 -0
  91. package/lib/validator/bomValidator.poku.js +70 -0
  92. package/lib/validator/complianceRules.js +70 -7
  93. package/lib/validator/complianceRules.poku.js +30 -0
  94. package/lib/validator/reporters/annotations.js +2 -2
  95. package/lib/validator/reporters/console.js +11 -0
  96. package/lib/validator/reporters.poku.js +13 -0
  97. package/package.json +10 -7
  98. package/types/bin/audit.d.ts +3 -0
  99. package/types/bin/audit.d.ts.map +1 -0
  100. package/types/bin/convert.d.ts +3 -0
  101. package/types/bin/convert.d.ts.map +1 -0
  102. package/types/bin/repl.d.ts.map +1 -1
  103. package/types/lib/audit/index.d.ts +115 -0
  104. package/types/lib/audit/index.d.ts.map +1 -0
  105. package/types/lib/audit/progress.d.ts +27 -0
  106. package/types/lib/audit/progress.d.ts.map +1 -0
  107. package/types/lib/audit/reporters.d.ts +35 -0
  108. package/types/lib/audit/reporters.d.ts.map +1 -0
  109. package/types/lib/audit/scoring.d.ts +35 -0
  110. package/types/lib/audit/scoring.d.ts.map +1 -0
  111. package/types/lib/audit/targets.d.ts +63 -0
  112. package/types/lib/audit/targets.d.ts.map +1 -0
  113. package/types/lib/cli/index.d.ts +8 -0
  114. package/types/lib/cli/index.d.ts.map +1 -1
  115. package/types/lib/helpers/analyzer.d.ts +13 -0
  116. package/types/lib/helpers/analyzer.d.ts.map +1 -1
  117. package/types/lib/helpers/annotationFormatter.d.ts +23 -0
  118. package/types/lib/helpers/annotationFormatter.d.ts.map +1 -0
  119. package/types/lib/helpers/bomUtils.d.ts +5 -0
  120. package/types/lib/helpers/bomUtils.d.ts.map +1 -0
  121. package/types/lib/helpers/chromextutils.d.ts +97 -0
  122. package/types/lib/helpers/chromextutils.d.ts.map +1 -0
  123. package/types/lib/helpers/ciParsers/githubActions.d.ts +3 -8
  124. package/types/lib/helpers/ciParsers/githubActions.d.ts.map +1 -1
  125. package/types/lib/helpers/containerRisk.d.ts +17 -0
  126. package/types/lib/helpers/containerRisk.d.ts.map +1 -0
  127. package/types/lib/helpers/display.d.ts +4 -1
  128. package/types/lib/helpers/display.d.ts.map +1 -1
  129. package/types/lib/helpers/exportUtils.d.ts +40 -0
  130. package/types/lib/helpers/exportUtils.d.ts.map +1 -0
  131. package/types/lib/helpers/formulationParsers.d.ts.map +1 -1
  132. package/types/lib/helpers/gtfobins.d.ts +17 -0
  133. package/types/lib/helpers/gtfobins.d.ts.map +1 -0
  134. package/types/lib/helpers/lolbas.d.ts +16 -0
  135. package/types/lib/helpers/lolbas.d.ts.map +1 -0
  136. package/types/lib/helpers/osqueryTransform.d.ts +7 -0
  137. package/types/lib/helpers/osqueryTransform.d.ts.map +1 -0
  138. package/types/lib/helpers/provenanceUtils.d.ts +90 -0
  139. package/types/lib/helpers/provenanceUtils.d.ts.map +1 -0
  140. package/types/lib/helpers/pylockutils.d.ts +51 -0
  141. package/types/lib/helpers/pylockutils.d.ts.map +1 -0
  142. package/types/lib/helpers/registryProvenance.d.ts +17 -0
  143. package/types/lib/helpers/registryProvenance.d.ts.map +1 -0
  144. package/types/lib/helpers/source.d.ts +141 -0
  145. package/types/lib/helpers/source.d.ts.map +1 -0
  146. package/types/lib/helpers/spdxUtils.d.ts +2 -0
  147. package/types/lib/helpers/spdxUtils.d.ts.map +1 -0
  148. package/types/lib/helpers/unicodeScan.d.ts +46 -0
  149. package/types/lib/helpers/unicodeScan.d.ts.map +1 -0
  150. package/types/lib/helpers/utils.d.ts +29 -11
  151. package/types/lib/helpers/utils.d.ts.map +1 -1
  152. package/types/lib/managers/binary.d.ts.map +1 -1
  153. package/types/lib/managers/docker.d.ts.map +1 -1
  154. package/types/lib/managers/oci.d.ts.map +1 -1
  155. package/types/lib/server/server.d.ts +0 -36
  156. package/types/lib/server/server.d.ts.map +1 -1
  157. package/types/lib/stages/postgen/annotator.d.ts.map +1 -1
  158. package/types/lib/stages/postgen/auditBom.d.ts.map +1 -1
  159. package/types/lib/stages/postgen/postgen.d.ts.map +1 -1
  160. package/types/lib/stages/postgen/ruleEngine.d.ts.map +1 -1
  161. package/types/lib/stages/postgen/spdxConverter.d.ts +11 -0
  162. package/types/lib/stages/postgen/spdxConverter.d.ts.map +1 -0
  163. package/types/lib/validator/bomValidator.d.ts +1 -0
  164. package/types/lib/validator/bomValidator.d.ts.map +1 -1
  165. package/types/lib/validator/complianceRules.d.ts.map +1 -1
  166. package/types/lib/validator/reporters/console.d.ts.map +1 -1
  167. package/types/bin/dependencies.d.ts +0 -3
  168. package/types/bin/dependencies.d.ts.map +0 -1
  169. package/types/bin/licenses.d.ts +0 -3
  170. package/types/bin/licenses.d.ts.map +0 -1
@@ -5,16 +5,14 @@ import esmock from "esmock";
5
5
  import { afterEach, assert, beforeEach, describe, it } from "poku";
6
6
  import sinon from "sinon";
7
7
 
8
- import { isWin } from "../helpers/utils.js";
9
8
  import {
10
- getQueryParams,
11
9
  isAllowedHost,
12
10
  isAllowedPath,
13
11
  isAllowedWinPath,
14
- parseQueryString,
15
- parseValue,
16
12
  validateAndRejectGitSource,
17
- } from "./server.js";
13
+ } from "../helpers/source.js";
14
+ import { isWin } from "../helpers/utils.js";
15
+ import { getQueryParams, parseQueryString, parseValue } from "./server.js";
18
16
 
19
17
  function nullProtoObj(obj) {
20
18
  if (obj === null || typeof obj !== "object") {
@@ -130,6 +128,14 @@ describe("parseQueryString tests", () => {
130
128
  checkEqual(result.autoCreate, false);
131
129
  checkEqual(result.isLatest, true);
132
130
  });
131
+
132
+ it("parses format for SPDX export requests", () => {
133
+ const q = {
134
+ format: "spdx",
135
+ };
136
+ const result = parseQueryString(q, {}, {});
137
+ checkEqual(result.format, "spdx");
138
+ });
133
139
  });
134
140
 
135
141
  describe("isAllowedHost()", () => {
@@ -619,7 +625,7 @@ describe("gitClone() hardening tests", () => {
619
625
  .stub()
620
626
  .returns(path.join(os.tmpdir(), "fake-repo"));
621
627
 
622
- const { gitClone } = await esmock("./server.js", {
628
+ const { gitClone } = await esmock("../helpers/source.js", {
623
629
  "../helpers/utils.js": {
624
630
  safeSpawnSync: spawnStub,
625
631
  isSecureMode: false,
@@ -663,7 +669,7 @@ describe("gitClone() hardening tests", () => {
663
669
  .stub()
664
670
  .returns(path.join(os.tmpdir(), "fake-repo"));
665
671
 
666
- const { gitClone } = await esmock("./server.js", {
672
+ const { gitClone } = await esmock("../helpers/source.js", {
667
673
  "../helpers/utils.js": {
668
674
  safeSpawnSync: spawnStub,
669
675
  isSecureMode: true,
@@ -707,7 +713,7 @@ describe("gitClone() hardening tests", () => {
707
713
  .stub()
708
714
  .returns(path.join(os.tmpdir(), "fake-repo"));
709
715
 
710
- const { gitClone } = await esmock("./server.js", {
716
+ const { gitClone } = await esmock("../helpers/source.js", {
711
717
  "../helpers/utils.js": {
712
718
  safeSpawnSync: spawnStub,
713
719
  isSecureMode: secureMode,
@@ -740,7 +746,7 @@ describe("gitClone() hardening tests", () => {
740
746
  .stub()
741
747
  .returns(path.join(os.tmpdir(), "fake-repo"));
742
748
 
743
- const { gitClone } = await esmock("./server.js", {
749
+ const { gitClone } = await esmock("../helpers/source.js", {
744
750
  "../helpers/utils.js": {
745
751
  safeSpawnSync: spawnStub,
746
752
  isSecureMode: false,
@@ -770,7 +776,7 @@ describe("gitClone() hardening tests", () => {
770
776
  .stub()
771
777
  .returns(path.join(os.tmpdir(), "fake-repo"));
772
778
 
773
- const { gitClone } = await esmock("./server.js", {
779
+ const { gitClone } = await esmock("../helpers/source.js", {
774
780
  "../helpers/utils.js": {
775
781
  safeSpawnSync: spawnStub,
776
782
  isSecureMode: false,
@@ -2,6 +2,7 @@ import { readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
 
4
4
  import { thoughtLog } from "../../helpers/logger.js";
5
+ import { getTrustedPublishingComponentCounts } from "../../helpers/provenanceUtils.js";
5
6
  import { dirNameStr } from "../../helpers/utils.js";
6
7
 
7
8
  // Tags per BOM type.
@@ -295,6 +296,9 @@ export function textualMetadata(bomJson) {
295
296
  c?.purl?.startsWith("pkg:swid"),
296
297
  ).length;
297
298
  const githubStats = getGitHubWorkflowStats(bomJson?.components);
299
+ const trustedPublishingCounts = getTrustedPublishingComponentCounts(
300
+ bomJson?.components,
301
+ );
298
302
  const isGitHubBom = bomType === "SBOM";
299
303
  if (metadata?.timestamp) {
300
304
  text = `This ${bomTypeDescription} document was created on ${humanifyTimestamp(metadata.timestamp)}`;
@@ -469,6 +473,9 @@ export function textualMetadata(bomJson) {
469
473
  if (!isGitHubBom) {
470
474
  text = `${text} There are ${bomJson.components.length} components.`;
471
475
  }
476
+ if (trustedPublishingCounts.total > 0) {
477
+ text = `${text} Trusted publishing metadata is present for ${trustedPublishingCounts.npm} npm component(s) and ${trustedPublishingCounts.pypi} PyPI component(s).`;
478
+ }
472
479
  } else {
473
480
  text = `${text} BOM file is empty without components.`;
474
481
  thoughtLog(
@@ -263,6 +263,46 @@ it("textualMetadata tests", () => {
263
263
  }),
264
264
  "This Operations Bill-of-Materials (OBOM) document was created on Monday, November 11, 2024 with cdxgen. The lifecycles phases represented are: pre-build and operations. The document describes an operating system named 'Microsoft Windows 11 Pro' with version '22H2'. BOM file is empty without components. The OS uses the x64 architecture and has the build version '10.0.22621'.",
265
265
  );
266
+
267
+ assert.ok(
268
+ textualMetadata({
269
+ metadata: {
270
+ component: {
271
+ name: "trusted-demo",
272
+ type: "application",
273
+ version: "1.0.0",
274
+ },
275
+ timestamp: "2026-01-01T00:00:00Z",
276
+ tools: {
277
+ components: [{ name: "cdxgen" }],
278
+ },
279
+ },
280
+ components: [
281
+ {
282
+ name: "left-pad",
283
+ properties: [
284
+ {
285
+ name: "cdx:npm:trustedPublishing",
286
+ value: "true",
287
+ },
288
+ ],
289
+ type: "library",
290
+ },
291
+ {
292
+ name: "requests",
293
+ properties: [
294
+ {
295
+ name: "cdx:pypi:trustedPublishing",
296
+ value: "true",
297
+ },
298
+ ],
299
+ type: "library",
300
+ },
301
+ ],
302
+ }).includes(
303
+ "Trusted publishing metadata is present for 1 npm component(s) and 1 PyPI component(s).",
304
+ ),
305
+ );
266
306
  });
267
307
 
268
308
  it("extractTags tests", () => {
@@ -5,6 +5,7 @@
5
5
  import { join, resolve } from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
 
8
+ import { buildAnnotationText } from "../../helpers/annotationFormatter.js";
8
9
  import { table } from "../../helpers/table.js";
9
10
  import {
10
11
  DEBUG_MODE,
@@ -15,7 +16,6 @@ import { evaluateRules, loadRules } from "./ruleEngine.js";
15
16
 
16
17
  const __dirname = fileURLToPath(new URL(".", import.meta.url));
17
18
  const BUILTIN_RULES_DIR = join(__dirname, "..", "..", "..", "data", "rules");
18
- const CODE_BLOCK = "```";
19
19
 
20
20
  /**
21
21
  * Audit BOM formulation section using JSONata-powered rule engine
@@ -89,6 +89,7 @@ export function formatConsoleOutput(findings) {
89
89
  columnDefault: { wrapWord: true, width: 100 },
90
90
  columns: [
91
91
  { width: 10 },
92
+ { width: 26 },
92
93
  { width: 35 },
93
94
  { width: 50 },
94
95
  { width: 50 },
@@ -99,10 +100,13 @@ export function formatConsoleOutput(findings) {
99
100
  content: "BOM Audit Findings\nGenerated with \u2665 by cdxgen",
100
101
  },
101
102
  };
102
- const data = [["Rule", "Message", "Description", "Ref", "File"]];
103
+ const data = [["Rule", "ATT&CK", "Message", "Description", "Ref", "File"]];
103
104
  for (const f of findings) {
104
105
  const line = [];
105
106
  line.push(f.ruleId);
107
+ line.push(
108
+ [...(f.attackTactics || []), ...(f.attackTechniques || [])].join(", "),
109
+ );
106
110
  line.push(f.message);
107
111
  line.push(f.description || "");
108
112
  line.push(f.location?.purl || f.location?.bomRef || "");
@@ -143,6 +147,18 @@ export function formatAnnotations(findings, bomJson) {
143
147
  if (f.mitigation) {
144
148
  properties.push({ name: "cdx:audit:mitigation", value: f.mitigation });
145
149
  }
150
+ if (f.attackTactics?.length) {
151
+ properties.push({
152
+ name: "cdx:audit:attack:tactics",
153
+ value: f.attackTactics.join(","),
154
+ });
155
+ }
156
+ if (f.attackTechniques?.length) {
157
+ properties.push({
158
+ name: "cdx:audit:attack:techniques",
159
+ value: f.attackTechniques.join(","),
160
+ });
161
+ }
146
162
  if (f?.location?.purl) {
147
163
  properties.push({
148
164
  name: "cdx:audit:location:purl",
@@ -177,7 +193,7 @@ export function formatAnnotations(findings, bomJson) {
177
193
  component: cdxgenAnnotator[0],
178
194
  },
179
195
  timestamp: getTimestamp(),
180
- text: `${f.message}\n${CODE_BLOCK}\n${JSON.stringify(properties)}\n${CODE_BLOCK}`,
196
+ text: buildAnnotationText(f.message, properties),
181
197
  };
182
198
  });
183
199
  }