Skip to content

Split Github Vulnerability Scan into separate SCA & SAST parsers #12773

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

Open
wants to merge 17 commits into
base: dev
Choose a base branch
from
Open
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,9 @@
---
title: "Github SAST Scan"
toc_hide: true
---
Import findings in JSON format from Github Code Scanning REST API:
<https://docs.github.com/en/rest/code-scanning/code-scanning>

### Sample Scan Data
Sample Github SAST scans can be found [here](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/github_sast).
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: "Github Vulnerability"
title: "Github Vulnerability Scan"
toc_hide: true
---
Import findings from Github vulnerability scan (GraphQL Query):
Expand All @@ -15,6 +15,8 @@ vulnerabilityAlerts (RepositoryVulnerabilityAlert object)
+ createdAt (optional)
+ vulnerableManifestPath
+ state (optional)
+ dependabotUpdate (DependabotUpdate object) (optional)
+ pullRequest (PullRequest object) (optional)
+ securityVulnerability (SecurityVulnerability object)
+ severity (CRITICAL/HIGH/LOW/MODERATE)
+ package (optional)
Expand All @@ -27,10 +29,17 @@ vulnerabilityAlerts (RepositoryVulnerabilityAlert object)
+ value
+ references (optional)
+ url (optional)
+ cvss (optional)
+ cvss (optional - deprecated, use cvssSeverities instead)
+ score (optional)
+ vectorString (optional)
+ cvssSeverities (optional)
+ cvssV3 (CVSS object) (optional)
+ score (optional)
+ vectorString (optional)
+ cwes (optional)
+ epss (EPSS object) (optional)
+ percentage (optional)
+ percentile (optional)
```

References:
Expand Down
12 changes: 10 additions & 2 deletions docs/content/en/open_source/upgrading/2.49.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@
title: 'Upgrading to DefectDojo Version 2.49.x'
toc_hide: true
weight: -20250707
description: No special instructions.
description: GitHub scan types split into two distinct scan types.
---
There are no special instructions for upgrading to 2.49.x. Check the [Release Notes](https://github.com/DefectDojo/django-DefectDojo/releases/tag/2.49.0) for the contents of the release.

## GitHub Scan Type and Parser Updates
The Github Vulnerability scan type and parser has been split into two disctinct scan types:
- [Github Vulnerability](https://github.com/DefectDojo/django-DefectDojo/blob/master/docs/content/en/connecting_your_tools/parsers/file/github_vulnerability.md) (original)
- [Github SAST](https://github.com/DefectDojo/django-DefectDojo/blob/master/docs/content/en/connecting_your_tools/parsers/file/github_sast.md)

The original Github Vulnerability scan type will continue to accept SCA vulnerabilities uploaded in GitHub's GraphQL format, as it has always done. However, it will no longer accept GitHub Advanced Security code scanning results. Instead, please use the new Github SAST scan type for uploading these types of vulnerabilities. This new scan type will accept the raw JSON response from [GitHub's REST API for code scanning alerts](https://docs.github.com/en/rest/code-scanning/code-scanning). Sample Github SAST scan data can be found [here](https://github.com/DefectDojo/django-DefectDojo/tree/master/unittests/scans/github_sast).

Check the [Release Notes](https://github.com/DefectDojo/django-DefectDojo/releases/tag/2.49.0) for the contents of the release.
2 changes: 2 additions & 0 deletions dojo/settings/settings.dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -1294,6 +1294,7 @@ def saml2_attrib_map_format(din):
"JFrog Xray On Demand Binary Scan": ["title", "component_name", "component_version"],
"Scout Suite Scan": ["file_path", "vuln_id_from_tool"], # for now we use file_path as there is no attribute for "service"
"Meterian Scan": ["cwe", "component_name", "component_version", "description", "severity"],
"Github SAST Scan": ["vuln_id_from_tool", "severity", "file_path", "line"],
"Github Vulnerability Scan": ["title", "severity", "component_name", "vulnerability_ids", "file_path"],
"Solar Appscreener Scan": ["title", "file_path", "line", "severity"],
"pip-audit Scan": ["vuln_id_from_tool", "component_name", "component_version"],
Expand Down Expand Up @@ -1536,6 +1537,7 @@ def saml2_attrib_map_format(din):
"Scout Suite Scan": DEDUPE_ALGO_HASH_CODE,
"AWS Security Hub Scan": DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL,
"Meterian Scan": DEDUPE_ALGO_HASH_CODE,
"Github SAST Scan": DEDUPE_ALGO_HASH_CODE,
"Github Vulnerability Scan": DEDUPE_ALGO_HASH_CODE,
"Cloudsploit Scan": DEDUPE_ALGO_HASH_CODE,
"SARIF": DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL_OR_HASH_CODE,
Expand Down
Empty file.
85 changes: 85 additions & 0 deletions dojo/tools/github_sast/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import json

from dojo.models import Finding


class GithubSASTParser:
def get_scan_types(self):
return ["Github SAST Scan"]

def get_label_for_scan_types(self, scan_type):
return scan_type

def get_description_for_scan_types(self, scan_type):
return "GitHub SAST report file can be imported in JSON format."

def get_findings(self, filename, test):
data = json.load(filename)
if not isinstance(data, list):
error_msg = "Invalid SAST report format, expected a JSON list of alerts."
raise TypeError(error_msg)

findings = []
for vuln in data:
rule = vuln.get("rule", {})
inst = vuln.get("most_recent_instance", {})
loc = inst.get("location", {})
html_url = vuln.get("html_url")
rule_id = rule.get("id")
title = f"{rule.get('description')} ({rule_id})"
severity = rule.get("security_severity_level", "Info").title()
active = vuln.get("state") == "open"

# Build description with context
desc_lines = []
if html_url:
desc_lines.append(f"GitHub Alert: [{html_url}]({html_url})")
owner = repo = None
commit_sha = inst.get("commit_sha")
if html_url:
from urllib.parse import urlparse

parsed = urlparse(html_url)
parts = parsed.path.strip("/").split("/")
# URL is /<owner>/<repo>/security/... so parts[0]=owner, parts[1]=repo
if len(parts) >= 2:
owner, repo = parts[0], parts[1]
if owner and repo and commit_sha and loc.get("path") and loc.get("start_line"):
file_link = (
f"{parsed.scheme}://{parsed.netloc}/"
f"{owner}/{repo}/blob/{commit_sha}/"
f"{loc['path']}#L{loc['start_line']}"
)
desc_lines.append(f"Location: [{loc['path']}:{loc['start_line']}]({file_link})")
elif loc.get("path") and loc.get("start_line"):
# fallback if something is missing
desc_lines.append(f"Location: {loc['path']}:{loc['start_line']}")
msg = inst.get("message", {}).get("text")
if msg:
desc_lines.append(f"Message: {msg}")
if severity:
desc_lines.append(f"Rule Severity: {severity}")
if rule.get("full_description"):
desc_lines.append(f"Description: {rule.get('full_description')}")
description = "\n".join(desc_lines)

finding = Finding(
title=title,
test=test,
description=description,
severity=severity,
active=active,
static_finding=True,
dynamic_finding=False,
vuln_id_from_tool=rule_id,
)

# File path & line
finding.file_path = loc.get("path")
finding.line = loc.get("start_line")

if html_url:
finding.url = html_url

findings.append(finding)
return findings
Loading
Loading