Data Model Relationships in MobileAudit: Applications, Scans, Findings, and Metadata Explained
MobileAudit implements a hierarchical Django ORM structure where Applications own multiple Scans, which generate Findings linked to Patterns, CWEs, and Risks, while auxiliary metadata like Permissions and Components hang off Scans via ForeignKey relationships.
The open-source MobileAudit project (mpast/mobileaudit) provides a Django-based framework for static analysis of Android APK files. Understanding the data model relationship between Applications, Scans, Findings, and their associated metadata is essential for developers integrating with the API or extending the platform's security reporting capabilities. The schema defined in app/models.py creates a strict parent-child hierarchy that mirrors real-world mobile security audit workflows.
Core Entity Hierarchy
The system models mobile app security analysis as a three-tier hierarchy. At the root, an Application represents the mobile app being analyzed. Each Scan captures a specific analysis run against an APK binary. Every Finding represents an individual security issue discovered during that scan. This cascade ensures complete traceability from a vulnerability back to the specific application version that introduced it.
Application to Scan Relationship
Each Scan belongs to exactly one Application via a ForeignKey, while an Application can host multiple Scans over time. In app/models.py (lines 59-87), the Scan model declares:
class Scan(models.Model):
app = models.ForeignKey(Application, on_delete=models.CASCADE)
This one-to-many relationship enables version tracking and regression testing. When an Application is deleted, Django’s cascade configuration automatically purges all associated Scans and their downstream data. The Scan model stores the APK file, version info, package name, hash digests, and auto-extracted manifest data.
Scan to Finding Relationship
Findings represent individual security issues discovered during static analysis. Each Finding belongs to exactly one Scan, establishing a clear audit trail. The Finding model (lines 17-44 in app/models.py) implements:
class Finding(models.Model):
scan = models.ForeignKey(Scan, on_delete=models.CASCADE)
This ensures that deleting a Scan removes all associated Findings. The model captures precise location data (path, line_number), severity, status, and descriptive evidence (snippet, description, mitigation).
Finding Classification Metadata
Findings link to three critical classification models that provide vulnerability context. The Finding model contains ForeignKey fields pointing to taxonomy definitions (also in app/models.py):
class Finding(models.Model):
type = models.ForeignKey(Pattern, on_delete=models.CASCADE, blank=True, null=True)
cwe = models.ForeignKey(Cwe, on_delete=models.CASCADE)
risk = models.ForeignKey(Risk, on_delete=models.CASCADE, null=True)
- Pattern: The detection rule (
type) that triggered the match. Nullable to support manual findings. - Cwe: The Common Weakness Enumeration identifier (e.g., CWE-79 for XSS).
- Risk: The severity rating normalized to MobileAudit’s risk framework.
Auxiliary Metadata Models
Beyond the core triad, MobileAudit captures extensive APK metadata through specialized models. These maintain ForeignKey relationships to either Scan (for application-wide data) or Finding (for vulnerability-specific evidence).
Scan-Level Attack Surface Data
Static analysis extracts application-wide metadata during the Scan process. The Permission, Component, IntentFilter, and Certificate models link to the parent Scan. For example, the Permission model (lines 64-72) declares:
class Permission(models.Model):
scan = models.ForeignKey(Scan, on_delete=models.CASCADE)
permission = models.ForeignKey(PermissionType, on_delete=models.CASCADE)
These entities describe the attack surface: declared Android permissions, exported components, intent filters, and cryptographic certificates. Similar patterns exist for Malware, Domain, VirusTotalScan, and Antivirus models, all cascading from Scan.
Finding-Level Evidence
Specific evidence supporting a Finding attaches directly to the Finding record. The String, DatabaseInfo, and File models capture extracted hardcoded strings, database references, and file paths relevant to specific vulnerabilities. These maintain ForeignKey relations to Finding rather than Scan, providing precise localization of security issues within the codebase.
Enumerations and State Tracking
MobileAudit uses Django’s TextChoices to standardize vocabulary across the relationship graph. Defined in app/models.py (lines 30-47), these ensure data integrity:
class Severity(models.TextChoices):
CR = ('CR', 'Critical')
HI = ('HI', 'High')
ME = ('ME', 'Medium')
LO = ('LO', 'Low')
IN = ('IN', 'Informational')
class Status(models.TextChoices):
VF = ('VF', 'Verified')
FP = ('FP', 'False Positive')
NV = ('NV', 'Not Verified')
NA = ('NA', 'Not Applicable')
UR = ('UR', 'Under Review')
The Finding model references these via severity and status fields, allowing workflow management across the vulnerability lifecycle.
Practical Implementation Examples
Creating the Parent-Child Chain
To programmatically populate the hierarchy according to the mpast/mobileaudit source code:
from app.models import Application, Scan, Finding, Pattern, Cwe, Risk, Severity, Status
# Create Application
app = Application.objects.create(
name="BankingApp",
description="Mobile banking client v2.1.0",
user=request.user
)
# Create Scan
scan = Scan.objects.create(
app=app,
name="v2.1.0 Production Analysis",
apk=uploaded_apk_file,
user=request.user
)
# Create Finding with full metadata
finding = Finding.objects.create(
scan=scan,
type=Pattern.objects.get(id=1), # Detection rule
cwe=Cwe.objects.get(cwe=89), # SQL Injection
risk=Risk.objects.get(id=2), # High Risk classification
severity=Severity.HI,
status=Status.VF,
name="SQL Injection in LoginActivity",
path="com/bank/LoginActivity.java",
line_number=142,
line="rawQuery(sql, null);",
snippet="... rawQuery(sql, null); ...",
description="User input concatenated into SQL query without parameterization.",
mitigation="Use parameterized queries or PreparedStatement.",
user=request.user
)
Traversing Relationships
Query across the hierarchy using Django’s double-underscore notation:
# All findings for a specific application
findings = Finding.objects.filter(scan__app__name="BankingApp")
# All permissions declared in a specific scan
perms = Permission.objects.filter(scan__id=42).select_related('permission')
# Findings by CWE category across all scans of an app
xss_findings = Finding.objects.filter(
cwe__cwe=79,
scan__app=app
).select_related('scan', 'cwe')
API Serialization and Integrity
The app/serializers.py file exposes these relationships through Django REST Framework. ModelSerializer definitions enforce read-only constraints on immutable fields (id, timestamps, user) while allowing nested serialization of related objects. This protects the referential integrity of the Application → Scan → Finding chain during API operations, ensuring that clients cannot accidentally reparent findings to different scans or alter audit timestamps.
Summary
- Application sits at the root, identified by name and user ownership, containing multiple Scans over time.
- Scan stores the APK binary, version metadata, and analysis status, cascading to Findings and scan-level metadata (Permissions, Components, Certificates, Malware scans).
- Finding captures individual vulnerabilities with precise file locations, linking to Pattern (detection rule), Cwe (taxonomy), and Risk (severity classification).
- Auxiliary models attach to either Scans (attack surface) or Findings (evidence) via ForeignKey relationships, creating a complete audit trail from uploaded APK to individual security issues.
- Enumerations (Severity, Status) provide standardized vocabulary across the entire relationship graph, supporting workflow states from "Not Verified" to "False Positive".
Frequently Asked Questions
How does MobileAudit handle deletion of an Application?
When an Application is deleted, Django’s on_delete=models.CASCADE configuration in the Scan model automatically removes all related Scans, which in turn delete their associated Findings and all metadata (Permissions, Components, Certificates). This ensures referential integrity but requires caution when removing production data, as the deletion is irreversible and removes the complete audit history.
Can a Finding exist without being linked to a Pattern?
Yes. While the type field (ForeignKey to Pattern) is recommended for traceability, the model definition in app/models.py explicitly allows blank=True, null=True. This supports manual findings creation or imports from external scanners that lack corresponding MobileAudit detection rules, though such findings will not display the rule description or remediation guidance stored in the Pattern model.
What is the difference between Severity and Risk in the data model?
Severity is a TextChoices enumeration (CR, HI, ME, LO, IN) used by the Finding model to indicate immediate technical impact based on the vulnerability’s inherent danger. Risk is a separate model (ForeignKey from Finding) that typically represents organizational or contextual risk ratings, allowing security teams to apply custom risk frameworks that factor in business context, asset exposure, and exploitability beyond the raw technical severity.
How are extracted APK strings associated with Findings versus Scans?
The String model maintains a ForeignKey to Finding (not Scan), linking specific hardcoded strings, API keys, or secrets directly to the vulnerability that references them. This differs from Permission or Component models, which attach to Scan because they describe application-wide attack surface discovered during manifest analysis rather than specific vulnerability evidence found in code review.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →