Skip to content
Merged
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
170 changes: 170 additions & 0 deletions docs/content/supported_tools/parsers/file/orca_security.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
---
title: "Orca Security Alerts"
toc_hide: true
---

The [Orca Security](https://orca.security/) parser for DefectDojo supports imports from CSV and JSON formats. This document details the parsing of Orca Security alert exports into DefectDojo field mappings and unmapped fields.

## Supported File Types

The Orca Security parser accepts CSV and JSON file formats. To generate these files from Orca Security:

1. Log into the Orca Security console
2. Navigate to the Alerts page
3. Apply desired filters (scope, severity, status)
4. Click "Export" and select either CSV or JSON format
5. Save the exported file
6. Upload to DefectDojo using the "Orca Security Alerts" scan type

The parser auto-detects the format: files starting with `[` are treated as JSON, otherwise CSV.

## Default Deduplication Hashcode Fields

By default, DefectDojo identifies duplicate Findings using the [hashcode deduplication algorithm](https://docs.defectdojo.com/en/working_with_findings/finding_deduplication/about_deduplication/) with the following fields:

- title
- component_name

### Sample Scan Data

Sample Orca Security scans can be found in the [sample scan data folder](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/orca_security).

## Link To Tool

- [Orca Security](https://orca.security/)
- [Orca Security Documentation](https://docs.orcasecurity.io/)

## CSV Format

### Total Fields in CSV

- Total data fields: 12
- Total data fields parsed: 12
- Total data fields NOT parsed: 0

### CSV Format Field Mapping Details

<details>
<summary>Click to expand Field Mapping Table</summary>

| Source Field | DefectDojo Field | Notes |
| ------------ | ---------------- | ----- |
| Title | title | Truncated at 500 characters with "..." suffix |
| OrcaScore | severity | Float mapped to severity string (see Severity Conversion) |
| OrcaScore | severity_justification | Stored as "OrcaScore: X.X" |
| Category | description | Included in structured markdown description |
| Inventory.Name | component_name | Cloud resource name |
| CloudAccount.Name | description | Included in description and used for dedup hash |
| Source | service | Orca resource identifier populates service field |
| Source | description | Also included in description |
| Status | active | "open" = active, all else = inactive |
| CreatedAt | date | ISO 8601 parsed to date object |
| LastSeen | description | Included in description |
| Labels | tags | JSON-encoded array parsed and stored as finding tags |

</details>

### Additional Finding Field Settings (CSV Format)

<details>
<summary>Click to expand Additional Settings Table</summary>

| Finding Field | Default Value | Notes |
|---------------|---------------|-------|
| static_finding | True | CSPM scan data is static analysis |
| dynamic_finding | False | Not a dynamic/runtime scan |
| active | Varies | Based on Status field ("open" = True) |
| mitigation | Not set | Orca exports do not include remediation text |

</details>

## JSON Format

### Total Fields in JSON

- Total data fields: 10
- Total data fields parsed: 10
- Total data fields NOT parsed: 0

### JSON Format Field Mapping Details

<details>
<summary>Click to expand Field Mapping Table</summary>

| Source Field | DefectDojo Field | Notes |
| ------------ | ---------------- | ----- |
| Title | title | Truncated at 500 characters with "..." suffix |
| OrcaScore | severity | Float mapped to severity string (see Severity Conversion) |
| OrcaScore | severity_justification | Stored as "OrcaScore: X.X" |
| Category | description | Included in structured markdown description |
| Inventory.Name | component_name | Nested object, cloud resource name |
| CloudAccount.Name | description | Nested object, included in description and dedup hash |
| Source | service | Orca resource identifier populates service field |
| Source | description | Also included in description |
| Status | active | "open" = active, all else = inactive |
| CreatedAt | date | ISO 8601 parsed to date object |
| LastSeen | description | Included in description |
| Labels | tags | Array of strings stored as finding tags |

</details>

### Additional Finding Field Settings (JSON Format)

<details>
<summary>Click to expand Additional Settings Table</summary>

| Finding Field | Default Value | Notes |
|---------------|---------------|-------|
| static_finding | True | CSPM scan data is static analysis |
| dynamic_finding | False | Not a dynamic/runtime scan |
| active | Varies | Based on Status field ("open" = True) |
| mitigation | Not set | Orca exports do not include remediation text |

</details>

## Special Processing Notes

### Date Processing

The parser uses `dateutil.parser.parse()` to handle ISO 8601 date formats from Orca Security exports. The datetime is converted to a date object using `.date()`. Invalid or missing date strings return `None`.

### Severity Conversion

OrcaScore (float 0-10) is converted to DefectDojo severity levels:
- `0` or missing → Info
- `0.1 - 3.9` → Low
- `4.0 - 6.9` → Medium
- `7.0 - 8.9` → High
- `9.0 - 10.0` → Critical

The conversion uses `float()` with error handling — non-numeric values default to Info severity.

### Severity Justification

The OrcaScore is also stored in the `severity_justification` field as "OrcaScore: X.X". This preserves the original numeric score for reference while the severity field contains the mapped categorical value.

### Description Construction

The parser builds a structured markdown description from all available alert fields. Each field is formatted as a bold label followed by its value, separated by double newlines. Fields with empty values are omitted. The description includes: Title, Category, Source, Inventory name, Cloud Account name, Orca Score, Status, Created date, Last Seen date, and Labels.

### Title Format

Finding titles use the alert's Title field directly. Titles longer than 500 characters are truncated with a "..." suffix. Alerts with no title receive the default "Orca Security Alert".

### Service Field

The Source field from Orca Security populates the DefectDojo `service` field. This represents the cloud resource or service that generated the alert.

### Mitigation Construction

Orca Security CSV and JSON exports do not include remediation or mitigation text. The mitigation field is not populated by this parser.

### Deduplication

Deduplication uses the hashcode algorithm configured in `settings.dist.py` with the fields `title` and `component_name`. This ensures findings with the same alert title on the same resource are deduplicated across reimports. Each row/item in the export becomes one Finding with no internal deduplication.

### Tags Handling

Labels from Orca Security are stored as finding tags using the `unsaved_tags` field. This makes labels searchable and filterable in DefectDojo.

In CSV format, the Labels column contains a JSON-encoded array of strings. The parser uses `json.loads()` to parse this embedded JSON. If parsing fails, the raw string is used as a single tag. In JSON format, Labels is a native array of strings.
2 changes: 2 additions & 0 deletions dojo/settings/settings.dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -1488,6 +1488,7 @@ def saml2_attrib_map_format(din):
"Snyk Issue API Scan": ["vuln_id_from_tool", "file_path"],
"OpenReports": ["vulnerability_ids", "component_name", "component_version", "severity"],
"n0s1 Scanner": ["description"],
"Orca Security Alerts": ["title", "component_name"],
}

# Override the hardcoded settings here via the env var
Expand Down Expand Up @@ -1752,6 +1753,7 @@ def saml2_attrib_map_format(din):
"OpenVAS Parser v2": DEDUPE_ALGO_HASH_CODE,
"Snyk Issue API Scan": DEDUPE_ALGO_HASH_CODE,
"OpenReports": DEDUPE_ALGO_HASH_CODE,
"Orca Security Alerts": DEDUPE_ALGO_HASH_CODE,
}

# Override the hardcoded settings here via the env var
Expand Down
Empty file.
102 changes: 102 additions & 0 deletions dojo/tools/orca_security/csv_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
"""
CSV parser for Orca Security alert exports.

This module handles parsing of Orca Security alerts exported in CSV format.
The CSV export contains one row per alert with columns for all alert metadata.

Expected CSV columns:
OrcaScore, Title, Category, Inventory, Inventory.Name, CloudAccount,
CloudAccount.Name, Source, Status, CreatedAt, LastSeen, Labels

Note: The Labels column contains a JSON-encoded array of strings within the CSV.
"""
import csv
import io
import json

from dojo.models import Finding
from dojo.tools.orca_security.helpers import (
build_description,
build_severity_justification,
map_orca_severity,
parse_date,
truncate_title,
)


class OrcaSecurityCSVParser:

"""Parse Orca Security CSV alert exports."""

def parse(self, content):
"""
Parse CSV content and return a list of Finding objects.

Args:
content: String containing the CSV file content

Returns:
list[Finding]: List of DefectDojo Finding objects

"""
reader = csv.DictReader(io.StringIO(content), delimiter=",", quotechar='"')
findings = []

for row in reader:
# Extract all fields from the CSV row
title_raw = (row.get("Title") or "").strip()
category = (row.get("Category") or "").strip()
source = (row.get("Source") or "").strip()
inventory_name = (row.get("Inventory.Name") or "").strip()
cloud_account_name = (row.get("CloudAccount.Name") or "").strip()
orca_score_raw = (row.get("OrcaScore") or "").strip()
status = (row.get("Status") or "").strip()
created_at = (row.get("CreatedAt") or "").strip()
last_seen = (row.get("LastSeen") or "").strip()
labels_raw = (row.get("Labels") or "").strip()

# Parse labels from JSON string embedded in CSV
# Orca exports labels as a JSON array within the CSV cell
labels = []
if labels_raw:
try:
labels = json.loads(labels_raw)
except (json.JSONDecodeError, TypeError):
# If JSON parsing fails, treat the raw string as a single label
labels = [labels_raw]

# Transform fields for DefectDojo
title = truncate_title(title_raw)
severity = map_orca_severity(orca_score_raw)

# Build structured description with all alert metadata
description = build_description(
title_raw, category, source, inventory_name, cloud_account_name,
orca_score_raw, status, created_at, last_seen, labels,
)

# Create the Finding object with all mapped fields
finding = Finding(
title=title,
severity=severity,
description=description,
# Preserve original OrcaScore in severity_justification
severity_justification=build_severity_justification(orca_score_raw),
static_finding=True, # CSPM scan data is static analysis
dynamic_finding=False,
service=source or None, # Source identifies the cloud resource/service
component_name=inventory_name or None, # Inventory is the specific resource
date=parse_date(created_at),
)

# Set active status based on Orca's status field
# "open" alerts are active, all other statuses (closed, resolved, etc.) are inactive
finding.active = status.lower() == "open" if status else True

# Store labels as tags for searchability in DefectDojo
if labels:
finding.unsaved_tags = labels

findings.append(finding)

return findings
Loading