Mobile DAST CI/CD
Python script for automating security analysis of mobile applications.
Overview
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).
Key Features
- 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
What It Does
- 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
Table of Contents
- Installation
- Quick Start
- Usage Modes
- Installation Modes (Monolith and Kubernetes)
- Distribution Systems
- Scan Configuration
- Reports
- Advanced Features
- Troubleshooting
- Exit Codes
- Examples
Installation
Docker (Recommended)
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 1
Benefits:
- No Python environment setup required
- All dependencies pre-installed
- Consistent execution environment
- Easy CI/CD integration
PyPI Package
Install from PyPI using pip:
pip install mdast_cli
After installation, you can use the mdast_cli command directly:
mdast_cli --help
Note: You may need to install additional system dependencies depending on your distribution system choice (e.g., apkeep for Google Play).
From Source
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.txt
Then run the script directly:
python3 mdast_cli/mdast_scan.py --help
Quick Start
Download an Application
mdast_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"
Run a Security Scan
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.json
Usage Modes
Download Only Mode
Use 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
Scan Mode
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 (PDF by default)
Installation Modes (Monolith and Kubernetes)
mdast_cli is a single package that works against both flavours of the DAST
platform, with no change to the command line:
- Monolith - the classic all-in-one installation (
Tokenauth, tenant carried in the/organizations/{company_id}/...URL path).--company_idis required. - Microservices / Kubernetes - the platform behind the Clark facade (
Bearerauth with an organization token issued in the platform UI; the tenant is resolved server-side, so--company_idmay be omitted).
By default the CLI auto-detects the installation by probing GET /rest/architectures/
and inspecting the response shape (it does not merely trust an HTTP 200). The same
--url, --token and scan flags are used either way; --company_id is required
only after the target has been identified as a monolith installation.
Selecting the mode explicitly
Auto-detection can be overridden with the MDAST_CLI_MODE environment variable -
recommended in CI, where the target platform is known and a deterministic choice is
preferable to a probe:
export MDAST_CLI_MODE=microservices # or: monolith (default: auto)
Note for SaaS users: this setting describes the stand you connect to, not anything deployed in your own infrastructure. If you use the hosted service, you do not need to install anything - just set the value matching your stand (ask us if unsure).
Behind a TLS-inspecting proxy
Corporate proxies that inspect TLS re-sign traffic with an internal CA, which Python does not trust by default. The symptom is:
certificate verify failed: self-signed certificate in certificate chain
Point REQUESTS_CA_BUNDLE at your corporate root CA (PEM format):
export REQUESTS_CA_BUNDLE=/path/to/corporate-ca.pem
Adding the CA to the OS trust store is not enough. The CLI uses requests,
which reads the certifi bundle rather than the system store - update-ca-certificates
alone will not help. To keep trusting public CAs as well, concatenate the two:
cat $(python3 -c "import certifi; print(certifi.where())") corporate-ca.pem > ca-bundle.pem
export REQUESTS_CA_BUNDLE=ca-bundle.pem
If the CA is not available, the CLI still proceeds: mode detection retries without
certificate verification and logs a warning. Setting MDAST_CLI_MODE explicitly skips
the probe altogether.
Environment variables (microservices installation)
| Variable | Default | Purpose |
|---|---|---|
MDAST_CLI_MODE |
auto |
Force the installation mode (auto / monolith / microservices). |
MDAST_TLS_VERIFY |
(on) | TLS certificate verification. Secure by default. Set to 0/false/no/off to disable for a self-signed stand (the org token is a credential - use only on trusted networks). An unset or empty value stays secure. |
MDAST_UPLOAD_TIMEOUT |
(server default) | Optional server-side upload/parse wait in seconds (1-300) for large builds. |
Differences on the microservices installation
- Authentication uses an organization CLI token issued in the platform UI
(
Bearer), not a per-user CI/CD token. There is one active token per organization. --architecture_idis ignored (the platform resolves the architecture from the application binary itself).--appium_script_path(Appium scans) and--cr_report(CR report) are not supported and exit withINVALID_ARGS(2).- A report render failure is soft-fail: a finished scan stays green (exit 0) with a warning, because the report service (Pepper) is separate from the scan itself.
- Transient gateway errors (502/503/504) during polling, upload and report download are retried, so a rolling restart of a backend mid-scan does not fail a CI job.
Distribution Systems
Local File
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 1
Docker 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.json
Google Play
Download 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.json
AppStore
Download 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 3
Troubleshooting:
- "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!
Firebase
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_app
Security Note: Never commit Service Account JSON files to version control. Use environment variables or secure secret management in CI/CD.
Nexus Repository
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.0
Nexus2 Repository
Download 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_app
RuStore
Download 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.app
AppGallery
Download 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_app
RuMarket
Download 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.app
Scan Configuration
Scan Types
Manual Scan (Default)
When --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 scan
Automated Scan with Testcase
When --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 #4
Architecture Selection
Parameter: --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 5
Profile Management
Parameter: --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 #10
Reports
Reports work the same way on both installations.
Choosing the report format
Parameter: --report_format {pdf|json|all|none} (default: pdf)
After a successful scan the CLI downloads a PDF report by default - a plain scan always produces one. Use the flag to choose otherwise:
--report_format |
Result |
|---|---|
pdf (default) |
PDF only |
json |
JSON summary only |
all |
both PDF and JSON |
none |
no report (e.g. gate on exit code only) |
File names. If you do not pass an explicit file name, reports are saved next to
the working directory as scan_report_<scan_id>.pdf / scan_report_<scan_id>.json,
so the file is always tied to the scan it came from. Passing --pdf_report_file_name
and/or --summary_report_json_file_name overrides the name and forces that format
(so those flags remain fully backward compatible). PDF is always downloaded first.
On the microservices installation a report that fails to render (e.g. the report service returns 502) is a warning, not a failure: the scan already succeeded, so the CLI still exits 0 and any other requested report is still saved.
In Docker: the default
scan_report_<scan_id>.*name is written to the container's working directory, which is discarded when the container exits. Mount a host directory and point the report there with an absolute path (e.g.-v /host/reports:/mdast/report+--pdf_report_file_name /mdast/report/report.pdf), otherwise the report is lost. See the Docker example under Local File.
JSON Summary Report
Generate a structured JSON report with scan summary and statistics.
Parameter: --summary_report_json_file_name <filename> (implies --report_format json)
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.json
Use Case: CI/CD integration, automated reporting, data analysis
PDF Report
Generate a detailed PDF report with full scan results.
Parameter: --pdf_report_file_name <filename> (implies --report_format pdf; a PDF is produced by default even without this flag)
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.pdf
Use Case: Compliance reporting, stakeholder presentations, documentation
CR Report
Generate a CR (Compliance) report in HTML format - a Bank-of-Russia regulatory report on call-trace immutability (KNTV) that compares two versions of a mobile application. Not a "change request" report.
Monolith installation only. On the microservices installation
--cr_reportexits withINVALID_ARGS(2).
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.html
Advanced Features
Non-blocking Scans
Use --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 scan
Note: Reports will not be generated when using --nowait. Poll the API separately for results.
Long-running Scans
Use --long_wait to extend the maximum wait time to ~1 week (instead of the default
~1 hour timeout).
If a scan still has not reached a terminal state when the wait budget is exhausted,
the CLI fails with SCAN_FAILED (5) rather than exiting 0 - a scan that never
finished is never reported as success.
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 completion
Appium Integration
Use --appium_script_path to provide a custom Appium script for automated testing.
Monolith installation only. On the microservices installation
--appium_script_pathexits withINVALID_ARGS(2); use a recorded test case (--testcase_id) instead.
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.py
Download Path Configuration
Parameter: --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/apps
Troubleshooting
Common Issues
Google Play Authentication Errors
Problem: 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/)
AppStore Login Issues
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)
Firebase Service Account Errors
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)
Network/Timeout Issues
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
File Not Found Errors
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
Getting Help
- 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
Exit Codes
The script uses standardized exit codes for CI/CD integration. They are identical on both installations (monolith and microservices), so pipelines behave the same regardless of which platform they target:
| Code | Constant | Description |
|---|---|---|
| 0 | SUCCESS |
Operation completed successfully |
| 1 | INTERNAL_ERROR |
Unexpected internal error |
| 2 | INVALID_ARGS |
Invalid command-line arguments (also: Appium/CR report on the microservices installation) |
| 4 | DOWNLOAD_FAILED |
Application download failed / file not found |
| 5 | SCAN_FAILED |
Scan created but failed, no active engine, or finished in a non-success terminal state |
| 6 | NETWORK_ERROR |
Network/connection error, gateway 5xx (incl. upload 502/503/504), non-JSON gateway response |
| 7 | AUTH_ERROR |
Authentication/authorization error (bad or revoked token, 401/403) |
| 8 | PRECHECK_BLOCKED |
Scan pre-check returned blocking warnings (microservices gate) |
There is no code 3. Codes 6/7/8 are meaningful mostly on the microservices installation but are used consistently wherever they apply.
Example CI/CD Usage:
#!/bin/bash
mdast_cli --distribution_system file --file_path app.apk ...
EXIT_CODE=$?
case $EXIT_CODE in
0) echo "Scan completed successfully" ;;
4) echo "Download failed - check distribution system"; exit 1 ;;
6|7) echo "Infrastructure/auth problem (code $EXIT_CODE) - safe to retry"; exit 1 ;;
8) echo "Scan blocked by pre-check warnings"; exit 1 ;;
*) echo "Scan failed with code $EXIT_CODE"; exit 1 ;;
esac
Examples
Complete CI/CD Pipeline Example
#!/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
fi
Docker Compose Example
version: '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
Multi-Distribution Example
# 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
Additional Resources
- 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
License
See LICENSE file for details.
Support
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.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mdast_cli-2026.8.5.tar.gz.
File metadata
- Download URL: mdast_cli-2026.8.5.tar.gz
- Upload date:
- Size: 173.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.9.25
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ed94c93e069b4f2f39f3a06b1e013d65131ce9397dc7b291246cd87c3d6f4467
|
|
| MD5 |
2a92ae21f1e60c8b24c21692cb3d21fe
|
|
| BLAKE2b-256 |
8d3568d1bc72a29b853d99539ffe2f5b95ed75247a700aae8183cc368bb9c094
|
File details
Details for the file mdast_cli-2026.8.5-py3-none-any.whl.
File metadata
- Download URL: mdast_cli-2026.8.5-py3-none-any.whl
- Upload date:
- Size: 144.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/6.2.0 CPython/3.9.25
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a2029e4bd96538d4558df02174e6f5f8be0e9aec61c96a1cc24fc92940f2f785
|
|
| MD5 |
de3e4d167e29cdac753ca7894e9519fe
|
|
| BLAKE2b-256 |
4065d202ea3de097b42aa9a13fe90346ed3e4540d0ed9dba8c1fe94c949543ac
|