Skip to content

🔨 Merge the MobSF scanner #12501

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 10 commits into
base: dev
Choose a base branch
from

Conversation

manuel-sommer
Copy link
Contributor

@manuel-sommer manuel-sommer commented May 23, 2025

It is more userfriendly to have only one MobSF scan type to choose. Before parsing MobSF files, you have to analyse which parser is the right one.

Copy link

dryrunsecurity bot commented May 23, 2025

DryRun Security

🔴 Risk threshold exceeded.

This pull request involves configuration changes for scanner support, primarily updating MobSF Scan integration in the settings, with a potential information injection risk in the MobSF report processing that may require additional input validation and sanitization.

⚠️ Configured Codepaths Edit in dojo/db_migrations/0230_merge_mobsf.py
Vulnerability Configured Codepaths Edit
Description Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml.
⚠️ Configuration Change Risk in dojo/settings/settings.dist.py
Vulnerability Configuration Change Risk
Description The suggested vulnerability highlights a potential reduction in security monitoring capabilities due to the removal of scanner configurations for Nexpose and SonarQube. However, this is not a security vulnerability in the traditional sense, but rather an operational configuration change. The addition of the MobSF Scan configuration suggests an intentional update to the scanner ecosystem. This does not represent a security risk that requires remediation through code changes.

"Dockle Scan": ["title", "description", "vuln_id_from_tool"],
"Dependency Track Finding Packaging Format (FPF) Export": ["component_name", "component_version", "vulnerability_ids"],
"Horusec Scan": ["title", "description", "file_path", "line"],
"Tenable Scan": ["title", "severity", "vulnerability_ids", "cwe", "description"],
"Nexpose Scan": ["title", "severity", "vulnerability_ids", "cwe"],
# possible improvement: in the scanner put the library name into file_path, then dedup on cwe + file_path + severity

⚠️ Configuration Change Risk in dojo/settings/settings.dist.py
Vulnerability Configuration Change Risk
Description Similar to the previous hunk, this is an intentional configuration update for scanner support. The removal of existing scanner configurations and addition of MobSF Scan appears to be a deliberate modification to the application's scanner integration. This does not constitute a security vulnerability requiring immediate action.

"HCLAppScan XML": ["title", "description"],
"HCL AppScan on Cloud SAST XML": ["title", "file_path", "line", "severity"],
"KICS Scan": ["file_path", "line", "severity", "description", "title"],
"MobSF Scan": ["title", "description", "severity", "file_path"],
"MobSF Scorecard Scan": ["title", "description", "severity"],
"OSV Scan": ["title", "description", "severity"],
"Snyk Code Scan": ["vuln_id_from_tool", "file_path"],

⚠️ Configuration Change Risk in dojo/settings/settings.dist.py
Vulnerability Configuration Change Risk
Description Continuing the pattern of previous hunks, this configuration change is part of a coordinated update to the scanner support in the application. The changes do not introduce a security risk that would require mitigation.

"Cloudsploit Scan": True,
"SonarQube Scan": False,
"Dependency Check Scan": True,
"Tenable Scan": True,
"Nexpose Scan": True,
"NPM Audit Scan": True,

⚠️ Configuration Change Risk in dojo/settings/settings.dist.py
Vulnerability Configuration Change Risk
Description Consistent with the previous hunks, this configuration modification is part of a planned update to the application's scanner integration. It does not represent a security vulnerability.

"Crunch42 Scan": DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL,
"Dependency Track Finding Packaging Format (FPF) Export": DEDUPE_ALGO_HASH_CODE,
"Horusec Scan": DEDUPE_ALGO_HASH_CODE,
"SonarQube Scan detailed": DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL,
"SonarQube Scan": DEDUPE_ALGO_HASH_CODE,
"SonarQube API Import": DEDUPE_ALGO_HASH_CODE,

⚠️ Potential Information Injection in dojo/tools/mobsf/api_report_json.py
Vulnerability Potential Information Injection
Description The code constructs descriptions by directly incorporating fields from an external MobSF report without comprehensive sanitization. While the use of html2text() provides some mitigation, there's a potential risk of information disclosure or content manipulation if the input is maliciously crafted. The code should implement additional input validation and sanitization to prevent potential risks.

