@conterra/vuln-scan 1.0.9 → 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 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
- ## Pre-Requisites
12
-
13
- You need to have `docker` and `node/npm` installed on your machine.
14
-
15
- ## Installation
16
-
17
- The utility is available as an npm package it can be installed globally or locally.
18
-
19
- For global installation use:
20
-
21
- ```sh
22
- $ npm install -g @conterra/vuln-scan
23
- ```
24
-
25
- For local installation use:
26
-
27
- ```sh
28
- $ npm install @conterra/vuln-scan
29
- ```
30
-
31
- or reference it in your `package.json` file:
32
-
33
- ```json
34
- {
35
- "dependencies": {
36
- "@conterra/vuln-scan": "<current version>"
37
- }
38
- }
39
- ```
40
-
41
- After that you have following commands available:
42
-
43
- - `ct-vuln-scan`
44
- - Triggers the vulnerability scan
45
- - `ct-vuln-add-vex`
46
- - Triggers a process to add a new vex statement to a file
47
- - `ct-vuln-add-project`
48
- - Adds a new project to the configuration file based on an existing project entry and adds it to all vex statements
49
- - `ct-vuln-remove-project`
50
- - Removes a project from the configuration file and all vex statements related to the project.
51
- - Or removes a project from all vex statements matching a given vulnerability id.
52
- - `ct-vuln-add-project-to-vex`
53
- - Update vex files to include a new project version. The project is added to each vex file that references the existing project version given.
54
- - This step is already included in the `ct-vuln-add-project` command, but it can be run separately to update the vex files for an already existing project entry in the configuration file.
55
- - `ct-vuln-jira-report`
56
- - Reads a `scan-summary.json` produced by `ct-vuln-scan` and creates Jira issues for newly detected vulnerabilities.
57
- - Issues are only created when no existing issue with the same vulnerability ID already exists in the target Jira project.
58
- - Routing rules in a `jira.json` file control which scan projects are reported to which Jira projects.
59
-
60
- ### Vulnerability scan
61
-
62
- The `ct-vuln-scan` command can be used to scan one or many projects for vulnerabilities.
63
- It uses the configured scanners to analyze a projects `sbom` file and produces aggregated results.
64
- The `sbom` file can be provided as a local file or fetched from a maven repository.
65
-
66
- You might for example prepare a folder structure like this:
67
-
68
- ```plain
69
- /vuln-scan/ # root folder
70
- ├── vuln-scan-conf.json # configuration file for projects to scan
71
- ├── input # input to be processed by the scanners
72
- │ └── sbom # sbom files
73
- │ └── vex # vex files
74
- │── output # folder for aggregated scan results
75
- ```
76
-
77
- Running the command `ct-vuln-scan` from the root folder will scan all projects defined in the `vuln-scan-conf.json` file.
78
- By default, output will created in the `output` folder per project.
79
-
80
- In the output folder different files per project are generated, see below.
81
- There are also tow summary files directly in the output folder:
82
-
83
- - `scan-summary.txt`
84
- - A text file with a summary of the scan results.
85
- - It contains the console "scan summary" output, which is printed to the console during the scan.
86
- - `scan-summary.json`
87
- - A json file with a summary of the scan results.
88
- - This can be used to post-process the scan results or to integrate them into other tools.
89
- - Example:
90
-
91
- ```json
92
- {
93
- "errors": [
94
- {
95
- "project": "mapapps@4.18.2",
96
- "error": "Failed to fetch sbom file from maven repository: https://repository.conterra.de/repository/maven-mirror-ct/de/conterra/mapapps/ct-mapapps-rollout/4.18.2/ct-mapapps-rollout-4.18.2-sbom.cdx.json"
97
- }
98
- ],
99
- "vulnerabilities": {
100
- "CVE-2025-48988": {
101
- "id": "CVE-2025-48988",
102
- "severity": "HIGH",
103
- "title": "tomcat: Apache Tomcat DoS in multipart upload",
104
- "description": "Allocation of Resources Without Limits or Throttling vulnerability in Apache Tomcat.\n\nThis issue affects Apache Tomcat: from 11.0.0-M1 through 11.0.7, from 10.1.0-M1 through 10.1.41, from 9.0.0.M1 through 9.0.105.\n\nUsers are recommended to upgrade to version 11.0.8, 10.1.42 or 9.0.106, which fix the issue.",
105
- "components": [
106
- "pkg:maven/org.apache.tomcat.embed/tomcat-embed-core@10.1.41"
107
- ],
108
- "refs": [
109
- "https://avd.aquasec.com/nvd/cve-2025-48988",
110
- "https://github.com/advisories/GHSA-h3gc-qfqq-6h8f"
111
- ]
112
- },
113
- "CVE-2025-49125": {
114
- "id": "CVE-2025-49125",
115
- "severity": "MEDIUM",
116
- "title": "tomcat: Apache Tomcat: Security constraint bypass for pre/post-resources",
117
- "description": "Authentication Bypass Using an Alternate Path or Channel vulnerability in Apache Tomcat.  When using PreResources or PostResources mounted other than at the root of the web application, it was possible to access those resources via an unexpected path. That path was likely not to be protected by the same security constraints as the expected path, allowing those security constraints to be bypassed.\n\nThis issue affects Apache Tomcat: from 11.0.0-M1 through 11.0.7, from 10.1.0-M1 through 10.1.41, from 9.0.0.M1 through 9.0.105.\n\nUsers are recommended to upgrade to version 11.0.8, 10.1.42 or 9.0.106, which fix the issue.",
118
- "components": [
119
- "pkg:maven/org.apache.tomcat.embed/tomcat-embed-core@10.1.41"
120
- ],
121
- "refs": [
122
- "https://avd.aquasec.com/nvd/cve-2025-49125",
123
- "https://github.com/advisories/GHSA-wc4r-xq3c-5cf3"
124
- ]
125
- }
126
- },
127
- "vulnerabilityToProject": {
128
- "CVE-2025-48988": ["smartfinder-sdi@2.7.1-SNAPSHOT"],
129
- "CVE-2025-49125": ["smartfinder-sdi@2.7.1-SNAPSHOT"]
130
- },
131
- "projectToVulnerability": {
132
- "smartfinder-sdi@2.7.1-SNAPSHOT": [
133
- "CVE-2025-48988",
134
- "CVE-2025-49125"
135
- ]
136
- },
137
- "noLongerDetectedVulnerabilities": {
138
- "CVE-2023-52070": [
139
- "mapapps@4.19.3-SNAPSHOT",
140
- "smartfinder-sdi@2.7.1-SNAPSHOT"
141
- ]
142
- }
143
- }
144
- ```
145
-
146
- ## Configuration
147
-
148
- You need to create a `vuln-scan-conf.json` file in the root of your project.
149
-
150
- A minimal configuration file specifies only the project to scan:
151
-
152
- ```json
153
- {
154
- "$schema": "./node_modules/@conterra/vuln-scan/dist/schema/conf-schema.json",
155
- "projects": [
156
- {
157
- "name": "mapapps",
158
- "version": "4.18.2",
159
- "purl": "pkg:maven/de.conterra.mapapps/ct-mapapps@4.18.2",
160
- "sbomFile": "./input/sboms/mapapps-4.18.2.cdx.json"
161
- }
162
- ]
163
- }
164
- ```
165
-
166
- The following sample lists all available options with their default values:
167
-
168
- ```json
169
- {
170
- "$schema": "./node_modules/@conterra/vuln-scan/dist/schema/conf-schema.json",
171
-
172
- /* Defines if the process should fail or only log a warning or ignore vulnerabilities.
173
- If "fail" exit 1 is called.
174
- If "warn" the log statement "##vso[task.complete result=SucceededWithIssues;] is issued.
175
- If "ignore" nothing happens.
176
- */
177
- "onNewVulnerabilities": "fail",
178
- /* Defines if the process should fail or only log a warning or ignore no longer detected vulnerabilities.
179
- If "fail" exit 1 is called.
180
- If "warn" the log statement "##vso[task.complete result=SucceededWithIssues;] is issued.
181
- If "ignore" nothing happens.
182
- */
183
- "onNoLongerDetectedVulnerabilities": "ignore",
184
- /* The maven repository to fetch sboms from.
185
- If the maven repository requires authentication, the environment variables MAVEN_REPO_USER and MAVEN_REPO_PW must be set.
186
- */
187
- "mavenRepo": "https://repository.conterra.de/repository/maven-mirror-ct",
188
- /* The list of scanners to use. Possible values are "grype", "trivy" and "ossindex".
189
- Use an optional version number to pin the scanner to a certain version, e.g. `trivy@0.69.3`.
190
- */
191
- "scanners": ["grype", "trivy", "ossindex"],
192
- /* (optional) The list of vex files to download. Default is empty array. */
193
- "vexUrls": ["https://example.com/my-vex.json"],
194
- /* The directory where the vex files are stored. */
195
- "vexDir": "./input/vex",
196
- /* The directory where the scan results are written to. */
197
- "outDir": "./output",
198
- /* The directory where the cache of the grype and trivy is stored. Both tools maintain databases, which are downloaded and stored in the cache directory. */
199
- "cacheDir": "./cache",
200
- /* Flag if true then for each project a sub directory is created in the output directory.
201
- If false all files are directly stored in the output dir without a subfolder structure.
202
- */
203
- "createSubDirsForProject": true,
204
- /* (optional) Defines how the console output is reported.
205
- Possible values are:
206
- - "BY_PROJECT_DETAILED" (default): order by project and print cve details
207
- - "BY_PROJECT" : order by project but do not print cve details
208
- - "BY_CVE" : order by cve and print cve details
209
- - "BY_CVE_DETAILED": order by cve but do not print cve details
210
- */
211
- "consoleReport": "BY_PROJECT_DETAILED",
212
- /* The list of projects to scan. */
213
- "projects": [
214
- {
215
- "name": "mapapps",
216
- "version": "4.18.3",
217
- /* identifier of the project in the vex files, used to filter the vex statements. */
218
- "purl": "pkg:maven/de.conterra.mapapps/ct-mapapps@4.18.3",
219
- /* The maven coordinates of the sbom file, used to download the file from the maven remote repository. */
220
- "sbomMavenCoordinates": "de.conterra.mapapps:ct-mapapps-rollout:4.18.3:sbom"
221
- },
222
- {
223
- "name": "mapapps",
224
- "version": "4.18.2",
225
- "purl": "pkg:maven/de.conterra.mapapps/ct-mapapps@4.18.2",
226
- /* The location of the sbom file. */
227
- "sbomFile": "./input/sboms/mapapps-4.18.2.cdx.json"
228
- },
229
- {
230
- "name": "mapapps-sample-disabled",
231
- "version": "4.17.0",
232
- /* this project is disabled and not scanned */
233
- "enabled": false,
234
- "purl": "pkg:maven/de.conterra.mapapps/ct-mapapps@4.17.0",
235
- "sbomFile": "./input/sboms/mapapps-4.17.0.cdx.json"
236
- },
237
- {
238
- "name": "mapapps-sample-silent",
239
- "version": "4.17.2",
240
- /* this project is scanned, but detected vulnerabilities are ignored and will never break the build */
241
- "silent": true,
242
- "purl": "pkg:maven/de.conterra.mapapps/ct-mapapps@4.17.2",
243
- "sbomFile": "./input/sboms/mapapps-4.17.2.cdx.json"
244
- },
245
- {
246
- "name": "mapapps-sample-with-minium-severity",
247
- "version": "4.17.2",
248
- /* This project is scanned, but only vulnerabilities with a severity >= HIGH will break the build
249
- Available values are: "CRITICAL", "HIGH", "MEDIUM", "LOW", "UNKNOWN"
250
- */
251
- "minimumSeverity": "HIGH",
252
- "purl": "pkg:maven/de.conterra.mapapps/ct-mapapps@4.17.2",
253
- "sbomFile": "./input/sboms/mapapps-4.17.2.cdx.json"
254
- }
255
- ]
256
- }
257
- ```
258
-
259
- ## Usage - Scan
260
-
261
- To scan all projects run following command in the directory where the configuration file is located:
262
-
263
- ```sh
264
- $ ct-vuln-scan
265
- ```
266
-
267
- To scan a specific project run following command:
268
-
269
- ```sh
270
- $ ct-vuln-scan mapapps 4.18.2
271
- ```
272
-
273
- To scan all versions of a project run:
274
-
275
- ```sh
276
- $ ct-vuln-scan mapapps
277
- ```
278
-
279
- You may need following environment variables for a successful scan:
280
-
281
- ```bash
282
- # (optional) to fetch sboms from maven repository
283
- MAVEN_REPO_USER=[username]
284
- MAVEN_REPO_PW=[password]
285
-
286
- # (optional) for oss index authenticated api
287
- OSS_INDEX_API_USER=[username] # sonatype guide api user name
288
- OSS_INDEX_API_TOKEN=[token] # sonatype guide api token
289
- OSS_INDEX_BASE_URL=[url] # default since 1.0.8 is "https://api.guide.sonatype.com", before that it was "https://ossindex.sonatype.org",
290
-
291
- # (optional) for authentication downloading vex files specified by vexUrls option
292
- VEX_URLS_USER=[username]
293
- VEX_URLS_PW=[password]
294
-
295
- # (optional) helps to fix the rate limit issue during downloading the trivy database from github
296
- # See <https://aquasecurity.github.io/trivy/v0.38/docs/references/troubleshooting/#github-rate-limiting>
297
- TRIVY_GITHUB_TOKEN=[token]
298
-
299
- # (optional) to enable verbose logging, otherwise only scan results are printed
300
- VERBOSE=true
301
-
302
- # (optional) flag to enable automatic removal of unused statements in the vex files, during a scan.
303
- AUTO_REMOVE_UNUSED_STATEMENTS=true
304
-
305
- ```
306
-
307
- NOTE: This variables can be set in the `.env` file in the root of the project.
308
-
309
- For each project following files are created in the `output` folder:
310
-
311
- - `<project-name>-<project-version>-sbom.cdx.json`
312
- - Copy of the sbom file.
313
- - `<project-name>-<project-version>-scan-grype-results.json`
314
- - Result of the grype scanner in the grype json format.
315
- - `<project-name>-<project-version>-scan-trivy-results.json`
316
- - Result of the trivy scanner in the trivy json format.
317
- - `<project-name>-<project-version>-scan-ossindex-results.json`
318
- - Result of the sonatype oss index api requests in json format.
319
- - `<project-name>-<project-version>-scan-aggregated-results.json`
320
- - Aggregations of the results of all scanners in an own `@conterra/vuln-scan` json format.
321
- - `<project-name>-<project-version>-scan-aggregated-results.sarif.json`
322
- - Aggregations of the results of all scanners in an own [sarif json](https://sarifweb.azurewebsites.net/) format.
323
- - `<project-name>-<project-version>-openvex.vex.json`
324
- - Aggregation of all vex statements available for the project.
325
-
326
- If the `createSubDirsForProject` flag is set to `true`, the scanner will create a sub directory in the `output` folder named `<project-name>-<project-version>` for each project scanned.
327
-
328
- ### Azure DevOps Pipelines Integration
329
-
330
- Attach the output directory as build artifact `CodeAnalysisLogs`.
331
-
332
- ```yaml
333
- - task: PublishBuildArtifacts@1
334
- inputs:
335
- PathtoPublish: "output"
336
- ArtifactName: "CodeAnalysisLogs"
337
- ```
338
-
339
- Make sure that your Azure DevOps organization has the extension [SARIF SAST Scans Tab](https://marketplace.visualstudio.com/items?itemName=sariftools.scans) installed.
340
- This extension visualizes the `.sarif.json` files which are part of the `CodeAnalysisLogs`.
341
-
342
- If the configuration option `failOnNewVulnerabilities` flag is set to `false`, the pipeline will be set to `SucceededWithIssues` if vulnerabilities are found with no matching vex statement.
343
- Otherwise the pipeline will be set to `Failed` (because of exit code 1).
344
-
345
- ## Usage - Add Vex
346
-
347
- This is a currently experimental maintenance workflow, where you want to make a decision if a vulnerability is affecting your project or not or if you currently investigating a vulnerability.
348
-
349
- To add a new `CVE-<>.json` file in the `vexDir`, run:
350
-
351
- ```sh
352
- $ ct-vuln-add-vex
353
- ```
354
-
355
- And follow the instructions.
356
-
357
- This will create a new `CVE-[id].json` file in the `vexDir` folder.
358
-
359
- A `CVE-2023-52070.json` file may look like this:
360
-
361
- ```json
362
- {
363
- "@context": "https://openvex.dev/ns/v0.2.0",
364
- "@id": "https://openvex.dev/docs/public/vex-fc763e6eb63bfbcda20b7cbbddd4e9f8feaaa317fe8d8a6f51a5663a1180343e",
365
- "author": "conterra",
366
- "timestamp": "2024-10-01T18:54:23.3233227+02:00",
367
- "last_updated": "2024-10-01T19:58:13.2159221+02:00",
368
- "version": 2,
369
- "statements": [
370
- {
371
- "vulnerability": {
372
- "name": "CVE-2023-52070"
373
- },
374
- "timestamp": "2024-10-01T19:58:13.2159221+02:00",
375
- "products": [
376
- {
377
- "@id": "pkg:maven/de.conterra.mapapps/ct-mapapps@4.18.2"
378
- },
379
- {
380
- "@id": "pkg:maven/de.conterra.mapapps/ct-mapapps@4.18.3"
381
- }
382
- ],
383
- "status": "not_affected",
384
- "impact_statement": "Reported to JFreeChart library. The CVE was created by an LLM and valued by the JFreeChart team as invalid and bogus. See https://github.com/jfree/jfreechart/issues/396 for more information."
385
- }
386
- ]
387
- }
388
- ```
389
-
390
- For more information about the OpenVex specification, please refer to the [OpenVex Spec](https://github.com/openvex/spec/blob/main/OPENVEX-SPEC.md).
391
-
392
- Here some valid values for the `status` field:
393
-
394
- | Status | Description |
395
- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
396
- | not_affected | This product is known to be not affected by this vulnerability. |
397
- | affected | This product is known to be affected by this vulnerability. |
398
- | fixed | This product contains a fix for this vulnerability. |
399
- | under_investigation | It is not known yet whether these versions are or are not affected by the vulnerability. However, it is still under investigation. |
400
-
401
- Here some valid values for the `justification` field (if status is `not_affected`):
402
-
403
- [VEX Status Justification PDF](https://www.cisa.gov/sites/default/files/publications/VEX_Status_Justification_Jun22.pdf)
404
-
405
- | Justification | Description |
406
- | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
407
- | component_not_present | The vulnerable component is not present in the product. |
408
- | vulnerable_code_not_present | The vulnerable code is not present. Typically this case occurs when source code is configured or built in a way that excludes the vulnerable code. |
409
- | vulnerable_code_not_in_execute_path | The vulnerable code can not be executed. Typically this case occurs when the product includes the vulnerable code but does not call or use the vulnerable code. |
410
- | vulnerable_code_cannot_be_controlled_by_adversary | The vulnerable code cannot be controlled by an attacker to exploit the vulnerability. |
411
- | inline_mitigations_already_exist | The product includes built-in protections or features that prevent exploitation of the vulnerability. These built-in protections cannot be subverted by the attacker and cannot be configured or disabled by the user. These mitigations completely prevent exploitation based on known attack vectors. |
412
-
413
- ## Usage - Add Project
414
-
415
- To add a new project to the configuration file and all vex statements, run:
416
-
417
- ```sh
418
- $ ct-vuln-add-project <project-name> <project-version> <project-version-to-add>
419
- ```
420
-
421
- for example:
422
-
423
- ```sh
424
- $ ct-vuln-add-project mapapps 4.18.2-SNAPSHOT 4.18.2
425
- ```
426
-
427
- This will add a new project entry `mapapps` with version `4.18.2` to the configuration file clone all options from the reference project `mapapps` with version `4.18.2-SNAPSHOT`. It will also add the product-id to all vex statements, where the reference project it listed, too.
428
-
429
- This is useful if you release a snapshot version.
430
-
431
- ## Usage - Remove Project
432
-
433
- To remove a project from the configuration file and all vex statements, run:
434
-
435
- ```sh
436
- $ ct-vuln-remove-project <project-name> <project-version>
437
- ```
438
-
439
- for example:
440
-
441
- ```sh
442
- $ ct-vuln-remove-project mapapps 4.18.2-SNAPSHOT
443
- ```
444
-
445
- This will remove the project entry `mapapps` with version `4.18.2-SNAPSHOT` from the configuration file and remove the product-id from all vex statements, where the project is listed.
446
-
447
- ## Usage - Remove Project from vex files matching a vulnerability id
448
-
449
- To remove a project from all vex statements matching a vulnerability id, run:
450
-
451
- ```sh
452
- $ ct-vuln-remove-project <project-name> <project-version> <vuln-id>
453
- ```
454
-
455
- for example:
456
-
457
- ```sh
458
- $ ct-vuln-remove-project mapapps 4.18.2-SNAPSHOT CVE-2024-38820
459
- ```
460
-
461
- This will remove the product-id from all vex statements matching the given vulnerability.
462
- This is useful if a vulnerability is no longer detected for a project and you like to remove an existing statement.
463
-
464
- ## Usage - Add project to vex
465
-
466
- Adds a new project version to all vex statements matching a reference project version.
467
- This step is already included in the `ct-vuln-add-project` command, but it can be run separately to update the vex files for an already existing project entry in the configuration file.
468
-
469
- To update vex files to include a new project version, run:
470
-
471
- ```sh
472
- $ ct-vuln-add-project-to-vex <existingPurl> <newPurl>
473
- ```
474
-
475
- for example:
476
-
477
- ```sh
478
- $ ct-vuln-add-project-to-vex pkg:maven/de.conterra.mapapps/ct-mapapps@4.19.0 pkg:maven/de.conterra.mapapps/ct-mapapps@4.19.1
479
- ```
480
-
481
- ## Usage - Simulate a version release with preparation of the next dev version
482
-
483
- By combining the `ct-vuln-add-project` and `ct-vuln-remove-project` commands, you can simulate a version release.
484
-
485
- For example, you have a project `mapapps` with version `4.18.3-SNAPSHOT` and you want to release version `4.18.3`
486
- and prepare the next dev version `4.18.4-SNAPSHOT`, then you can run following commands:
487
-
488
- ```sh
489
- $ ct-vuln-add-project mapapps 4.18.3-SNAPSHOT 4.18.3
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.