@conterra/vuln-scan 1.0.10 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +489 -609
- package/dist/add-project-to-vex.js +2 -2
- package/dist/add-project.js +18 -18
- package/dist/add-statement.js +20 -20
- package/dist/auto-vex-creator.js +5 -0
- package/dist/jira-reporter.js +7 -18
- package/dist/remove-project.js +19 -19
- package/dist/schema/auto-vex-config.schema.json +36 -0
- package/dist/schema/conf-schema.json +33 -2
- package/dist/schema/jira-config-schema.json +10 -0
- package/dist/vuln-scan.js +12 -12
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -1,609 +1,489 @@
|
|
|
1
|
-
# ct-vuln-scan
|
|
2
|
-
|
|
3
|
-
The utility is a wrapper around the following tools:
|
|
4
|
-
|
|
5
|
-
- [grype](https://github.com/anchore/grype) - Vulnerability scanner for container images and filesystems.
|
|
6
|
-
- [trivy](https://github.com/aquasecurity/trivy) - Comprehensive and versatile security scanner
|
|
7
|
-
- [oss index](https://ossindex.sonatype.org/) - Free catalogue of open source components and scanning tools to help developers identify vulnerabilities
|
|
8
|
-
|
|
9
|
-
It also supports the [OpenVex](https://github.com/openvex) specification for vulnerability status and justification.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
"
|
|
252
|
-
"
|
|
253
|
-
"
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
"
|
|
369
|
-
"
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
"
|
|
375
|
-
"
|
|
376
|
-
{
|
|
377
|
-
"
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
|
398
|
-
|
|
|
399
|
-
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
[
|
|
404
|
-
|
|
405
|
-
|
|
|
406
|
-
|
|
|
407
|
-
|
|
|
408
|
-
|
|
|
409
|
-
|
|
|
410
|
-
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
$ ct-vuln-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
$
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
$ ct-vuln-remove-project mapapps 4.18.3-SNAPSHOT
|
|
491
|
-
$ ct-vuln-add-project mapapps 4.18.3 4.18.4-SNAPSHOT
|
|
492
|
-
```
|
|
493
|
-
|
|
494
|
-
## Usage - Jira Report
|
|
495
|
-
|
|
496
|
-
The `ct-vuln-jira-report` command reads the `scan-summary.json` produced by `ct-vuln-scan` and creates Jira issues for every new vulnerability that has not already been reported.
|
|
497
|
-
|
|
498
|
-
For each vulnerability the tool:
|
|
499
|
-
|
|
500
|
-
1. Checks (via JQL) whether an issue with the same vulnerability ID already exists in the target Jira project.
|
|
501
|
-
2. If not, creates a new issue with a structured description including CVE details, affected artefacts, affected products, and placeholder sections for assessment and remediation.
|
|
502
|
-
|
|
503
|
-
### Environment Variables
|
|
504
|
-
|
|
505
|
-
```bash
|
|
506
|
-
# Required
|
|
507
|
-
JIRA_USER=user@example.com # Jira account e-mail address
|
|
508
|
-
JIRA_API_TOKEN=<token> # Jira API token (https://id.atlassian.com/manage-profile/security/api-tokens)
|
|
509
|
-
```
|
|
510
|
-
|
|
511
|
-
NOTE: These variables can also be placed in a `.env` file in the working directory.
|
|
512
|
-
|
|
513
|
-
### Configuration – `jira-conf.json`
|
|
514
|
-
|
|
515
|
-
Create a `jira-conf.json` file (path is configurable) that defines the Jira instance and the routing rules:
|
|
516
|
-
|
|
517
|
-
```json
|
|
518
|
-
{
|
|
519
|
-
"$schema": "./node_modules/@conterra/vuln-scan/dist/schema/jira-config-schema.json",
|
|
520
|
-
"jiraUrl": "https://myorg.atlassian.net",
|
|
521
|
-
"issues": [
|
|
522
|
-
{
|
|
523
|
-
"jiraProject": "PLATFORM",
|
|
524
|
-
"issueType": "CVE",
|
|
525
|
-
"issueLabels": ["security"],
|
|
526
|
-
"addToBoard": [
|
|
527
|
-
{
|
|
528
|
-
"sprintBoardId": 10,
|
|
529
|
-
"issueLabels": ["team-a"],
|
|
530
|
-
"matchProjects": ["mapapps@*"]
|
|
531
|
-
},
|
|
532
|
-
{
|
|
533
|
-
"issueLabels": ["team-b"],
|
|
534
|
-
"matchProjects": ["smartfinder@*"]
|
|
535
|
-
}
|
|
536
|
-
],
|
|
537
|
-
"reportProjects": ["mapapps@*", "smartfinder@4.*"]
|
|
538
|
-
},
|
|
539
|
-
{
|
|
540
|
-
"jiraProject": "OTHER",
|
|
541
|
-
"issueType": "Bug",
|
|
542
|
-
"reportProjects": ["*"]
|
|
543
|
-
}
|
|
544
|
-
]
|
|
545
|
-
}
|
|
546
|
-
```
|
|
547
|
-
|
|
548
|
-
| Field | Required | Description |
|
|
549
|
-
| ---------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
550
|
-
| `jiraUrl` | ✓ | Base URL of the Jira instance |
|
|
551
|
-
| `issues` | ✓ | List of routing rules |
|
|
552
|
-
| `issues[].jiraProject` | ✓ | Jira project key, e.g. `PLATFORM` |
|
|
553
|
-
| `issues[].issueType` | ✓ | Jira issue type name, e.g. `Bug`, `CVE` |
|
|
554
|
-
| `issues[].reportProjects` | ✓ | Glob patterns (`*` wildcard) for scan project identifiers (`name@version`) |
|
|
555
|
-
| `issues[].issueLabels` | – | Global labels attached to every issue created by this rule, merged with any labels from `addToBoard` entries |
|
|
556
|
-
| `issues[].addToBoard` | – | Per-board routing rules (see table below). Enables Scrum sprint assignment and/or Kanban label routing |
|
|
557
|
-
| `addToBoard[].sprintBoardId` | – | Scrum board ID. When set, the issue is assigned to the currently active sprint of that board via `customfield_10020`. Omit for Kanban boards. |
|
|
558
|
-
| `addToBoard[].issueLabels` | – | Labels contributed by this board entry (see routing rules below) |
|
|
559
|
-
| `addToBoard[].matchProjects` | – | Glob patterns to activate this entry. Defaults to `["*"]` (matches any project) |
|
|
560
|
-
|
|
561
|
-
**`addToBoard` routing rules:**
|
|
562
|
-
|
|
563
|
-
- Each entry in `addToBoard` is checked: its `matchProjects` patterns are tested against the scan projects affected by the current vulnerability. If none match, the entry is skipped.
|
|
564
|
-
- **Scrum (sprintBoardId set):** The **first** matching entry with a `sprintBoardId` claims the sprint. All subsequent matching sprint entries – including their labels – are ignored. This avoids ambiguous multi-sprint assignments, which Jira does not support.
|
|
565
|
-
- **Kanban (no sprintBoardId):** Matching entries always contribute their labels. This allows issues to appear on Kanban boards that filter by label.
|
|
566
|
-
- The final label set on the created issue is: `issueLabels` (top-level) + labels from the winning sprint entry (if any) + labels from all matching Kanban entries.
|
|
567
|
-
|
|
568
|
-
**`reportProjects` routing:** The `reportProjects` patterns are matched against each scan project identifier (`name@version`). The `*` wildcard matches any sequence of characters. Each routing rule is evaluated independently, so a vulnerability can be reported to multiple Jira projects. Rules that match no project are silently skipped.
|
|
569
|
-
|
|
570
|
-
### CLI Usage
|
|
571
|
-
|
|
572
|
-
```sh
|
|
573
|
-
$ ct-vuln-jira-report [options]
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
```
|
|
577
|
-
Options:
|
|
578
|
-
-c, --jira-config <path> Path to jira-conf.json configuration file
|
|
579
|
-
(default: ./jira-conf.json)
|
|
580
|
-
-s, --summary <path> Path to scan-summary.json produced by ct-vuln-scan
|
|
581
|
-
(default: ./output/scan-summary.json)
|
|
582
|
-
--dry-run Print what would be created without calling Jira
|
|
583
|
-
-h, --help Show help
|
|
584
|
-
```
|
|
585
|
-
|
|
586
|
-
Examples:
|
|
587
|
-
|
|
588
|
-
```sh
|
|
589
|
-
# Use defaults (./jira-conf.json + ./output/scan-summary.json)
|
|
590
|
-
$ ct-vuln-jira-report
|
|
591
|
-
|
|
592
|
-
# Custom paths
|
|
593
|
-
$ ct-vuln-jira-report --jira-config ./config/jira-conf.json --summary ./output/scan-summary.json
|
|
594
|
-
|
|
595
|
-
# Preview without creating issues
|
|
596
|
-
$ ct-vuln-jira-report --dry-run
|
|
597
|
-
```
|
|
598
|
-
|
|
599
|
-
## Dev Notes
|
|
600
|
-
|
|
601
|
-
Setup:
|
|
602
|
-
|
|
603
|
-
1. Checkout via git
|
|
604
|
-
2. Run `pnpm install`
|
|
605
|
-
|
|
606
|
-
Run tests (vitest): `pnpm test` (watch mode)
|
|
607
|
-
To run tests once (without watch), use `pnpm test run`.
|
|
608
|
-
|
|
609
|
-
Build the project (esbuild): `pnpm run build` (or `watch`)
|
|
1
|
+
# ct-vuln-scan
|
|
2
|
+
|
|
3
|
+
The utility is a wrapper around the following tools:
|
|
4
|
+
|
|
5
|
+
- [grype](https://github.com/anchore/grype) - Vulnerability scanner for container images and filesystems.
|
|
6
|
+
- [trivy](https://github.com/aquasecurity/trivy) - Comprehensive and versatile security scanner.
|
|
7
|
+
- [oss index](https://ossindex.sonatype.org/) - Free catalogue of open source components and scanning tools to help developers identify vulnerabilities.
|
|
8
|
+
|
|
9
|
+
It also supports the [OpenVex](https://github.com/openvex) specification for vulnerability status and justification.
|
|
10
|
+
|
|
11
|
+
For an architectural overview see [ARCHITECTURE.md](./ARCHITECTURE.md).
|
|
12
|
+
For build, test and contribution guidelines see [AGENTS.md](./AGENTS.md).
|
|
13
|
+
|
|
14
|
+
## Pre-Requisites
|
|
15
|
+
|
|
16
|
+
You need to have `docker` and `node` (>= 20) installed on your machine.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
The utility is published as an npm package and can be installed globally or locally.
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
# global
|
|
24
|
+
$ npm install -g @conterra/vuln-scan
|
|
25
|
+
|
|
26
|
+
# local
|
|
27
|
+
$ npm install @conterra/vuln-scan
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
After installation the following commands are available:
|
|
31
|
+
|
|
32
|
+
- `ct-vuln-scan` - Triggers the vulnerability scan.
|
|
33
|
+
- `ct-vuln-add-vex` - Interactive helper to add a new vex statement to a file.
|
|
34
|
+
- `ct-vuln-add-project` - Adds a new project to the configuration file based on an existing project entry and adds it to all vex statements.
|
|
35
|
+
- `ct-vuln-remove-project` - Removes a project from the configuration file and all vex statements (or only from vex statements matching a given vulnerability id).
|
|
36
|
+
- `ct-vuln-add-project-to-vex` - Updates vex files to include a new project version. Already part of `ct-vuln-add-project`.
|
|
37
|
+
- `ct-vuln-jira-report` - Reads a `scan-summary.json` and creates Jira issues for newly detected vulnerabilities.
|
|
38
|
+
- `ct-vuln-auto-vex` - Auto-creates OpenVEX statements for newly detected vulnerabilities below a configurable severity threshold.
|
|
39
|
+
|
|
40
|
+
## Configuration
|
|
41
|
+
|
|
42
|
+
Create a `vuln-scan-conf.json` file in the working directory.
|
|
43
|
+
|
|
44
|
+
Minimal configuration:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"$schema": "./node_modules/@conterra/vuln-scan/dist/schema/conf-schema.json",
|
|
49
|
+
"projects": [
|
|
50
|
+
{
|
|
51
|
+
"name": "mapapps",
|
|
52
|
+
"version": "4.18.2",
|
|
53
|
+
"purl": "pkg:maven/de.conterra.mapapps/ct-mapapps@4.18.2",
|
|
54
|
+
"sbomFile": "./input/sboms/mapapps-4.18.2.cdx.json"
|
|
55
|
+
}
|
|
56
|
+
]
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Full configuration with all available options and their defaults:
|
|
61
|
+
|
|
62
|
+
```jsonc
|
|
63
|
+
{
|
|
64
|
+
"$schema": "./node_modules/@conterra/vuln-scan/dist/schema/conf-schema.json",
|
|
65
|
+
|
|
66
|
+
// Action on newly detected vulnerabilities: "fail" | "warn" | "ignore"
|
|
67
|
+
// - "fail": exits with code 1
|
|
68
|
+
// - "warn": logs ##vso[task.complete result=SucceededWithIssues;]
|
|
69
|
+
// - "ignore": no effect
|
|
70
|
+
"onNewVulnerabilities": "fail",
|
|
71
|
+
// Action on vulnerabilities no longer detected, same values as above.
|
|
72
|
+
"onNoLongerDetectedVulnerabilities": "ignore",
|
|
73
|
+
// Maven repository to fetch sboms from.
|
|
74
|
+
// Set MAVEN_REPO_USER / MAVEN_REPO_PW for authentication.
|
|
75
|
+
"mavenRepo": "https://repository.conterra.de/repository/maven-mirror-ct",
|
|
76
|
+
// Scanners to use. Pin a version with `<scanner>@<version>`, e.g. `trivy@0.69.3`.
|
|
77
|
+
"scanners": ["grype", "trivy", "ossindex"],
|
|
78
|
+
// (optional) URLs of additional vex files to download.
|
|
79
|
+
"vexUrls": ["https://example.com/my-vex.json"],
|
|
80
|
+
// Directory containing vex files.
|
|
81
|
+
"vexDir": "./input/vex",
|
|
82
|
+
// Directory for scan output.
|
|
83
|
+
"outDir": "./output",
|
|
84
|
+
// Cache directory for grype/trivy databases.
|
|
85
|
+
"cacheDir": "./cache",
|
|
86
|
+
// If true, results for each project are written to a sub-directory of outDir.
|
|
87
|
+
"createSubDirsForProject": true,
|
|
88
|
+
// Console output style:
|
|
89
|
+
// "BY_PROJECT_DETAILED" (default) - group by project, print CVE details
|
|
90
|
+
// "BY_PROJECT" - group by project, no CVE details
|
|
91
|
+
// "BY_CVE" - group by CVE, no CVE details
|
|
92
|
+
// "BY_CVE_DETAILED" - group by CVE, print CVE details
|
|
93
|
+
"consoleReport": "BY_PROJECT_DETAILED",
|
|
94
|
+
"projects": [
|
|
95
|
+
{
|
|
96
|
+
"name": "mapapps",
|
|
97
|
+
"version": "4.18.3",
|
|
98
|
+
// identifier used to match vex statements
|
|
99
|
+
"purl": "pkg:maven/de.conterra.mapapps/ct-mapapps@4.18.3",
|
|
100
|
+
// maven coordinates used to download the sbom from mavenRepo
|
|
101
|
+
"sbomMavenCoordinates": "de.conterra.mapapps:ct-mapapps-rollout:4.18.3:sbom"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"name": "mapapps",
|
|
105
|
+
"version": "4.18.2",
|
|
106
|
+
"purl": "pkg:maven/de.conterra.mapapps/ct-mapapps@4.18.2",
|
|
107
|
+
// local sbom file
|
|
108
|
+
"sbomFile": "./input/sboms/mapapps-4.18.2.cdx.json",
|
|
109
|
+
// (optional) skip this project
|
|
110
|
+
"enabled": true,
|
|
111
|
+
// (optional) scan but never break the build
|
|
112
|
+
"silent": false,
|
|
113
|
+
// (optional) only break the build for severities >= this value.
|
|
114
|
+
// One of: "CRITICAL", "HIGH", "MEDIUM", "LOW", "UNKNOWN"
|
|
115
|
+
"minimumSeverity": "HIGH"
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Usage - Scan
|
|
122
|
+
|
|
123
|
+
Run from the directory containing `vuln-scan-conf.json`:
|
|
124
|
+
|
|
125
|
+
```sh
|
|
126
|
+
# scan all projects
|
|
127
|
+
$ ct-vuln-scan
|
|
128
|
+
|
|
129
|
+
# scan all versions of one project
|
|
130
|
+
$ ct-vuln-scan mapapps
|
|
131
|
+
|
|
132
|
+
# scan a specific project version
|
|
133
|
+
$ ct-vuln-scan mapapps 4.18.2
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
A typical working directory layout:
|
|
137
|
+
|
|
138
|
+
```plain
|
|
139
|
+
/vuln-scan/ # working directory
|
|
140
|
+
├── vuln-scan-conf.json
|
|
141
|
+
├── input/
|
|
142
|
+
│ ├── sbom/ # sbom files
|
|
143
|
+
│ └── vex/ # vex files
|
|
144
|
+
└── output/ # aggregated scan results
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Environment variables
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# (optional) maven repo authentication for sbom download
|
|
151
|
+
MAVEN_REPO_USER=[username]
|
|
152
|
+
MAVEN_REPO_PW=[password]
|
|
153
|
+
|
|
154
|
+
# (optional) sonatype OSS Index authenticated API
|
|
155
|
+
OSS_INDEX_API_USER=[username]
|
|
156
|
+
OSS_INDEX_API_TOKEN=[token]
|
|
157
|
+
OSS_INDEX_BASE_URL=[url] # default: https://api.guide.sonatype.com (since 1.0.8)
|
|
158
|
+
|
|
159
|
+
# (optional) authentication for vex files referenced via vexUrls
|
|
160
|
+
VEX_URLS_USER=[username]
|
|
161
|
+
VEX_URLS_PW=[password]
|
|
162
|
+
|
|
163
|
+
# (optional) helps with the trivy DB GitHub rate limit
|
|
164
|
+
# https://aquasecurity.github.io/trivy/v0.38/docs/references/troubleshooting/#github-rate-limiting
|
|
165
|
+
TRIVY_GITHUB_TOKEN=[token]
|
|
166
|
+
|
|
167
|
+
# (optional) verbose logging
|
|
168
|
+
VERBOSE=true
|
|
169
|
+
|
|
170
|
+
# (optional) if "true", a project's product-id is automatically removed
|
|
171
|
+
# from vex statements covering vulnerabilities no longer detected for that project.
|
|
172
|
+
AUTO_REMOVE_UNUSED_STATEMENTS=true
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
These variables can also be defined in a `.env` file in the working directory.
|
|
176
|
+
|
|
177
|
+
### Output files
|
|
178
|
+
|
|
179
|
+
Two summary files are written directly into `outDir`:
|
|
180
|
+
|
|
181
|
+
- `scan-summary.txt` - The console "scan summary" output as plain text.
|
|
182
|
+
- `scan-summary.json` - Machine-readable summary, for post-processing or downstream tools. Example (truncated):
|
|
183
|
+
|
|
184
|
+
```json
|
|
185
|
+
{
|
|
186
|
+
"errors": [
|
|
187
|
+
{
|
|
188
|
+
"project": "mapapps@4.18.2",
|
|
189
|
+
"error": "Failed to fetch sbom file ..."
|
|
190
|
+
}
|
|
191
|
+
],
|
|
192
|
+
"vulnerabilities": {
|
|
193
|
+
"CVE-2025-48988": {
|
|
194
|
+
"id": "CVE-2025-48988",
|
|
195
|
+
"severity": "HIGH",
|
|
196
|
+
"title": "tomcat: Apache Tomcat DoS in multipart upload",
|
|
197
|
+
"description": "...",
|
|
198
|
+
"components": ["pkg:maven/org.apache.tomcat.embed/tomcat-embed-core@10.1.41"],
|
|
199
|
+
"refs": ["https://avd.aquasec.com/nvd/cve-2025-48988"]
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
"vulnerabilityToProject": {
|
|
203
|
+
"CVE-2025-48988": ["smartfinder-sdi@2.7.1-SNAPSHOT"]
|
|
204
|
+
},
|
|
205
|
+
"projectToVulnerability": {
|
|
206
|
+
"smartfinder-sdi@2.7.1-SNAPSHOT": ["CVE-2025-48988"]
|
|
207
|
+
},
|
|
208
|
+
"noLongerDetectedVulnerabilities": {
|
|
209
|
+
"CVE-2023-52070": ["mapapps@4.19.3-SNAPSHOT"]
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
For each scanned project, raw scanner output, an aggregated JSON, a [SARIF](https://sarifweb.azurewebsites.net/) file (used by the Azure DevOps SARIF tab) and the matching OpenVEX statements are written to a `<project-name>-<project-version>/` sub-directory (when `createSubDirsForProject` is `true`). See [ARCHITECTURE.md §5](./ARCHITECTURE.md#5-output-folder-layout) for the full file layout.
|
|
215
|
+
|
|
216
|
+
### Azure DevOps Pipelines integration
|
|
217
|
+
|
|
218
|
+
Publish the output directory as build artifact `CodeAnalysisLogs`:
|
|
219
|
+
|
|
220
|
+
```yaml
|
|
221
|
+
- task: PublishBuildArtifacts@1
|
|
222
|
+
inputs:
|
|
223
|
+
PathtoPublish: "output"
|
|
224
|
+
ArtifactName: "CodeAnalysisLogs"
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
The [SARIF SAST Scans Tab](https://marketplace.visualstudio.com/items?itemName=sariftools.scans) extension visualizes the `.sarif.json` files in the artifact.
|
|
228
|
+
|
|
229
|
+
If `onNewVulnerabilities` is set to `warn`, the pipeline will be marked as `SucceededWithIssues` when new vulnerabilities are found. With the default `fail`, the pipeline fails (exit code 1).
|
|
230
|
+
|
|
231
|
+
## Usage - Add Vex
|
|
232
|
+
|
|
233
|
+
Experimental maintenance workflow for recording a decision about whether a vulnerability affects a project.
|
|
234
|
+
|
|
235
|
+
```sh
|
|
236
|
+
$ ct-vuln-add-vex
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
The interactive prompt creates a `CVE-<id>.json` file in `vexDir`, e.g.:
|
|
240
|
+
|
|
241
|
+
```json
|
|
242
|
+
{
|
|
243
|
+
"@context": "https://openvex.dev/ns/v0.2.0",
|
|
244
|
+
"@id": "https://openvex.dev/docs/public/vex-fc763e6e...",
|
|
245
|
+
"author": "conterra",
|
|
246
|
+
"timestamp": "2024-10-01T18:54:23+02:00",
|
|
247
|
+
"last_updated": "2024-10-01T19:58:13+02:00",
|
|
248
|
+
"version": 2,
|
|
249
|
+
"statements": [
|
|
250
|
+
{
|
|
251
|
+
"vulnerability": { "name": "CVE-2023-52070" },
|
|
252
|
+
"timestamp": "2024-10-01T19:58:13+02:00",
|
|
253
|
+
"products": [
|
|
254
|
+
{ "@id": "pkg:maven/de.conterra.mapapps/ct-mapapps@4.18.2" },
|
|
255
|
+
{ "@id": "pkg:maven/de.conterra.mapapps/ct-mapapps@4.18.3" }
|
|
256
|
+
],
|
|
257
|
+
"status": "not_affected",
|
|
258
|
+
"impact_statement": "<short rationale>"
|
|
259
|
+
}
|
|
260
|
+
]
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
For details see the [OpenVex spec](https://github.com/openvex/spec/blob/main/OPENVEX-SPEC.md).
|
|
265
|
+
|
|
266
|
+
Valid `status` values:
|
|
267
|
+
|
|
268
|
+
| Status | Description |
|
|
269
|
+
| ------------------- | -------------------------------------------------------------------------- |
|
|
270
|
+
| not_affected | The product is known to be not affected by this vulnerability. |
|
|
271
|
+
| affected | The product is known to be affected by this vulnerability. |
|
|
272
|
+
| fixed | The product contains a fix for this vulnerability. |
|
|
273
|
+
| under_investigation | It is not yet known whether the product is affected; still being assessed. |
|
|
274
|
+
|
|
275
|
+
Valid `justification` values when `status` is `not_affected` (full text in the [VEX Status Justification PDF](https://www.cisa.gov/sites/default/files/publications/VEX_Status_Justification_Jun22.pdf)):
|
|
276
|
+
|
|
277
|
+
| Justification | Short description |
|
|
278
|
+
| ------------------------------------------------- | ------------------------------------------------------------ |
|
|
279
|
+
| component_not_present | Vulnerable component is not present in the product. |
|
|
280
|
+
| vulnerable_code_not_present | Vulnerable code is excluded by configuration or build. |
|
|
281
|
+
| vulnerable_code_not_in_execute_path | Vulnerable code is shipped but never called. |
|
|
282
|
+
| vulnerable_code_cannot_be_controlled_by_adversary | Attacker cannot influence the vulnerable code path. |
|
|
283
|
+
| inline_mitigations_already_exist | Built-in, non-disable-able mitigations prevent exploitation. |
|
|
284
|
+
|
|
285
|
+
## Usage - Add Project
|
|
286
|
+
|
|
287
|
+
Add a new project entry to the configuration and propagate it to vex statements:
|
|
288
|
+
|
|
289
|
+
```sh
|
|
290
|
+
$ ct-vuln-add-project <reference-name> <reference-version> <new-version>
|
|
291
|
+
|
|
292
|
+
# example: clone mapapps@4.18.2-SNAPSHOT into a new mapapps@4.18.2 entry
|
|
293
|
+
$ ct-vuln-add-project mapapps 4.18.2-SNAPSHOT 4.18.2
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
The new entry inherits all options from the reference project, and its product-id is added to every vex statement that already references the reference project.
|
|
297
|
+
|
|
298
|
+
## Usage - Remove Project
|
|
299
|
+
|
|
300
|
+
Remove a project entry from the configuration and all matching vex statements:
|
|
301
|
+
|
|
302
|
+
```sh
|
|
303
|
+
$ ct-vuln-remove-project <name> <version>
|
|
304
|
+
|
|
305
|
+
$ ct-vuln-remove-project mapapps 4.18.2-SNAPSHOT
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Remove project from vex files matching a vulnerability id
|
|
309
|
+
|
|
310
|
+
Useful when a single vulnerability is no longer relevant for a project but the project itself remains:
|
|
311
|
+
|
|
312
|
+
```sh
|
|
313
|
+
$ ct-vuln-remove-project <name> <version> <vuln-id>
|
|
314
|
+
|
|
315
|
+
$ ct-vuln-remove-project mapapps 4.18.2-SNAPSHOT CVE-2024-38820
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Usage - Add Project to Vex
|
|
319
|
+
|
|
320
|
+
Add a new project version to all vex statements that already reference an existing version. Already part of `ct-vuln-add-project`; useful when the configuration entry already exists.
|
|
321
|
+
|
|
322
|
+
```sh
|
|
323
|
+
$ ct-vuln-add-project-to-vex <existingPurl> <newPurl>
|
|
324
|
+
|
|
325
|
+
$ ct-vuln-add-project-to-vex \
|
|
326
|
+
pkg:maven/de.conterra.mapapps/ct-mapapps@4.19.0 \
|
|
327
|
+
pkg:maven/de.conterra.mapapps/ct-mapapps@4.19.1
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## Usage - Simulate a release
|
|
331
|
+
|
|
332
|
+
Combine the project commands to simulate releasing `4.18.3` and preparing the next dev version `4.18.4-SNAPSHOT`:
|
|
333
|
+
|
|
334
|
+
```sh
|
|
335
|
+
$ ct-vuln-add-project mapapps 4.18.3-SNAPSHOT 4.18.3
|
|
336
|
+
$ ct-vuln-remove-project mapapps 4.18.3-SNAPSHOT
|
|
337
|
+
$ ct-vuln-add-project mapapps 4.18.3 4.18.4-SNAPSHOT
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Usage - Jira Report
|
|
341
|
+
|
|
342
|
+
`ct-vuln-jira-report` reads `scan-summary.json` produced by `ct-vuln-scan` and creates Jira issues for new vulnerabilities. For each vulnerability the tool:
|
|
343
|
+
|
|
344
|
+
1. Checks (via JQL) whether an issue with the same vulnerability ID already exists in the target Jira project.
|
|
345
|
+
2. If not, creates a new issue with a structured description (CVE details, affected artefacts and products, placeholders for assessment and remediation) and tags every currently-affected scan project as a `proj:<name>_v<version>` label on the issue.
|
|
346
|
+
3. If an issue already exists, the project tags are compared with the current scan result. For every newly-affected project the issue is updated:
|
|
347
|
+
- the missing `proj:` label is added,
|
|
348
|
+
- a comment is posted listing only the additional projects (without re-emitting the CVE details),
|
|
349
|
+
- and if the issue is currently in status category `done` it is transitioned to status `Open` and re-added to the active sprint of its routed scrum board (when one is configured).
|
|
350
|
+
|
|
351
|
+
Project tags use the form `proj:<name>_v<version>` (the `@` separator is replaced with `_v` to keep labels JQL-friendly). On the very first run after upgrading, existing Jira issues will not yet carry these labels, so every currently-matching project will be added at once and a single follow-up comment will be posted.
|
|
352
|
+
|
|
353
|
+
When an issue is updated because new projects became affected, any `addToBoard` `issueLabels` that *would now match* (per the same first-sprint-wins / Kanban-aggregate rules used at creation) are added to the issue retroactively. Labels already present on the issue are not re-emitted. Sprint placement is **not** changed for issues that are still in progress; only when a closed issue is re-opened is it placed in the active sprint of whichever scrum entry currently matches first — which may differ from the original sprint if the routing config has been edited since the issue was created.
|
|
354
|
+
|
|
355
|
+
### Environment variables
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
JIRA_USER=user@example.com # Jira account e-mail
|
|
359
|
+
JIRA_API_TOKEN=<token> # https://id.atlassian.com/manage-profile/security/api-tokens
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
These can also be set in a `.env` file in the working directory.
|
|
363
|
+
|
|
364
|
+
### Configuration - `jira-conf.json`
|
|
365
|
+
|
|
366
|
+
```json
|
|
367
|
+
{
|
|
368
|
+
"$schema": "./node_modules/@conterra/vuln-scan/dist/schema/jira-config-schema.json",
|
|
369
|
+
"jiraUrl": "https://myorg.atlassian.net",
|
|
370
|
+
"issues": [
|
|
371
|
+
{
|
|
372
|
+
"jiraProject": "PLATFORM",
|
|
373
|
+
"issueType": "CVE",
|
|
374
|
+
"issueLabels": ["security"],
|
|
375
|
+
"addToBoard": [
|
|
376
|
+
{
|
|
377
|
+
"sprintBoardId": 10,
|
|
378
|
+
"issueLabels": ["team-a"],
|
|
379
|
+
"matchProjects": ["mapapps@*"]
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
"issueLabels": ["team-b"],
|
|
383
|
+
"matchProjects": ["smartfinder@*"]
|
|
384
|
+
}
|
|
385
|
+
],
|
|
386
|
+
"reportProjects": ["mapapps@*", "smartfinder@4.*"]
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
"jiraProject": "OTHER",
|
|
390
|
+
"issueType": "Bug",
|
|
391
|
+
"reportProjects": ["*"]
|
|
392
|
+
}
|
|
393
|
+
]
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
| Field | Required | Description |
|
|
398
|
+
| --------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
399
|
+
| `jiraUrl` | yes | Base URL of the Jira instance. |
|
|
400
|
+
| `issues` | yes | Routing rules. Each rule is evaluated independently, so a vulnerability can be reported to multiple projects. |
|
|
401
|
+
| `issues[].jiraProject` | yes | Jira project key, e.g. `PLATFORM`. |
|
|
402
|
+
| `issues[].issueType` | yes | Jira issue type, e.g. `Bug`, `CVE`. |
|
|
403
|
+
| `issues[].reportProjects` | yes | Glob patterns (`*` wildcard) matched against scan project identifiers (`name@version`). |
|
|
404
|
+
| `issues[].issueLabels` | no | Labels added to every issue created by this rule, merged with labels from matching `addToBoard` entries. |
|
|
405
|
+
| `issues[].addToBoard` | no | Per-board routing rules; see below. |
|
|
406
|
+
| `addToBoard[].sprintBoardId` | no | Scrum board id. If set, the issue is assigned to the board's active sprint via `customfield_10020`. |
|
|
407
|
+
| `addToBoard[].issueLabels` | no | Labels contributed by this entry. |
|
|
408
|
+
| `addToBoard[].matchProjects` | no | Glob patterns to activate this entry. Defaults to `["*"]`. |
|
|
409
|
+
| `autoCloseBelowSeverity` | no | Default auto-close threshold inherited by every `issues[]` entry that does not override it. One of `UNKNOWN`, `LOW`, `MEDIUM`, `HIGH`, `CRITICAL`. |
|
|
410
|
+
| `issues[].autoCloseBelowSeverity` | no | Per-rule override of the top-level threshold. |
|
|
411
|
+
|
|
412
|
+
`addToBoard` resolution: only entries whose `matchProjects` cover the vulnerability's affected scan projects are considered. The **first** matching scrum entry (with `sprintBoardId`) claims the sprint; further scrum entries are ignored. Kanban entries (no `sprintBoardId`) all contribute their labels.
|
|
413
|
+
|
|
414
|
+
#### Auto-close low-severity issues
|
|
415
|
+
|
|
416
|
+
When `autoCloseBelowSeverity` (top-level or per-issue rule) is set, newly-created issues whose vulnerability severity is **strictly below** the threshold are immediately transitioned to a status whose Jira status category is `done` (the first such transition exposed by the workflow is used, regardless of the status's display name and locale). The board/sprint assignment, labels and comment described above still happen *before* the transition, so the issue is reachable on the configured board even though it starts out closed. A short German-language comment is added explaining the auto-closure.
|
|
417
|
+
|
|
418
|
+
For *existing* issues, the threshold only suppresses re-opening: when a closed sub-threshold issue gains a newly-affected project, the project label and "Zusätzlich gemeldet für" comment are added as usual, but the issue is **not** re-opened (and never re-closed). Issues at or above the threshold keep the existing re-open + re-add-to-active-sprint behavior.
|
|
419
|
+
|
|
420
|
+
Threshold semantics mirror `ct-vuln-auto-vex`: with `"HIGH"`, severities `MEDIUM`, `LOW` and `UNKNOWN` are auto-closed, while `HIGH` and `CRITICAL` stay open.
|
|
421
|
+
|
|
422
|
+
### CLI
|
|
423
|
+
|
|
424
|
+
```
|
|
425
|
+
Options:
|
|
426
|
+
-c, --jira-config <path> Path to jira-conf.json (default: ./jira-conf.json)
|
|
427
|
+
-s, --summary <path> Path to scan-summary.json (default: ./output/scan-summary.json)
|
|
428
|
+
--dry-run Print what would be created without calling Jira
|
|
429
|
+
-h, --help Show help
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
Examples:
|
|
433
|
+
|
|
434
|
+
```sh
|
|
435
|
+
$ ct-vuln-jira-report
|
|
436
|
+
$ ct-vuln-jira-report --jira-config ./config/jira-conf.json --summary ./output/scan-summary.json
|
|
437
|
+
$ ct-vuln-jira-report --dry-run
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
## Usage - Auto VEX
|
|
441
|
+
|
|
442
|
+
`ct-vuln-auto-vex` is a post-processor for `ct-vuln-scan`. It auto-creates OpenVEX statements for newly detected vulnerabilities whose severity is **strictly below** a configurable threshold, reducing manual triage noise for low-impact CVEs.
|
|
443
|
+
|
|
444
|
+
Generated files are written to `<vexDir>/<subDir>/<CVE-ID>.json` and are picked up automatically by the next scan run (the vex store recurses into sub-directories). Manual VEX files in `vexDir` always take precedence: any existing statement covering a CVE suppresses it from the "new vulnerabilities" list before this tool runs.
|
|
445
|
+
|
|
446
|
+
### Configuration - `auto-vex-conf.json`
|
|
447
|
+
|
|
448
|
+
Place this file next to `vuln-scan-conf.json`.
|
|
449
|
+
|
|
450
|
+
```json
|
|
451
|
+
{
|
|
452
|
+
"$schema": "./node_modules/@conterra/vuln-scan/dist/schema/auto-vex-config.schema.json",
|
|
453
|
+
"impactStatement": "Auto-acknowledged: no evidence of exploitability in our deployment context.",
|
|
454
|
+
"maxSeverity": "HIGH",
|
|
455
|
+
"author": "conterra",
|
|
456
|
+
"subDir": "auto"
|
|
457
|
+
}
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
| Field | Required | Default | Description |
|
|
461
|
+
| ----------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
462
|
+
| `impactStatement` | yes | - | `impact_statement` written into every auto-generated VEX statement. |
|
|
463
|
+
| `maxSeverity` | no | `"HIGH"` | Exclusive threshold; CVEs strictly below this severity get a statement. One of `UNKNOWN`, `LOW`, `MEDIUM`, `HIGH`, `CRITICAL`. With the default, `MEDIUM` is auto-vexed but `HIGH` is not. |
|
|
464
|
+
| `author` | no | `"conterra"` | `author` field of the OpenVEX document. |
|
|
465
|
+
| `subDir` | no | `"auto"` | Sub-directory of `vexDir` for generated files. |
|
|
466
|
+
|
|
467
|
+
### Usage
|
|
468
|
+
|
|
469
|
+
```bash
|
|
470
|
+
# defaults: ./auto-vex-conf.json + ./output/scan-summary.json + vexDir from vuln-scan-conf.json
|
|
471
|
+
$ ct-vuln-auto-vex
|
|
472
|
+
|
|
473
|
+
# custom paths
|
|
474
|
+
$ ct-vuln-auto-vex --auto-vex-config ./config/auto-vex-conf.json --summary ./output/scan-summary.json
|
|
475
|
+
|
|
476
|
+
# preview without writing files
|
|
477
|
+
$ ct-vuln-auto-vex --dry-run
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### Behaviour
|
|
481
|
+
|
|
482
|
+
- Only entries from `summary.vulnerabilities` are processed; this map already contains only newly detected CVEs, so previously declared vulnerabilities are never re-processed.
|
|
483
|
+
- If `<vexDir>/<subDir>/<CVE-ID>.json` already exists, the new statement is **appended** (document version is bumped). Manual edits in the same file are preserved.
|
|
484
|
+
- The generated statement uses `status: "not_affected"` and the configured `impactStatement`. No `justification` is set.
|
|
485
|
+
- Affected scan project identifiers (`name@version`) are used as the product `@id`, matching what `VexStore` expects when populated from the scan summary.
|
|
486
|
+
|
|
487
|
+
## Contributing
|
|
488
|
+
|
|
489
|
+
See [AGENTS.md](./AGENTS.md) for toolchain, build, lint and test conventions.
|