from datetime import datetime
from html2text import html2text
from dojo.models import Finding
class MobSFapireport:
def get_findings(self, data, test):
dupes = {}
find_date = datetime.now()
test_description = ""
if "name" in data:
test_description = "**Info:**\n"
if "packagename" in data:
test_description = "{} **Package Name:** {}\n".format(test_description, data["packagename"])
if "mainactivity" in data:
test_description = "{} **Main Activity:** {}\n".format(test_description, data["mainactivity"])
if "pltfm" in data:
test_description = "{} **Platform:** {}\n".format(test_description, data["pltfm"])
if "sdk" in data:
test_description = "{} **SDK:** {}\n".format(test_description, data["sdk"])
if "min" in data:
test_description = "{} **Min SDK:** {}\n".format(test_description, data["min"])
if "targetsdk" in data:
test_description = "{} **Target SDK:** {}\n".format(test_description, data["targetsdk"])
if "minsdk" in data:
test_description = "{} **Min SDK:** {}\n".format(test_description, data["minsdk"])
if "maxsdk" in data:
test_description = "{} **Max SDK:** {}\n".format(test_description, data["maxsdk"])
test_description = f"{test_description}\n**File Information:**\n"
if "name" in data:
test_description = "{} **Name:** {}\n".format(test_description, data["name"])
if "md5" in data:
test_description = "{} **MD5:** {}\n".format(test_description, data["md5"])
if "sha1" in data:
test_description = "{} **SHA-1:** {}\n".format(test_description, data["sha1"])
if "sha256" in data:
test_description = "{} **SHA-256:** {}\n".format(test_description, data["sha256"])
if "size" in data:
test_description = "{} **Size:** {}\n".format(test_description, data["size"])
if "urls" in data:
curl = ""
for url in data["urls"]:
for durl in url["urls"]:
curl = f"{durl}\n"
if curl:
test_description = f"{test_description}\n**URL's:**\n {curl}\n"
if "bin_anal" in data:
test_description = "{} \n**Binary Analysis:** {}\n".format(test_description, data["bin_anal"])
test.description = html2text(test_description)
mobsf_findings = []
# Mobile Permissions
if "permissions" in data:
# for permission, details in data["permissions"].items():
if isinstance(data["permissions"], list):
for details in data["permissions"]:
mobsf_item = {
"category": "Mobile Permissions",
"title": details.get("name", ""),
"severity": self.getSeverityForPermission(details.get("status")),
"description": "**Permission Type:** " + details.get("name", "") + " (" + details.get("status", "") + ")\n\n**Description:** " + details.get("description", "") + "\n\n**Reason:** " + details.get("reason", ""),
"file_path": None,
}
mobsf_findings.append(mobsf_item)
else:
for permission, details in list(data["permissions"].items()):
mobsf_item = {
"category": "Mobile Permissions",
"title": permission,
"severity": self.getSeverityForPermission(details.get("status", "")),
"description": "**Permission Type:** " + permission + "\n\n**Description:** " + details.get("description", ""),
"file_path": None,
}
mobsf_findings.append(mobsf_item)
# Insecure Connections
if "insecure_connections" in data:
for details in data["insecure_connections"]:
insecure_urls = ""
for url in details.split(","):
insecure_urls = insecure_urls + url + "\n"
mobsf_item = {
"category": None,
"title": "Insecure Connections",
"severity": "Low",
"description": insecure_urls,
"file_path": None,
}
mobsf_findings.append(mobsf_item)
# Certificate Analysis
if "certificate_analysis" in data:
if data["certificate_analysis"] != {}:
certificate_info = data["certificate_analysis"]["certificate_info"]
for details in data["certificate_analysis"]["certificate_findings"]:
if len(details) == 3:
mobsf_item = {
"category": "Certificate Analysis",
"title": details[2],
"severity": details[0].title(),
"description": details[1] + "\n\n**Certificate Info:** " + certificate_info,
"file_path": None,
}
mobsf_findings.append(mobsf_item)
elif len(details) == 2:
mobsf_item = {
"category": "Certificate Analysis",
"title": details[1],
"severity": details[0].title(),
"description": details[1] + "\n\n**Certificate Info:** " + certificate_info,
"file_path": None,
}
mobsf_findings.append(mobsf_item)
# Manifest Analysis
if "manifest_analysis" in data:
if data["manifest_analysis"] != {} and isinstance(data["manifest_analysis"], dict):
if data["manifest_analysis"]["manifest_findings"]:
for details in data["manifest_analysis"]["manifest_findings"]:
mobsf_item = {
"category": "Manifest Analysis",
"title": details["title"],
"severity": details["severity"].title(),
"description": details["description"] + "\n\n " + details["name"],
"file_path": None,
}
mobsf_findings.append(mobsf_item)
else:
for details in data["manifest_analysis"]:
mobsf_item = {
"category": "Manifest Analysis",
"title": details["title"],
"severity": details["stat"].title(),
"description": details["desc"] + "\n\n " + details["name"],
"file_path": None,
}
mobsf_findings.append(mobsf_item)
# Code Analysis
if "code_analysis" in data:
if data["code_analysis"] != {}:
if data["code_analysis"].get("findings"):
for details in data["code_analysis"]["findings"]:
metadata = data["code_analysis"]["findings"][details]
mobsf_item = {
"category": "Code Analysis",
"title": details,
"severity": metadata["metadata"]["severity"].title(),
"description": metadata["metadata"]["description"],
"file_path": None,
}
mobsf_findings.append(mobsf_item)
else:
for details in data["code_analysis"]:
metadata = data["code_analysis"][details]
if metadata.get("metadata"):
mobsf_item = {
"category": "Code Analysis",
"title": details,
"severity": metadata["metadata"]["severity"].title(),
"description": metadata["metadata"]["description"],
"file_path": None,
}
mobsf_findings.append(mobsf_item)
# Binary Analysis
if "binary_analysis" in data:
if isinstance(data["binary_analysis"], list):
for details in data["binary_analysis"]:
for binary_analysis_type in details:
if binary_analysis_type != "name":
mobsf_item = {
"category": "Binary Analysis",
"title": details[binary_analysis_type]["description"].split(".")[0],
"severity": details[binary_analysis_type]["severity"].title(),
"description": details[binary_analysis_type]["description"],
"file_path": details["name"],
}
mobsf_findings.append(mobsf_item)
elif data["binary_analysis"].get("findings"):
for details in data["binary_analysis"]["findings"].values():
# "findings":{
# "Binary makes use of insecure API(s)":{
# "detailed_desc":"The binary may contain the following insecure API(s) _memcpy\n, _strlen\n",
# "severity":"high",
# "cvss":6,
# "cwe":"CWE-676: Use of Potentially Dangerous Function",
# "owasp-mobile":"M7: Client Code Quality",
# "masvs":"MSTG-CODE-8"
# },
mobsf_item = {
"category": "Binary Analysis",
"title": details["detailed_desc"],
"severity": details["severity"].title(),
"description": details["detailed_desc"],
"file_path": None,
}
mobsf_findings.append(mobsf_item)
else:
for details in data["binary_analysis"].values():
# "Binary makes use of insecure API(s)":{
# "detailed_desc":"The binary may contain the following insecure API(s) _vsprintf.",
# "severity":"high",
# "cvss":6,
# "cwe":"CWE-676 - Use of Potentially Dangerous Function",
# "owasp-mobile":"M7: Client Code Quality",
# "masvs":"MSTG-CODE-8"
# }
mobsf_item = {
"category": "Binary Analysis",
"title": details["detailed_desc"],
"severity": details["severity"].title(),
"description": details["detailed_desc"],
"file_path": None,
}
mobsf_findings.append(mobsf_item)
# specific node for Android reports
if "android_api" in data:
# "android_insecure_random": {
# "files": {
# "u/c/a/b/a/c.java": "9",
# "kotlinx/coroutines/repackaged/net/bytebuddy/utility/RandomString.java": "3",
# ...
# "hu/mycompany/vbnmqweq/gateway/msg/Response.java": "13"
# },
# "metadata": {
# "id": "android_insecure_random",
# "description": "The App uses an insecure Random Number Generator.",
# "type": "Regex",
# "pattern": "java\\.util\\.Random;",
# "severity": "high",
# "input_case": "exact",
# "cvss": 7.5,
# "cwe": "CWE-330 Use of Insufficiently Random Values",
# "owasp-mobile": "M5: Insufficient Cryptography",
# "masvs": "MSTG-CRYPTO-6"
# }
# },
for api, details in list(data["android_api"].items()):
mobsf_item = {
"category": "Android API",
"title": details["metadata"]["description"],
"severity": details["metadata"]["severity"].title(),
"description": "**API:** " + api + "\n\n**Description:** " + details["metadata"]["description"],
"file_path": None,
}
mobsf_findings.append(mobsf_item)
# Manifest
if "manifest" in data:
for details in data["manifest"]:
mobsf_item = {
"category": "Manifest",
"title": details["title"],
"severity": details["stat"],
"description": details["desc"],
"file_path": None,
}
mobsf_findings.append(mobsf_item)
# MobSF Findings
if "findings" in data:
for title, finding in list(data["findings"].items()):
description = title
file_path = None
if "path" in finding:
description += "\n\n**Files:**\n"
for path in finding["path"]:
if file_path is None:
file_path = path
description = description + " * " + path + "\n"
mobsf_item = {
"category": "Findings",
"title": title,
"severity": finding["level"],
"description": description,
"file_path": file_path,
}
mobsf_findings.append(mobsf_item)
if isinstance(data, list):
for finding in data:
mobsf_item = {
"category": finding["category"],
"title": finding["name"],
"severity": finding["severity"],
"description": finding["description"] + "\n" + "**apk_exploit_dict:** " + str(finding["apk_exploit_dict"]) + "\n" + "**line_number:** " + str(finding["line_number"]),
"file_path": finding["file_object"],
}
mobsf_findings.append(mobsf_item)
for mobsf_finding in mobsf_findings:
title = mobsf_finding["title"]
sev = self.getCriticalityRating(mobsf_finding["severity"])
description = ""
file_path = None
if mobsf_finding["category"]:
description += "**Category:** " + mobsf_finding["category"] + "\n\n"
description += html2text(mobsf_finding["description"])
finding = Finding(
title=title,
cwe=919, # Weaknesses in Mobile Applications
test=test,
description=description,
severity=sev,
references=None,
date=find_date,
static_finding=True,
dynamic_finding=False,
nb_occurences=1,
)
if mobsf_finding["file_path"]:
finding.file_path = mobsf_finding["file_path"]
dupe_key = sev + title + description + mobsf_finding["file_path"]
else:
dupe_key = sev + title + description
if mobsf_finding["category"]:
dupe_key += mobsf_finding["category"]
if dupe_key in dupes:
find = dupes[dupe_key]
if description is not None:
find.description += description
find.nb_occurences += 1
else:
dupes[dupe_key] = finding
return list(dupes.values())
def getSeverityForPermission(self, status):
"""
Convert status for permission detection to severity
In MobSF there is only 4 know values for permission,
we map them as this:
dangerous => High (Critical?)
normal => Info
signature => Info (it's positive so... Info)
signatureOrSystem => Info (it's positive so... Info)
"""
if status == "dangerous":
return "High"
return "Info"
# Criticality rating
def getCriticalityRating(self, rating):
criticality = "Info"
if rating.lower() == "good":
criticality = "Info"
elif rating.lower() == "warning":
criticality = "Low"
elif rating.lower() == "vulnerability":
criticality = "Medium"
else:
criticality = rating.lower().capitalize()
return criticality
def suite_data(self, suites):
suite_info = ""
suite_info += suites["name"] + "\n"
suite_info += "Cipher Strength: " + str(suites["cipherStrength"]) + "\n"
if "ecdhBits" in suites:
suite_info += "ecdhBits: " + str(suites["ecdhBits"]) + "\n"
if "ecdhStrength" in suites:
suite_info += "ecdhStrength: " + str(suites["ecdhStrength"])
suite_info += "\n\n"
return suite_info

