Skip to content
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

Add parser for Talisman #7933

Merged
merged 1 commit into from
Apr 10, 2023
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
41 changes: 41 additions & 0 deletions docs/content/en/integrations/parsers/file/talisman.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
title: "Talisman"
toc_hide: true
---
Run [Talisman](https://github.com/thoughtworks/talisman) in CLI mode and use "**--scan**" argument to scan the git commit history along with "**--reportDirectory**" argument to save the scan reports to a directory. The report will be in JSON format.

Additionally, you can set up Git Hooks to automate the scan and then send the generated reports to DefectDojo using its API.

Example:

```bash
#!/bin/sh

# Set DefectDojo API credential and other variables
DEFECTDOJO_API_KEY="your-api-key"
DEFECTDOJO_URL="https://your-defectdojo-url.com"
TALISMAN_RESULTS_DIR="$HOME"

# Run talisman in CLI mode and output the result in JSON format
CMD="talisman --scan --ignoreHistory --reportDirectory $TALISMAN_RESULTS_DIR"
$CMD

# Extract the result
result=$(jq '.results[].filename' "${TALISMAN_RESULTS_DIR}/talisman_reports/data/report.json")

# Check if result is not empty
if [ -n "$result" ]; then
# If talisman found issues, send the JSON output to DefectDojo API endpoint
curl -X POST \
-H "Authorization: Token $DEFECTDOJO_API_KEY" \
-H "Content-Type: application/json" \
-d "@$TALISMAN_RESULTS_DIR/talisman_reports/data/report.json" \
"$DEFECTDOJO_URL/api/v2/import-scan/"

# Exit with a non-zero status code to indicate that the commit should be rejected
exit 1
else
# If talisman did not find any issues, exit with a zero status code
exit 0
fi
```
Empty file added dojo/tools/talisman/__init__.py
Empty file.
80 changes: 80 additions & 0 deletions dojo/tools/talisman/parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import hashlib
import json

from dojo.models import Finding


class TalismanParser(object):
"""
A class that can be used to parse the Talisman JSON report files
"""

def get_scan_types(self):
"""
Get scan type
"""
return ["Talisman Scan"]

def get_label_for_scan_types(self, scan_type):
"""
Get label for scan type
"""
return scan_type

def get_description_for_scan_types(self, scan_type):
"""
Get description for scan type
"""
return "Import Talisman Scan findings in JSON format."

def get_findings(self, filename, test):
"""
Converts a Talisman JSON report to DefectDojo findings
"""
if filename is None:
return list()

json_data = json.load(filename)
results = json_data.get("results")

dupes = {}

for result in results:
file_path = result["filename"]
for issue in result["failure_list"]:
if issue["commits"]:
message = issue["message"]
commit_ids = issue["commits"]
severity = issue["severity"].capitalize()
title = f"Secret pattern found in {file_path} file"

description = ""
if file_path:
description += f"**File path:** {file_path}\n"
if severity:
description += f"**Severity:** {severity}\n"
if message:
description += f"**Message:** {message}\n"
if commit_ids:
description += f"**Commit hash:** {commit_ids}\n"

finding = Finding(
title=title,
test=test,
description=description,
cwe=798,
file_path=file_path,
dynamic_finding=False,
static_finding=True,
severity=severity,
)

key = hashlib.md5(
(title + message + file_path + description + severity).encode(
"utf-8"
)
).hexdigest()

if key not in dupes:
dupes[key] = finding
return list(dupes.values())
73 changes: 73 additions & 0 deletions unittests/scans/talisman/many_findings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{
"summary": {
"types": {
"filecontent": 5,
"filesize": 0,
"filename": 0,
"warnings": 0,
"ignores": 0
}
},
"results": [
{
"filename": "talisman_report/talisman_reports/data/report.json",
"failure_list": [
{
"type": "filecontent",
"message": "Expected file to not to contain hex encoded texts such as: aws_secret=\\\"AKIAIMNOJVGFDXXXE4UI\\\"\",\"commits\":...",
"commits": [],
"severity": "high"
},
{
"type": "filecontent",
"message": "Expected file to not to contain hex encoded texts such as: aws_secret=\\\"AKIAIMNOJVGFDXXXE4MA\\\"\",\"commits\":...",
"commits": [],
"severity": "high"
},
{
"type": "filecontent",
"message": "Potential secret pattern : {\"summary\":{\"types\":{\"filecontent\":2,\"filesize\":0,\"filename\":0,\"warnings\":0,\"ignores\":0}},\"results\":[{\"filename\":\"talisman/tools/gitleaks/findings.txt\",\"failure_list\":[{\"type\":\"filecontent\",\"message\":\"Potential secret pattern : aws_secret=\\\"AKIAIMNOJVGFDXXXE4UI\\\"\",\"commits\":[\"ccb9316f83ec989ec3e565dba290b2041491799e\"],\"severity\":\"low\"}],\"warning_list\":[],\"ignore_list\":[]},{\"filename\":\"README.md\",\"failure_list\":[{\"type\":\"filecontent\",\"message\":\"Potential secret pattern : aws_secret=\\\"AKIAIMNOJVGFDXXXE4MA\\\"\",\"commits\":[\"ccb9316f83ec989ec3e565dba290b2041491799e\"",
"commits": [
"4261e6743687ca774c35d969531fee48b2a4b70d"
],
"severity": "low"
}
],
"warning_list": [],
"ignore_list": []
},
{
"filename": "README.md",
"failure_list": [
{
"type": "filecontent",
"message": "Potential secret pattern : aws_secret=\"AKIAIMNOJVGFDXXXE4MA\"",
"commits": [
"320de646d26477d2ec547e5af1c3251a5484efa9",
"4261e6743687ca774c35d969531fee48b2a4b70d",
"ccb9316f83ec989ec3e565dba290b2041491799e"
],
"severity": "low"
}
],
"warning_list": [],
"ignore_list": []
},
{
"filename": "talisman/tools/gitleaks/findings.txt",
"failure_list": [
{
"type": "filecontent",
"message": "Potential secret pattern : aws_secret=\"AKIAIMNOJVGFDXXXE4UI\"",
"commits": [
"4261e6743687ca774c35d969531fee48b2a4b70d",
"ccb9316f83ec989ec3e565dba290b2041491799e"
],
"severity": "low"
}
],
"warning_list": [],
"ignore_list": []
}
]
}
12 changes: 12 additions & 0 deletions unittests/scans/talisman/no_finding.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"summary": {
"types": {
"filecontent": 0,
"filesize": 0,
"filename": 0,
"warnings": 0,
"ignores": 0
}
},
"results": []
}
28 changes: 28 additions & 0 deletions unittests/scans/talisman/one_finding.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"summary": {
"types": {
"filecontent": 0,
"filesize": 0,
"filename": 1,
"warnings": 0,
"ignores": 0
}
},
"results": [
{
"filename": "password.html",
"failure_list": [
{
"type": "filename",
"message": "The file name \"password.html\" failed checks against the pattern password",
"commits": [
"0ab760b933186f5490c5699fb5ec777d4b6a5bc4"
],
"severity": "low"
}
],
"warning_list": [],
"ignore_list": []
}
]
}
44 changes: 44 additions & 0 deletions unittests/tools/test_talisman_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from dojo.models import Test
from dojo.tools.talisman.parser import TalismanParser

