Skip to content

new parser ReversingLabs Spectra Assure #12476

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# ReversingLabs SpectraAssure Parser

## File Types

The parser accepts only `report.rl.json` files.
You can find instructions how to export the `rl-json` report from the cli and portal scanners.

- [Spectra Assure Cli](https://docs.secure.software/cli/).
- [Spectra Assure Portal](https://docs.secure.software/portal/).
- [docker:rl-scanner](https://hub.docker.com/r/reversinglabs/rl-scanner).
- [docker:rl-scanner-cloud](https://hub.docker.com/r/reversinglabs/rl-scanner-cloud).


## Relevant Fields in report.rl.json

The format of the `rl-json` report is shown in:

- [analysis-reports:rl-json](https://docs.secure.software/concepts/analysis-reports#rl-json).

The RlJsonInfo module is the principal parser vor the `report.rl.json` data.
The primary focus is on extracting vulnerabilities (CVE) that occur on rl-json `components` and rl-json `dependencies`. All items without vulnerabilities are currently ignored.

All data is stored only once in the rl-json file and it uses references to map relevant related data:

Component -> Vulnerabilities
Component -> Dependencies -> Vulnerabilities

During the parsing we follow the references and add each item to a individual `CveInfoNode` record that has the vulnerablity and the component and the dependency data.

The `CveInfoNode` basically maps almost directly to the `DefectDojo Finding`.

The `title` and `description` are build using the collected data.

### Title

#### Component

For a Components, the title shows:

- the CVE.
- the type: `Component`.
- the `purl` of the `Component` if present, otherwise name and version.
- the component-name.
- the component-sha256.

The sha256 is added as sometimes a file scan my have multiple items with the same name and version but with a different hash.
Typically this happens with multi language windows installeres.

#### Dependency

The title shows the:

- the CVE.
- the type: `Dependecy`.
- the `purl` of the `Dependency` if present, otherwise name and version.

### Description

#### Component

For a component we repeat the title.

#### Dependency

For a dependency we repeat the title and then add the component_name and component_hash.
For duplicates we add one additional line to the description for each duplicate, showing its title and component.

### Vulnerabilities

From the vulnerability we fetch:

- the CVE unique id
- cvss version
- cvss.basescore

From the cvss.basescore we map the severity into:

- Info
- Low
- Medium
- High
- Critical

### Other

We extract the scan date and the scanner version and set a static scanner-name.

## Field Mapping Details


- Currently no endpoints are created

- On detecting a duplicate `dependency` we increment the number of occurrences.
`Components` have no duplicates so the nr of occurrences is always 1.

- Deduplication is done only on Dependencies and we use the title (cve + dependency_name and version) + the `component-path` as the hash_key to detect duplicates.

- The default severity if no mapping is matched is `Info`.

## Sample Scan Data or Unit Tests

- [Sample Scan Data Folder](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/reversinglabs_spectraassure)

## Link To Tools

- [Spectra Assure Cli](https://docs.secure.software/cli/)
- [Spectra Assure Portal](https://docs.secure.software/portal/)
- [docker:rl-scanner](https://docs.secure.software/cli/integrations/docker-image)
- [docker:rl-scanner-cloud](https://docs.secure.software/portal/docker-image)
Empty file.
117 changes: 117 additions & 0 deletions dojo/tools/reversinglabs_spectraassure/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# noqa: RUF100
import hashlib
import logging
from typing import Any

from dojo.models import Finding
from dojo.tools.reversinglabs_spectraassure.rlJsonInfo import RlJsonInfo
from dojo.tools.reversinglabs_spectraassure.rlJsonInfo.cve_info_node import CveInfoNode

logger = logging.getLogger(__name__)

WHAT = "ReversingLabs Spectra Assure"


class ReversinglabsSpectraassureParser:

# --------------------------------------------
# This class MUST have an empty constructor or no constructor

def _find_duplicate(self, key: str) -> Finding | None:
logger.debug("")

if key in self._duplicates:
return self._duplicates

return None

def _make_hash(
self,
data: str,
) -> str:
# Calculate SHA-256 hash
d = data.encode()
return hashlib.sha256(d).hexdigest()

def _one_finding(
self,
*,
node: CveInfoNode,
test: Any,
) -> Finding:
logger.debug("%s", node)

key = self._make_hash(node.title + " " + node.component_file_path)
cve = node.cve
finding = Finding(
date=node.scan_date,
title=node.title,
description=node.title + " " + node.description + "\n",
cve=cve,
cvssv3_score=node.score,
severity=node.score_severity,
vuln_id_from_tool=node.vuln_id_from_tool,
unique_id_from_tool=node.unique_id_from_tool, # purl if we have one ?
file_path=node.component_file_path,
component_name=node.component_name,
component_version=node.component_version,
nb_occurences=1,
hash_code=key, # sha256 on title
references=None, # future urls
active=True, # this is the DefectDojo active field, nothing to do with node.active field
test=test,
static_finding=True,
dynamic_finding=False,
)
finding.unsaved_vulnerability_ids = [cve]
finding.unsaved_tags = node.tags
finding.impact = node.impact

return finding

# --------------------------------------------
# PUBLIC
def get_scan_types(self) -> list[str]:
return [WHAT]

def get_label_for_scan_types(self, scan_type: str) -> str:
return scan_type

def get_description_for_scan_types(self, scan_type: str) -> str:
if scan_type == WHAT:
return "Import the SpectraAssure report.rl.json file."
return f"Unknown Scan Type; {scan_type}"

def get_findings(
self,
file: Any,
test: Any,
) -> list[Finding]:
# ------------------------------------
rji = RlJsonInfo(file_handle=file)
rji.get_cve_active_all()

self._findings: list[Finding] = []
self._duplicates: dict[str, Finding] = {}

for cin in rji.get_results_list():
finding = self._one_finding(
node=cin,
test=test,
)
if finding is None:
continue

key = finding.hash_code
if key not in self._duplicates:
self._findings.append(finding)
self._duplicates[key] = finding
continue

dup = self._duplicates[key] # but that may be on a different component file, name, version
if dup:
dup.description += finding.description
dup.nb_occurences += 1

# ------------------------------------
return self._findings
Loading