We've notified @mtesauro.


All finding details can be found in the DryRun Security Dashboard.

@github-actions github-actions bot added the New Migration Adding a new migration file. Take care when merging. label May 26, 2025
@valentijnscholten
Copy link
Member

I notice 3 MobSF entries in settings.dist.py

@github-actions github-actions bot added the settings_changes Needs changes to settings.py based on changes in settings.dist.py included in this PR label May 26, 2025
Copy link
Contributor

github-actions bot commented Jun 2, 2025

This pull request has conflicts, please resolve those before we can evaluate the pull request.

Copy link
Contributor

github-actions bot commented Jun 2, 2025

Conflicts have been resolved. A maintainer will review the pull request shortly.

Copy link
Member

@valentijnscholten valentijnscholten left a comment

Choose a reason for hiding this comment

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

I looked at the mobsfscan github repo and website. It looks like there are two ways to generate reports. One via the CLI, the other via the API (of the SAAS platform?). And we used to have two parsers for them and this PR merges them into 1 parser?

If this is correct, could you add a little of this info to the docs and/or description of the PR?

Could you also add instructions to the upgrade notes about the removal of the Mobsfscan Scan scan type and the new value to be used (MobSF Scan)

@manuel-sommer
Copy link
Contributor Author

manuel-sommer commented Jun 10, 2025

