Python script for automating security analysis of mobile applications.
This script is designed to integrate mobile applications' security analysis into the continuous development process (CI/CD). It supports downloading applications from various distribution systems and performing dynamic security analysis (DAST).
- Multiple Distribution Systems: Support for 9+ distribution platforms (Google Play, AppStore, Firebase, Nexus, etc.)
- Download Only Mode: Download applications without scanning for testing or manual analysis
- Automated Scanning: Full integration with DAST scanning platform
- Multiple Report Formats: Generate JSON and PDF reports
- CI/CD Ready: Designed for seamless integration into CI/CD pipelines
- Docker Support: Pre-built Docker images for easy deployment
- Downloads mobile applications (APK/IPA) from supported distribution systems
- Uploads applications to the DAST scanning platform
- Executes security scans (manual or automated with testcases)
- Generates detailed security reports in JSON and/or PDF formats
- Returns structured output suitable for CI/CD integration
- Installation
- Quick Start
- Usage Modes
- Distribution Systems
- Scan Configuration
- Reports
- Advanced Features
- Troubleshooting
- Exit Codes
- Examples
The easiest way to use mdast_cli is via Docker:
# Pull the latest image
docker pull mobilesecurity/mdast_cli:latest
# Run with volume mounts for files and reports
docker run -it \
-v /path/to/apps:/mdast/files \
-v /path/to/reports:/mdast/report \
mobilesecurity/mdast_cli:latest \
--distribution_system file \
--file_path /mdast/files/app.apk \
--url "https://saas.mobile.appsec.world" \
--company_id 1 \
--token "YOUR_TOKEN" \
--profile_id 1Benefits:
- No Python environment setup required
- All dependencies pre-installed
- Consistent execution environment
- Easy CI/CD integration
Install from PyPI using pip:
pip install mdast_cliAfter installation, you can use the mdast_cli command directly:
mdast_cli --helpNote: You may need to install additional system dependencies depending on your distribution system choice (e.g., apkeep for Google Play).
Requirements:
- Python 3.9 or higher (3.9, 3.10, 3.11, 3.12 supported)
Clone the repository and install dependencies:
git clone https://github.com/Dynamic-Mobile-Security/mdast-cli.git
cd mdast-cli
pip install -r requirements.txtThen run the script directly:
python3 mdast_cli/mdast_scan.py --helpmdast_cli -d \
--distribution_system google_play \
--google_play_package_name com.example.app \
--google_play_email user@example.com \
--google_play_aas_token "YOUR_AAS_TOKEN"mdast_cli \
--distribution_system file \
--file_path app.apk \
--url "https://saas.mobile.appsec.world" \
--company_id 1 \
--token "YOUR_TOKEN" \
--profile_id 1 \
--testcase_id 4 \
--summary_report_json_file_name report.jsonUse the --download_only (or -d) flag to download applications without scanning:
mdast_cli -d --distribution_system google_play --google_play_package_name com.example.app ...Output:
- Downloads the application to the specified
--download_path(default:downloaded_apps/) - Prints
DOWNLOAD_PATH=/path/to/app.apkfor CI/CD parsing - Exits immediately after download
Use Cases:
- Testing download functionality
- Manual application analysis
- CI/CD pipelines that handle scanning separately
Without --download_only, the script will:
- Download the application (if not using
filedistribution) - Upload to the DAST platform
- Start a security scan
- Wait for completion (unless
--nowaitis set) - Generate reports (if specified)
Use a local APK/IPA file for scanning.
Required Parameters:
--distribution_system file--file_path <path>- Absolute or relative path to the application file
Example:
mdast_cli \
--distribution_system file \
--file_path /path/to/app.apk \
--url "https://saas.mobile.appsec.world" \
--company_id 1 \
--token "YOUR_TOKEN" \
--profile_id 1Docker Example:
docker run -it \
-v /host/path/to/apps:/mdast/files \
-v /host/path/to/reports:/mdast/report \
mobilesecurity/mdast_cli:latest \
--distribution_system file \
--file_path /mdast/files/app.apk \
--url "https://saas.mobile.appsec.world" \
--company_id 1 \
--token "YOUR_TOKEN" \
--summary_report_json_file_name /mdast/report/report.jsonDownload applications from Google Play Store using apkeep.
Prerequisites:
-
Install
apkeep:# Using Rust (requires Rust toolchain) cargo install apkeep # Or download prebuilt binary from: # https://github.com/EFForg/apkeep/releases # Place binary in your PATH
-
Obtain authentication:
- Option A: OAuth2 token (recommended for first-time setup)
- Option B: AAS token (reuse from previous runs)
Required Parameters:
--distribution_system google_play--google_play_package_name <package>- Package name (e.g.,com.instagram.android)--google_play_email <email>- Google account email- Either:
--google_play_oauth2_token <token>- OAuth2 token to fetch AAS automatically--google_play_aas_token <token>- Direct AAS token (from previous run)
Optional Parameters:
--google_play_file_name <name>- Custom filename for downloaded app--google_play_proxy <proxy>- Proxy configuration (e.g.,socks5://user:pass@host:port)
Getting Package Name:
- Visit the app page on Google Play
- Package name is visible in the URL:
play.google.com/store/apps/details?id=<PACKAGE_NAME> - Or check the app's page source
Authentication Flow:
-
First Run (OAuth2):
mdast_cli -d \ --distribution_system google_play \ --google_play_package_name com.example.app \ --google_play_email user@example.com \ --google_play_oauth2_token "ya29.a0AVvZ..."- The script will fetch an AAS token automatically
- Save the AAS token from logs for future runs
- Look for
AAS token: aas_et/...in the output
-
Subsequent Runs (AAS Token):
mdast_cli -d \ --distribution_system google_play \ --google_play_package_name com.example.app \ --google_play_email user@example.com \ --google_play_aas_token "aas_et/AKppINZUCsnVs80yu3k4ZpiApuOlHlOnxSlwNNMOPjomkWDDbNi1SKd0PRTbOFSS6TNLQFlY70SIrUoxUnababWUcBXuhuVdpmrVUvff5etUCWqToxpRkHV8jf4RLcwX56AMkGhlslqrY4hrAH28-yCyOf9FFeLnhCo9p3ydbRrT5at3Le3Tnc-0CPILroJ_NldfLpDeQvBcj2BM_wBM-Tc"
Important Notes:
- Use a temporary Google account with 2FA disabled (recommended)
- Split APKs are automatically packaged into a ZIP archive
- Python 3.9 or higher required (3.9, 3.10, 3.11, 3.12 supported)
- AAS tokens are long-lived but may expire; keep OAuth2 token as backup
Example with Scan:
mdast_cli \
--distribution_system google_play \
--google_play_package_name com.instagram.android \
--google_play_email user@example.com \
--google_play_aas_token "YOUR_AAS_TOKEN" \
--google_play_file_name instagram_latest \
--url "https://saas.mobile.appsec.world" \
--company_id 1 \
--token "YOUR_TOKEN" \
--profile_id 1 \
--testcase_id 4 \
--summary_report_json_file_name report.jsonDownload iOS applications (.ipa) from the App Store.
Prerequisites:
- iTunes account with valid credentials
- 2FA enabled on the Apple ID
- Application ID or Bundle ID
Required Parameters:
--distribution_system appstore- Either:
--appstore_app_id <id>- Application ID from App Store URL--appstore_bundle_id <bundle>- Bundle identifier
--appstore_apple_id <email>- iTunes account email--appstore_password <password>- iTunes account password--appstore_2FA <code>- 6-digit 2FA code
Optional Parameters:
--appstore_file_name <name>- Custom filename for downloaded IPA
Getting App ID:
- Visit the app page in App Store (web or app)
- Extract ID from URL:
apps.apple.com/app/id{APP_ID} - Example: URL contains
id398129933→ use398129933
2FA Setup (First Time):
- Run the script with email and password
- You'll receive a 2FA code on your device
- Use the code with
--appstore_2FA - Save the combined password+2FA format for 6 months:
password2FA(e.g.,P@ssword742877)
Deprecated Parameter:
--appstore_password2FA- Will be removed on 01.05.2023. Use separate--appstore_passwordand--appstore_2FAinstead.
Example:
mdast_cli \
--distribution_system appstore \
--appstore_app_id 564177498 \
--appstore_apple_id user@icloud.com \
--appstore_password "YourPassword" \
--appstore_2FA 123456 \
--appstore_file_name my_app \
--url "https://saas.mobile.appsec.world" \
--company_id 2 \
--token "YOUR_TOKEN" \
--profile_id 1246 \
--architecture_id 3Troubleshooting:
- "Wrong Apple ID" error: Contact support to coordinate the Apple ID for AppStore integration
- Session expired: Re-authenticate and save the new 2FA code
- Login errors: Ensure 2FA is enabled and code is current (6-digit format)
Note: This integration uses ipatool - thanks to all contributors!
Download applications from Firebase App Distribution.
Prerequisites:
- Firebase project with App Distribution enabled
- Service Account with
cloud-platformscope - JSON key file for the Service Account
Required Parameters:
--distribution_system firebase--firebase_project_number <number>- Project number (integer)--firebase_app_id <id>- Application ID (format:1:PROJECT:android:APP_ID)--firebase_account_json_path <path>- Path to Service Account JSON key file--firebase_file_extension <ext>- File extension:apkoripa
Optional Parameters:
--firebase_file_name <name>- Custom filename (defaults to version name)
Finding Project Number:
- Go to Firebase Console
- Select your project
- Click Settings (gear icon) → Project settings
- Find "Project number" in the General tab
Finding App ID:
- In Project settings, scroll to "Your apps"
- Find your app and copy the App ID
- Format:
1:PROJECT_NUMBER:android:APP_IDor1:PROJECT_NUMBER:ios:APP_ID
Creating Service Account:
- Go to Google Cloud Console
- Navigate to IAM & Admin → Service accounts
- Create a new Service Account or use existing
- Grant the
Cloud Platformscope (/auth/cloud-platform) - Create and download a JSON key file
- Save the file securely (keep it out of version control!)
Example:
mdast_cli -d \
--distribution_system firebase \
--firebase_project_number 1231231337 \
--firebase_app_id "1:1337:android:123123" \
--firebase_account_json_path /path/to/service_account.json \
--firebase_file_extension apk \
--firebase_file_name my_appSecurity Note: Never commit Service Account JSON files to version control. Use environment variables or secure secret management in CI/CD.
Download applications from Nexus Repository Manager 3.x (Maven repository).
Required Parameters:
--distribution_system nexus--nexus_url <url>- Nexus server URL (e.g.,https://nexus.example.com)--nexus_login <username>- Nexus username--nexus_password <password>- Nexus password--nexus_repo_name <name>- Repository name--nexus_group_id <group>- Maven group ID--nexus_artifact_id <artifact>- Maven artifact ID--nexus_version <version>- Application version
Maven Coordinates: The script uses standard Maven coordinates to locate artifacts:
- Format:
group_id:artifact_id:version - Example:
com.example:myapp:1.0.0
Uploading to Nexus: See these gists for uploading apps to Nexus:
Example:
mdast_cli -d \
--distribution_system nexus \
--nexus_url https://nexus.example.com \
--nexus_login myuser \
--nexus_password mypass \
--nexus_repo_name releases \
--nexus_group_id com.example \
--nexus_artifact_id myapp \
--nexus_version 1.0.0Download applications from Nexus Repository Manager 2.x.
Required Parameters:
--distribution_system nexus2--nexus2_url <url>- Nexus2 server URL--nexus2_login <username>- Nexus2 username--nexus2_password <password>- Nexus2 password--nexus2_repo_name <name>- Repository name--nexus2_group_id <group>- Maven group ID--nexus2_artifact_id <artifact>- Maven artifact ID--nexus2_version <version>- Application version--nexus2_extension <ext>- File extension (e.g.,apk,ipa)
Optional Parameters:
--nexus2_file_name <name>- Custom filename
Example:
mdast_cli -d \
--distribution_system nexus2 \
--nexus2_url http://nexus:8081/nexus/ \
--nexus2_login admin \
--nexus2_password admin123 \
--nexus2_repo_name releases \
--nexus2_group_id com.example \
--nexus2_artifact_id myapp \
--nexus2_version 1.337 \
--nexus2_extension apk \
--nexus2_file_name my_appDownload Android applications from RuStore (Russian app store).
Required Parameters:
--distribution_system rustore--rustore_package_name <package>- Package name (e.g.,ru.example.app)
Getting Package Name:
- Visit the app page on RuStore
- Package name is typically visible in the URL or app details
Example:
mdast_cli -d \
--distribution_system rustore \
--rustore_package_name ru.example.appDownload applications from Huawei AppGallery.
Required Parameters:
--distribution_system appgallery--appgallery_app_id <id>- Application ID from AppGallery
Optional Parameters:
--appgallery_file_name <name>- Custom filename
Getting App ID:
- Visit the app page in AppGallery
- Extract ID from URL:
appgallery.huawei.com/app/{APP_ID} - Example: URL contains
C101184875→ useC101184875
Example:
mdast_cli -d \
--distribution_system appgallery \
--appgallery_app_id C123456789 \
--appgallery_file_name huawei_appDownload Android applications from RuMarket (Russian app store).
Required Parameters:
--distribution_system rumarket--rumarket_package_name <package>- Package name
Example:
mdast_cli -d \
--distribution_system rumarket \
--rumarket_package_name com.example.appWhen --testcase_id is not specified:
- Application is installed on the device
- Application is launched automatically
- Waits 30 seconds for user interaction
- Application is stopped
- Security analysis is performed
Use Case: Quick security checks, initial assessments
Example:
mdast_cli \
--distribution_system file \
--file_path app.apk \
--url "https://saas.mobile.appsec.world" \
--company_id 1 \
--token "YOUR_TOKEN" \
--profile_id 1
# No --testcase_id = manual scanWhen --testcase_id is specified:
- Previously recorded testcase is replayed
- All recorded user interactions are executed
- Comprehensive security analysis is performed
Use Case: Deep security analysis, regression testing, CI/CD integration
Example:
mdast_cli \
--distribution_system file \
--file_path app.apk \
--url "https://saas.mobile.appsec.world" \
--company_id 1 \
--token "YOUR_TOKEN" \
--profile_id 1 \
--testcase_id 4 # Automated scan with testcase #4Parameter: --architecture_id <id>
Select the target architecture/OS version for scanning:
- If not specified, defaults to Android 11 or iOS 14 (depending on file type)
- Use specific architecture ID for testing on different OS versions
- Available architectures depend on your DAST platform configuration
Example:
mdast_cli ... --architecture_id 5 # Use architecture ID 5Parameter: --profile_id <id> (optional)
- If not specified: A new profile is created automatically
- If specified: Uses existing profile with the given ID
- Profiles contain device configuration, app settings, and scan parameters
Auto-create in Existing Project:
mdast_cli ... --project_id 10 # Create profile in project #10Generate a structured JSON report with scan summary and statistics.
Parameter: --summary_report_json_file_name <filename>
Output Format:
- Total number of vulnerabilities
- Vulnerability breakdown by severity
- Scan metadata (timestamp, duration, etc.)
- Application information
Example:
mdast_cli \
--distribution_system file \
--file_path app.apk \
--url "https://saas.mobile.appsec.world" \
--company_id 1 \
--token "YOUR_TOKEN" \
--profile_id 1 \
--summary_report_json_file_name scan_results.jsonUse Case: CI/CD integration, automated reporting, data analysis
Generate a detailed PDF report with full scan results.
Parameter: --pdf_report_file_name <filename>
Output Format:
- Detailed vulnerability descriptions
- Screenshots and evidence
- Remediation recommendations
- Executive summary
Example:
mdast_cli \
--distribution_system file \
--file_path app.apk \
--url "https://saas.mobile.appsec.world" \
--company_id 1 \
--token "YOUR_TOKEN" \
--profile_id 1 \
--pdf_report_file_name detailed_report.pdfUse Case: Compliance reporting, stakeholder presentations, documentation
Generate a CR (Change Request) report in HTML format.
Required Parameters (when --cr_report is set):
--cr_report- Enable CR report generation--stingray_login <login>- Stingray platform login--stingray_password <password>- Stingray platform password
Optional Parameters:
--organization_name <name>- Organization name (default: "ООО Стингрей Технолоджиз")--engineer_name <name>- Engineer name--controller_name <name>- Controller name--use_ldap- Use LDAP authentication--authority_server_id <id>- Authority server ID--cr_report_path <path>- Output file path (default:stingray-CR-report.html)
Example:
mdast_cli \
--distribution_system file \
--file_path app.apk \
--url "https://saas.mobile.appsec.world" \
--company_id 1 \
--token "YOUR_TOKEN" \
--profile_id 1337 \
--architecture_id 3 \
--cr_report \
--stingray_login user@example.com \
--stingray_password "password" \
--organization_name "My Company" \
--engineer_name "John Doe" \
--controller_name "Jane Smith" \
--cr_report_path custom-report.htmlUse --nowait (or -nw) to start a scan and exit immediately without waiting for completion.
Use Case:
- Long-running scans
- Fire-and-forget scenarios
- CI/CD pipelines that poll for results separately
Example:
mdast_cli \
--distribution_system file \
--file_path app.apk \
--url "https://saas.mobile.appsec.world" \
--company_id 1 \
--token "YOUR_TOKEN" \
--profile_id 1 \
--nowait # Exit immediately after starting scanNote: Reports will not be generated when using --nowait. Poll the API separately for results.
Use --long_wait to extend the maximum wait time to 1 week (instead of default timeout).
Use Case:
- Very long testcases
- Deep analysis scenarios
- Extended monitoring periods
Example:
mdast_cli \
--distribution_system file \
--file_path app.apk \
--url "https://saas.mobile.appsec.world" \
--company_id 1 \
--token "YOUR_TOKEN" \
--profile_id 1 \
--testcase_id 4 \
--long_wait # Wait up to 1 week for completionUse --appium_script_path to provide a custom Appium script for automated testing.
Parameter: --appium_script_path <path>
Use Case:
- Custom test automation
- Integration with existing Appium test suites
- Advanced interaction scenarios
Example:
mdast_cli \
--distribution_system file \
--file_path app.apk \
--url "https://saas.mobile.appsec.world" \
--company_id 1 \
--token "YOUR_TOKEN" \
--profile_id 1 \
--appium_script_path /path/to/appium_script.pyParameter: --download_path <path> (or -p <path>)
Specify where downloaded applications should be saved.
- Default:
downloaded_apps/ - Can be absolute or relative path
- Directory is created automatically if it doesn't exist
Example:
mdast_cli -d \
--distribution_system google_play \
--google_play_package_name com.example.app \
--google_play_email user@example.com \
--google_play_aas_token "TOKEN" \
--download_path /custom/path/to/appsProblem: DF-DFERH-01 error or authentication failures
Solutions:
- Ensure
apkeepis installed and in PATH:apkeep --version - Verify OAuth2 token is valid and not expired
- Use a temporary Google account with 2FA disabled
- Check that AAS token format is correct (starts with
aas_et/)
Problem: "Wrong Apple ID" or login failures
Solutions:
- Ensure 2FA is enabled on the Apple ID
- Use the 6-digit 2FA code (not the longer backup code)
- Format password+2FA correctly:
password2FA(e.g.,P@ssword742877) - Contact support if Apple ID needs to be whitelisted
- Re-authenticate if session expired (sessions last ~6 months)
Problem: Authentication or permission errors
Solutions:
- Verify Service Account JSON file path is correct
- Ensure Service Account has
cloud-platformscope enabled - Check that Service Account has access to Firebase App Distribution
- Verify project number and app ID format
- Ensure JSON file is valid (not corrupted)
Problem: Downloads fail or time out
Solutions:
- Check network connectivity
- Verify distribution system URLs are accessible
- Use
--google_play_proxyfor Google Play if behind firewall - Increase timeout values (if configurable)
- Check firewall/proxy settings
Problem: Application file not found after download
Solutions:
- Check
--download_pathdirectory permissions - Verify disk space is available
- Check file system permissions
- Review download logs for errors
- Ensure distribution system returned valid file
- Check Logs: Review console output for detailed error messages
- Verify Parameters: Use
--helpto see all available options - Test Download Only: Use
-dflag to isolate download issues - Contact Support: Reach out with:
- Full command used
- Error messages
- Distribution system and parameters (redact sensitive data)
- Log output
The script uses standardized exit codes for CI/CD integration:
| Code | Constant | Description |
|---|---|---|
| 0 | SUCCESS |
Operation completed successfully |
| 1 | INVALID_ARGS |
Invalid command-line arguments |
| 2 | AUTH_ERROR |
Authentication failed |
| 3 | DOWNLOAD_FAILED |
Application download failed |
| 4 | NETWORK_ERROR |
Network/connection error |
| 5 | SCAN_FAILED |
Scan execution or upload failed |
Example CI/CD Usage:
#!/bin/bash
mdast_cli --distribution_system file --file_path app.apk ...
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
echo "Scan completed successfully"
elif [ $EXIT_CODE -eq 3 ]; then
echo "Download failed - check distribution system"
exit 1
else
echo "Scan failed with code $EXIT_CODE"
exit 1
fi#!/bin/bash
set -e
# Download application
mdast_cli -d \
--distribution_system google_play \
--google_play_package_name com.example.app \
--google_play_email ci@example.com \
--google_play_aas_token "$GOOGLE_PLAY_AAS_TOKEN" \
--download_path ./artifacts
# Extract download path from output
DOWNLOAD_PATH=$(mdast_cli -d ... 2>&1 | grep "DOWNLOAD_PATH=" | cut -d'=' -f2)
# Run security scan
mdast_cli \
--distribution_system file \
--file_path "$DOWNLOAD_PATH" \
--url "$DAST_URL" \
--company_id "$COMPANY_ID" \
--token "$DAST_TOKEN" \
--profile_id "$PROFILE_ID" \
--testcase_id "$TESTCASE_ID" \
--summary_report_json_file_name ./reports/scan_results.json \
--pdf_report_file_name ./reports/scan_results.pdf
# Check results
if [ -f ./reports/scan_results.json ]; then
CRITICAL_COUNT=$(jq '.vulnerabilities.critical' ./reports/scan_results.json)
if [ "$CRITICAL_COUNT" -gt 0 ]; then
echo "Critical vulnerabilities found!"
exit 1
fi
fiversion: '3.8'
services:
mdast-scan:
image: mobilesecurity/mdast_cli:latest
volumes:
- ./apps:/mdast/files
- ./reports:/mdast/report
environment:
- DAST_URL=https://saas.mobile.appsec.world
- COMPANY_ID=1
- TOKEN=${DAST_TOKEN}
command:
- --distribution_system
- file
- --file_path
- /mdast/files/app.apk
- --url
- ${DAST_URL}
- --company_id
- ${COMPANY_ID}
- --token
- ${TOKEN}
- --profile_id
- "1"
- --summary_report_json_file_name
- /mdast/report/results.json# Test multiple distribution systems
for DIST in google_play appstore firebase; do
echo "Testing $DIST..."
mdast_cli -d \
--distribution_system "$DIST" \
--download_path "./downloads/$DIST" \
# ... distribution-specific parameters
done- Docker Hub: https://hub.docker.com/r/mobilesecurity/mdast_cli
- PyPI Package: https://pypi.org/project/mdast-cli/
- GitHub Repository: https://github.com/Dynamic-Mobile-Security/mdast-cli
- apkeep (Google Play): https://github.com/EFForg/apkeep
- ipatool (AppStore): https://github.com/majd/ipatool
See LICENSE file for details.
For issues, questions, or contributions, please visit the GitHub repository or contact support.
Note: This documentation is maintained alongside the codebase. For the latest information, always refer to the version-specific documentation or the --help command output.