@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
@@ -7,8 +7,11 @@
7
7
  description: "GitHub Actions referenced by tag/branch in workflows with write permissions pose supply chain risk"
8
8
  severity: high
9
9
  category: ci-permission
10
+ attack:
11
+ tactics: [TA0001, TA0004]
12
+ techniques: [T1195.001]
10
13
  condition: |
11
- components[
14
+ $auditComponents($)[
12
15
  $prop($, 'cdx:github:action:isShaPinned') = 'false'
13
16
  and (
14
17
  $prop($, 'cdx:github:workflow:hasWritePermissions') = 'true'
@@ -31,30 +34,45 @@
31
34
  }
32
35
  - id: CI-002
33
36
  name: "OIDC token issuance to non-official action"
34
- description: "Workflows granting id-token:write to third-party actions may enable token exfiltration"
37
+ description: "Workflows or jobs granting id-token:write to third-party actions may enable token exfiltration"
35
38
  severity: high
36
39
  category: ci-permission
40
+ attack:
41
+ tactics: [TA0006]
42
+ techniques: [T1528]
37
43
  condition: |
38
- components[
39
- $prop($, 'cdx:github:workflow:hasIdTokenWrite') = 'true'
44
+ $auditComponents($)[
45
+ (
46
+ $prop($, 'cdx:github:workflow:hasIdTokenWrite') = 'true'
47
+ or $prop($, 'cdx:github:job:hasIdTokenWrite') = 'true'
48
+ )
40
49
  and $prop($, 'cdx:actions:isOfficial') = 'false'
41
50
  ]
42
51
  location: |
43
- { "bomRef": $."bom-ref", "purl": purl }
52
+ {
53
+ "bomRef": $."bom-ref",
54
+ "purl": purl,
55
+ "file": $prop($, 'cdx:github:workflow:file')
56
+ }
44
57
  message: "Workflow grants OIDC token access to non-official action '{{ $prop($, 'cdx:github:action:uses') }}'"
45
58
  mitigation: "Restrict id-token scope or use only verified/official actions for OIDC operations"
46
59
  evidence: |
47
60
  {
48
61
  "isVerified": $prop($, 'cdx:actions:isVerified'),
49
- "actionGroup": group
62
+ "actionGroup": group,
63
+ "jobHasIdTokenWrite": $prop($, 'cdx:github:job:hasIdTokenWrite'),
64
+ "workflowHasIdTokenWrite": $prop($, 'cdx:github:workflow:hasIdTokenWrite')
50
65
  }
51
66
  - id: CI-003
52
67
  name: "Action pinned to mutable tag"
53
68
  description: "GitHub Actions pinned to tags (vs SHA) can change behavior unexpectedly if tag is moved"
54
69
  severity: medium
55
70
  category: ci-permission
71
+ attack:
72
+ tactics: [TA0001, TA0005]
73
+ techniques: [T1195.001]
56
74
  condition: |
57
- components[
75
+ $auditComponents($)[
58
76
  $prop($, 'cdx:github:action:versionPinningType') = 'tag'
59
77
  ]
60
78
  location: |
@@ -71,11 +89,11 @@
71
89
  description: "pull_request_target can execute code in the context of the base branch, risking secret exposure"
72
90
  severity: medium
73
91
  category: ci-permission
74
- # Check workflows (not components) for this trigger pattern
92
+ attack:
93
+ tactics: [TA0001, TA0004]
75
94
  condition: |
76
- formulation.workflows[
77
- $hasProp($, 'cdx:github:workflow:triggers')
78
- and $nullSafeProp($, 'cdx:github:workflow:triggers') ~> $trim() ~> $contains('pull_request_target')
95
+ $auditWorkflows($)[
96
+ $prop($, 'cdx:github:workflow:hasPullRequestTargetTrigger') = 'true'
79
97
  ]
80
98
  location: |
81
99
  {
@@ -94,8 +112,11 @@
94
112
  description: "actions/checkout with persist-credentials=true (default) exposes GITHUB_TOKEN to subsequent steps"
95
113
  severity: medium
96
114
  category: ci-permission
115
+ attack:
116
+ tactics: [TA0004, TA0006]
117
+ techniques: [T1552]
97
118
  condition: |
98
- components[
119
+ $auditComponents($)[
99
120
  $contains($nullSafeProp($, 'cdx:github:action:uses'), 'actions/checkout')
100
121
  and $propBool($, 'cdx:github:checkout:persistCredentials') != false
101
122
  and (
@@ -121,10 +142,22 @@
121
142
  description: "GitHub Actions cache can be poisoned when used in workflows triggered by untrusted input (e.g., pull_request from forks)"
122
143
  severity: high
123
144
  category: ci-permission
145
+ attack:
146
+ tactics: [TA0001, TA0005]
147
+ techniques: [T1195.001]
124
148
  condition: |
125
- components[
149
+ $auditComponents($)[
126
150
  $nullSafeProp($, 'cdx:github:action:uses') ~> $contains('actions/cache')
127
- and $nullSafeProp($, 'cdx:github:workflow:triggers') ~> $contains('pull_request')
151
+ and (
152
+ $prop($, 'cdx:github:workflow:hasPullRequestTrigger') = 'true'
153
+ or $prop($, 'cdx:github:workflow:hasPullRequestTargetTrigger') = 'true'
154
+ )
155
+ and (
156
+ $propBool($, 'cdx:github:workflow:hasWritePermissions') = true
157
+ or $propBool($, 'cdx:github:job:hasWritePermissions') = true
158
+ or $prop($, 'cdx:github:cache:hasRestoreKeys') = 'true'
159
+ or $prop($, 'cdx:github:cache:keyUsesHashFiles') != 'true'
160
+ )
128
161
  ]
129
162
  location: |
130
163
  {
@@ -132,11 +165,13 @@
132
165
  "purl": purl,
133
166
  "file": $prop($, 'cdx:github:workflow:file')
134
167
  }
135
- message: "Cache action used in pull_request-triggered workflow; cache key '{{ $prop($, 'cdx:github:cache:key') }}' may be writable by untrusted code"
168
+ message: "Cache action used in pull-request-reachable workflow; cache key '{{ $prop($, 'cdx:github:cache:key') }}' may be writable by untrusted code"
136
169
  mitigation: "Scope cache keys to PR-specific values, validate restored cache contents, or avoid caching in untrusted workflows"
137
170
  evidence: |
138
171
  {
139
172
  "cacheKey": $prop($, 'cdx:github:cache:key'),
173
+ "hasRestoreKeys": $prop($, 'cdx:github:cache:hasRestoreKeys'),
174
+ "keyUsesHashFiles": $prop($, 'cdx:github:cache:keyUsesHashFiles'),
140
175
  "cachePath": $prop($, 'cdx:github:cache:path'),
141
176
  "triggers": $prop($, 'cdx:github:workflow:triggers')
142
177
  }
@@ -145,8 +180,11 @@
145
180
  description: "Direct interpolation of github.event.* or inputs.* into run: blocks enables command injection"
146
181
  severity: critical
147
182
  category: ci-permission
183
+ attack:
184
+ tactics: [TA0002, TA0004]
185
+ techniques: [T1059]
148
186
  condition: |
149
- formulation.components[
187
+ $auditComponents($)[
150
188
  $prop($, 'cdx:github:step:hasUntrustedInterpolation') = 'true'
151
189
  and $prop($, 'cdx:github:step:type') = 'run'
152
190
  ]
@@ -167,9 +205,15 @@
167
205
  description: "Triggers like pull_request_target, issue_comment, or workflow_run combined with write permissions enable privilege escalation"
168
206
  severity: high
169
207
  category: ci-permission
208
+ attack:
209
+ tactics: [TA0001, TA0004]
170
210
  condition: |
171
- formulation.workflows[
172
- $prop($, 'cdx:github:workflow:hasHighRiskTrigger') = 'true'
211
+ $auditWorkflows($)[
212
+ (
213
+ $prop($, 'cdx:github:workflow:hasPullRequestTargetTrigger') = 'true'
214
+ or $prop($, 'cdx:github:workflow:hasIssueCommentTrigger') = 'true'
215
+ or $prop($, 'cdx:github:workflow:hasWorkflowRunTrigger') = 'true'
216
+ )
173
217
  and $prop($, 'cdx:github:workflow:hasWritePermissions') = 'true'
174
218
  ]
175
219
  location: |
@@ -184,3 +228,419 @@
184
228
  "triggers": $prop($, 'cdx:github:workflow:triggers'),
185
229
  "hasWritePermissions": $prop($, 'cdx:github:workflow:hasWritePermissions')
186
230
  }
231
+
232
+ - id: CI-009
233
+ name: "Workflow file contains hidden Unicode characters"
234
+ description: "Hidden Unicode in workflow files can disguise malicious logic, comments, or diffs and should be reviewed before merge"
235
+ severity: medium
236
+ category: ci-permission
237
+ attack:
238
+ tactics: [TA0005]
239
+ techniques: [T1027]
240
+ condition: |
241
+ $auditWorkflows($)[
242
+ $prop($, 'cdx:github:workflow:hasHiddenUnicode') = 'true'
243
+ ]
244
+ location: |
245
+ {
246
+ "bomRef": $."bom-ref",
247
+ "file": $prop($, 'cdx:github:workflow:file')
248
+ }
249
+ message: "Workflow '{{ $prop($, 'cdx:github:workflow:name') }}' contains hidden Unicode characters"
250
+ mitigation: "Review the file in an editor that reveals bidirectional, zero-width, and control characters; remove suspicious hidden Unicode before merge"
251
+ evidence: |
252
+ {
253
+ "codePoints": $prop($, 'cdx:github:workflow:hiddenUnicodeCodePoints'),
254
+ "lineNumbers": $prop($, 'cdx:github:workflow:hiddenUnicodeLineNumbers'),
255
+ "inComments": $prop($, 'cdx:github:workflow:hiddenUnicodeInComments')
256
+ }
257
+
258
+ - id: CI-010
259
+ name: "Legacy token-based package publishing step"
260
+ description: "npm and PyPI publishing should prefer trusted publishing or OIDC-backed flows instead of long-lived token secrets or explicit --token arguments"
261
+ severity: medium
262
+ category: ci-permission
263
+ attack:
264
+ tactics: [TA0006, TA0010]
265
+ techniques: [T1528]
266
+ condition: |
267
+ $auditComponents($)[
268
+ $prop($, 'cdx:github:step:isPublishCommand') = 'true'
269
+ and $prop($, 'cdx:github:step:usesLegacyPublishToken') = 'true'
270
+ ]
271
+ location: |
272
+ {
273
+ "bomRef": $."bom-ref",
274
+ "file": $prop($, 'cdx:github:workflow:file')
275
+ }
276
+ message: "Workflow publish step '{{ name }}' uses legacy {{ $prop($, 'cdx:github:step:publishEcosystem') }} token-based publishing"
277
+ mitigation: "Prefer trusted publishing or short-lived OIDC-backed release flows instead of persistent package tokens or explicit --token arguments"
278
+ evidence: |
279
+ {
280
+ "ecosystem": $prop($, 'cdx:github:step:publishEcosystem'),
281
+ "tokenSources": $prop($, 'cdx:github:step:legacyPublishTokenSources'),
282
+ "stepCommand": $prop($, 'cdx:github:step:command')
283
+ }
284
+
285
+ - id: CI-011
286
+ name: "External reusable workflow inherits caller secrets"
287
+ description: "Reusable workflows invoked from external repositories with secrets: inherit expand the trust boundary and can expose repository credentials"
288
+ severity: high
289
+ category: ci-permission
290
+ attack:
291
+ tactics: [TA0006, TA0008]
292
+ techniques: [T1528, T1552]
293
+ condition: |
294
+ $auditComponents($)[
295
+ $prop($, 'cdx:github:reusableWorkflow:isExternal') = 'true'
296
+ and $prop($, 'cdx:github:reusableWorkflow:secretsInherit') = 'true'
297
+ ]
298
+ location: |
299
+ {
300
+ "bomRef": $."bom-ref",
301
+ "purl": purl,
302
+ "file": $prop($, 'cdx:github:workflow:file')
303
+ }
304
+ message: "Reusable workflow '{{ $prop($, 'cdx:github:reusableWorkflow:uses') }}' inherits caller secrets from an external repository"
305
+ mitigation: "Avoid secrets: inherit for external reusable workflows; pass only the minimum explicit secrets and pin the reusable workflow to a full SHA"
306
+ evidence: |
307
+ {
308
+ "uses": $prop($, 'cdx:github:reusableWorkflow:uses'),
309
+ "isShaPinned": $prop($, 'cdx:github:reusableWorkflow:isShaPinned'),
310
+ "withKeys": $prop($, 'cdx:github:reusableWorkflow:withKeys')
311
+ }
312
+
313
+ - id: CI-012
314
+ name: "External reusable workflow pinned to mutable ref"
315
+ description: "Reusable workflows referenced by tag or branch can change behavior without review and should be pinned to immutable SHAs"
316
+ severity: medium
317
+ category: ci-permission
318
+ attack:
319
+ tactics: [TA0001, TA0005]
320
+ techniques: [T1195.001]
321
+ condition: |
322
+ $auditComponents($)[
323
+ $prop($, 'cdx:github:reusableWorkflow:isExternal') = 'true'
324
+ and $prop($, 'cdx:github:reusableWorkflow:isShaPinned') = 'false'
325
+ ]
326
+ location: |
327
+ {
328
+ "bomRef": $."bom-ref",
329
+ "purl": purl,
330
+ "file": $prop($, 'cdx:github:workflow:file')
331
+ }
332
+ message: "External reusable workflow '{{ $prop($, 'cdx:github:reusableWorkflow:uses') }}' is pinned to a mutable ref"
333
+ mitigation: "Pin reusable workflows to full SHAs and review updates through a normal change-management process"
334
+ evidence: |
335
+ {
336
+ "pinningType": $prop($, 'cdx:github:reusableWorkflow:versionPinningType'),
337
+ "ref": $prop($, 'cdx:github:reusableWorkflow:ref')
338
+ }
339
+
340
+ - id: CI-013
341
+ name: "High-risk trigger reaches a self-hosted runner"
342
+ description: "High-risk triggers executing on self-hosted runners can expose internal network access, credentials, and long-lived runner state"
343
+ severity: high
344
+ category: ci-permission
345
+ attack:
346
+ tactics: [TA0004, TA0008]
347
+ condition: |
348
+ $auditComponents($)[
349
+ (
350
+ $prop($, 'cdx:github:workflow:hasPullRequestTargetTrigger') = 'true'
351
+ or $prop($, 'cdx:github:workflow:hasIssueCommentTrigger') = 'true'
352
+ or $prop($, 'cdx:github:workflow:hasWorkflowRunTrigger') = 'true'
353
+ )
354
+ and $prop($, 'cdx:github:job:isSelfHosted') = 'true'
355
+ ]
356
+ location: |
357
+ {
358
+ "bomRef": $."bom-ref",
359
+ "purl": purl,
360
+ "file": $prop($, 'cdx:github:workflow:file')
361
+ }
362
+ message: "High-risk workflow trigger reaches self-hosted runner in job '{{ $prop($, 'cdx:github:job:name') }}'"
363
+ mitigation: "Restrict self-hosted runners to trusted workflows only, isolate runner credentials, and gate high-risk triggers with manual approval"
364
+ evidence: |
365
+ {
366
+ "triggers": $prop($, 'cdx:github:workflow:triggers'),
367
+ "jobRunner": $prop($, 'cdx:github:job:runner'),
368
+ "jobName": $prop($, 'cdx:github:job:name')
369
+ }
370
+
371
+ - id: CI-014
372
+ name: "Privileged workflow mutates downstream runner state"
373
+ description: "Writing to GITHUB_ENV, GITHUB_PATH, or GITHUB_OUTPUT in privileged workflows can persist attacker-controlled state across later steps and jobs"
374
+ severity: high
375
+ category: ci-permission
376
+ attack:
377
+ tactics: [TA0003, TA0004, TA0005]
378
+ techniques: [T1059]
379
+ condition: |
380
+ $auditComponents($)[
381
+ $prop($, 'cdx:github:step:type') = 'run'
382
+ and $prop($, 'cdx:github:step:mutatesRunnerState') = 'true'
383
+ and (
384
+ $prop($, 'cdx:github:workflow:hasWritePermissions') = 'true'
385
+ or $prop($, 'cdx:github:job:hasWritePermissions') = 'true'
386
+ or $prop($, 'cdx:github:workflow:hasIdTokenWrite') = 'true'
387
+ or $prop($, 'cdx:github:job:hasIdTokenWrite') = 'true'
388
+ )
389
+ ]
390
+ location: |
391
+ {
392
+ "bomRef": $."bom-ref",
393
+ "file": $prop($, 'cdx:github:workflow:file')
394
+ }
395
+ message: "Privileged workflow step '{{ name }}' mutates runner state via '{{ $prop($, 'cdx:github:step:runnerStateTargets') }}'"
396
+ mitigation: "Avoid mutating shared runner state in privileged jobs; prefer tightly-scoped step outputs and review all GITHUB_ENV/GITHUB_PATH/GITHUB_OUTPUT writes"
397
+ evidence: |
398
+ {
399
+ "runnerStateTargets": $prop($, 'cdx:github:step:runnerStateTargets'),
400
+ "stepCommand": $prop($, 'cdx:github:step:command'),
401
+ "jobName": $prop($, 'cdx:github:job:name')
402
+ }
403
+
404
+ - id: CI-015
405
+ name: "Outbound network command references sensitive context"
406
+ description: "Run steps that invoke outbound network tools while transmitting secrets, github.token, or OIDC request context are strong exfiltration indicators"
407
+ severity: high
408
+ category: ci-permission
409
+ attack:
410
+ tactics: [TA0006, TA0010]
411
+ techniques: [T1048]
412
+ condition: |
413
+ $auditComponents($)[
414
+ $prop($, 'cdx:github:step:type') = 'run'
415
+ and $prop($, 'cdx:github:step:hasOutboundNetworkCommand') = 'true'
416
+ and $prop($, 'cdx:github:step:referencesSensitiveContext') = 'true'
417
+ and $prop($, 'cdx:github:step:likelyExfiltration') = 'true'
418
+ ]
419
+ location: |
420
+ {
421
+ "bomRef": $."bom-ref",
422
+ "file": $prop($, 'cdx:github:workflow:file')
423
+ }
424
+ message: "Outbound command in step '{{ name }}' references sensitive context '{{ $prop($, 'cdx:github:step:sensitiveContextRefs') }}'"
425
+ mitigation: "Review outbound network steps for secret or token use, disable unnecessary network egress, and move sensitive values into least-privileged isolated jobs"
426
+ evidence: |
427
+ {
428
+ "networkTools": $prop($, 'cdx:github:step:outboundNetworkTools'),
429
+ "sensitiveContextRefs": $prop($, 'cdx:github:step:sensitiveContextRefs'),
430
+ "exfiltrationIndicators": $prop($, 'cdx:github:step:exfiltrationIndicators'),
431
+ "stepCommand": $prop($, 'cdx:github:step:command')
432
+ }
433
+
434
+ - id: CI-016
435
+ name: "Privileged reusable workflow accepts caller secrets"
436
+ description: "workflow_call producers that request caller-provided secrets while also holding write or OIDC permissions expand the blast radius across repositories and workflows"
437
+ severity: high
438
+ category: ci-permission
439
+ attack:
440
+ tactics: [TA0006, TA0008]
441
+ techniques: [T1528, T1552]
442
+ condition: |
443
+ $auditWorkflows($)[
444
+ $prop($, 'cdx:github:workflow:isWorkflowCallProducer') = 'true'
445
+ and $safeStr($prop($, 'cdx:github:workflow:workflowCallSecrets')) != ''
446
+ and (
447
+ $propBool($, 'cdx:github:workflow:hasWritePermissions') = true
448
+ or $propBool($, 'cdx:github:workflow:hasIdTokenWrite') = true
449
+ )
450
+ ]
451
+ location: |
452
+ {
453
+ "bomRef": $."bom-ref",
454
+ "file": $prop($, 'cdx:github:workflow:file')
455
+ }
456
+ message: "Reusable workflow '{{ $prop($, 'cdx:github:workflow:name') }}' accepts caller secrets while running with privileged permissions"
457
+ mitigation: "Avoid broad secret interfaces on reusable workflows with write or OIDC permissions; split privileged work into a narrowly scoped follow-up job and require only explicit minimal secrets"
458
+ evidence: |
459
+ {
460
+ "workflowCallSecrets": $prop($, 'cdx:github:workflow:workflowCallSecrets'),
461
+ "writeScopes": $prop($, 'cdx:github:workflow:writeScopes'),
462
+ "hasWritePermissions": $prop($, 'cdx:github:workflow:hasWritePermissions'),
463
+ "hasIdTokenWrite": $prop($, 'cdx:github:workflow:hasIdTokenWrite')
464
+ }
465
+
466
+ - id: CI-017
467
+ name: "Privileged reusable workflow exports caller-influenced outputs"
468
+ description: "workflow_call producers that both accept caller-controlled inputs and emit outputs from privileged execution contexts can propagate unsafe values into downstream trusted jobs"
469
+ severity: medium
470
+ category: ci-permission
471
+ attack:
472
+ tactics: [TA0003, TA0004]
473
+ condition: |
474
+ $auditWorkflows($)[
475
+ $prop($, 'cdx:github:workflow:isWorkflowCallProducer') = 'true'
476
+ and $safeStr($prop($, 'cdx:github:workflow:workflowCallInputs')) != ''
477
+ and $safeStr($prop($, 'cdx:github:workflow:workflowCallOutputs')) != ''
478
+ and (
479
+ $propBool($, 'cdx:github:workflow:hasWritePermissions') = true
480
+ or $propBool($, 'cdx:github:workflow:hasIdTokenWrite') = true
481
+ )
482
+ ]
483
+ location: |
484
+ {
485
+ "bomRef": $."bom-ref",
486
+ "file": $prop($, 'cdx:github:workflow:file')
487
+ }
488
+ message: "Reusable workflow '{{ $prop($, 'cdx:github:workflow:name') }}' exports outputs from a privileged workflow_call interface that also accepts caller inputs"
489
+ mitigation: "Review workflow_call outputs for trust-boundary crossings, sanitize caller-controlled data before emitting outputs, and avoid combining privileged permissions with broad producer interfaces"
490
+ evidence: |
491
+ {
492
+ "workflowCallInputs": $prop($, 'cdx:github:workflow:workflowCallInputs'),
493
+ "workflowCallOutputs": $prop($, 'cdx:github:workflow:workflowCallOutputs'),
494
+ "hasWritePermissions": $prop($, 'cdx:github:workflow:hasWritePermissions'),
495
+ "hasIdTokenWrite": $prop($, 'cdx:github:workflow:hasIdTokenWrite')
496
+ }
497
+
498
+ - id: CI-018
499
+ name: "Fork-reachable workflow dispatches downstream workflow or repository events"
500
+ description: "Dispatching workflow_dispatch or repository_dispatch from fork-reachable or privileged jobs can create a lateral-movement path into downstream workflows with broader credentials"
501
+ severity: high
502
+ category: ci-permission
503
+ attack:
504
+ tactics: [TA0004, TA0008]
505
+ techniques: [T1528]
506
+ condition: |
507
+ $auditComponents($)[
508
+ $prop($, 'cdx:github:step:dispatchesWorkflow') = 'true'
509
+ and $prop($, 'cdx:github:step:referencesSensitiveContext') = 'true'
510
+ and (
511
+ $propBool($, 'cdx:github:workflow:hasPullRequestTrigger') = true
512
+ or $propBool($, 'cdx:github:workflow:hasPullRequestTargetTrigger') = true
513
+ or $propBool($, 'cdx:github:workflow:hasIssueCommentTrigger') = true
514
+ or $propBool($, 'cdx:github:workflow:hasWorkflowRunTrigger') = true
515
+ or $propBool($, 'cdx:github:workflow:hasWritePermissions') = true
516
+ or $propBool($, 'cdx:github:job:hasWritePermissions') = true
517
+ or $propBool($, 'cdx:github:workflow:hasIdTokenWrite') = true
518
+ or $propBool($, 'cdx:github:job:hasIdTokenWrite') = true
519
+ )
520
+ ]
521
+ location: |
522
+ {
523
+ "bomRef": $."bom-ref",
524
+ "purl": purl,
525
+ "file": $prop($, 'cdx:github:workflow:file')
526
+ }
527
+ message: "Workflow step '{{ name }}' dispatches '{{ $prop($, 'cdx:github:step:dispatchKinds') }}' toward '{{ $prop($, 'cdx:github:step:dispatchTargets') }}' from a fork-reachable or privileged context"
528
+ mitigation: "Avoid chaining downstream workflow_dispatch or repository_dispatch calls from pull-request/fork-reachable jobs; isolate release dispatchers into trusted workflows with narrowly scoped credentials"
529
+ evidence: |
530
+ {
531
+ "dispatchKinds": $prop($, 'cdx:github:step:dispatchKinds'),
532
+ "dispatchMechanisms": $prop($, 'cdx:github:step:dispatchMechanisms'),
533
+ "dispatchTargets": $prop($, 'cdx:github:step:dispatchTargets'),
534
+ "localReceiverWorkflowFiles": $prop($, 'cdx:github:step:dispatchReceiverWorkflowFiles'),
535
+ "localReceiverWorkflowNames": $prop($, 'cdx:github:step:dispatchReceiverWorkflowNames'),
536
+ "sensitiveContextRefs": $prop($, 'cdx:github:step:sensitiveContextRefs'),
537
+ "workflowTriggers": $prop($, 'cdx:github:workflow:triggers'),
538
+ "writeScopes": $prop($, 'cdx:github:workflow:writeScopes')
539
+ }
540
+
541
+ - id: CI-019
542
+ name: "Workflow dispatch step references explicit fork context"
543
+ description: "Dispatch chains that inspect pull_request head-repository or fork context before invoking downstream workflows are strong signals of fork-to-privileged lateral movement"
544
+ severity: critical
545
+ category: ci-permission
546
+ attack:
547
+ tactics: [TA0004, TA0008]
548
+ techniques: [T1528, T1552]
549
+ condition: |
550
+ $auditComponents($)[
551
+ $prop($, 'cdx:github:step:dispatchesWorkflow') = 'true'
552
+ and $prop($, 'cdx:github:step:referencesForkContext') = 'true'
553
+ and $prop($, 'cdx:github:step:referencesSensitiveContext') = 'true'
554
+ and (
555
+ $prop($, 'cdx:github:step:hasLocalDispatchReceiver') = 'true'
556
+ or $safeStr($prop($, 'cdx:github:step:hasLocalDispatchReceiver')) = ''
557
+ )
558
+ ]
559
+ location: |
560
+ {
561
+ "bomRef": $."bom-ref",
562
+ "purl": purl,
563
+ "file": $prop($, 'cdx:github:workflow:file')
564
+ }
565
+ message: "Workflow step '{{ name }}' combines explicit fork/head-repository context with downstream dispatch '{{ $safeStr($prop($, 'cdx:github:step:dispatchReceiverWorkflowNames')) != '' ? $prop($, 'cdx:github:step:dispatchReceiverWorkflowNames') : $prop($, 'cdx:github:step:dispatchTargets') }}'"
566
+ mitigation: "Do not route fork-derived context into workflow_dispatch or repository_dispatch calls. Split privileged follow-up workflows behind trusted branch-only triggers and avoid sharing long-lived tokens into fork-reachable jobs"
567
+ evidence: |
568
+ {
569
+ "forkContextRefs": $prop($, 'cdx:github:step:forkContextRefs'),
570
+ "dispatchKinds": $prop($, 'cdx:github:step:dispatchKinds'),
571
+ "dispatchTargets": $prop($, 'cdx:github:step:dispatchTargets'),
572
+ "hasLocalDispatchReceiver": $prop($, 'cdx:github:step:hasLocalDispatchReceiver'),
573
+ "localReceiverWorkflowFiles": $prop($, 'cdx:github:step:dispatchReceiverWorkflowFiles'),
574
+ "localReceiverWorkflowNames": $prop($, 'cdx:github:step:dispatchReceiverWorkflowNames'),
575
+ "localReceiverMatchBasis": $prop($, 'cdx:github:step:dispatchReceiverMatchBasis'),
576
+ "sensitiveContextRefs": $prop($, 'cdx:github:step:sensitiveContextRefs')
577
+ }
578
+
579
+ - id: CI-020
580
+ name: "pull_request_target checks out PR head or fork context"
581
+ description: "Checking out github.event.pull_request.head.* repository or ref inside pull_request_target executes untrusted fork code with base-repository privileges"
582
+ severity: critical
583
+ category: ci-permission
584
+ attack:
585
+ tactics: [TA0001, TA0004]
586
+ techniques: [T1195.001, T1552]
587
+ condition: |
588
+ $auditComponents($)[
589
+ $contains($nullSafeProp($, 'cdx:github:action:uses'), 'actions/checkout')
590
+ and $prop($, 'cdx:github:workflow:hasPullRequestTargetTrigger') = 'true'
591
+ and (
592
+ $prop($, 'cdx:github:checkout:checksOutUntrustedRef') = 'true'
593
+ or $prop($, 'cdx:github:checkout:referencesForkContext') = 'true'
594
+ )
595
+ ]
596
+ location: |
597
+ {
598
+ "bomRef": $."bom-ref",
599
+ "purl": purl,
600
+ "file": $prop($, 'cdx:github:workflow:file')
601
+ }
602
+ message: "Checkout step '{{ $prop($, 'cdx:github:action:uses') }}' pulls PR head or fork context inside pull_request_target"
603
+ mitigation: "Do not checkout github.event.pull_request.head.* in pull_request_target workflows. Prefer pull_request with read-only permissions or split trusted follow-up work into a separate branch-only workflow"
604
+ evidence: |
605
+ {
606
+ "checkoutRef": $prop($, 'cdx:github:checkout:ref'),
607
+ "checkoutRepository": $prop($, 'cdx:github:checkout:repository'),
608
+ "untrustedRefContexts": $prop($, 'cdx:github:checkout:untrustedRefContexts'),
609
+ "forkContextRefs": $prop($, 'cdx:github:checkout:forkContextRefs'),
610
+ "persistCredentials": $prop($, 'cdx:github:checkout:persistCredentials'),
611
+ "workflowHasWritePermissions": $prop($, 'cdx:github:workflow:hasWritePermissions')
612
+ }
613
+
614
+ - id: CI-021
615
+ name: "Heuristic: high-risk trigger with implicit permissions and sensitive operations"
616
+ description: "High-risk GitHub Actions workflows that omit explicit permissions blocks while still performing sensitive operations may rely on repository-default token scopes. This is a review heuristic, not proof of write access."
617
+ severity: medium
618
+ category: ci-permission
619
+ attack:
620
+ tactics: [TA0004, TA0006]
621
+ techniques: [T1528, T1552]
622
+ condition: |
623
+ $auditComponents($)[
624
+ $prop($, 'cdx:github:workflow:hasHighRiskTrigger') = 'true'
625
+ and $prop($, 'cdx:github:workflow:hasAnyExplicitPermissionsBlock') = 'false'
626
+ and $prop($, 'cdx:github:step:hasSensitiveOperations') = 'true'
627
+ ]
628
+ location: |
629
+ {
630
+ "bomRef": $."bom-ref",
631
+ "purl": purl,
632
+ "file": $prop($, 'cdx:github:workflow:file')
633
+ }
634
+ message: "Heuristic review: workflow '{{ $prop($, 'cdx:github:workflow:name') }}' uses a high-risk trigger without an explicit permissions block while step '{{ $safeStr($prop($, 'cdx:github:step:name')) != '' ? $prop($, 'cdx:github:step:name') : name }}' performs sensitive operation(s) '{{ $prop($, 'cdx:github:step:sensitiveOperations') }}'"
635
+ mitigation: "Add an explicit top-level permissions: block (for example permissions: {}) and re-grant only the minimum per-job scopes needed for the sensitive step"
636
+ evidence: |
637
+ {
638
+ "workflowTriggers": $prop($, 'cdx:github:workflow:triggers'),
639
+ "workflowHasExplicitPermissionsBlock": $prop($, 'cdx:github:workflow:hasExplicitPermissionsBlock'),
640
+ "workflowHasAnyExplicitPermissionsBlock": $prop($, 'cdx:github:workflow:hasAnyExplicitPermissionsBlock'),
641
+ "jobHasExplicitPermissionsBlock": $prop($, 'cdx:github:job:hasExplicitPermissionsBlock'),
642
+ "sensitiveOperations": $prop($, 'cdx:github:step:sensitiveOperations'),
643
+ "sensitiveContextRefs": $prop($, 'cdx:github:step:sensitiveContextRefs'),
644
+ "dispatchKinds": $prop($, 'cdx:github:step:dispatchKinds')
645
+ }
646
+