I looked at the mobsfscan github repo and website. It looks like there are two ways to generate reports. One via the CLI, the other via the API (of the SAAS platform?). And we used to have two parsers for them and this PR merges them into 1 parser?

If this is correct, could you add a little of this info to the docs and/or description of the PR?

Could you also add instructions to the upgrade notes about the removal of the Mobsfscan Scan scan type and the new value to be used (MobSF Scan)

The thing is that I checked out MobSF and I wanted to import it to Defectdojo, but was confused about two parsers being listed. Thus, I am not really familiar with MobSF, but that PR helps beginners.
I will add upgrade instructions.

Copy link

DryRun Security

🔴 Risk threshold exceeded.

This pull request involves refactoring scan type processing for MobSF and Mobsfscan, with multiple configuration changes in settings files and a potential information disclosure in error reporting that appears to be part of a structured diagnostic process. The changes seem intentional and focused on consolidating scan type handling.

⚠️ Degradation of Security Data Processing in dojo/settings/settings.dist.py
Vulnerability Degradation of Security Data Processing
Description The changes in the settings files appear to be part of a deliberate refactoring of scan type processing, specifically related to MobSF and Mobsfscan scan types. The removals of specific scan type configurations do not inherently represent a security vulnerability, but rather a planned consolidation of scan type handling.