from ..dojo_test_case import DojoTestCase


class TestTalismanParser(DojoTestCase):
def test_parse_empty(self):
testfile = open("unittests/scans/talisman/no_finding.json")
parser = TalismanParser()
findings = parser.get_findings(testfile, Test())
testfile.close()
self.assertEqual(0, len(findings))

def test_parse_one_finding(self):
testfile = open("unittests/scans/talisman/one_finding.json")
parser = TalismanParser()
findings = parser.get_findings(testfile, Test())
testfile.close()
self.assertEqual(1, len(findings))
finding = findings[0]
self.assertEqual("password.html", finding.file_path)
self.assertEqual("Secret pattern found in password.html file", finding.title)
self.assertIsNotNone(finding.description)

def test_parse_many_finding(self):
testfile = open("unittests/scans/talisman/many_findings.json")
parser = TalismanParser()
findings = parser.get_findings(testfile, Test())
testfile.close()
self.assertEqual(3, len(findings))
finding = findings[0]
self.assertEqual(
"talisman_report/talisman_reports/data/report.json", finding.file_path
)
self.assertEqual(
"Secret pattern found in talisman_report/talisman_reports/data/report.json file",
finding.title,
)
self.assertIsNotNone(finding.description)
finding = findings[1]
self.assertEqual("README.md", finding.file_path)
self.assertEqual("Secret pattern found in README.md file", finding.title)
self.assertIsNotNone(finding.description)