Skip to content
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason to have this here rather than at the top?


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