"Dockle Scan": ["title", "description", "vuln_id_from_tool"],
"Dependency Track Finding Packaging Format (FPF) Export": ["component_name", "component_version", "vulnerability_ids"],
"Horusec Scan": ["title", "description", "file_path", "line"],
"Tenable Scan": ["title", "severity", "vulnerability_ids", "cwe", "description"],
"Nexpose Scan": ["title", "severity", "vulnerability_ids", "cwe"],
# possible improvement: in the scanner put the library name into file_path, then dedup on cwe + file_path + severity

⚠️ Degradation of Security Data Processing in dojo/settings/settings.dist.py
Vulnerability Degradation of Security Data Processing
Description The changes in the settings files appear to be part of a deliberate refactoring of scan type processing, specifically related to MobSF and Mobsfscan scan types. The removals of specific scan type configurations do not inherently represent a security vulnerability, but rather a planned consolidation of scan type handling.

"HCLAppScan XML": ["title", "description"],
"HCL AppScan on Cloud SAST XML": ["title", "file_path", "line", "severity"],
"KICS Scan": ["file_path", "line", "severity", "description", "title"],
"MobSF Scan": ["title", "description", "severity", "file_path"],
"MobSF Scorecard Scan": ["title", "description", "severity"],
"OSV Scan": ["title", "description", "severity"],
"Snyk Code Scan": ["vuln_id_from_tool", "file_path"],

⚠️ Potential Information Disclosure in dojo/tools/mobsf/api_report_json.py
Vulnerability Potential Information Disclosure
Description While the code does include potentially sensitive information in error descriptions, the context suggests this is part of a structured report generation process. The str() conversion of apk_exploit_dict and line_number is unlikely to expose critical secrets. The method appears designed to provide detailed diagnostic information for security analysis, which is typically intentional in security scanning tools.

