Conversation
There was a problem hiding this comment.
Hey - I've found 2 issues, and left some high level feedback:
- The
sbomScanCommandstruct has several fields that are never used (includeSnoozed,severities,printer,standardizedFormat,noOutputFormat), which makes the implementation harder to follow; consider removing them or wiring them into behavior in this PR. - The
--fail/failOnFindingflag is defined and exposed on the CLI but never used inScanSBOM, which will surprise users; either implement the failure behavior or drop the flag until it is supported. - The JSON/SPDX detection in
guessMediaTypeis based on simple string checks and may be brittle; for robustness consider actually parsing the initial bytes as JSON and checking aspdxVersionfield in the parsed structure instead of relying on string indexes.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `sbomScanCommand` struct has several fields that are never used (`includeSnoozed`, `severities`, `printer`, `standardizedFormat`, `noOutputFormat`), which makes the implementation harder to follow; consider removing them or wiring them into behavior in this PR.
- The `--fail`/`failOnFinding` flag is defined and exposed on the CLI but never used in `ScanSBOM`, which will surprise users; either implement the failure behavior or drop the flag until it is supported.
- The JSON/SPDX detection in `guessMediaType` is based on simple string checks and may be brittle; for robustness consider actually parsing the initial bytes as JSON and checking a `spdxVersion` field in the parsed structure instead of relying on string indexes.
## Individual Comments
### Comment 1
<location> `roxctl/sbom/scan/scan.go:60-48` </location>
<code_context>
+ contentType string
+ includeSnoozed bool
+ severities []string
+ failOnFinding bool
+
+ // injected or constructed values
</code_context>
<issue_to_address>
**issue (bug_risk):** The `--fail` flag is defined but never influences the command’s exit behavior.
`failOnFinding` is wired into the options and flag parsing but never used in `ScanSBOM` (or elsewhere here), so `--fail` currently has no effect, which can break scripts that rely on exit status.
If this flag is meant to be supported now, the handler should inspect the scan result and return a non-nil error when findings are present. If that behavior isn’t ready, it’s better to remove or clearly disable the flag rather than ship a no-op option.
</issue_to_address>
### Comment 2
<location> `roxctl/sbom/scan/scan.go:55` </location>
<code_context>
+}
+
+// imageScanCommand holds all configurations and metadata to execute an image scan
+type sbomScanCommand struct {
+ sbomFilePath string
+ contentType string
</code_context>
<issue_to_address>
**issue (complexity):** Consider simplifying the SBOM scan command by removing unused fields and using JSON decoding in guessMediaType to reduce brittle string handling.
You can trim some complexity here without changing behavior.
### 1. Remove unused fields from `sbomScanCommand`
The extra fields (`includeSnoozed`, `severities`, `printer`, `standardizedFormat`, `noOutputFormat`) are not used anywhere and suggest behaviors that don’t exist. You can simplify the command struct and keep it tightly focused:
```go
type sbomScanCommand struct {
sbomFilePath string
contentType string
failOnFinding bool
// injected or constructed values
env environment.Environment
client common.RoxctlHTTPClient
}
```
Because these fields aren’t referenced elsewhere in this file, this change is local and doesn’t alter functionality.
### 2. Simplify `guessMediaType` using `encoding/json`
The manual string scanning in `guessMediaType` is brittle and harder to maintain. You can keep auto‑detection but leverage the JSON package to only inspect `spdxVersion`:
```go
import (
"encoding/json"
// ...
)
func guessMediaType(sbomFile *os.File) (string, error) {
// Read a small prefix for detection (4KB is still fine)
buf := make([]byte, 4096)
n, err := sbomFile.Read(buf)
if err != nil && err != io.EOF {
return "", errors.Wrap(err, "reading SBOM file")
}
if _, err := sbomFile.Seek(0, 0); err != nil {
return "", errors.Wrap(err, "resetting file position")
}
// Minimal struct to inspect only the version
var header struct {
SPDXVersion string `json:"spdxVersion"`
}
dec := json.NewDecoder(strings.NewReader(string(buf[:n])))
if err := dec.Decode(&header); err != nil {
return "", errox.InvalidArgs.New("SBOM file does not appear to be valid JSON")
}
if header.SPDXVersion == "SPDX-2.3" {
return "text/spdx+json", nil
}
return "", errox.InvalidArgs.New("unsupported SBOM format")
}
```
This keeps the same user-facing behavior (“auto-detect SPDX 2.3 JSON or error”) but:
- Delegates JSON parsing to the stdlib.
- Is less sensitive to whitespace / ordering.
- Makes the responsibility explicit: “parse minimal JSON → inspect `spdxVersion` → choose media type.”
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
|
Images are ready for the commit at cdbdb4c. To use with deploy scripts, first |
9e98a1c to
6073028
Compare
There was a problem hiding this comment.
Hey - I've found 2 issues, and left some high level feedback:
- In
sbomScanCommand,Validateonly checks the SBOM file path; consider reusing the severity validation logic from the image scan command so invalid--severityvalues are caught early instead of silently defaulting to LOW. PrintCVESummarystill printsScan results for image: %sbut is now used by SBOM scans with the SBOM file path; consider either renaming the label to something neutral (e.g.target) or passing a more descriptive string for SBOM scans to avoid confusing output.- The
guessMediaTypeheuristic will treat any JSON object containing a"spdxVersion": "SPDX-2.3"substring as SPDX 2.3, even if it appears in a nested or non-spec field; if this is a concern, tightening the check (e.g. minimal JSON parsing of the root object to look for a top-levelspdxVersionfield) would make media-type detection more robust.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `sbomScanCommand`, `Validate` only checks the SBOM file path; consider reusing the severity validation logic from the image scan command so invalid `--severity` values are caught early instead of silently defaulting to LOW.
- `PrintCVESummary` still prints `Scan results for image: %s` but is now used by SBOM scans with the SBOM file path; consider either renaming the label to something neutral (e.g. `target`) or passing a more descriptive string for SBOM scans to avoid confusing output.
- The `guessMediaType` heuristic will treat any JSON object containing a `"spdxVersion": "SPDX-2.3"` substring as SPDX 2.3, even if it appears in a nested or non-spec field; if this is a concern, tightening the check (e.g. minimal JSON parsing of the root object to look for a top-level `spdxVersion` field) would make media-type detection more robust.
## Individual Comments
### Comment 1
<location> `roxctl/sbom/scan/scan.go:116-111` </location>
<code_context>
+
+// Validate will validate the injected values and check whether it's possible to execute the operation with the
+// provided values.
+func (s *sbomScanCommand) Validate() error {
+ // Check if the SBOM file exists.
+ if _, err := os.Stat(s.sbomFilePath); err != nil {
+ if os.IsNotExist(err) {
+ return errox.InvalidArgs.Newf("SBOM file does not exist: %q", s.sbomFilePath)
+ }
+ return errors.Wrapf(err, "checking SBOM file %q", s.sbomFilePath)
+ }
+
+ return nil
+}
+
</code_context>
<issue_to_address>
**issue (bug_risk):** SBOM scan does not validate `--severity` values, unlike `image scan`.
`sbomScanCommand.Validate` only checks the SBOM file and never validates `s.severities`. For `image scan`, `Validate` rejects invalid severities by checking against `scan.AllSeverities`. Here, invalid values reach `NewCVESummaryForPrinting` and are mapped by `cveSeverityFromString`, which currently defaults to `LOW`, leading to inconsistent behavior and potentially misleading filtering. Please reuse the `image scan` severity validation (e.g., uppercase + membership in `scan.AllSeverities`) and fail fast on invalid values.
</issue_to_address>
### Comment 2
<location> `roxctl/sbom/scan/scan_test.go:587-239` </location>
<code_context>
+ s.Assert().Equal(1, cveSummary.CountComponents(), "should count 1 component")
+}
+
+func (s *sbomScanTestSuite) TestPrintSBOMScanResults_FormattedOutput() {
+ responseData, err := protojson.Marshal(testSBOMScanResponse)
+ s.Require().NoError(err)
+
+ jsonFactory := printer.NewJSONPrinterFactory(false, false)
+ jsonPrinter, err := jsonFactory.CreatePrinter("json")
+ s.Require().NoError(err)
+
+ testIO, _, out, _ := cliIO.TestIO()
+ env := environment.NewTestCLIEnvironment(s.T(), testIO, printer.DefaultColorPrinter())
+
+ cmd := &sbomScanCommand{
+ env: env,
+ printer: jsonPrinter,
+ standardizedFormat: true,
+ severities: scan.AllSeverities,
+ sbomFilePath: "test.json",
+ }
+
+ err = cmd.printSBOMScanResults(bytes.NewReader(responseData))
+ s.Require().NoError(err)
+ s.Assert().Contains(out.String(), "CVE-2024-1234")
+ s.Assert().Contains(out.String(), "test-component")
+}
+
</code_context>
<issue_to_address>
**suggestion (testing):** Add coverage for non-standardized (legacy-style) SBOM output path
`TestPrintSBOMScanResults_FormattedOutput` only covers the `standardizedFormat: true` path, so the SBOM branches that call `scan.PrintCVESummary` and `scan.PrintCVEWarning` remain untested. Please add a companion test with `standardizedFormat: false` using a tabular printer that asserts on the logger output (summary and warning lines), mirroring the image-scan behavior to verify the human-readable SBOM output.
Suggested implementation:
```golang
func (s *sbomScanTestSuite) TestPrintSBOMScanResults_FormattedOutput() {
responseData, err := protojson.Marshal(testSBOMScanResponse)
s.Require().NoError(err)
jsonFactory := printer.NewJSONPrinterFactory(false, false)
jsonPrinter, err := jsonFactory.CreatePrinter("json")
s.Require().NoError(err)
testIO, _, out, _ := cliIO.TestIO()
env := environment.NewTestCLIEnvironment(s.T(), testIO, printer.DefaultColorPrinter())
cmd := &sbomScanCommand{
env: env,
printer: jsonPrinter,
standardizedFormat: true,
severities: scan.AllSeverities,
sbomFilePath: "test.json",
}
err = cmd.printSBOMScanResults(bytes.NewReader(responseData))
s.Require().NoError(err)
s.Assert().Contains(out.String(), "CVE-2024-1234")
s.Assert().Contains(out.String(), "test-component")
}
func (s *sbomScanTestSuite) TestPrintSBOMScanResults_LegacyFormattedOutput() {
// Use the same test response as the standardized-format test.
responseData, err := protojson.Marshal(testSBOMScanResponse)
s.Require().NoError(err)
// Use a tabular (human-readable) printer to exercise the non-standardized path,
// which calls scan.PrintCVESummary and scan.PrintCVEWarning.
tabularFactory := printer.NewTabularPrinterFactory(false, false)
tabularPrinter, err := tabularFactory.CreatePrinter("table")
s.Require().NoError(err)
testIO, _, _, errOut := cliIO.TestIO()
env := environment.NewTestCLIEnvironment(s.T(), testIO, printer.DefaultColorPrinter())
cmd := &sbomScanCommand{
env: env,
printer: tabularPrinter,
standardizedFormat: false,
severities: scan.AllSeverities,
sbomFilePath: "test.json",
}
err = cmd.printSBOMScanResults(bytes.NewReader(responseData))
s.Require().NoError(err)
// Legacy-style output is logged via scan.PrintCVESummary/scan.PrintCVEWarning.
loggerOutput := errOut.String()
// Assert summary information is present.
s.Assert().Contains(loggerOutput, "1", "summary should mention 1 vulnerability")
s.Assert().True(strings.Contains(loggerOutput, "vulnerab"), "summary should mention vulnerabilities")
s.Assert().True(strings.Contains(loggerOutput, "component") || strings.Contains(loggerOutput, "package"),
"summary should mention affected components/packages")
// Assert that a warning line is emitted for the SBOM scan.
s.Assert().Contains(strings.ToLower(loggerOutput), "warning", "should emit a CVE warning line for SBOM scans")
}
```
1. Ensure `printer.NewTabularPrinterFactory` and the `"table"` printer type match the actual tabular printer factory API in your codebase; if the constructor signature or printer type string differ, adjust them to mirror how image-scan tests obtain a tabular printer.
2. Confirm that `cliIO.TestIO()` returns an `errOut` buffer that receives logger output for `environment.NewTestCLIEnvironment`; if your image-scan tests assert on a different stream (e.g. `out` instead of `errOut`), update `loggerOutput := ...` to use the same stream they use.
3. The current assertions on `loggerOutput` use generic substrings so they are resilient to minor wording changes. If your existing image-scan tests assert on more specific summary/warning text produced by `scan.PrintCVESummary` and `scan.PrintCVEWarning`, tighten these expectations to match those strings for stronger regression coverage.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #18503 +/- ##
========================================
Coverage 49.58% 49.59%
========================================
Files 2761 2762 +1
Lines 208140 208314 +174
========================================
+ Hits 103214 103310 +96
- Misses 97260 97326 +66
- Partials 7666 7678 +12
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
1aa7166 to
be324f8
Compare
8f01722 to
dedb482
Compare
|
rebased to address conflicts from base branch |
dedb482 to
483bac0
Compare
|
rebased |
2643358 to
01e65a6
Compare
483bac0 to
7f8a80b
Compare
|
rebased to address conflicts |
7f8a80b to
c43153d
Compare
janisz
left a comment
There was a problem hiding this comment.
Can you split it into 2 PRs?
- Move severity to common/scan
- sbom scan.
It will be easier to review as 1 will be mechanical review as it should be only name change
Sure, the diff for those files will be no different from what it is right now FYI |
c43153d to
9259334
Compare
ec5db7e to
9ebda95
Compare
3523a7a to
bdf3a36
Compare
|
|
||
| // Guess the media type. | ||
| if s.contentType == "" { | ||
| s.contentType, err = guessMediaType(sbomFile) |
There was a problem hiding this comment.
Can we rely on file extension instead of guessing?
There was a problem hiding this comment.
We need to determine the type of SBOM the file represents in addition to the file being json, for that we cannot rely solely on extension and need to interrogate the file.
| // | ||
| // The backend currently requires SPDX 2.3 JSON, which when detected will return | ||
| // media type `text/spdx+json`. | ||
| func guessMediaType(sbomFile *os.File) (string, error) { |
There was a problem hiding this comment.
Can we simplify it by using json path or parse dummy struct and read the .spdxVersion? ALternatively we don't need to fail fast as we can let central handle the type.
There was a problem hiding this comment.
Can we simplify it by using json path or parse dummy struct and read the .spdxVersion?
I'll switch to use a token based json decoder, should help simplify (but make errors less specific). Parsing into a dummy struct is where I started but some SPDX docs are 500+ MiB, even if we're only interested in a single field the whole file was being loaded/parsed which was painfully slow.
ALternatively we don't need to fail fast as we can let central handle the type.
The API, by design, requires the type be provided by the client. The guess in roxctl isn't required but it is a better user experience compared to forcing the user to specify the type on every invocation.
Also the SBOM is opaque to Central by design, it is passed to scanner for processing. It would be a poor experience to wait for a large upload and processing before getting an 'unsupported SBOM' error.
Switches the media type guess to use a token based json decoder (less brittle).
bdf3a36 to
997fd14
Compare
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe pull request adds a new Changes
Sequence DiagramsequenceDiagram
participant User as User / CLI
participant Command as sbom scan<br/>Command
participant FileSystem as File System
participant MediaDetect as Media Type<br/>Detector
participant HTTPClient as HTTP Client
participant API as Central API<br/>(/api/v1/sboms/scan)
participant Printer as Output<br/>Formatter
User->>Command: Execute with --file, --severity, --content-type
Command->>Command: Validate() - Check file exists, severity supported
Command->>FileSystem: Open SBOM file
FileSystem-->>Command: File handle
Command->>MediaDetect: guessMediaType() - Detect SPDX version
MediaDetect->>FileSystem: Read and parse JSON
FileSystem-->>MediaDetect: SPDX document
MediaDetect-->>Command: Content-Type (text/spdx+json)
Command->>HTTPClient: POST /api/v1/sboms/scan
HTTPClient->>API: Send SBOM with Content-Type
API-->>HTTPClient: Response (200 or error)
HTTPClient-->>Command: Response status & body
alt HTTP 200 Success
Command->>Printer: Parse response as storage.Image
Printer->>Printer: Format CVE summary, components, vulnerabilities
Printer-->>User: Output results
else Non-200 Status
Command->>Command: Check for HTML response
Command-->>User: Error with status code and body
end
alt --fail flag enabled
Command->>Command: Check vulnerability count
Command-->>User: Exit with error if vulnerabilities found
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
roxctl/sbom/sbom.go (1)
12-12: Fix copy-paste error in doc comment.The comment says "image command tree" but this defines the
sbomcommand tree.📝 Suggested fix
-// Command defines the image command tree +// Command defines the sbom command tree🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@roxctl/sbom/sbom.go` at line 12, The top-of-file doc comment is a copy-paste error: it currently says "image command tree" but should describe the sbom command; update the comment above the sbom command definition (the doc for the sbom command tree in this file) to correctly refer to the "sbom command tree" or similar accurate description so the comment matches the sbom command symbol.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@roxctl/sbom/sbom.go`:
- Line 12: The top-of-file doc comment is a copy-paste error: it currently says
"image command tree" but should describe the sbom command; update the comment
above the sbom command definition (the doc for the sbom command tree in this
file) to correctly refer to the "sbom command tree" or similar accurate
description so the comment matches the sbom command symbol.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Central YAML (base), Organization UI (inherited)
Review profile: CHILL
Plan: Pro
Run ID: bfca8271-2407-4162-8782-631490c5ef65
📒 Files selected for processing (7)
roxctl/maincommand/command.goroxctl/maincommand/command_tree_debug.yamlroxctl/maincommand/command_tree_release.yamlroxctl/sbom/sbom.goroxctl/sbom/scan/scan.goroxctl/sbom/scan/scan_test.gotests/roxctl/bats-tests/local/expect/roxctl--help.txt
Description
Adds the
roxctl sbom scancommand.PR Stack:
User-facing documentation
Testing and quality
Automated testing
How I validated my change
output
{ "id": "fake HashId", "scan": { "scannerVersion": "matcher=v7", "scanTime": "2026-01-16T00:48:23.865085543Z", "components": [ { "name": "Fake Package #1", "version": "v1.0.0", "vulns": [ { "cve": "Fake Vuln #1", "cvss": 3.3, "link": "https://nvd.nist.gov/vuln/detail/CVE-5678-1234", "scoreVersion": "V3", "cvssV3": { "vector": "CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:N", "exploitabilityScore": 0.8, "impactScore": 2.5, "attackComplexity": "COMPLEXITY_HIGH", "privilegesRequired": "PRIVILEGE_LOW", "userInteraction": "UI_REQUIRED", "confidentiality": "IMPACT_LOW", "integrity": "IMPACT_LOW", "score": 3.3, "severity": "LOW" }, "severity": "CRITICAL_VULNERABILITY_SEVERITY", "cvssMetrics": [ { "source": "SOURCE_NVD", "url": "https://nvd.nist.gov/vuln/detail/CVE-5678-1234", "cvssv3": { "vector": "CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:N", "exploitabilityScore": 0.8, "impactScore": 2.5, "attackComplexity": "COMPLEXITY_HIGH", "privilegesRequired": "PRIVILEGE_LOW", "userInteraction": "UI_REQUIRED", "confidentiality": "IMPACT_LOW", "integrity": "IMPACT_LOW", "score": 3.3, "severity": "LOW" } } ] }, { "cve": "Fake Vuln #2", "cvss": 3.3, "link": "https://nvd.nist.gov/vuln/detail/CVE-5678-1234", "scoreVersion": "V3", "cvssV3": { "vector": "CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:N", "exploitabilityScore": 0.8, "impactScore": 2.5, "attackComplexity": "COMPLEXITY_HIGH", "privilegesRequired": "PRIVILEGE_LOW", "userInteraction": "UI_REQUIRED", "confidentiality": "IMPACT_LOW", "integrity": "IMPACT_LOW", "score": 3.3, "severity": "LOW" }, "severity": "IMPORTANT_VULNERABILITY_SEVERITY", "cvssMetrics": [ { "source": "SOURCE_NVD", "url": "https://nvd.nist.gov/vuln/detail/CVE-5678-1234", "cvssv3": { "vector": "CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:N", "exploitabilityScore": 0.8, "impactScore": 2.5, "attackComplexity": "COMPLEXITY_HIGH", "privilegesRequired": "PRIVILEGE_LOW", "userInteraction": "UI_REQUIRED", "confidentiality": "IMPACT_LOW", "integrity": "IMPACT_LOW", "score": 3.3, "severity": "LOW" } } ] }, { "cve": "Fake Vuln #3", "cvss": 8.2, "link": "https://access.redhat.com/security/cve/CVE-1234-567", "scoreVersion": "V3", "cvssV3": { "vector": "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:H", "exploitabilityScore": 2.8, "impactScore": 4.7, "attackVector": "ATTACK_ADJACENT", "scope": "CHANGED", "confidentiality": "IMPACT_LOW", "availability": "IMPACT_HIGH", "score": 8.2, "severity": "HIGH" }, "severity": "MODERATE_VULNERABILITY_SEVERITY", "cvssMetrics": [ { "source": "SOURCE_RED_HAT", "url": "https://access.redhat.com/security/cve/CVE-1234-567", "cvssv3": { "vector": "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:H", "exploitabilityScore": 2.8, "impactScore": 4.7, "attackVector": "ATTACK_ADJACENT", "scope": "CHANGED", "confidentiality": "IMPACT_LOW", "availability": "IMPACT_HIGH", "score": 8.2, "severity": "HIGH" } }, { "source": "SOURCE_NVD", "url": "https://nvd.nist.gov/vuln/detail/CVE-1234-567", "cvssv2": { "vector": "AV:N/AC:M/Au:M/C:C/I:N/A:P", "attackVector": "ATTACK_NETWORK", "accessComplexity": "ACCESS_MEDIUM", "confidentiality": "IMPACT_COMPLETE", "availability": "IMPACT_PARTIAL", "exploitabilityScore": 5.5, "impactScore": 7.8, "score": 6.4, "severity": "MEDIUM" } } ] }, { "cve": "Fake Vuln #4", "cvss": 3.3, "link": "https://nvd.nist.gov/vuln/detail/CVE-5678-1234", "scoreVersion": "V3", "cvssV3": { "vector": "CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:N", "exploitabilityScore": 0.8, "impactScore": 2.5, "attackComplexity": "COMPLEXITY_HIGH", "privilegesRequired": "PRIVILEGE_LOW", "userInteraction": "UI_REQUIRED", "confidentiality": "IMPACT_LOW", "integrity": "IMPACT_LOW", "score": 3.3, "severity": "LOW" }, "severity": "LOW_VULNERABILITY_SEVERITY", "cvssMetrics": [ { "source": "SOURCE_NVD", "url": "https://nvd.nist.gov/vuln/detail/CVE-5678-1234", "cvssv3": { "vector": "CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:N", "exploitabilityScore": 0.8, "impactScore": 2.5, "attackComplexity": "COMPLEXITY_HIGH", "privilegesRequired": "PRIVILEGE_LOW", "userInteraction": "UI_REQUIRED", "confidentiality": "IMPACT_LOW", "integrity": "IMPACT_LOW", "score": 3.3, "severity": "LOW" } } ] } ] }, { "name": "Fake Package #2", "version": "v2.3.4", "vulns": [ { "cve": "Fake Vuln #3", "cvss": 8.2, "link": "https://access.redhat.com/security/cve/CVE-1234-567", "scoreVersion": "V3", "cvssV3": { "vector": "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:H", "exploitabilityScore": 2.8, "impactScore": 4.7, "attackVector": "ATTACK_ADJACENT", "scope": "CHANGED", "confidentiality": "IMPACT_LOW", "availability": "IMPACT_HIGH", "score": 8.2, "severity": "HIGH" }, "severity": "MODERATE_VULNERABILITY_SEVERITY", "cvssMetrics": [ { "source": "SOURCE_RED_HAT", "url": "https://access.redhat.com/security/cve/CVE-1234-567", "cvssv3": { "vector": "CVSS:3.1/AV:A/AC:L/PR:N/UI:N/S:C/C:L/I:N/A:H", "exploitabilityScore": 2.8, "impactScore": 4.7, "attackVector": "ATTACK_ADJACENT", "scope": "CHANGED", "confidentiality": "IMPACT_LOW", "availability": "IMPACT_HIGH", "score": 8.2, "severity": "HIGH" } }, { "source": "SOURCE_NVD", "url": "https://nvd.nist.gov/vuln/detail/CVE-1234-567", "cvssv2": { "vector": "AV:N/AC:M/Au:M/C:C/I:N/A:P", "attackVector": "ATTACK_NETWORK", "accessComplexity": "ACCESS_MEDIUM", "confidentiality": "IMPACT_COMPLETE", "availability": "IMPACT_PARTIAL", "exploitabilityScore": 5.5, "impactScore": 7.8, "score": 6.4, "severity": "MEDIUM" } } ] }, { "cve": "Fake Vuln #4", "cvss": 3.3, "link": "https://nvd.nist.gov/vuln/detail/CVE-5678-1234", "scoreVersion": "V3", "cvssV3": { "vector": "CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:N", "exploitabilityScore": 0.8, "impactScore": 2.5, "attackComplexity": "COMPLEXITY_HIGH", "privilegesRequired": "PRIVILEGE_LOW", "userInteraction": "UI_REQUIRED", "confidentiality": "IMPACT_LOW", "integrity": "IMPACT_LOW", "score": 3.3, "severity": "LOW" }, "severity": "LOW_VULNERABILITY_SEVERITY", "cvssMetrics": [ { "source": "SOURCE_NVD", "url": "https://nvd.nist.gov/vuln/detail/CVE-5678-1234", "cvssv3": { "vector": "CVSS:3.1/AV:L/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:N", "exploitabilityScore": 0.8, "impactScore": 2.5, "attackComplexity": "COMPLEXITY_HIGH", "privilegesRequired": "PRIVILEGE_LOW", "userInteraction": "UI_REQUIRED", "confidentiality": "IMPACT_LOW", "integrity": "IMPACT_LOW", "score": 3.3, "severity": "LOW" } } ] } ] } ], "operatingSystem": "unknown", "dataSource": { "id": "a87471e6-9678-4e66-8348-91e302b6de07", "name": "Scanner V4" } } }output
{ "result": { "summary": { "CRITICAL": 1, "IMPORTANT": 1, "LOW": 1, "MODERATE": 1, "TOTAL-COMPONENTS": 2, "TOTAL-VULNERABILITIES": 4 }, "vulnerabilities": [ { "cveId": "Fake Vuln #1", "cveSeverity": "CRITICAL", "cveCVSS": 3.3, "cveInfo": "https://nvd.nist.gov/vuln/detail/CVE-5678-1234", "advisoryId": "", "advisoryInfo": "", "componentName": "Fake Package #1", "componentVersion": "v1.0.0", "componentFixedVersion": "" }, { "cveId": "Fake Vuln #2", "cveSeverity": "IMPORTANT", "cveCVSS": 3.3, "cveInfo": "https://nvd.nist.gov/vuln/detail/CVE-5678-1234", "advisoryId": "", "advisoryInfo": "", "componentName": "Fake Package #1", "componentVersion": "v1.0.0", "componentFixedVersion": "" }, { "cveId": "Fake Vuln #3", "cveSeverity": "MODERATE", "cveCVSS": 8.2, "cveInfo": "https://access.redhat.com/security/cve/CVE-1234-567", "advisoryId": "", "advisoryInfo": "", "componentName": "Fake Package #1", "componentVersion": "v1.0.0", "componentFixedVersion": "" }, { "cveId": "Fake Vuln #4", "cveSeverity": "LOW", "cveCVSS": 3.3, "cveInfo": "https://nvd.nist.gov/vuln/detail/CVE-5678-1234", "advisoryId": "", "advisoryInfo": "", "componentName": "Fake Package #1", "componentVersion": "v1.0.0", "componentFixedVersion": "" }, { "cveId": "Fake Vuln #3", "cveSeverity": "MODERATE", "cveCVSS": 8.2, "cveInfo": "https://access.redhat.com/security/cve/CVE-1234-567", "advisoryId": "", "advisoryInfo": "", "componentName": "Fake Package #2", "componentVersion": "v2.3.4", "componentFixedVersion": "" }, { "cveId": "Fake Vuln #4", "cveSeverity": "LOW", "cveCVSS": 3.3, "cveInfo": "https://nvd.nist.gov/vuln/detail/CVE-5678-1234", "advisoryId": "", "advisoryInfo": "", "componentName": "Fake Package #2", "componentVersion": "v2.3.4", "componentFixedVersion": "" } ] } }output
output