from datetime import datetime
from html2text import html2text
from dojo.models import Finding
class MobSFapireport:
def get_findings(self, data, test):
dupes = {}
find_date = datetime.now()
test_description = ""
if "name" in data:
test_description = "**Info:**\n"
if "packagename" in data:
test_description = "{} **Package Name:** {}\n".format(test_description, data["packagename"])
if "mainactivity" in data:
test_description = "{} **Main Activity:** {}\n".format(test_description, data["mainactivity"])
if "pltfm" in data:
test_description = "{} **Platform:** {}\n".format(test_description, data["pltfm"])
if "sdk" in data:
test_description = "{} **SDK:** {}\n".format(test_description, data["sdk"])
if "min" in data:
test_description = "{} **Min SDK:** {}\n".format(test_description, data["min"])
if "targetsdk" in data:
test_description = "{} **Target SDK:** {}\n".format(test_description, data["targetsdk"])
if "minsdk" in data:
test_description = "{} **Min SDK:** {}\n".format(test_description, data["minsdk"])
if "maxsdk" in data:
test_description = "{} **Max SDK:** {}\n".format(test_description, data["maxsdk"])
test_description = f"{test_description}\n**File Information:**\n"
if "name" in data:
test_description = "{} **Name:** {}\n".format(test_description, data["name"])
if "md5" in data:
test_description = "{} **MD5:** {}\n".format(test_description, data["md5"])
if "sha1" in data:
test_description = "{} **SHA-1:** {}\n".format(test_description, data["sha1"])
if "sha256" in data:
test_description = "{} **SHA-256:** {}\n".format(test_description, data["sha256"])
if "size" in data:
test_description = "{} **Size:** {}\n".format(test_description, data["size"])
if "urls" in data:
curl = ""
for url in data["urls"]:
for durl in url["urls"]:
curl = f"{durl}\n"
if curl:
test_description = f"{test_description}\n**URL's:**\n {curl}\n"
if "bin_anal" in data:
test_description = "{} \n**Binary Analysis:** {}\n".format(test_description, data["bin_anal"])
test.description = html2text(test_description)
mobsf_findings = []
# Mobile Permissions
if "permissions" in data:
# for permission, details in data["permissions"].items():
if isinstance(data["permissions"], list):
for details in data["permissions"]:
mobsf_item = {
"category": "Mobile Permissions",
"title": details.get("name", ""),
"severity": self.getSeverityForPermission(details.get("status")),
"description": "**Permission Type:** " + details.get("name", "") + " (" + details.get("status", "") + ")\n\n**Description:** " + details.get("description", "") + "\n\n**Reason:** " + details.get("reason", ""),
"file_path": None,
}
mobsf_findings.append(mobsf_item)
else:
for permission, details in list(data["permissions"].items()):
mobsf_item = {
"category": "Mobile Permissions",
"title": permission,
"severity": self.getSeverityForPermission(details.get("status", "")),
"description": "**Permission Type:** " + permission + "\n\n**Description:** " + details.get("description", ""),
"file_path": None,
}
mobsf_findings.append(mobsf_item)
# Insecure Connections
if "insecure_connections" in data:
for details in data["insecure_connections"]:
insecure_urls = ""
for url in details.split(","):
insecure_urls = insecure_urls + url + "\n"
mobsf_item = {
"category": None,
"title": "Insecure Connections",
"severity": "Low",
"description": insecure_urls,
"file_path": None,
}
mobsf_findings.append(mobsf_item)
# Certificate Analysis
if "certificate_analysis" in data:
if data["certificate_analysis"] != {}:
certificate_info = data["certificate_analysis"]["certificate_info"]
for details in data["certificate_analysis"]["certificate_findings"]:
if len(details) == 3:
mobsf_item = {
"category": "Certificate Analysis",
"title": details[2],
"severity": details[0].title(),
"description": details[1] + "\n\n**Certificate Info:** " + certificate_info,
"file_path": None,
}
mobsf_findings.append(mobsf_item)
elif len(details) == 2:
mobsf_item = {
"category": "Certificate Analysis",
"title": details[1],
"severity": details[0].title(),
"description": details[1] + "\n\n**Certificate Info:** " + certificate_info,
"file_path": None,
}
mobsf_findings.append(mobsf_item)
# Manifest Analysis
if "manifest_analysis" in data:
if data["manifest_analysis"] != {} and isinstance(data["manifest_analysis"], dict):
if data["manifest_analysis"]["manifest_findings"]:
for details in data["manifest_analysis"]["manifest_findings"]:
mobsf_item = {
"category": "Manifest Analysis",
"title": details["title"],
"severity": details["severity"].title(),
"description": details["description"] + "\n\n " + details["name"],
"file_path": None,
}
mobsf_findings.append(mobsf_item)
else:
for details in data["manifest_analysis"]:
mobsf_item = {
"category": "Manifest Analysis",
"title": details["title"],
"severity": details["stat"].title(),
"description": details["desc"] + "\n\n " + details["name"],
"file_path": None,
}
mobsf_findings.append(mobsf_item)
# Code Analysis
if "code_analysis" in data:
if data["code_analysis"] != {}:
if data["code_analysis"].get("findings"):
for details in data["code_analysis"]["findings"]:
metadata = data["code_analysis"]["findings"][details]
mobsf_item = {
"category": "Code Analysis",
"title": details,
"severity": metadata["metadata"]["severity"].title(),
"description": metadata["metadata"]["description"],
"file_path": None,
}
mobsf_findings.append(mobsf_item)
else:
for details in data["code_analysis"]:
metadata = data["code_analysis"][details]
if metadata.get("metadata"):
mobsf_item = {
"category": "Code Analysis",
"title": details,
"severity": metadata["metadata"]["severity"].title(),
"description": metadata["metadata"]["description"],
"file_path": None,
}
mobsf_findings.append(mobsf_item)
# Binary Analysis
if "binary_analysis" in data:
if isinstance(data["binary_analysis"], list):
for details in data["binary_analysis"]:
for binary_analysis_type in details:
if binary_analysis_type != "name":
mobsf_item = {
"category": "Binary Analysis",
"title": details[binary_analysis_type]["description"].split(".")[0],
"severity": details[binary_analysis_type]["severity"].title(),
"description": details[binary_analysis_type]["description"],
"file_path": details["name"],
}
mobsf_findings.append(mobsf_item)
elif data["binary_analysis"].get("findings"):
for details in data["binary_analysis"]["findings"].values():
# "findings":{
# "Binary makes use of insecure API(s)":{
# "detailed_desc":"The binary may contain the following insecure API(s) _memcpy\n, _strlen\n",
# "severity":"high",
# "cvss":6,
# "cwe":"CWE-676: Use of Potentially Dangerous Function",
# "owasp-mobile":"M7: Client Code Quality",
# "masvs":"MSTG-CODE-8"
# },
mobsf_item = {
"category": "Binary Analysis",
"title": details["detailed_desc"],
"severity": details["severity"].title(),
"description": details["detailed_desc"],
"file_path": None,
}
mobsf_findings.append(mobsf_item)
else:
for details in data["binary_analysis"].values():
# "Binary makes use of insecure API(s)":{
# "detailed_desc":"The binary may contain the following insecure API(s) _vsprintf.",
# "severity":"high",
# "cvss":6,
# "cwe":"CWE-676 - Use of Potentially Dangerous Function",
# "owasp-mobile":"M7: Client Code Quality",
# "masvs":"MSTG-CODE-8"
# }
mobsf_item = {
"category": "Binary Analysis",
"title": details["detailed_desc"],
"severity": details["severity"].title(),
"description": details["detailed_desc"],
"file_path": None,
}
mobsf_findings.append(mobsf_item)
# specific node for Android reports
if "android_api" in data:
# "android_insecure_random": {
# "files": {
# "u/c/a/b/a/c.java": "9",
# "kotlinx/coroutines/repackaged/net/bytebuddy/utility/RandomString.java": "3",
# ...
# "hu/mycompany/vbnmqweq/gateway/msg/Response.java": "13"
# },
# "metadata": {
# "id": "android_insecure_random",
# "description": "The App uses an insecure Random Number Generator.",
# "type": "Regex",
# "pattern": "java\\.util\\.Random;",
# "severity": "high",
# "input_case": "exact",
# "cvss": 7.5,
# "cwe": "CWE-330 Use of Insufficiently Random Values",
# "owasp-mobile": "M5: Insufficient Cryptography",
# "masvs": "MSTG-CRYPTO-6"
# }
# },
for api, details in list(data["android_api"].items()):
mobsf_item = {
"category": "Android API",
"title": details["metadata"]["description"],
"severity": details["metadata"]["severity"].title(),
"description": "**API:** " + api + "\n\n**Description:** " + details["metadata"]["description"],
"file_path": None,
}
mobsf_findings.append(mobsf_item)
# Manifest
if "manifest" in data:
for details in data["manifest"]:
mobsf_item = {
"category": "Manifest",
"title": details["title"],
"severity": details["stat"],
"description": details["desc"],
"file_path": None,
}
mobsf_findings.append(mobsf_item)
# MobSF Findings
if "findings" in data:
for title, finding in list(data["findings"].items()):
description = title
file_path = None
if "path" in finding:
description += "\n\n**Files:**\n"
for path in finding["path"]:
if file_path is None:
file_path = path
description = description + " * " + path + "\n"
mobsf_item = {
"category": "Findings",
"title": title,
"severity": finding["level"],
"description": description,
"file_path": file_path,
}
mobsf_findings.append(mobsf_item)
if isinstance(data, list):
for finding in data:
mobsf_item = {
"category": finding["category"],
"title": finding["name"],
"severity": finding["severity"],
"description": finding["description"] + "\n" + "**apk_exploit_dict:** " + str(finding["apk_exploit_dict"]) + "\n" + "**line_number:** " + str(finding["line_number"]),
"file_path": finding["file_object"],
}
mobsf_findings.append(mobsf_item)
for mobsf_finding in mobsf_findings:
title = mobsf_finding["title"]
sev = self.getCriticalityRating(mobsf_finding["severity"])
description = ""
file_path = None
if mobsf_finding["category"]:
description += "**Category:** " + mobsf_finding["category"] + "\n\n"
description += html2text(mobsf_finding["description"])
finding = Finding(
title=title,
cwe=919, # Weaknesses in Mobile Applications
test=test,
description=description,
severity=sev,
references=None,
date=find_date,
static_finding=True,
dynamic_finding=False,
nb_occurences=1,
)
if mobsf_finding["file_path"]:
finding.file_path = mobsf_finding["file_path"]
dupe_key = sev + title + description + mobsf_finding["file_path"]
else:
dupe_key = sev + title + description
if mobsf_finding["category"]:
dupe_key += mobsf_finding["category"]
if dupe_key in dupes:
find = dupes[dupe_key]
if description is not None:
find.description += description
find.nb_occurences += 1
else:
dupes[dupe_key] = finding
return list(dupes.values())
def getSeverityForPermission(self, status):
"""
Convert status for permission detection to severity
In MobSF there is only 4 know values for permission,
we map them as this:
dangerous => High (Critical?)
normal => Info
signature => Info (it's positive so... Info)
signatureOrSystem => Info (it's positive so... Info)
"""
if status == "dangerous":
return "High"
return "Info"
# Criticality rating
def getCriticalityRating(self, rating):
criticality = "Info"
if rating.lower() == "good":
criticality = "Info"
elif rating.lower() == "warning":
criticality = "Low"
elif rating.lower() == "vulnerability":
criticality = "Medium"
else:
criticality = rating.lower().capitalize()
return criticality
def suite_data(self, suites):
suite_info = ""
suite_info += suites["name"] + "\n"
suite_info += "Cipher Strength: " + str(suites["cipherStrength"]) + "\n"
if "ecdhBits" in suites:
suite_info += "ecdhBits: " + str(suites["ecdhBits"]) + "\n"
if "ecdhStrength" in suites:
suite_info += "ecdhStrength: " + str(suites["ecdhStrength"])
suite_info += "\n\n"
return suite_info

⚠️ Degradation of Security Data Processing in dojo/settings/settings.dist.py
Vulnerability Degradation of Security Data Processing
Description The changes in the settings files appear to be part of a deliberate refactoring of scan type processing, specifically related to MobSF and Mobsfscan scan types. The removals of specific scan type configurations do not inherently represent a security vulnerability, but rather a planned consolidation of scan type handling.

"Cloudsploit Scan": True,
"SonarQube Scan": False,
"Dependency Check Scan": True,
"Tenable Scan": True,
"Nexpose Scan": True,
"NPM Audit Scan": True,

⚠️ Degradation of Security Data Processing in dojo/settings/settings.dist.py
Vulnerability Degradation of Security Data Processing
Description The changes in the settings files appear to be part of a deliberate refactoring of scan type processing, specifically related to MobSF and Mobsfscan scan types. The removals of specific scan type configurations do not inherently represent a security vulnerability, but rather a planned consolidation of scan type handling.

"Crunch42 Scan": DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL,
"Dependency Track Finding Packaging Format (FPF) Export": DEDUPE_ALGO_HASH_CODE,
"Horusec Scan": DEDUPE_ALGO_HASH_CODE,
"SonarQube Scan detailed": DEDUPE_ALGO_UNIQUE_ID_FROM_TOOL,
"SonarQube Scan": DEDUPE_ALGO_HASH_CODE,
"SonarQube API Import": DEDUPE_ALGO_HASH_CODE,

⚠️ Configured Codepaths Edit in dojo/db_migrations/0230_merge_mobsf.py
Vulnerability Configured Codepaths Edit
Description Sensitive edits detected for this file. Sensitive file paths and allowed authors can be configured in .dryrunsecurity.yaml.

We've notified @mtesauro.


All finding details can be found in the DryRun Security Dashboard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs New Migration Adding a new migration file. Take care when merging. parser settings_changes Needs changes to settings.py based on changes in settings.dist.py included in this PR unittests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants