mirror of
https://github.com/MobSF/Mobile-Security-Framework-MobSF.git
synced 2026-10-12 02:06:59 +00:00
[EFR-02]Enterprise Feature Request - False Positive Triaging (#2000)
* Suppression logic * Android code analysis suppression * Fixes #1981 * iOS source support bundle id extraction * iOS Source Code - Suppression support * Remove check in CFBundleURLName * iOS Binary code analysis suppression support * Add Code QL * Suppression support for Manifest analysis * Fixes #2014 * REST API + Docs * Address review comments * update suppression wordings * Fixes #2043 * Icon analysis code QA
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ "master" ]
|
||||
schedule:
|
||||
- cron: '17 16 * * 0'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'python' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
@@ -1,5 +1,5 @@
|
||||
# Mobile Security Framework (MobSF)
|
||||
Version: v3.5 beta
|
||||
Version: v3.6 beta
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -195,25 +195,23 @@ def get_app_files(apk_dir, md5_hash, package):
|
||||
shutil.rmtree(untar_dir)
|
||||
try:
|
||||
with tarfile.open(tar_loc, errorlevel=1) as tar:
|
||||
|
||||
def is_within_directory(directory, target):
|
||||
|
||||
abs_directory = os.path.abspath(directory)
|
||||
abs_target = os.path.abspath(target)
|
||||
|
||||
prefix = os.path.commonprefix([abs_directory, abs_target])
|
||||
|
||||
return prefix == abs_directory
|
||||
|
||||
def safe_extract(tar, path=".", members=None, *, numeric_owner=False):
|
||||
|
||||
|
||||
def safe_extract(tar, path='.',
|
||||
members=None,
|
||||
*,
|
||||
numeric_owner=False):
|
||||
for member in tar.getmembers():
|
||||
member_path = os.path.join(path, member.name)
|
||||
if not is_within_directory(path, member_path):
|
||||
raise Exception("Attempted Path Traversal in Tar File")
|
||||
|
||||
tar.extractall(path, members, numeric_owner)
|
||||
|
||||
|
||||
raise Exception('Attempted Path Traversal in Tar File')
|
||||
tar.extractall(path, members, numeric_owner)
|
||||
|
||||
safe_extract(tar, untar_dir, members=safe_paths(tar))
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
@@ -31,18 +31,14 @@ from mobsf.MobSF.utils import (
|
||||
is_md5,
|
||||
is_path_traversal,
|
||||
is_safe_path,
|
||||
key,
|
||||
print_n_send_error_response,
|
||||
read_sqlite,
|
||||
)
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@register.filter
|
||||
def key(d, key_name):
|
||||
"""To get dict element by key name in template."""
|
||||
return d.get(key_name)
|
||||
register.filter('key', key)
|
||||
|
||||
|
||||
def view_report(request, checksum, api=False):
|
||||
|
||||
+6
-6
@@ -10,13 +10,13 @@ from mobsf.install.windows.setup import windows_config_local
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
VERSION = '3.5.2'
|
||||
VERSION = '3.6.0'
|
||||
BANNER = """
|
||||
__ __ _ ____ _____ _____ ____
|
||||
| \/ | ___ | |__/ ___|| ___|_ _|___ / | ___|
|
||||
| |\/| |/ _ \| '_ \___ \| |_ \ \ / / |_ \ |___ \
|
||||
| | | | (_) | |_) |__) | _| \ V / ___) | ___) |
|
||||
|_| |_|\___/|_.__/____/|_| \_/ |____(_)____/
|
||||
__ __ _ ____ _____ _____ __
|
||||
| \/ | ___ | |__/ ___|| ___|_ _|___ / / /_
|
||||
| |\/| |/ _ \| '_ \___ \| |_ \ \ / / |_ \| '_ \
|
||||
| | | | (_) | |_) |__) | _| \ V / ___) | (_) |
|
||||
|_| |_|\___/|_.__/____/|_| \_/ |____(_)___/
|
||||
""" # noqa: W291
|
||||
# ASCII Font: Standard
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ DATABASES = {
|
||||
# End Postgres support
|
||||
"""
|
||||
# ===============================================
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
||||
DEBUG = True
|
||||
DJANGO_LOG_LEVEL = DEBUG
|
||||
ALLOWED_HOSTS = ['127.0.0.1', 'mobsf', '*']
|
||||
|
||||
@@ -16,6 +16,7 @@ from mobsf.StaticAnalyzer.views.common import (
|
||||
appsec,
|
||||
pdf,
|
||||
shared_func,
|
||||
suppression,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.android import (
|
||||
find,
|
||||
@@ -45,6 +46,11 @@ urlpatterns = [
|
||||
re_path(r'^api/v1/scans$', api_sz.api_recent_scans),
|
||||
re_path(r'^api/v1/compare$', api_sz.api_compare),
|
||||
re_path(r'^api/v1/scorecard$', api_sz.api_scorecard),
|
||||
# Static Suppression
|
||||
re_path(r'^api/v1/suppress_by_rule$', api_sz.api_suppress_by_rule_id),
|
||||
re_path(r'^api/v1/suppress_by_files$', api_sz.api_suppress_by_files),
|
||||
re_path(r'^api/v1/list_suppressions$', api_sz.api_list_suppressions),
|
||||
re_path(r'^api/v1/delete_suppression$', api_sz.api_delete_suppression),
|
||||
# Dynamic Analysis
|
||||
re_path(r'^api/v1/dynamic/get_apps$', api_dz.api_get_apps),
|
||||
re_path(r'^api/v1/dynamic/start_analysis$', api_dz.api_start_analysis),
|
||||
@@ -103,6 +109,19 @@ if settings.API_ONLY == '0':
|
||||
re_path(r'^appsec_dashboard/(?P<checksum>[0-9a-f]{32})/$',
|
||||
appsec.appsec_dashboard,
|
||||
name='appsec_dashboard'),
|
||||
# Suppression
|
||||
re_path(r'^suppress_by_rule/$',
|
||||
suppression.suppress_by_rule_id,
|
||||
name='suppress_by_rule'),
|
||||
re_path(r'^suppress_by_files/$',
|
||||
suppression.suppress_by_files,
|
||||
name='suppress_by_files'),
|
||||
re_path(r'^list_suppressions/$',
|
||||
suppression.list_suppressions,
|
||||
name='list_suppressions'),
|
||||
re_path(r'^delete_suppression/$',
|
||||
suppression.delete_suppression,
|
||||
name='delete_suppression'),
|
||||
# App Compare
|
||||
re_path(r'^compare/(?P<hash1>[0-9a-f]{32})/(?P<hash2>[0-9a-f]{32})/$',
|
||||
shared_func.compare_apps),
|
||||
|
||||
@@ -601,3 +601,39 @@ def disable_print():
|
||||
# Restore
|
||||
def enable_print():
|
||||
sys.stdout = sys.__stdout__
|
||||
|
||||
|
||||
def find_key_in_dict(key, var):
|
||||
"""Recursively look up a key in a nested dict."""
|
||||
if hasattr(var, 'items'):
|
||||
for k, v in var.items():
|
||||
if k == key:
|
||||
yield v
|
||||
if isinstance(v, dict):
|
||||
for result in find_key_in_dict(key, v):
|
||||
yield result
|
||||
elif isinstance(v, list):
|
||||
for d in v:
|
||||
for result in find_key_in_dict(key, d):
|
||||
yield result
|
||||
|
||||
|
||||
def key(data, key_name):
|
||||
"""Return the data for a key_name."""
|
||||
return data.get(key_name)
|
||||
|
||||
|
||||
def android_component(data):
|
||||
"""Return Android component from data."""
|
||||
cmp = ''
|
||||
if 'Activity-Alias' in data:
|
||||
cmp = 'activity_alias_'
|
||||
elif 'Activity' in data:
|
||||
cmp = 'activity_'
|
||||
elif 'Service' in data:
|
||||
cmp = 'service_'
|
||||
elif 'Content Provider' in data:
|
||||
cmp = 'provider_'
|
||||
elif 'Broadcast Receiver' in data:
|
||||
cmp = 'receiver_'
|
||||
return cmp
|
||||
|
||||
@@ -12,6 +12,12 @@ from mobsf.StaticAnalyzer.views.android.static_analyzer import static_analyzer
|
||||
from mobsf.StaticAnalyzer.views.ios import view_source as ios_view_source
|
||||
from mobsf.StaticAnalyzer.views.ios.static_analyzer import static_analyzer_ios
|
||||
from mobsf.StaticAnalyzer.views.common.shared_func import compare_apps
|
||||
from mobsf.StaticAnalyzer.views.common.suppression import (
|
||||
delete_suppression,
|
||||
list_suppressions,
|
||||
suppress_by_files,
|
||||
suppress_by_rule_id,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.common.pdf import pdf
|
||||
from mobsf.StaticAnalyzer.views.common.appsec import appsec_dashboard
|
||||
from mobsf.StaticAnalyzer.views.windows import windows
|
||||
@@ -48,7 +54,7 @@ def api_scan(request):
|
||||
{'error': 'Missing Parameters'}, 422)
|
||||
scan_type = request.POST['scan_type']
|
||||
# APK, Android ZIP and iOS ZIP
|
||||
if scan_type in {'xapk', 'apk', 'zip'}:
|
||||
if scan_type in {'xapk', 'apk', 'apks', 'zip'}:
|
||||
resp = static_analyzer(request, True)
|
||||
if 'type' in resp:
|
||||
# For now it's only ios_zip
|
||||
@@ -203,3 +209,66 @@ def api_scorecard(request):
|
||||
response = make_api_response(
|
||||
{'error': 'JSON Generation Error'}, 500)
|
||||
return response
|
||||
|
||||
|
||||
@request_method(['POST'])
|
||||
@csrf_exempt
|
||||
def api_suppress_by_rule_id(request):
|
||||
"""POST - Suppress a rule by id."""
|
||||
params = {'rule', 'type', 'hash'}
|
||||
if set(request.POST) < params:
|
||||
return make_api_response(
|
||||
{'error': 'Missing Parameters'}, 422)
|
||||
resp = suppress_by_rule_id(request, True)
|
||||
if 'error' in resp:
|
||||
response = make_api_response(resp, 500)
|
||||
else:
|
||||
response = make_api_response(resp, 200)
|
||||
return response
|
||||
|
||||
|
||||
@request_method(['POST'])
|
||||
@csrf_exempt
|
||||
def api_suppress_by_files(request):
|
||||
"""POST - Suppress a rule by files."""
|
||||
params = {'rule', 'hash'}
|
||||
if set(request.POST) < params:
|
||||
return make_api_response(
|
||||
{'error': 'Missing Parameters'}, 422)
|
||||
resp = suppress_by_files(request, True)
|
||||
if 'error' in resp:
|
||||
response = make_api_response(resp, 500)
|
||||
else:
|
||||
response = make_api_response(resp, 200)
|
||||
return response
|
||||
|
||||
|
||||
@request_method(['POST'])
|
||||
@csrf_exempt
|
||||
def api_list_suppressions(request):
|
||||
"""POST - View Suppressions."""
|
||||
if 'hash' not in request.POST:
|
||||
return make_api_response(
|
||||
{'error': 'Missing Parameters'}, 422)
|
||||
resp = list_suppressions(request, True)
|
||||
if 'error' in resp:
|
||||
response = make_api_response(resp, 500)
|
||||
else:
|
||||
response = make_api_response(resp, 200)
|
||||
return response
|
||||
|
||||
|
||||
@request_method(['POST'])
|
||||
@csrf_exempt
|
||||
def api_delete_suppression(request):
|
||||
"""POST - Delete a suppression."""
|
||||
params = {'kind', 'type', 'rule', 'hash'}
|
||||
if set(request.POST) < params:
|
||||
return make_api_response(
|
||||
{'error': 'Missing Parameters'}, 422)
|
||||
resp = delete_suppression(request, True)
|
||||
if 'error' in resp:
|
||||
response = make_api_response(resp, 500)
|
||||
else:
|
||||
response = make_api_response(resp, 200)
|
||||
return response
|
||||
|
||||
@@ -21,6 +21,7 @@ from mobsf.MobSF.utils import (
|
||||
is_dir_exists,
|
||||
is_file_exists,
|
||||
is_safe_path,
|
||||
key,
|
||||
print_n_send_error_response,
|
||||
)
|
||||
from mobsf.MobSF.views.helpers import FileType
|
||||
@@ -36,12 +37,7 @@ from mobsf.StaticAnalyzer.models import (
|
||||
LINUX_PLATFORM = ['Darwin', 'Linux']
|
||||
HTTP_BAD_REQUEST = 400
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@register.filter
|
||||
def key(d, key_name):
|
||||
"""To get dict element by key name in template."""
|
||||
return d.get(key_name)
|
||||
register.filter('key', key)
|
||||
|
||||
|
||||
def index(request):
|
||||
|
||||
@@ -122,3 +122,10 @@ class StaticAnalyzerWindows(models.Model):
|
||||
STRINGS = models.TextField(default=[])
|
||||
BINARY_ANALYSIS = models.TextField(default=[])
|
||||
BINARY_WARNINGS = models.TextField(default=[])
|
||||
|
||||
|
||||
class SuppressFindings(models.Model):
|
||||
PACKAGE_NAME = models.CharField(max_length=260, default='')
|
||||
SUPPRESS_RULE_ID = models.TextField(default=[])
|
||||
SUPPRESS_FILES = models.TextField(default={})
|
||||
SUPPRESS_TYPE = models.TextField(default='')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
MANIFEST_DESC = {
|
||||
'a_clear_text': {
|
||||
'clear_text_traffic': {
|
||||
'title': ('Clear text traffic is Enabled For App'
|
||||
'<br>[android:usesCleartextTraffic=true]'),
|
||||
'level': 'high',
|
||||
@@ -16,7 +16,7 @@ MANIFEST_DESC = {
|
||||
'name': ('Clear text traffic is Enabled For App '
|
||||
'[android:usesCleartextTraffic=true]'),
|
||||
},
|
||||
'a_boot_aware': {
|
||||
'direct_boot_aware': {
|
||||
'title': 'App is direct-boot aware <br>[android:directBootAware=true]',
|
||||
'level': 'info',
|
||||
'description': ('This app can run before the user unlocks the device. '
|
||||
@@ -29,7 +29,7 @@ MANIFEST_DESC = {
|
||||
' in device protected storage.'),
|
||||
'name': 'App is direct-boot aware [android:directBootAware=true]',
|
||||
},
|
||||
'a_network_sec': {
|
||||
'has_network_security': {
|
||||
'title': ('App has a Network Security Configuration'
|
||||
'<br>[android:networkSecurityConfig=%s]'),
|
||||
'level': 'info',
|
||||
@@ -39,9 +39,9 @@ MANIFEST_DESC = {
|
||||
'app code. These settings can be configured for '
|
||||
'specific domains and for a specific app. '),
|
||||
'name': ('App has a Network Security Configuration '
|
||||
'[android:networkSecurityConfig]'),
|
||||
'[android:networkSecurityConfig=%s]'),
|
||||
},
|
||||
'a_debuggable': {
|
||||
'app_is_debuggable': {
|
||||
'title': 'Debug Enabled For App<br>[android:debuggable=true]',
|
||||
'level': 'high',
|
||||
'description': ('Debugging was enabled on the app which makes it '
|
||||
@@ -50,7 +50,7 @@ MANIFEST_DESC = {
|
||||
' debugging helper classes.'),
|
||||
'name': 'Debug Enabled For App [android:debuggable=true]',
|
||||
},
|
||||
'a_allowbackup': {
|
||||
'app_allowbackup': {
|
||||
'title': ('Application Data can be Backed up'
|
||||
'<br>[android:allowBackup=true]'),
|
||||
'level': 'warning',
|
||||
@@ -60,7 +60,7 @@ MANIFEST_DESC = {
|
||||
' device.'),
|
||||
'name': 'Application Data can be Backed up [android:allowBackup=true]',
|
||||
},
|
||||
'a_allowbackup_miss': {
|
||||
'allowbackup_not_set': {
|
||||
'title': ('Application Data can be Backed up<br>[android:allowBackup]'
|
||||
' flag is missing.'),
|
||||
'level': 'warning',
|
||||
@@ -72,15 +72,15 @@ MANIFEST_DESC = {
|
||||
'name': ('Application Data can be Backed up [android:allowBackup] flag'
|
||||
' is missing.'),
|
||||
},
|
||||
'a_testonly': {
|
||||
'app_in_test_mode': {
|
||||
'title': 'Application is in Test Mode <br>[android:testOnly=true]',
|
||||
'level': 'high',
|
||||
'description': ('It may expose functionality or data outside of itself'
|
||||
' that would cause a security hole.'),
|
||||
'name': 'Application is in Test Mode [android:testOnly=true]',
|
||||
},
|
||||
'a_taskaffinity': {
|
||||
'title': 'TaskAffinity is set for Activity <br>(%s)',
|
||||
'task_affinity_set': {
|
||||
'title': 'TaskAffinity is set for activity <br>(%s)',
|
||||
'level': 'warning',
|
||||
'description': ('If taskAffinity is set, then other application'
|
||||
' could read the Intents sent to Activities '
|
||||
@@ -89,10 +89,10 @@ MANIFEST_DESC = {
|
||||
' in order to prevent sensitive information inside'
|
||||
' sent or received Intents from being read by '
|
||||
'another application.'),
|
||||
'name': 'TaskAffinity is set for Activity',
|
||||
'name': 'TaskAffinity is set for Activity (%s)',
|
||||
},
|
||||
'a_launchmode': {
|
||||
'title': 'Launch Mode of Activity (%s) is not standard.',
|
||||
'non_standard_launchmode': {
|
||||
'title': 'Launch Mode of activity (%s) is not standard.',
|
||||
'level': 'high',
|
||||
'description': ('An Activity should not be having the launch mode'
|
||||
' attribute set to "singleTask/singleInstance" as '
|
||||
@@ -101,17 +101,66 @@ MANIFEST_DESC = {
|
||||
' calling Intent. So it is required to use the'
|
||||
' "standard" launch mode attribute when sensitive'
|
||||
' information is included in an Intent.'),
|
||||
'name': 'Launch Mode of Activity is not standard.',
|
||||
'name': 'Launch Mode of activity (%s) is not standard.',
|
||||
},
|
||||
'a_prot_sign': {
|
||||
'improper_provider_permission': {
|
||||
'title': 'Improper Content Provider Permissions<br>[%s]',
|
||||
'level': 'high',
|
||||
'description': ('A content provider permission was set to allows'
|
||||
' access from any other app on the device. '
|
||||
'Content providers may contain sensitive '
|
||||
'information about an app and therefore '
|
||||
'should not be shared.'),
|
||||
'name': 'Improper Content Provider Permissions',
|
||||
},
|
||||
'dialer_code_found': {
|
||||
'title': ('Dailer Code: %s Found'
|
||||
' <br>[android:scheme="android_secret_code"]'),
|
||||
'level': 'warning',
|
||||
'description': ('A secret code was found in the manifest. These codes,'
|
||||
' when entered into the dialer grant access to hidden'
|
||||
' content that may contain sensitive information.'),
|
||||
'name': ('Dailer Code: %s Found.'
|
||||
' [android:scheme="android_secret_code"]'),
|
||||
},
|
||||
'sms_receiver_port_found': {
|
||||
'title': 'Data SMS Receiver Set on Port: %s Found<br>[android:port]',
|
||||
'level': 'warning',
|
||||
'description': ('A binary SMS receiver is configured to listen on a'
|
||||
' port. Binary SMS messages sent to a device are '
|
||||
'processed by the application in whichever way the'
|
||||
' developer choses. The data in this SMS should be'
|
||||
' properly validated by the application. Furthermore,'
|
||||
' the application should assume that the SMS being'
|
||||
' received is from an untrusted source.'),
|
||||
'name': 'Data SMS Receiver Set on Port: %s Found. [android:port]',
|
||||
},
|
||||
'high_intent_priority_found': {
|
||||
'title': 'High Intent Priority (%s)<br>[android:priority]',
|
||||
'level': 'warning',
|
||||
'description': ('By setting an intent priority higher than another'
|
||||
' intent, the app effectively overrides '
|
||||
'other requests.'),
|
||||
'name': 'High Intent Priority (%s). [android:priority]',
|
||||
},
|
||||
'high_action_priority_found': {
|
||||
'title': 'High Action Priority (%s)<br>[android:priority] ',
|
||||
'level': 'warning',
|
||||
'description': ('By setting an action priority higher than'
|
||||
' another action, the app effectively '
|
||||
'overrides other requests.'),
|
||||
'name': 'High Action Priority (%s). [android:priority]',
|
||||
},
|
||||
'exported_protected_permission_signature': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission.'
|
||||
'<br>%s<br>[android:exported=true]'),
|
||||
'level': 'info',
|
||||
'description': ('A%s %s is found to be exported, but'
|
||||
' is protected by permission.'),
|
||||
'name': 'is Protected by a permission.[android:exported=true]',
|
||||
'name': ('%s %s is Protected by a permission.'
|
||||
' [%s] [android:exported=true]'),
|
||||
},
|
||||
'a_prot_normal': {
|
||||
'exported_protected_permission_normal': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission, but'
|
||||
' the protection level of the permission should be checked.'
|
||||
'<br>%s <br>[android:exported=true]'),
|
||||
@@ -126,10 +175,12 @@ MANIFEST_DESC = {
|
||||
' If it was set to signature, only applications '
|
||||
'signed with the same certificate could obtain '
|
||||
'the permission.'),
|
||||
'name': ('is Protected by a permission, but the protection level of '
|
||||
'the permission should be checked.[android:exported=true]'),
|
||||
'name': ('%s %s is Protected by a permission,'
|
||||
' but the protection level of'
|
||||
' the permission should be checked.'
|
||||
' [%s] [android:exported=true]'),
|
||||
},
|
||||
'a_prot_danger': {
|
||||
'exported_protected_permission_dangerous': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission, but'
|
||||
' the protection level of the permission should be checked.'
|
||||
'<br>%s <br>[android:exported=true]'),
|
||||
@@ -143,10 +194,12 @@ MANIFEST_DESC = {
|
||||
' permission and interact with the component. If it'
|
||||
' was set to signature, only applications signed with'
|
||||
' the same certificate could obtain the permission.'),
|
||||
'name': ('is Protected by a permission, but the protection level of'
|
||||
' the permission should be checked.[android:exported=true]'),
|
||||
'name': ('%s %s is Protected by a permission,'
|
||||
' but the protection level of'
|
||||
' the permission should be checked.'
|
||||
' [%s] [android:exported=true]'),
|
||||
},
|
||||
'a_prot_sign_sys': {
|
||||
'exported_protected_permission_signatureorsystem': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission, but'
|
||||
' the protection level of the permission should be checked.'
|
||||
'<br>%s <br>[android:exported=true]'),
|
||||
@@ -158,10 +211,12 @@ MANIFEST_DESC = {
|
||||
'Signature level should suffice for most purposes, '
|
||||
'and does not depend on where the applications are '
|
||||
'installed on the device.'),
|
||||
'name': ('is Protected by a permission, but the protection level of'
|
||||
' the permission should be checked.[android:exported=true]'),
|
||||
'name': ('%s %s is Protected by a permission,'
|
||||
' but the protection level of'
|
||||
' the permission should be checked.'
|
||||
' [%s] [android:exported=true]'),
|
||||
},
|
||||
'a_prot_unknown': {
|
||||
'exported_protected_permission_not_defined': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission, but'
|
||||
' the protection level of the permission should be checked.'
|
||||
'<br>%s <br>[android:exported=true]'),
|
||||
@@ -177,11 +232,12 @@ MANIFEST_DESC = {
|
||||
' interact with the component. If it is set to '
|
||||
'signature, only applications signed with the same '
|
||||
'certificate can obtain the permission.'),
|
||||
'name': ('is Protected by a permission, but the protection level '
|
||||
'name': ('%s %s is Protected by a permission,'
|
||||
' but the protection level '
|
||||
'of the permission should be '
|
||||
'checked.[android:exported=true]'),
|
||||
'checked. [%s] [android:exported=true]'),
|
||||
},
|
||||
'a_prot_normal_appl': {
|
||||
'exported_protected_permission_normal_app_level': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission at'
|
||||
' the application level, but the protection level of the '
|
||||
'permission should be checked.<br>%s <br>'
|
||||
@@ -197,11 +253,11 @@ MANIFEST_DESC = {
|
||||
'component. If it was set to signature, only '
|
||||
'applications signed with the same certificate '
|
||||
'could obtain the permission.'),
|
||||
'name': ('is Protected by a permission at the application level,'
|
||||
'name': ('%s %s is Protected by a permission at the application level,'
|
||||
' but the protection level of the permission should be '
|
||||
'checked.[android:exported=true]'),
|
||||
'checked. [%s] [android:exported=true]'),
|
||||
},
|
||||
'a_prot_danger_appl': {
|
||||
'exported_protected_permission_dangerous_app_level': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission at'
|
||||
' the application level, but the protection level of the '
|
||||
'permission should be checked.'
|
||||
@@ -217,20 +273,21 @@ MANIFEST_DESC = {
|
||||
'with the component. If it was set to signature, '
|
||||
'only applications signed with the same certificate'
|
||||
' could obtain the permission.'),
|
||||
'name': ('is Protected by a permission at the application level, but'
|
||||
'name': ('%s %s is Protected by a permission'
|
||||
' at the application level, but'
|
||||
' the protection level of the permission should be '
|
||||
'checked.[android:exported=true]'),
|
||||
'checked. [%s] [android:exported=true]'),
|
||||
},
|
||||
'a_prot_sign_appl': {
|
||||
'exported_protected_permission': {
|
||||
'title': ('<strong>%s</strong> (%s) Protected by a permission at'
|
||||
' the application level.<br>%s<br>[android:exported=true]'),
|
||||
'level': 'info',
|
||||
'description': ('A%s %s is found to be exported, but is protected by'
|
||||
' a permission at the application level.'),
|
||||
'name': ('Protected by a permission at the application level.'
|
||||
'[android:exported=true]'),
|
||||
'name': ('%s %s Protected by a permission at the application level.'
|
||||
' [%s] [android:exported=true]'),
|
||||
},
|
||||
'a_prot_sign_sys_appl': {
|
||||
'exported_protected_permission_signatureorsystem_app_level': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission at '
|
||||
'the application level, but the protection level of the '
|
||||
'permission should be checked.'
|
||||
@@ -244,11 +301,12 @@ MANIFEST_DESC = {
|
||||
' should suffice for most purposes, and does not '
|
||||
'depend on where the applications are installed'
|
||||
' on the device.'),
|
||||
'name': ('is Protected by a permission at the application level, but'
|
||||
'name': ('%s %s is Protected by a permission'
|
||||
' at the application level, but'
|
||||
' the protection level of the permission should be checked.'
|
||||
'[android:exported=true]'),
|
||||
' [%s] [android:exported=true]'),
|
||||
},
|
||||
'a_prot_unknown_appl': {
|
||||
'exported_protected_permission_app_level': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission'
|
||||
' at the application, but the protection level of the '
|
||||
'permission should be checked.'
|
||||
@@ -266,20 +324,21 @@ MANIFEST_DESC = {
|
||||
' with the component. If it is set to signature, only'
|
||||
' applications signed with the same certificate can'
|
||||
' obtain the permission.'),
|
||||
'name': ('is Protected by a permission at the application, but the'
|
||||
'name': ('%s %s is Protected by a permission'
|
||||
' at the application, but the'
|
||||
' protection level of the permission should be checked.'
|
||||
'[android:exported=true]'),
|
||||
' [%s] [android:exported=true]'),
|
||||
},
|
||||
'a_not_protected': {
|
||||
'explicitly_exported': {
|
||||
'title': ('<strong>%s</strong> (%s) is not Protected.'
|
||||
' <br>[android:exported=true]'),
|
||||
'level': 'high',
|
||||
'description': ('A%s %s is found to be shared with other apps on the'
|
||||
' device therefore leaving it accessible to any other'
|
||||
' application on the device.'),
|
||||
'name': 'is not Protected. [android:exported=true]',
|
||||
'name': '%s %s is not Protected. [android:exported=true]',
|
||||
},
|
||||
'a_not_protected_filter': {
|
||||
'exported_intent_filter_exists': {
|
||||
'title': ('<strong>%s</strong> (%s) is not Protected.<br>'
|
||||
'An intent-filter exists.'),
|
||||
'level': 'warning',
|
||||
@@ -288,11 +347,11 @@ MANIFEST_DESC = {
|
||||
'application on the device. The presence of '
|
||||
'intent-filter indicates that the %s'
|
||||
' is explicitly exported.'),
|
||||
'name': 'is not Protected.An intent-filter exists.',
|
||||
'name': '%s %s is not Protected.An intent-filter exists.',
|
||||
},
|
||||
'c_not_protected': {
|
||||
'exported_provider': {
|
||||
'title': ('<strong>%s</strong> (%s) is not Protected. <br>'
|
||||
'[[Content Provider, targetSdkVersion < 17]'),
|
||||
'[Content Provider, targetSdkVersion < 17]'),
|
||||
'level': 'warning',
|
||||
'description': ('A%s %s is found to be shared with other apps'
|
||||
' on the device therefore leaving it accessible '
|
||||
@@ -301,9 +360,10 @@ MANIFEST_DESC = {
|
||||
'under 17, which makes it exported by default,'
|
||||
' regardless of the API level of the system '
|
||||
'that the application runs on.'),
|
||||
'name': 'is not Protected.[[Content Provider, targetSdkVersion < 17]',
|
||||
'name': ('%s %s is not Protected.'
|
||||
' [Content Provider, targetSdkVersion < 17]'),
|
||||
},
|
||||
'c_not_protected2': {
|
||||
'exported_provider_2': {
|
||||
'title': ('<strong>%s</strong> (%s) would not be Protected if the'
|
||||
' application ran on a device where the the API level was'
|
||||
' less than 17. <br>[Content Provider, '
|
||||
@@ -315,11 +375,12 @@ MANIFEST_DESC = {
|
||||
'shared with other apps on the device therefore '
|
||||
'leaving it accessible to any other application '
|
||||
'on the device.'),
|
||||
'name': ('would not be Protected if the application ran on a device'
|
||||
' where the the API level was less than 17.[Content Provider,'
|
||||
' targetSdkVersion >= 17]'),
|
||||
'name': ('%s %s would not be Protected if'
|
||||
' the application ran on a device'
|
||||
' where the the API level was less than 17.'
|
||||
' [Content Provider, targetSdkVersion >= 17]'),
|
||||
},
|
||||
'c_prot_normal': {
|
||||
'exported_provider_normal': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission, but'
|
||||
' the protection level of the permission should be checked.'
|
||||
'<br>%s <br>[Content Provider, targetSdkVersion < 17]'),
|
||||
@@ -334,11 +395,13 @@ MANIFEST_DESC = {
|
||||
'If it was set to signature, only applications signed '
|
||||
'with the same certificate could obtain '
|
||||
'the permission.'),
|
||||
'name': ('is Protected by a permission, but the protection level'
|
||||
' of the permission should be checked.[Content Provider,'
|
||||
'name': ('%s %s is Protected by a permission,'
|
||||
' but the protection level'
|
||||
' of the permission should be checked.'
|
||||
' [%s] [Content Provider,'
|
||||
' targetSdkVersion < 17]'),
|
||||
},
|
||||
'c_prot_danger': {
|
||||
'exported_provider_danger': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission, '
|
||||
'but the protection level of the permission should be '
|
||||
'checked.<br>%s <br>[Content Provider, '
|
||||
@@ -354,11 +417,12 @@ MANIFEST_DESC = {
|
||||
' was set to signature, only applications signed with'
|
||||
' the same certificate could obtain '
|
||||
'the permission.'),
|
||||
'name': ('is Protected by a permission, but the protection level of '
|
||||
'the permission should be checked.[Content Provider, '
|
||||
'name': ('%s %s is Protected by a permission,'
|
||||
' but the protection level of '
|
||||
'the permission should be checked. [%s] [Content Provider, '
|
||||
'targetSdkVersion < 17]'),
|
||||
},
|
||||
'c_prot_sign': {
|
||||
'exported_provider_signature': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission.'
|
||||
'<br>%s <br>[Content Provider, targetSdkVersion < 17]'),
|
||||
'level': 'info',
|
||||
@@ -366,10 +430,10 @@ MANIFEST_DESC = {
|
||||
' device therefore leaving it accessible to any other'
|
||||
' application on the device. It is '
|
||||
'protected by permission.'),
|
||||
'name': ('is Protected by a permission.[Content Provider, '
|
||||
'name': ('%s %s is Protected by a permission. [%s] [Content Provider, '
|
||||
'targetSdkVersion < 17]'),
|
||||
},
|
||||
'c_prot_sign_sys': {
|
||||
'exported_provider_signatureorsystem': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission,'
|
||||
' but the protection level of the permission should be '
|
||||
'checked.<br>%s <br>[Content Provider, '
|
||||
@@ -382,11 +446,12 @@ MANIFEST_DESC = {
|
||||
' Signature level should suffice for most purposes,'
|
||||
' and does not depend on where the applications are'
|
||||
' installed on the device.'),
|
||||
'name': ('is Protected by a permission, but the protection level of '
|
||||
'the permission should be checked.[Content Provider, '
|
||||
'name': ('%s %s is Protected by a permission,'
|
||||
' but the protection level of '
|
||||
'the permission should be checked. [%s] [Content Provider, '
|
||||
'targetSdkVersion < 17]'),
|
||||
},
|
||||
'c_prot_unknown': {
|
||||
'exported_provider_unknown': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission, but'
|
||||
' the protection level of the permission should be checked.'
|
||||
'<br>%s <br>[Content Provider, targetSdkVersion < 17]'),
|
||||
@@ -402,11 +467,12 @@ MANIFEST_DESC = {
|
||||
' interact with the component. If it is set to '
|
||||
'signature, only applications signed with the same '
|
||||
'certificate can obtain the permission.'),
|
||||
'name': ('is Protected by a permission, but the protection level of'
|
||||
' the permission should be checked.[Content Provider,'
|
||||
'name': ('%s %s is Protected by a permission,'
|
||||
' but the protection level of'
|
||||
' the permission should be checked. [%s] [Content Provider,'
|
||||
' targetSdkVersion < 17]'),
|
||||
},
|
||||
'c_prot_normal_appl': {
|
||||
'exported_provider_normal_app': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission at'
|
||||
' the application level, but the protection level of the'
|
||||
' permission should be checked.'
|
||||
@@ -422,11 +488,12 @@ MANIFEST_DESC = {
|
||||
' component. If it was set to signature, only '
|
||||
'applications signed with the same certificate could'
|
||||
' obtain the permission.'),
|
||||
'name': ('is Protected by a permission at the application level, but'
|
||||
'name': ('%s %s is Protected by a permission'
|
||||
' at the application level, but'
|
||||
' the protection level of the permission should be checked.'
|
||||
'[Content Provider, targetSdkVersion < 17]'),
|
||||
' [%s] [Content Provider, targetSdkVersion < 17]'),
|
||||
},
|
||||
'c_prot_danger_appl': {
|
||||
'exported_provider_danger_appl': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission at '
|
||||
'the application level, but the protection level of the '
|
||||
'permission should be checked.'
|
||||
@@ -442,11 +509,12 @@ MANIFEST_DESC = {
|
||||
' with the component. If it was set to signature, '
|
||||
'only applications signed with the same certificate'
|
||||
' could obtain the permission.'),
|
||||
'name': ('is Protected by a permission at the application level, but'
|
||||
'name': ('%s %s is Protected by a permission'
|
||||
' at the application level, but'
|
||||
' the protection level of the permission should be checked.'
|
||||
'[Content Provider, targetSdkVersion < 17]'),
|
||||
'[%s] [Content Provider, targetSdkVersion < 17]'),
|
||||
},
|
||||
'c_prot_sign_appl': {
|
||||
'exported_provider_signature_appl': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission at'
|
||||
' the application level.<br>%s <br>[Content Provider,'
|
||||
' targetSdkVersion < 17]'),
|
||||
@@ -455,10 +523,10 @@ MANIFEST_DESC = {
|
||||
' the device therefore leaving it accessible to any'
|
||||
' other application on the device. It is protected '
|
||||
'by permission at the application level.'),
|
||||
'name': ('is Protected by a permission at the application level.'
|
||||
'[Content Provider, targetSdkVersion < 17]'),
|
||||
'name': ('%s %s is Protected by a permission at the application level.'
|
||||
'[%s] [Content Provider, targetSdkVersion < 17]'),
|
||||
},
|
||||
'c_prot_sign_sys_appl': {
|
||||
'exported_provider_signatureorsystem_app': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission at'
|
||||
' the application level, but the protection level of the'
|
||||
' permission should be checked.'
|
||||
@@ -471,11 +539,12 @@ MANIFEST_DESC = {
|
||||
'level is used instead. Signature level should suffice'
|
||||
' for most purposes, and does not depend on where the'
|
||||
' applications are installed on the device.'),
|
||||
'name': ('is Protected by a permission at the application level, '
|
||||
'name': ('%s %s is Protected by a permission'
|
||||
' at the application level, '
|
||||
'but the protection level of the permission should be '
|
||||
'checked.[Content Provider, targetSdkVersion < 17]'),
|
||||
'checked. [%s] [Content Provider, targetSdkVersion < 17]'),
|
||||
},
|
||||
'c_prot_unknown_appl': {
|
||||
'exported_provider_unknown_app': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission at'
|
||||
' application level, but the protection level of the '
|
||||
'permission should be checked.<br>%s '
|
||||
@@ -493,11 +562,11 @@ MANIFEST_DESC = {
|
||||
'component. If it is set to signature, only '
|
||||
'applications signed with the same certificate '
|
||||
'can obtain the permission.'),
|
||||
'name': ('is Protected by a permission at application level, but'
|
||||
'name': ('%s %s is Protected by a permission at application level, but'
|
||||
' the protection level of the permission should be checked.'
|
||||
'[Content Provider, targetSdkVersion < 17]'),
|
||||
' [%s] [Content Provider, targetSdkVersion < 17]'),
|
||||
},
|
||||
'c_prot_normal_new': {
|
||||
'exported_provider_normal_new': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission, '
|
||||
'but the protection level of the permission should be '
|
||||
'checked if the application runs on a device where the '
|
||||
@@ -514,12 +583,13 @@ MANIFEST_DESC = {
|
||||
' component. If it was set to signature, only'
|
||||
' applications signed with the same certificate '
|
||||
'could obtain the permission.'),
|
||||
'name': ('is Protected by a permission, but the protection level of'
|
||||
'name': ('%s %s is Protected by a permission,'
|
||||
' but the protection level of'
|
||||
' the permission should be checked if the application runs '
|
||||
'on a device where the the API level is less than 17 '
|
||||
'[Content Provider, targetSdkVersion >= 17]'),
|
||||
' [%s] [Content Provider, targetSdkVersion >= 17]'),
|
||||
},
|
||||
'c_prot_danger_new': {
|
||||
'exported_provider_danger_new': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission,'
|
||||
' but the protection level of the permission should be '
|
||||
'checked if the application runs on a device where '
|
||||
@@ -536,12 +606,13 @@ MANIFEST_DESC = {
|
||||
' with the component. If it was set to signature, only'
|
||||
' applications signed with the same certificate could'
|
||||
' obtain the permission.'),
|
||||
'name': ('is Protected by a permission, but the protection level of'
|
||||
'name': ('%s %s is Protected by a permission'
|
||||
', but the protection level of'
|
||||
' the permission should be checked if the application runs on'
|
||||
' a device where the the API level is less than 17.'
|
||||
'[Content Provider, targetSdkVersion >= 17]'),
|
||||
' [%s] [Content Provider, targetSdkVersion >= 17]'),
|
||||
},
|
||||
'c_prot_sign_new': {
|
||||
'exported_provider_signature_new': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission.'
|
||||
'<br>%s <br>[Content Provider, targetSdkVersion >= 17]'),
|
||||
'level': 'info',
|
||||
@@ -549,10 +620,10 @@ MANIFEST_DESC = {
|
||||
' application ran on a device where the the API level'
|
||||
' was less than 17. Nevertheless, it is protected '
|
||||
'by a permission.'),
|
||||
'name': ('is Protected by a permission.'
|
||||
'[Content Provider, targetSdkVersion >= 17]'),
|
||||
'name': ('%s %s is Protected by a permission.'
|
||||
' [%s] [Content Provider, targetSdkVersion >= 17]'),
|
||||
},
|
||||
'c_prot_sign_sys_new': {
|
||||
'exported_provider_signatureorsystem_new': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission, but'
|
||||
' the protection level of the permission should be checked.'
|
||||
'<br>%s <br>[Content Provider, targetSdkVersion >= 17]'),
|
||||
@@ -566,14 +637,15 @@ MANIFEST_DESC = {
|
||||
'instead. Signature level should suffice for most'
|
||||
' purposes, and does not depend on where the'
|
||||
' applications are installed on the device.'),
|
||||
'name': ('is Protected by a permission, but the protection level of'
|
||||
'name': ('%s %s is Protected by a permission,'
|
||||
' but the protection level of'
|
||||
' the permission should be checked.'
|
||||
'[Content Provider, targetSdkVersion >= 17]'),
|
||||
' [%s] [Content Provider, targetSdkVersion >= 17]'),
|
||||
},
|
||||
'c_prot_unknown_new': {
|
||||
'exported_provider_unknown_new': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission, but'
|
||||
' the protection level of the permission should be checked'
|
||||
' if the application runs on a device where the the API '
|
||||
' if the application runs on a device where the the API '
|
||||
'level is less than 17.<br>%s <br>'
|
||||
'[Content Provider, targetSdkVersion >= 17]'),
|
||||
'level': 'warning',
|
||||
@@ -589,12 +661,13 @@ MANIFEST_DESC = {
|
||||
'component. If it is set to signature, only '
|
||||
'applications signed with the same certificate'
|
||||
' can obtain the permission.'),
|
||||
'name': ('is Protected by a permission, but the protection level of'
|
||||
' the permission should be checked if the application runs'
|
||||
'name': ('%s %s is Protected by a permission, but the'
|
||||
' protection level of the permission should be'
|
||||
' checked if the application runs'
|
||||
' on a device where the the API level is less than 17.'
|
||||
'[Content Provider, targetSdkVersion >= 17]'),
|
||||
' [%s] [Content Provider, targetSdkVersion >= 17]'),
|
||||
},
|
||||
'c_prot_normal_new_appl': {
|
||||
'exported_provider_normal_app_new': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission at'
|
||||
' the application level, but the protection level of the'
|
||||
' permission should be checked if the application runs on'
|
||||
@@ -612,13 +685,13 @@ MANIFEST_DESC = {
|
||||
'If it was set to signature, only applications '
|
||||
'signed with the same certificate could obtain'
|
||||
' the permission.'),
|
||||
'name': ('is Protected by a permission at the application level '
|
||||
'name': ('%s %s is Protected by a permission at the application level '
|
||||
'should be checked, but the protection level of the '
|
||||
'permission if the application runs on a device where'
|
||||
' the the API level is less than 17.'
|
||||
'[Content Provider, targetSdkVersion >= 17]'),
|
||||
' [%s] [Content Provider, targetSdkVersion >= 17]'),
|
||||
},
|
||||
'c_prot_danger_new_appl': {
|
||||
'exported_provider_danger_app_new': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission at'
|
||||
' the application level, but the protection level of the'
|
||||
' permission should be checked if the application runs on'
|
||||
@@ -635,12 +708,14 @@ MANIFEST_DESC = {
|
||||
' with the component. If it was set to signature, only'
|
||||
' applications signed with the same certificate could'
|
||||
' obtain the permission.'),
|
||||
'name': ('is Protected by a permission at the application level, but'
|
||||
' the protection level of the permission should be checked '
|
||||
'if the application runs on a device where the the API level '
|
||||
'is less than 17.[Content Provider, targetSdkVersion >= 17]'),
|
||||
'name': ('%s %s is Protected by a permission at the application'
|
||||
' level, but the protection level of the'
|
||||
' permission should be checked'
|
||||
' if the application runs on a device where the the API level'
|
||||
' is less than 17. [%s] '
|
||||
'[Content Provider, targetSdkVersion >= 17]'),
|
||||
},
|
||||
'c_prot_sign_new_appl': {
|
||||
'exported_provider_signature_app_new': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission at'
|
||||
' the application level.<br>%s<br>'
|
||||
'[Content Provider, targetSdkVersion >= 17]'),
|
||||
@@ -649,10 +724,10 @@ MANIFEST_DESC = {
|
||||
' application ran on a device where the the API '
|
||||
'level was less than 17. Nevertheless, it is '
|
||||
'protected by a permission.'),
|
||||
'name': ('is Protected by a permission at the application level.'
|
||||
'[Content Provider, targetSdkVersion >= 17]'),
|
||||
'name': ('%s %s is Protected by a permission at the application level.'
|
||||
' [%s] [Content Provider, targetSdkVersion >= 17]'),
|
||||
},
|
||||
'c_prot_sign_sys_new_appl': {
|
||||
'exported_provider_signatureorsystem_app_new': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission at'
|
||||
' the application level, but the protection level of the'
|
||||
' permission should be checked.'
|
||||
@@ -667,11 +742,12 @@ MANIFEST_DESC = {
|
||||
' instead. Signature level should suffice for most'
|
||||
' purposes, and does not depend on where the '
|
||||
'applications are installed on the device.'),
|
||||
'name': ('is Protected by a permission at the application level, but'
|
||||
' the protection level of the permission should be checked.'
|
||||
'[Content Provider, targetSdkVersion >= 17]'),
|
||||
'name': ('%s %s is Protected by a permission at the application'
|
||||
' level but the protection level of the permission'
|
||||
' should be checked.'
|
||||
' [%s] [Content Provider, targetSdkVersion >= 17]'),
|
||||
},
|
||||
'c_prot_unknown_new_appl': {
|
||||
'exported_provider_unknown_app_new': {
|
||||
'title': ('<strong>%s</strong> (%s) is Protected by a permission at '
|
||||
'the application level, but the protection level of the '
|
||||
'permission should be checked if the application runs on'
|
||||
@@ -690,58 +766,10 @@ MANIFEST_DESC = {
|
||||
' component. If it is set to signature, only '
|
||||
'applications signed with the same certificate'
|
||||
' can obtain the permission.'),
|
||||
'name': ('is Protected by a permission at the application level,'
|
||||
'name': ('%s %s is Protected by a permission at the application level,'
|
||||
' but the protection level of the permission should be'
|
||||
' checked if the application runs on a device where the'
|
||||
' the API level is less than 17.'
|
||||
'[Content Provider, targetSdkVersion >= 17]'),
|
||||
},
|
||||
'a_improper_provider': {
|
||||
'title': 'Improper Content Provider Permissions<br>[%s]',
|
||||
'level': 'high',
|
||||
'description': ('A content provider permission was set to allows'
|
||||
' access from any other app on the device. '
|
||||
'Content providers may contain sensitive '
|
||||
'information about an app and therefore '
|
||||
'should not be shared.'),
|
||||
'name': 'Improper Content Provider Permissions',
|
||||
},
|
||||
'a_dailer_code': {
|
||||
'title': ('Dailer Code: %s Found'
|
||||
' <br>[android:scheme="android_secret_code"]'),
|
||||
'level': 'warning',
|
||||
'description': ('A secret code was found in the manifest. These codes,'
|
||||
' when entered into the dialer grant access to hidden'
|
||||
' content that may contain sensitive information.'),
|
||||
'name': ('Dailer Code: Found '
|
||||
'<br>[android:scheme="android_secret_code"]'),
|
||||
},
|
||||
'a_sms_receiver_port': {
|
||||
'title': 'Data SMS Receiver Set on Port: %s Found<br>[android:port]',
|
||||
'level': 'warning',
|
||||
'description': ('A binary SMS receiver is configured to listen on a'
|
||||
' port. Binary SMS messages sent to a device are '
|
||||
'processed by the application in whichever way the'
|
||||
' developer choses. The data in this SMS should be'
|
||||
' properly validated by the application. Furthermore,'
|
||||
' the application should assume that the SMS being'
|
||||
' received is from an untrusted source.'),
|
||||
'name': 'Data SMS Receiver Set on Port: Found<br>[android:port]',
|
||||
},
|
||||
'a_high_intent_priority': {
|
||||
'title': 'High Intent Priority (%s)<br>[android:priority]',
|
||||
'level': 'warning',
|
||||
'description': ('By setting an intent priority higher than another'
|
||||
' intent, the app effectively overrides '
|
||||
'other requests.'),
|
||||
'name': 'High Intent Priority [android:priority]',
|
||||
},
|
||||
'a_high_action_priority': {
|
||||
'title': 'High Action Priority (%s)<br>[android:priority] ',
|
||||
'level': 'warning',
|
||||
'description': ('By setting an action priority higher than'
|
||||
' another action, the app effectively '
|
||||
'overrides other requests.'),
|
||||
'name': 'High Action Priority [android:priority]',
|
||||
' [%s] [Content Provider, targetSdkVersion >= 17]'),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -34,26 +34,30 @@ def dex_2_smali(app_dir, tools_dir):
|
||||
logger.info('DEX -> SMALI')
|
||||
dexes = get_dex_files(app_dir)
|
||||
for dex_path in dexes:
|
||||
logger.info('Converting %s to Smali Code',
|
||||
filename_from_path(dex_path))
|
||||
if (len(settings.BACKSMALI_BINARY) > 0
|
||||
and is_file_exists(settings.BACKSMALI_BINARY)):
|
||||
bs_path = settings.BACKSMALI_BINARY
|
||||
else:
|
||||
bs_path = os.path.join(tools_dir, 'baksmali-2.5.2.jar')
|
||||
output = os.path.join(app_dir, 'smali_source/')
|
||||
smali = [
|
||||
find_java_binary(),
|
||||
'-jar',
|
||||
bs_path,
|
||||
'd',
|
||||
dex_path,
|
||||
'-o',
|
||||
output,
|
||||
]
|
||||
trd = threading.Thread(target=subprocess.call, args=(smali,))
|
||||
trd.daemon = True
|
||||
trd.start()
|
||||
try:
|
||||
logger.info('Converting %s to Smali Code',
|
||||
filename_from_path(dex_path))
|
||||
if (len(settings.BACKSMALI_BINARY) > 0
|
||||
and is_file_exists(settings.BACKSMALI_BINARY)):
|
||||
bs_path = settings.BACKSMALI_BINARY
|
||||
else:
|
||||
bs_path = os.path.join(tools_dir, 'baksmali-2.5.2.jar')
|
||||
output = os.path.join(app_dir, 'smali_source/')
|
||||
smali = [
|
||||
find_java_binary(),
|
||||
'-jar',
|
||||
bs_path,
|
||||
'd',
|
||||
dex_path,
|
||||
'-o',
|
||||
output,
|
||||
]
|
||||
trd = threading.Thread(target=subprocess.call, args=(smali,))
|
||||
trd.daemon = True
|
||||
trd.start()
|
||||
except Exception:
|
||||
# Fixes a bug #2014
|
||||
pass
|
||||
except Exception:
|
||||
logger.exception('Converting DEX to SMALI')
|
||||
|
||||
|
||||
@@ -7,6 +7,10 @@ from django.db.models import QuerySet
|
||||
from mobsf.MobSF.utils import python_dict, python_list
|
||||
from mobsf.StaticAnalyzer.models import StaticAnalyzerAndroid
|
||||
from mobsf.StaticAnalyzer.models import RecentScansDB
|
||||
from mobsf.StaticAnalyzer.views.common.suppression import (
|
||||
process_suppression,
|
||||
process_suppression_manifest,
|
||||
)
|
||||
|
||||
"""Module holding the functions for the db."""
|
||||
|
||||
@@ -18,6 +22,13 @@ def get_context_from_db_entry(db_entry: QuerySet) -> dict:
|
||||
"""Return the context for APK/ZIP from DB."""
|
||||
try:
|
||||
logger.info('Analysis is already Done. Fetching data from the DB...')
|
||||
package = db_entry[0].PACKAGE_NAME
|
||||
code = process_suppression(
|
||||
python_dict(db_entry[0].CODE_ANALYSIS),
|
||||
package)
|
||||
manifest_analysis = process_suppression_manifest(
|
||||
python_list(db_entry[0].MANIFEST_ANALYSIS),
|
||||
package)
|
||||
context = {
|
||||
'version': settings.MOBSF_VER,
|
||||
'title': 'Static Analysis',
|
||||
@@ -28,7 +39,7 @@ def get_context_from_db_entry(db_entry: QuerySet) -> dict:
|
||||
'md5': db_entry[0].MD5,
|
||||
'sha1': db_entry[0].SHA1,
|
||||
'sha256': db_entry[0].SHA256,
|
||||
'package_name': db_entry[0].PACKAGE_NAME,
|
||||
'package_name': package,
|
||||
'main_activity': db_entry[0].MAIN_ACTIVITY,
|
||||
'exported_activities': db_entry[0].EXPORTED_ACTIVITIES,
|
||||
'browsable_activities': python_dict(
|
||||
@@ -48,12 +59,12 @@ def get_context_from_db_entry(db_entry: QuerySet) -> dict:
|
||||
'permissions': python_dict(db_entry[0].PERMISSIONS),
|
||||
'certificate_analysis': python_dict(
|
||||
db_entry[0].CERTIFICATE_ANALYSIS),
|
||||
'manifest_analysis': python_list(db_entry[0].MANIFEST_ANALYSIS),
|
||||
'manifest_analysis': manifest_analysis,
|
||||
'network_security': python_list(db_entry[0].NETWORK_SECURITY),
|
||||
'binary_analysis': python_list(db_entry[0].BINARY_ANALYSIS),
|
||||
'file_analysis': python_list(db_entry[0].FILE_ANALYSIS),
|
||||
'android_api': python_dict(db_entry[0].ANDROID_API),
|
||||
'code_analysis': python_dict(db_entry[0].CODE_ANALYSIS),
|
||||
'code_analysis': code,
|
||||
'niap_analysis': python_dict(db_entry[0].NIAP_ANALYSIS),
|
||||
'urls': python_list(db_entry[0].URLS),
|
||||
'domains': python_dict(db_entry[0].DOMAINS),
|
||||
@@ -84,6 +95,13 @@ def get_context_from_analysis(app_dic,
|
||||
trackers) -> dict:
|
||||
"""Get the context for APK/ZIP from analysis results."""
|
||||
try:
|
||||
package = man_data_dic['packagename']
|
||||
code = process_suppression(
|
||||
code_an_dic['findings'],
|
||||
package)
|
||||
manifest_analysis = process_suppression_manifest(
|
||||
man_an_dic['manifest_anal'],
|
||||
package)
|
||||
context = {
|
||||
'title': 'Static Analysis',
|
||||
'version': settings.MOBSF_VER,
|
||||
@@ -94,7 +112,7 @@ def get_context_from_analysis(app_dic,
|
||||
'md5': app_dic['md5'],
|
||||
'sha1': app_dic['sha1'],
|
||||
'sha256': app_dic['sha256'],
|
||||
'package_name': man_data_dic['packagename'],
|
||||
'package_name': package,
|
||||
'main_activity': man_data_dic['mainactivity'],
|
||||
'exported_activities': man_an_dic['exported_act'],
|
||||
'browsable_activities': man_an_dic['browsable_activities'],
|
||||
@@ -112,12 +130,12 @@ def get_context_from_analysis(app_dic,
|
||||
'icon_found': app_dic['icon_found'],
|
||||
'certificate_analysis': cert_dic,
|
||||
'permissions': man_an_dic['permissions'],
|
||||
'manifest_analysis': man_an_dic['manifest_anal'],
|
||||
'manifest_analysis': manifest_analysis,
|
||||
'network_security': man_an_dic['network_security'],
|
||||
'binary_analysis': bin_anal,
|
||||
'file_analysis': app_dic['certz'],
|
||||
'android_api': code_an_dic['api'],
|
||||
'code_analysis': code_an_dic['findings'],
|
||||
'code_analysis': code,
|
||||
'niap_analysis': code_an_dic['niap'],
|
||||
'urls': code_an_dic['urls'],
|
||||
'domains': code_an_dic['domains'],
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
import fnmatch
|
||||
import logging
|
||||
import os
|
||||
from shutil import copytree
|
||||
from pathlib import Path
|
||||
|
||||
from androguard.core.bytecodes import apk
|
||||
|
||||
@@ -134,3 +136,30 @@ def get_icon(apk_path, res_dir):
|
||||
}
|
||||
except Exception:
|
||||
logger.exception('Fetching icon function')
|
||||
|
||||
|
||||
def get_icon_apk(app_dic):
|
||||
"""Get/Guess icon from APK binary."""
|
||||
app_dir = Path(app_dic['app_dir'])
|
||||
res_path = app_dir / 'res'
|
||||
app_dic['icon_hidden'] = True
|
||||
# Even if the icon is hidden, try to guess it by the
|
||||
# default paths
|
||||
app_dic['icon_found'] = False
|
||||
app_dic['icon_path'] = ''
|
||||
if not res_path.exists():
|
||||
# If res directory is not found
|
||||
# piggyback on apktool decompiled resources
|
||||
try:
|
||||
apk_tool_res = app_dir / 'apktool_out' / 'res'
|
||||
copytree(apk_tool_res, res_path)
|
||||
except Exception:
|
||||
pass
|
||||
# Set app_dic with icon details
|
||||
if res_path.exists():
|
||||
icon_dic = get_icon(
|
||||
app_dic['app_path'], res_path.as_posix())
|
||||
if icon_dic:
|
||||
app_dic['icon_hidden'] = icon_dic['hidden']
|
||||
app_dic['icon_found'] = bool(icon_dic['path'])
|
||||
app_dic['icon_path'] = icon_dic['path']
|
||||
|
||||
@@ -307,24 +307,24 @@ def manifest_analysis(mfxml, man_data_dic, src_type, app_dir):
|
||||
perm_appl_level_exists = False
|
||||
# End
|
||||
if application.getAttribute('android:usesCleartextTraffic') == 'true':
|
||||
ret_list.append(('a_clear_text', (), ()))
|
||||
ret_list.append(('clear_text_traffic', (), ()))
|
||||
if application.getAttribute('android:directBootAware') == 'true':
|
||||
ret_list.append(('a_boot_aware', (), ()))
|
||||
ret_list.append(('direct_boot_aware', (), ()))
|
||||
if application.getAttribute('android:networkSecurityConfig'):
|
||||
item = application.getAttribute('android:networkSecurityConfig')
|
||||
ret_list.append(('a_network_sec', (item,), ()))
|
||||
ret_list.append(('has_network_security', (item,), ()))
|
||||
do_netsec = item
|
||||
if application.getAttribute('android:debuggable') == 'true':
|
||||
ret_list.append(('a_debuggable', (), ()))
|
||||
ret_list.append(('app_is_debuggable', (), ()))
|
||||
debuggable = True
|
||||
if application.getAttribute('android:allowBackup') == 'true':
|
||||
ret_list.append(('a_allowbackup', (), ()))
|
||||
ret_list.append(('app_allowbackup', (), ()))
|
||||
elif application.getAttribute('android:allowBackup') == 'false':
|
||||
pass
|
||||
else:
|
||||
ret_list.append(('a_allowbackup_miss', (), ()))
|
||||
ret_list.append(('allowbackup_not_set', (), ()))
|
||||
if application.getAttribute('android:testOnly') == 'true':
|
||||
ret_list.append(('a_testonly', (), ()))
|
||||
ret_list.append(('app_in_test_mode', (), ()))
|
||||
for node in application.childNodes:
|
||||
an_or_a = ''
|
||||
if node.nodeName == 'activity':
|
||||
@@ -362,7 +362,7 @@ def manifest_analysis(mfxml, man_data_dic, src_type, app_dir):
|
||||
node.getAttribute('android:taskAffinity')
|
||||
):
|
||||
item = node.getAttribute('android:name')
|
||||
ret_list.append(('a_taskaffinity', (item,), ()))
|
||||
ret_list.append(('task_affinity_set', (item,), ()))
|
||||
|
||||
# LaunchMode
|
||||
try:
|
||||
@@ -378,7 +378,7 @@ def manifest_analysis(mfxml, man_data_dic, src_type, app_dir):
|
||||
(node.getAttribute('android:launchMode') == 'singleInstance'
|
||||
or node.getAttribute('android:launchMode') == 'singleTask')):
|
||||
item = node.getAttribute('android:name')
|
||||
ret_list.append(('a_launchmode', (item,), ()))
|
||||
ret_list.append(('non_standard_launchmode', (item,), ()))
|
||||
# Exported Check
|
||||
item = ''
|
||||
is_inf = False
|
||||
@@ -415,27 +415,27 @@ def manifest_analysis(mfxml, man_data_dic, src_type, app_dir):
|
||||
if prot_level_exist:
|
||||
if protlevel == 'normal':
|
||||
ret_list.append(
|
||||
('a_prot_normal', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_protected_permission_normal', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
if itemname in ['Activity', 'Activity-Alias']:
|
||||
exported.append(item)
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
elif protlevel == 'dangerous':
|
||||
ret_list.append(
|
||||
('a_prot_danger', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_protected_permission_dangerous', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
if itemname in ['Activity', 'Activity-Alias']:
|
||||
exported.append(item)
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
elif protlevel == 'signature':
|
||||
ret_list.append(
|
||||
('a_prot_sign', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_protected_permission_signature', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
elif protlevel == 'signatureOrSystem':
|
||||
ret_list.append(
|
||||
('a_prot_sign_sys', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_protected_permission_signatureorsystem', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
else:
|
||||
ret_list.append(
|
||||
('a_prot_unknown', (itemname, item, perm), (an_or_a, itemname)))
|
||||
('exported_protected_permission_not_defined', (itemname, item, perm), (an_or_a, itemname)))
|
||||
if itemname in ['Activity', 'Activity-Alias']:
|
||||
exported.append(item)
|
||||
exp_count[cnt_id] = exp_count[cnt_id] + 1
|
||||
@@ -446,7 +446,7 @@ def manifest_analysis(mfxml, man_data_dic, src_type, app_dir):
|
||||
# are not protected.
|
||||
if perm_appl_level_exists is False:
|
||||
ret_list.append(
|
||||
('a_not_protected', (itemname, item), (an_or_a, itemname)))
|
||||
('exported', (itemname, item), (an_or_a, itemname)))
|
||||
if itemname in ['Activity', 'Activity-Alias']:
|
||||
exported.append(item)
|
||||
exp_count[cnt_id] = exp_count[cnt_id] + 1
|
||||
@@ -469,27 +469,27 @@ def manifest_analysis(mfxml, man_data_dic, src_type, app_dir):
|
||||
if prot_level_exist:
|
||||
if protlevel == 'normal':
|
||||
ret_list.append(
|
||||
('a_prot_normal_appl', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_protected_permission_normal_app_level', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
if itemname in ['Activity', 'Activity-Alias']:
|
||||
exported.append(item)
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
elif protlevel == 'dangerous':
|
||||
ret_list.append(
|
||||
('a_prot_danger_appl', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_protected_permission_dangerous_app_level', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
if itemname in ['Activity', 'Activity-Alias']:
|
||||
exported.append(item)
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
elif protlevel == 'signature':
|
||||
ret_list.append(
|
||||
('a_prot_sign_appl', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_protected_permission', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
elif protlevel == 'signatureOrSystem':
|
||||
ret_list.append(
|
||||
('a_prot_sign_sys_appl', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_protected_permission_signatureorsystem_app_level', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
else:
|
||||
ret_list.append(
|
||||
('a_prot_unknown_appl', (itemname, item, perm), (an_or_a, itemname)))
|
||||
('exported_protected_permission_app_level', (itemname, item, perm), (an_or_a, itemname)))
|
||||
if itemname in ['Activity', 'Activity-Alias']:
|
||||
exported.append(item)
|
||||
exp_count[cnt_id] = exp_count[
|
||||
@@ -528,27 +528,27 @@ def manifest_analysis(mfxml, man_data_dic, src_type, app_dir):
|
||||
if prot_level_exist:
|
||||
if protlevel == 'normal':
|
||||
ret_list.append(
|
||||
('a_prot_normal', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_protected_permission_normal', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
if itemname in ['Activity', 'Activity-Alias']:
|
||||
exported.append(item)
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
elif protlevel == 'dangerous':
|
||||
ret_list.append(
|
||||
('a_prot_danger', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_protected_permission_dangerous', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
if itemname in ['Activity', 'Activity-Alias']:
|
||||
exported.append(item)
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
elif protlevel == 'signature':
|
||||
ret_list.append(
|
||||
('a_prot_sign', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_protected_permission_signature', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
elif protlevel == 'signatureOrSystem':
|
||||
ret_list.append(
|
||||
('a_prot_sign_sys', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_protected_permission_signatureorsystem', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
else:
|
||||
ret_list.append(
|
||||
('a_prot_unknown', (itemname, item, perm), (an_or_a, itemname)))
|
||||
('exported_protected_permission_not_defined', (itemname, item, perm), (an_or_a, itemname)))
|
||||
if itemname in ['Activity', 'Activity-Alias']:
|
||||
exported.append(item)
|
||||
exp_count[cnt_id] = exp_count[
|
||||
@@ -560,7 +560,7 @@ def manifest_analysis(mfxml, man_data_dic, src_type, app_dir):
|
||||
# they are not protected.
|
||||
if perm_appl_level_exists is False:
|
||||
ret_list.append(
|
||||
('a_not_protected_filter', (itemname, item), (an_or_a, itemname, itemname)))
|
||||
('exported_intent_filter_exists', (itemname, item), (an_or_a, itemname, itemname)))
|
||||
if itemname in ['Activity', 'Activity-Alias']:
|
||||
exported.append(item)
|
||||
exp_count[cnt_id] = exp_count[
|
||||
@@ -584,27 +584,27 @@ def manifest_analysis(mfxml, man_data_dic, src_type, app_dir):
|
||||
if prot_level_exist:
|
||||
if protlevel == 'normal':
|
||||
ret_list.append(
|
||||
('a_prot_normal_appl', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_protected_permission_normal_app_level', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
if itemname in ['Activity', 'Activity-Alias']:
|
||||
exported.append(item)
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
elif protlevel == 'dangerous':
|
||||
ret_list.append(
|
||||
('a_prot_danger_appl', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_protected_permission_dangerous_app_level', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
if itemname in ['Activity', 'Activity-Alias']:
|
||||
exported.append(item)
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
elif protlevel == 'signature':
|
||||
ret_list.append(
|
||||
('a_prot_sign_appl', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_protected_permission', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
elif protlevel == 'signatureOrSystem':
|
||||
ret_list.append(
|
||||
('a_prot_sign_sys_appl', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_protected_permission_signatureorsystem_app_level', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
else:
|
||||
ret_list.append(
|
||||
('a_prot_unknown_appl', (itemname, item, perm), (an_or_a, itemname)))
|
||||
('exported_protected_permission_app_level', (itemname, item, perm), (an_or_a, itemname)))
|
||||
if itemname in ['Activity', 'Activity-Alias']:
|
||||
exported.append(item)
|
||||
exp_count[cnt_id] = exp_count[
|
||||
@@ -637,29 +637,29 @@ def manifest_analysis(mfxml, man_data_dic, src_type, app_dir):
|
||||
if prot_level_exist:
|
||||
if protlevel == 'normal':
|
||||
ret_list.append(
|
||||
('c_prot_normal', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_provider_normal', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
elif protlevel == 'dangerous':
|
||||
ret_list.append(
|
||||
('c_prot_danger', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_provider_danger', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
elif protlevel == 'signature':
|
||||
ret_list.append(
|
||||
('c_prot_sign', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_provider_signature', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
elif protlevel == 'signatureOrSystem':
|
||||
ret_list.append(
|
||||
('c_prot_sign_sys', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_provider_signatureorsystem', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
else:
|
||||
ret_list.append(
|
||||
('c_prot_unknown', (itemname, item, perm), (an_or_a, itemname)))
|
||||
('exported_provider_unknown', (itemname, item, perm), (an_or_a, itemname)))
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
else:
|
||||
if perm_appl_level_exists is False:
|
||||
ret_list.append(
|
||||
('c_not_protected', (itemname, item), (an_or_a, itemname)))
|
||||
('exported_provider', (itemname, item), (an_or_a, itemname)))
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
else:
|
||||
@@ -674,23 +674,23 @@ def manifest_analysis(mfxml, man_data_dic, src_type, app_dir):
|
||||
if prot_level_exist:
|
||||
if protlevel == 'normal':
|
||||
ret_list.append(
|
||||
('c_prot_normal_appl', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_provider_normal_app', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
elif protlevel == 'dangerous':
|
||||
ret_list.append(
|
||||
('c_prot_danger_appl', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_provider_danger_appl', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
elif protlevel == 'signature':
|
||||
ret_list.append(
|
||||
('c_prot_sign_appl', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_provider_signature_appl', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
elif protlevel == 'signatureOrSystem':
|
||||
ret_list.append(
|
||||
('c_prot_sign_sys_appl', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_provider_signatureorsystem_app', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
else:
|
||||
ret_list.append(
|
||||
('c_prot_unknown_appl', (itemname, item, perm), (an_or_a, itemname)))
|
||||
('exported_provider_unknown_app', (itemname, item, perm), (an_or_a, itemname)))
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
# Esteve 29.07.2016 - end
|
||||
@@ -718,29 +718,29 @@ def manifest_analysis(mfxml, man_data_dic, src_type, app_dir):
|
||||
if prot_level_exist:
|
||||
if protlevel == 'normal':
|
||||
ret_list.append(
|
||||
('c_prot_normal_new', (itemname, item, perm + prot), (itemname)))
|
||||
('exported_provider_normal_new', (itemname, item, perm + prot), (itemname)))
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
if protlevel == 'dangerous':
|
||||
ret_list.append(
|
||||
('c_prot_danger_new', (itemname, item, perm + prot), (itemname)))
|
||||
('exported_provider_danger_new', (itemname, item, perm + prot), (itemname)))
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
if protlevel == 'signature':
|
||||
ret_list.append(
|
||||
('c_prot_sign_new', (itemname, item, perm + prot), (itemname)))
|
||||
('exported_provider_signature_new', (itemname, item, perm + prot), (itemname)))
|
||||
if protlevel == 'signatureOrSystem':
|
||||
ret_list.append(
|
||||
('c_prot_sign_sys_new', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_provider_signatureorsystem_new', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
else:
|
||||
ret_list.append(
|
||||
('c_prot_unknown_new', (itemname, item, perm), (itemname)))
|
||||
('exported_provider_unknown_new', (itemname, item, perm), (itemname)))
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
else:
|
||||
if perm_appl_level_exists is False:
|
||||
ret_list.append(
|
||||
('c_not_protected2', (itemname, item), (an_or_a, itemname)))
|
||||
('exported_provider_2', (itemname, item), (an_or_a, itemname)))
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
else:
|
||||
@@ -755,23 +755,23 @@ def manifest_analysis(mfxml, man_data_dic, src_type, app_dir):
|
||||
if prot_level_exist:
|
||||
if protlevel == 'normal':
|
||||
ret_list.append(
|
||||
('c_prot_normal_new_appl', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_provider_normal_app_new', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
elif protlevel == 'dangerous':
|
||||
ret_list.append(
|
||||
('c_prot_danger_new_appl', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_provider_danger_app_new', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
elif protlevel == 'signature':
|
||||
ret_list.append(
|
||||
('c_prot_sign_new_appl', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_provider_signature_app_new', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
elif protlevel == 'signatureOrSystem':
|
||||
ret_list.append(
|
||||
('c_prot_sign_sys_new_appl', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
('exported_provider_signatureorsystem_app_new', (itemname, item, perm + prot), (an_or_a, itemname)))
|
||||
else:
|
||||
ret_list.append(
|
||||
('c_prot_unknown_new_appl', (itemname, item, perm), (an_or_a, itemname)))
|
||||
('exported_provider_unknown_app_new', (itemname, item, perm), (an_or_a, itemname)))
|
||||
exp_count[cnt_id] = exp_count[
|
||||
cnt_id] + 1
|
||||
# Esteve 08.08.2016 - end
|
||||
@@ -780,44 +780,45 @@ def manifest_analysis(mfxml, man_data_dic, src_type, app_dir):
|
||||
for granturi in granturipermissions:
|
||||
if granturi.getAttribute('android:pathPrefix') == '/':
|
||||
ret_list.append(
|
||||
('a_improper_provider', ('pathPrefix=/',), ()))
|
||||
('improper_provider_permission', ('pathPrefix=/',), ()))
|
||||
elif granturi.getAttribute('android:path') == '/':
|
||||
ret_list.append(('a_improper_provider', ('path=/',), ()))
|
||||
ret_list.append(('improper_provider_permission', ('path=/',), ()))
|
||||
elif granturi.getAttribute('android:pathPattern') == '*':
|
||||
ret_list.append(('a_improper_provider', ('path=*',), ()))
|
||||
ret_list.append(('improper_provider_permission', ('path=*',), ()))
|
||||
# DATA
|
||||
for data in data_tag:
|
||||
if data.getAttribute('android:scheme') == 'android_secret_code':
|
||||
xmlhost = data.getAttribute('android:host')
|
||||
ret_list.append(('a_dailer_code', (xmlhost,), ()))
|
||||
ret_list.append(('dialer_code_found', (xmlhost,), ()))
|
||||
|
||||
elif data.getAttribute('android:port'):
|
||||
dataport = data.getAttribute('android:port')
|
||||
ret_list.append(('a_sms_receiver_port', (dataport,), ()))
|
||||
ret_list.append(('sms_receiver_port_found', (dataport,), ()))
|
||||
# INTENTS
|
||||
for intent in intents:
|
||||
if intent.getAttribute('android:priority').isdigit():
|
||||
value = intent.getAttribute('android:priority')
|
||||
if int(value) > 100:
|
||||
ret_list.append(
|
||||
('a_high_intent_priority', (value,), ()))
|
||||
('high_intent_priority_found', (value,), ()))
|
||||
# ACTIONS
|
||||
for action in actions:
|
||||
if action.getAttribute('android:priority').isdigit():
|
||||
value = action.getAttribute('android:priority')
|
||||
if int(value) > 100:
|
||||
ret_list.append(
|
||||
('a_high_action_priority', (value,), ()))
|
||||
('high_action_priority_found', (value,), ()))
|
||||
for a_key, t_name, t_desc in ret_list:
|
||||
a_template = android_manifest_desc.MANIFEST_DESC.get(a_key)
|
||||
if a_template:
|
||||
ret_value.append(
|
||||
{'title': a_template['title'] % t_name,
|
||||
'stat': a_template['level'],
|
||||
'desc': a_template['description'] % t_desc,
|
||||
'name': a_template['name'],
|
||||
'component': t_name,
|
||||
})
|
||||
ret_value.append({
|
||||
'rule': a_key,
|
||||
'title': a_template['title'] % t_name,
|
||||
'severity': a_template['level'],
|
||||
'description': a_template['description'] % t_desc,
|
||||
'name': a_template['name'] % t_name,
|
||||
'component': t_name,
|
||||
})
|
||||
|
||||
for category in man_data_dic['categories']:
|
||||
if category == 'android.intent.category.LAUNCHER':
|
||||
@@ -878,7 +879,10 @@ def get_manifest_file(app_dir, app_path, tools_dir, typ, apk):
|
||||
|
||||
|
||||
def get_manifest_apk(app_path, app_dir, tools_dir):
|
||||
"""Get readable AndroidManifest.xml."""
|
||||
"""Get readable AndroidManifest.xml.
|
||||
|
||||
Should be called before get_icon_apk() function
|
||||
"""
|
||||
try:
|
||||
manifest = None
|
||||
if (len(settings.APKTOOL_BINARY) > 0
|
||||
|
||||
@@ -19,9 +19,11 @@ from django.shortcuts import render
|
||||
from django.template.defaulttags import register
|
||||
|
||||
from mobsf.MobSF.utils import (
|
||||
android_component,
|
||||
file_size,
|
||||
is_dir_exists,
|
||||
is_file_exists,
|
||||
key,
|
||||
print_n_send_error_response,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.models import (
|
||||
@@ -45,7 +47,7 @@ from mobsf.StaticAnalyzer.views.android.db_interaction import (
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.android.icon_analysis import (
|
||||
find_icon_path_zip,
|
||||
get_icon,
|
||||
get_icon_apk,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.android.manifest_analysis import (
|
||||
get_manifest,
|
||||
@@ -74,12 +76,8 @@ from androguard.core.bytecodes import apk
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logging.getLogger('androguard').setLevel(logging.ERROR)
|
||||
|
||||
|
||||
@register.filter
|
||||
def key(data, key_name):
|
||||
"""Return the data for a key_name."""
|
||||
return data.get(key_name)
|
||||
register.filter('key', key)
|
||||
register.filter('android_component', android_component)
|
||||
|
||||
|
||||
def static_analyzer(request, api=False):
|
||||
@@ -179,27 +177,8 @@ def static_analyzer(request, api=False):
|
||||
)
|
||||
|
||||
# Get icon
|
||||
res_path = os.path.join(app_dic['app_dir'], 'res')
|
||||
app_dic['icon_hidden'] = True
|
||||
# Even if the icon is hidden, try to guess it by the
|
||||
# default paths
|
||||
app_dic['icon_found'] = False
|
||||
app_dic['icon_path'] = ''
|
||||
# TODO: Check for possible different names for resource
|
||||
# folder?
|
||||
if not os.path.exists(res_path):
|
||||
import shutil
|
||||
try:
|
||||
shutil.copytree(os.path.join(app_dic['app_dir'], 'apktool_out', 'res'), res_path)
|
||||
except:
|
||||
pass
|
||||
if os.path.exists(res_path):
|
||||
icon_dic = get_icon(
|
||||
app_dic['app_path'], res_path)
|
||||
if icon_dic:
|
||||
app_dic['icon_hidden'] = icon_dic['hidden']
|
||||
app_dic['icon_found'] = bool(icon_dic['path'])
|
||||
app_dic['icon_path'] = icon_dic['path']
|
||||
# apktool should run before this
|
||||
get_icon_apk(app_dic)
|
||||
|
||||
# Set Manifest link
|
||||
app_dic['mani'] = ('../manifest_view/?md5='
|
||||
|
||||
@@ -161,8 +161,11 @@ def common_fields(findings, data):
|
||||
warn = len(findings.get('warning'))
|
||||
sec = len(findings.get('secure'))
|
||||
total = high + warn + sec
|
||||
findings['security_score'] = int(100 - (
|
||||
score = int(100 - (
|
||||
((high * 1) + (warn * .5) - (sec * .2)) / total) * 100)
|
||||
if score > 100:
|
||||
score = 100
|
||||
findings['security_score'] = score
|
||||
findings['app_name'] = data.get('app_name', '')
|
||||
findings['file_name'] = data.get('file_name', '')
|
||||
findings['hash'] = data['md5']
|
||||
@@ -209,16 +212,16 @@ def get_android_dashboard(context, from_ctx=False):
|
||||
})
|
||||
# Manifest Analysis
|
||||
for m in data['manifest_analysis']:
|
||||
if m['stat'] == 'info':
|
||||
if m['severity'] == 'info':
|
||||
continue
|
||||
title = m['title'].replace('<strong>', '')
|
||||
title = title.replace('</strong>', '')
|
||||
fmt = title.split('<br>', 1)
|
||||
if len(fmt) > 1:
|
||||
desc = fmt[1].replace('<br>', '') + '\n' + m['desc']
|
||||
desc = fmt[1].replace('<br>', '') + '\n' + m['description']
|
||||
else:
|
||||
desc = m['desc']
|
||||
findings[m['stat']].append({
|
||||
desc = m['description']
|
||||
findings[m['severity']].append({
|
||||
'title': fmt[0],
|
||||
'description': desc,
|
||||
'section': 'manifest',
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
# -*- coding: utf_8 -*-
|
||||
"""
|
||||
Shared Functions for Suppression logic.
|
||||
|
||||
Module provide support for finding suppression
|
||||
"""
|
||||
import logging
|
||||
from copy import copy, deepcopy
|
||||
|
||||
from django.views.decorators.http import require_http_methods
|
||||
|
||||
from mobsf.StaticAnalyzer.models import (
|
||||
StaticAnalyzerAndroid,
|
||||
StaticAnalyzerIOS,
|
||||
)
|
||||
from mobsf.DynamicAnalyzer.views.android.operations import (
|
||||
invalid_params,
|
||||
is_attack_pattern,
|
||||
send_response,
|
||||
)
|
||||
from mobsf.MobSF.utils import (
|
||||
android_component,
|
||||
is_md5,
|
||||
python_dict,
|
||||
python_list,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.models import (
|
||||
SuppressFindings,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_package(checksum):
|
||||
"""Get package from checksum."""
|
||||
try:
|
||||
andpkg = StaticAnalyzerAndroid.objects.get(
|
||||
MD5=checksum)
|
||||
return andpkg.PACKAGE_NAME
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
iospkg = StaticAnalyzerIOS.objects.get(
|
||||
MD5=checksum)
|
||||
return iospkg.BUNDLE_ID
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
# AJAX
|
||||
|
||||
@require_http_methods(['POST'])
|
||||
def suppress_by_rule_id(request, api=False):
|
||||
"""Suppress finding by rule id."""
|
||||
data = {
|
||||
'status': 'failed',
|
||||
'message': 'Failed to suppress finding by rule id'}
|
||||
try:
|
||||
if api:
|
||||
checksum = request.POST['hash']
|
||||
else:
|
||||
checksum = request.POST['checksum']
|
||||
rule = request.POST['rule']
|
||||
stype = request.POST['type']
|
||||
type_check = stype in {'code', 'manifest'}
|
||||
if not is_md5(checksum):
|
||||
return invalid_params(api)
|
||||
package = get_package(checksum)
|
||||
if not package or is_attack_pattern(rule) or not type_check:
|
||||
return invalid_params(api)
|
||||
sup_config = SuppressFindings.objects.filter(
|
||||
PACKAGE_NAME=package, SUPPRESS_TYPE=stype)
|
||||
if sup_config.exists():
|
||||
# Update Record
|
||||
sup_rules = set(
|
||||
python_list(sup_config[0].SUPPRESS_RULE_ID))
|
||||
if rule not in sup_rules:
|
||||
sup_rules.add(rule)
|
||||
sup_config.update(SUPPRESS_RULE_ID=list(sup_rules))
|
||||
else:
|
||||
# Create Record
|
||||
values = {
|
||||
'PACKAGE_NAME': package,
|
||||
'SUPPRESS_RULE_ID': [rule],
|
||||
'SUPPRESS_FILES': {},
|
||||
'SUPPRESS_TYPE': stype,
|
||||
}
|
||||
SuppressFindings.objects.create(**values)
|
||||
return send_response({'status': 'ok'}, api)
|
||||
except Exception:
|
||||
logger.exception('Error suppressing finding by rule id')
|
||||
return send_response(data, api)
|
||||
|
||||
# AJAX
|
||||
|
||||
|
||||
@require_http_methods(['POST'])
|
||||
def suppress_by_files(request, api=False):
|
||||
"""Suppress finding by files."""
|
||||
data = {
|
||||
'status': 'failed',
|
||||
'message': 'Failed to suppress finding by files'}
|
||||
try:
|
||||
if api:
|
||||
checksum = request.POST['hash']
|
||||
else:
|
||||
checksum = request.POST['checksum']
|
||||
rule = request.POST['rule']
|
||||
files_to_suppress = []
|
||||
old_files = []
|
||||
if not is_md5(checksum):
|
||||
return invalid_params(api)
|
||||
package = get_package(checksum)
|
||||
if not package or is_attack_pattern(rule):
|
||||
return invalid_params(api)
|
||||
sup_config = SuppressFindings.objects.filter(
|
||||
PACKAGE_NAME=package, SUPPRESS_TYPE='code')
|
||||
# Do Lookups
|
||||
android_static_db = StaticAnalyzerAndroid.objects.filter(
|
||||
MD5=checksum)
|
||||
ios_static_db = StaticAnalyzerIOS.objects.filter(
|
||||
MD5=checksum)
|
||||
if android_static_db.exists():
|
||||
code_res = python_dict(android_static_db[0].CODE_ANALYSIS)
|
||||
elif ios_static_db.exists():
|
||||
code_res = python_dict(ios_static_db[0].CODE_ANALYSIS)
|
||||
else:
|
||||
return send_response(data, api)
|
||||
files_to_suppress = list(code_res[rule]['files'].keys())
|
||||
if sup_config.exists():
|
||||
# Update Record
|
||||
old = python_dict(sup_config[0].SUPPRESS_FILES)
|
||||
# Empty dict or rule key not already present
|
||||
if not old or rule not in old:
|
||||
old[rule] = files_to_suppress
|
||||
else:
|
||||
old_files = old[rule]
|
||||
old[rule] = list(set(files_to_suppress + old_files))
|
||||
sup_config.update(SUPPRESS_FILES=old)
|
||||
else:
|
||||
# Create Record
|
||||
values = {
|
||||
'PACKAGE_NAME': package,
|
||||
'SUPPRESS_RULE_ID': [],
|
||||
'SUPPRESS_FILES': {rule: files_to_suppress},
|
||||
'SUPPRESS_TYPE': 'code',
|
||||
}
|
||||
SuppressFindings.objects.create(**values)
|
||||
return send_response({'status': 'ok'}, api)
|
||||
except Exception:
|
||||
logger.exception('Error suppressing finding by files')
|
||||
return send_response(data, api)
|
||||
|
||||
# AJAX
|
||||
|
||||
|
||||
@require_http_methods(['POST'])
|
||||
def list_suppressions(request, api=False):
|
||||
"""List Suppression Rules."""
|
||||
data = {
|
||||
'status': 'failed',
|
||||
'message': 'Failed to list suppression rules'}
|
||||
try:
|
||||
if api:
|
||||
checksum = request.POST['hash']
|
||||
else:
|
||||
checksum = request.POST['checksum']
|
||||
if not is_md5(checksum):
|
||||
return invalid_params(api)
|
||||
package = get_package(checksum)
|
||||
if not package:
|
||||
return invalid_params(api)
|
||||
|
||||
data = []
|
||||
all_configs = SuppressFindings.objects.filter(
|
||||
PACKAGE_NAME=package)
|
||||
if all_configs.exists():
|
||||
data = list(all_configs.values())
|
||||
for i in data:
|
||||
i['SUPPRESS_RULE_ID'] = python_list(i['SUPPRESS_RULE_ID'])
|
||||
i['SUPPRESS_FILES'] = python_dict(i['SUPPRESS_FILES'])
|
||||
return send_response(
|
||||
{'status': 'ok', 'message': data},
|
||||
api)
|
||||
except Exception:
|
||||
logger.exception('Error listing suppression rules')
|
||||
return send_response(data, api)
|
||||
|
||||
|
||||
# AJAX
|
||||
|
||||
@require_http_methods(['POST'])
|
||||
def delete_suppression(request, api=False):
|
||||
"""Delete suppression rule."""
|
||||
data = {
|
||||
'status': 'failed',
|
||||
'message': 'Failed to delete suppression rule'}
|
||||
try:
|
||||
if api:
|
||||
checksum = request.POST['hash']
|
||||
else:
|
||||
checksum = request.POST['checksum']
|
||||
rule = request.POST['rule']
|
||||
kind = request.POST['kind']
|
||||
stype = request.POST['type']
|
||||
type_check = stype in {'code', 'manifest'}
|
||||
if not is_md5(checksum):
|
||||
return invalid_params(api)
|
||||
package = get_package(checksum)
|
||||
if not package or is_attack_pattern(rule) or not type_check:
|
||||
return invalid_params(api)
|
||||
sup_config = SuppressFindings.objects.filter(
|
||||
PACKAGE_NAME=package, SUPPRESS_TYPE=stype)
|
||||
if sup_config.exists():
|
||||
if kind == 'rule':
|
||||
sup_rules = set(
|
||||
python_list(sup_config[0].SUPPRESS_RULE_ID))
|
||||
if rule in sup_rules:
|
||||
sup_rules.remove(rule)
|
||||
sup_config.update(SUPPRESS_RULE_ID=list(sup_rules))
|
||||
elif kind == 'file':
|
||||
files_config = python_dict(sup_config[0].SUPPRESS_FILES)
|
||||
if rule in files_config:
|
||||
del files_config[rule]
|
||||
sup_config.update(SUPPRESS_FILES=files_config)
|
||||
return send_response({'status': 'ok'}, api)
|
||||
except Exception:
|
||||
logger.exception('Error deleting suppression rule')
|
||||
return send_response(data, api)
|
||||
|
||||
|
||||
def process_suppression(data, package):
|
||||
"""Process all suppression for code."""
|
||||
filtered = {}
|
||||
filters = SuppressFindings.objects.filter(
|
||||
PACKAGE_NAME=package,
|
||||
SUPPRESS_TYPE='code')
|
||||
if not filters.exists():
|
||||
return data
|
||||
|
||||
# Priority to rules
|
||||
filter_rules = python_list(filters[0].SUPPRESS_RULE_ID)
|
||||
if filter_rules:
|
||||
for k in data:
|
||||
if k not in filter_rules:
|
||||
filtered[k] = data[k]
|
||||
else:
|
||||
filtered = deepcopy(data)
|
||||
|
||||
# Process by files
|
||||
filter_files = python_dict(filters[0].SUPPRESS_FILES)
|
||||
cleaned = copy(filtered)
|
||||
if filter_files:
|
||||
for k in filtered:
|
||||
if k not in filter_files.keys():
|
||||
continue
|
||||
for rem_file in filter_files[k]:
|
||||
if rem_file in filtered[k]['files']:
|
||||
del filtered[k]['files'][rem_file]
|
||||
# Remove rule_id with not files
|
||||
if len(filtered[k]['files']) == 0:
|
||||
del cleaned[k]
|
||||
return cleaned
|
||||
|
||||
|
||||
def process_suppression_manifest(data, package):
|
||||
"""Process all suppression for manifest."""
|
||||
filtered = []
|
||||
filters = SuppressFindings.objects.filter(
|
||||
PACKAGE_NAME=package,
|
||||
SUPPRESS_TYPE='manifest')
|
||||
if not filters.exists():
|
||||
return data
|
||||
|
||||
filter_rules = python_list(filters[0].SUPPRESS_RULE_ID)
|
||||
if filter_rules:
|
||||
for k in data:
|
||||
rule = k['rule']
|
||||
title = k['title']
|
||||
dynamic_rule = f'{android_component(title)}{rule}'
|
||||
if dynamic_rule not in filter_rules:
|
||||
filtered.append(k)
|
||||
else:
|
||||
filtered = data
|
||||
return filtered
|
||||
@@ -6,6 +6,7 @@ from django.conf import settings
|
||||
from mobsf.MobSF.utils import python_dict, python_list
|
||||
from mobsf.StaticAnalyzer.models import StaticAnalyzerIOS
|
||||
from mobsf.StaticAnalyzer.models import RecentScansDB
|
||||
from mobsf.StaticAnalyzer.views.common.suppression import process_suppression
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -14,6 +15,13 @@ def get_context_from_db_entry(db_entry):
|
||||
"""Return the context for IPA/ZIP from DB."""
|
||||
try:
|
||||
logger.info('Analysis is already Done. Fetching data from the DB...')
|
||||
bundle_id = db_entry[0].BUNDLE_ID
|
||||
code = process_suppression(
|
||||
python_dict(db_entry[0].CODE_ANALYSIS),
|
||||
bundle_id)
|
||||
binary = process_suppression(
|
||||
python_dict(db_entry[0].BINARY_ANALYSIS),
|
||||
bundle_id)
|
||||
context = {
|
||||
'version': settings.MOBSF_VER,
|
||||
'title': 'Static Analysis',
|
||||
@@ -29,7 +37,7 @@ def get_context_from_db_entry(db_entry):
|
||||
'sdk_name': db_entry[0].SDK_NAME,
|
||||
'platform': db_entry[0].PLATFORM,
|
||||
'min_os_version': db_entry[0].MIN_OS_VERSION,
|
||||
'bundle_id': db_entry[0].BUNDLE_ID,
|
||||
'bundle_id': bundle_id,
|
||||
'bundle_url_types': python_list(db_entry[0].BUNDLE_URL_TYPES),
|
||||
'bundle_supported_platforms':
|
||||
python_list(db_entry[0].BUNDLE_SUPPORTED_PLATFORMS),
|
||||
@@ -38,10 +46,10 @@ def get_context_from_db_entry(db_entry):
|
||||
'binary_info': python_dict(db_entry[0].BINARY_INFO),
|
||||
'permissions': python_dict(db_entry[0].PERMISSIONS),
|
||||
'ats_analysis': python_list(db_entry[0].ATS_ANALYSIS),
|
||||
'binary_analysis': python_list(db_entry[0].BINARY_ANALYSIS),
|
||||
'binary_analysis': binary,
|
||||
'macho_analysis': python_dict(db_entry[0].MACHO_ANALYSIS),
|
||||
'ios_api': python_dict(db_entry[0].IOS_API),
|
||||
'code_analysis': python_dict(db_entry[0].CODE_ANALYSIS),
|
||||
'code_analysis': code,
|
||||
'file_analysis': python_list(db_entry[0].FILE_ANALYSIS),
|
||||
'libraries': python_list(db_entry[0].LIBRARIES),
|
||||
'files': python_list(db_entry[0].FILES),
|
||||
@@ -67,6 +75,13 @@ def get_context_from_analysis(app_dict,
|
||||
all_files):
|
||||
"""Get the context for IPA/ZIP from analysis results."""
|
||||
try:
|
||||
bundle_id = info_dict['id']
|
||||
code = process_suppression(
|
||||
code_dict['code_anal'],
|
||||
bundle_id)
|
||||
binary = process_suppression(
|
||||
bin_dict['bin_code_analysis'],
|
||||
bundle_id)
|
||||
context = {
|
||||
'version': settings.MOBSF_VER,
|
||||
'title': 'Static Analysis',
|
||||
@@ -82,7 +97,7 @@ def get_context_from_analysis(app_dict,
|
||||
'sdk_name': info_dict['sdk'],
|
||||
'platform': info_dict['pltfm'],
|
||||
'min_os_version': info_dict['min'],
|
||||
'bundle_id': info_dict['id'],
|
||||
'bundle_id': bundle_id,
|
||||
'bundle_url_types': info_dict['bundle_url_types'],
|
||||
'bundle_supported_platforms':
|
||||
info_dict['bundle_supported_platforms'],
|
||||
@@ -91,10 +106,10 @@ def get_context_from_analysis(app_dict,
|
||||
'binary_info': bin_dict['bin_info'],
|
||||
'permissions': info_dict['permissions'],
|
||||
'ats_analysis': info_dict['inseccon'],
|
||||
'binary_analysis': bin_dict['bin_code_analysis'],
|
||||
'binary_analysis': binary,
|
||||
'macho_analysis': bin_dict['checksec'],
|
||||
'ios_api': code_dict['api'],
|
||||
'code_analysis': code_dict['code_anal'],
|
||||
'code_analysis': code,
|
||||
'file_analysis': all_files['special_files'],
|
||||
'libraries': bin_dict['libraries'],
|
||||
'files': all_files['files_short'],
|
||||
|
||||
@@ -74,6 +74,9 @@ COCOA_KEYS = {
|
||||
'NSVideoSubscriberAccountUsageDescription': (
|
||||
'Access the user’s TV provider account.',
|
||||
'normal'),
|
||||
'NSLocalNetworkUsageDescription': (
|
||||
'Allow app to request access to the local network.',
|
||||
'normnal'),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -6,16 +6,23 @@ import os
|
||||
from plistlib import (
|
||||
dumps,
|
||||
load,
|
||||
loads,
|
||||
)
|
||||
from pathlib import Path
|
||||
from re import sub
|
||||
|
||||
from openstep_parser import OpenStepDecoder
|
||||
|
||||
from biplist import (
|
||||
InvalidPlistException,
|
||||
readPlist,
|
||||
writePlistToString,
|
||||
)
|
||||
|
||||
from mobsf.MobSF.utils import is_file_exists
|
||||
from mobsf.MobSF.utils import (
|
||||
find_key_in_dict,
|
||||
is_file_exists,
|
||||
)
|
||||
from mobsf.StaticAnalyzer.views.ios.permission_analysis import (
|
||||
check_permissions,
|
||||
)
|
||||
@@ -27,6 +34,62 @@ from mobsf.StaticAnalyzer.views.common.shared_func import (
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
SKIP_PATH = {'__MACOSX', 'Pods'}
|
||||
|
||||
|
||||
def get_bundle_id(pobj, src):
|
||||
"""Get iOS Bundle ID from source.
|
||||
|
||||
Look up in Info.plist, entitlements, pbxproj
|
||||
"""
|
||||
possible_ids = set()
|
||||
skip_chars = {'$(', '${'}
|
||||
|
||||
# From old Info.plist
|
||||
bundle_id_og = pobj.get('CFBundleIdentifier', '')
|
||||
if not any(tmpl in bundle_id_og for tmpl in skip_chars):
|
||||
possible_ids.add(bundle_id_og)
|
||||
|
||||
# Look in entitlements, only present in newer iOS source
|
||||
path = Path(src)
|
||||
for p in path.rglob('*.entitlements'):
|
||||
if any(x in p.resolve().as_posix() for x in SKIP_PATH):
|
||||
continue
|
||||
try:
|
||||
ent = loads(p.read_bytes())
|
||||
groups = ent.get('com.apple.security.application-groups')
|
||||
if not groups:
|
||||
continue
|
||||
for i in groups:
|
||||
t = i.replace('.group', '').replace('group.', '')
|
||||
possible_ids.add(t.strip())
|
||||
except Exception:
|
||||
logger.warning('Error in parsing .entitlements')
|
||||
|
||||
# Look in project.pbxproj
|
||||
for p in path.rglob('*.pbxproj'):
|
||||
if any(x in p.resolve().as_posix() for x in SKIP_PATH):
|
||||
continue
|
||||
try:
|
||||
search = 'PRODUCT_BUNDLE_IDENTIFIER'
|
||||
parsed = OpenStepDecoder.ParseFromString(p.read_text())
|
||||
if not parsed:
|
||||
continue
|
||||
for i in find_key_in_dict(search, parsed):
|
||||
if i.startswith(skip_chars):
|
||||
continue
|
||||
for spl in skip_chars:
|
||||
tc = f'.{spl}'
|
||||
if tc in i:
|
||||
i = i.split(tc)[0]
|
||||
possible_ids.add(i)
|
||||
except Exception:
|
||||
logger.warning('Error in parsing .pbxproj')
|
||||
if possible_ids:
|
||||
possible_ids = filter(None, possible_ids)
|
||||
# Fuzzy logic: return the shortest bundle id string
|
||||
return min(possible_ids, key=len)
|
||||
return ''
|
||||
|
||||
|
||||
def convert_bin_xml(bin_xml_file):
|
||||
@@ -67,10 +130,12 @@ def plist_analysis(src, is_source):
|
||||
logger.info('Finding Info.plist in iOS Source')
|
||||
for dirpath, _dirnames, files in os.walk(src):
|
||||
for name in files:
|
||||
if (not any(x in dirpath for x in ['__MACOSX', 'Pods'])
|
||||
if (not any(x in dirpath for x in SKIP_PATH)
|
||||
and name.endswith('.plist')):
|
||||
plist_files.append(os.path.join(dirpath, name))
|
||||
if name == 'Info.plist':
|
||||
if name == 'GoogleService-Info.plist':
|
||||
continue
|
||||
if name == 'Info.plist' or '-Info.plist' in name:
|
||||
plist_file = os.path.join(dirpath, name)
|
||||
else:
|
||||
logger.info('Finding Info.plist in iOS Binary')
|
||||
@@ -83,7 +148,6 @@ def plist_analysis(src, is_source):
|
||||
bin_dir = os.path.join(src, dot_app_dir) # Full Dir/Payload/x.app
|
||||
plist_file = os.path.join(bin_dir, 'Info.plist')
|
||||
plist_files = [plist_file]
|
||||
|
||||
# Skip Plist Analysis if there is no Info.plist
|
||||
if not plist_file or not is_file_exists(plist_file):
|
||||
logger.warning(
|
||||
@@ -102,7 +166,7 @@ def plist_analysis(src, is_source):
|
||||
# For iOS IPA
|
||||
plist_info['bin_name'] = dot_app_dir.replace('.app', '')
|
||||
plist_info['bin'] = plist_obj.get('CFBundleExecutable', '')
|
||||
plist_info['id'] = plist_obj.get('CFBundleIdentifier', '')
|
||||
plist_info['id'] = get_bundle_id(plist_obj, src)
|
||||
plist_info['build'] = plist_obj.get('CFBundleVersion', '')
|
||||
plist_info['sdk'] = plist_obj.get('DTSDKName', '')
|
||||
plist_info['pltfm'] = plist_obj.get('DTPlatformVersion', '')
|
||||
|
||||
@@ -360,7 +360,7 @@ def parse_binskim_sarif(bin_an_dic, output):
|
||||
bin_an_dic['results'].append(result)
|
||||
else:
|
||||
logger.warning('binskim has no results.')
|
||||
# Create an warining for the gui
|
||||
# Create an warning for the gui
|
||||
warning = {
|
||||
'rule_id': 'No Binskim-Results',
|
||||
'status': 'Info',
|
||||
@@ -400,7 +400,7 @@ def parse_binskim_old(bin_an_dic, output):
|
||||
bin_an_dic['results'].append(result)
|
||||
else:
|
||||
logger.warning('binskim has no results.')
|
||||
# Create an warining for the gui
|
||||
# Create an warning for the gui
|
||||
warning = {
|
||||
'rule_id': 'No Binskim-Results',
|
||||
'status': 'Info',
|
||||
|
||||
File diff suppressed because one or more lines are too long
+131139
-116664
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,153 @@
|
||||
|
||||
$(document).ready(function () {
|
||||
if (window.location.hash == '#suppression') {
|
||||
$("#supbtn").click();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
//Suppression Logic
|
||||
|
||||
function slugify(str)
|
||||
{
|
||||
return str
|
||||
.toLowerCase()
|
||||
.trim()
|
||||
.replace(/[^\w\s-]/g, '')
|
||||
.replace(/[\s_-]+/g, '-')
|
||||
.replace(/^-+|-+$/g, '');
|
||||
}
|
||||
|
||||
function escapeHtml(unsafe)
|
||||
{
|
||||
return unsafe
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
function action(url, data, on_success){
|
||||
//Add CSRF
|
||||
data.csrfmiddlewaretoken = csrf;
|
||||
$.ajax({
|
||||
url : url,
|
||||
type : "POST",
|
||||
dataType: "json",
|
||||
data : data,
|
||||
success : function(json){ on_success(json) },
|
||||
error : function(xhr, ajaxOptions, thrownError) {
|
||||
console.log(xhr.responseText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function suppress(rule, files, tr, manifest=false){
|
||||
if (files){
|
||||
endpoint = suppress_by_files_url
|
||||
title = '<strong>Hide alert from files</strong>'
|
||||
html = `This will suppress any findings of the rule <b>${escapeHtml(rule)}</b> triggering from these files for <b>${escapeHtml(pkg)}</b> from now on.`
|
||||
} else {
|
||||
endpoint = suppress_by_rule_url
|
||||
title = '<strong>Disable Rule</strong>'
|
||||
html = `This will suppress the rule <b>${escapeHtml(rule)}</b> from tiggering for <b>${escapeHtml(pkg)}</b> from now on.`
|
||||
}
|
||||
if (manifest){
|
||||
table = '#table_manifest'
|
||||
type = 'manifest'
|
||||
} else {
|
||||
table = '#table_code'
|
||||
type = 'code'
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
title: title,
|
||||
type: 'warning',
|
||||
html: html,
|
||||
showCancelButton: true,
|
||||
cancelButtonText: 'Cancel',
|
||||
confirmButtonText: 'Suppress',
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
action(document.location.origin + endpoint, { checksum: hash, rule, type }, function(json) {
|
||||
if (json.status==="ok") {
|
||||
$(table).DataTable().row(tr).remove().draw();
|
||||
} else {
|
||||
Swal.fire("Failed to Suppress")
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function remove_suppression(ctx){
|
||||
kind = $(ctx).data('kind');
|
||||
rule = $(ctx).data('rule');
|
||||
type = $(ctx).data('type');
|
||||
|
||||
Swal.fire({
|
||||
title: 'Delete suppression rule?',
|
||||
type: 'warning',
|
||||
text: 'Do you want to delete the suppression rule?',
|
||||
showCancelButton: true,
|
||||
cancelButtonText: 'Cancel',
|
||||
confirmButtonText: 'Delete',
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
action(document.location.origin + delete_suppression_url, { checksum: hash, rule, kind, type }, function(json) {
|
||||
if (json.status==="ok") {
|
||||
window.location.hash = 'suppression';
|
||||
window.location.reload();
|
||||
} else {
|
||||
Swal.fire("Failed to remove suppression rule")
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function get_rules(type, rules){
|
||||
var html = ''
|
||||
rules.forEach(element => {
|
||||
html += `${escapeHtml(element)} - <a onclick='remove_suppression(this)' data-rule='${escapeHtml(element)}' data-type='${type}' data-kind='rule'><i class="fa fa-trash fa-2xs"></i></a></br>`
|
||||
});
|
||||
return html
|
||||
}
|
||||
|
||||
|
||||
function get_files(type, files){
|
||||
var html = ''
|
||||
for (const [rule, rfiles] of Object.entries(files)) {
|
||||
html += `<b>${escapeHtml(rule)}</b> - <a onclick='remove_suppression(this)' data-rule='${escapeHtml(rule)}' data-type='${type}' data-kind='file'><i class="fa fa-trash fa-2xs"></i></a></br>`
|
||||
html += `<a class="btn btn-primary btn-sm" data-toggle="collapse" href="#c_${slugify(escapeHtml(rule))}" role="button" aria-expanded="false" aria-controls="c_${slugify(escapeHtml(rule))}">Files ➜</a><div class="collapse" id="c_${slugify(escapeHtml(rule))}"><div class="card card-body">`
|
||||
rfiles.forEach(element => {
|
||||
html += `<li>${escapeHtml(element)}</li>`
|
||||
});
|
||||
html += '</div></div></br>'
|
||||
}
|
||||
return html
|
||||
}
|
||||
|
||||
function list_suppressions(){
|
||||
action(document.location.origin + list_suppressions_url, { checksum: hash }, function(json) {
|
||||
if (json.status==="ok") {
|
||||
|
||||
var tbl = $('#sup_table').DataTable();
|
||||
tbl.clear().draw();
|
||||
$(function() {
|
||||
$.each(json.message, function(i, item) {
|
||||
typ = item.SUPPRESS_TYPE
|
||||
rule_ids = get_rules(typ, item.SUPPRESS_RULE_ID)
|
||||
files = get_files(typ, item.SUPPRESS_FILES)
|
||||
tbl.row.add([typ, rule_ids, files]).draw(false)
|
||||
});
|
||||
});
|
||||
} else {
|
||||
Swal.fire("Failed to list Suppression rules")
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -36,6 +36,10 @@
|
||||
<li><code>api/v1/report_json</code> - <a href="#generate-json-report-api">Generate JSON Report</a></li>
|
||||
<li><code>api/v1/view_source</code> - <a href="#view-source-api">View Source Files</a></li>
|
||||
<li><code>api/v1/compare</code> - <a href="#compare-api">Compare Apps</a></li>
|
||||
<li><code>api/v1/suppress_by_rule</code> - <a href="#suppress-by-rule-api">Suppress by Rule</a></li>
|
||||
<li><code>api/v1/suppress_by_files</code> - <a href="#suppress-by-files-api">Suppress by Files</a></li>
|
||||
<li><code>api/v1/list_suppressions</code> - <a href="#list-suppressions-api">List Suppressions</a></li>
|
||||
<li><code>api/v1/delete_suppression</code> - <a href="#delete-suppression-api">Delete Suppressions</a></li>
|
||||
</ol>
|
||||
|
||||
<h1>Dynamic Analysis</h1>
|
||||
@@ -170,7 +174,7 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>scan_type</td>
|
||||
<td>xapk, apk, zip, ipa, or appx</td>
|
||||
<td>xapk, apk, apks, zip, ipa, or appx</td>
|
||||
<td>Yes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -853,6 +857,365 @@
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
<h2><a id="suppress-by-rule-api"></a><strong>Suppress by Rule</strong></h2>
|
||||
<p>Suppress findings by rule id.</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><strong>URL:</strong> <code>/api/v1/suppress_by_rule</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Method:</strong> <code>POST</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Header:</strong> <code>Authorization:<api_key></code> <strong>Or</strong> <code>X-Mobsf-Api-Key:<api_key></code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Data Params</strong></p>
|
||||
</li>
|
||||
</ul>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Param Name</th>
|
||||
<th>Param Value</th>
|
||||
<th>Required</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>hash</td>
|
||||
<td>hash of the scan</td>
|
||||
<td>Yes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>type</td>
|
||||
<td>code or manifest</td>
|
||||
<td>Yes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>rule</td>
|
||||
<td>rule id</td>
|
||||
<td>Yes</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br />
|
||||
<ul>
|
||||
<li>
|
||||
<p><strong>Success Response:</strong></p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Code:</strong> <code>200</code><br />
|
||||
<strong>Content-Type:</strong> <code>application/json; charset=utf-8</code> <br />
|
||||
<strong>Content:</strong> <code>JSON Contents</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Error Response:</strong></p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Code:</strong> <code>500 Internal Server Error</code> or <code>405 Method Not Allowed</code> or <code>422 Unprocessable Entity</code> <br />
|
||||
<strong>Content-Type:</strong> <code>application/json; charset=utf-8</code> <br />
|
||||
<strong>Content:</strong> <code>{"error": <error message> }</code>
|
||||
</li>
|
||||
</ul>
|
||||
<p>OR</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Code:</strong> <code>401 Unauthorized</code> <br />
|
||||
<strong>Content-Type:</strong> <code>application/json; charset=utf-8</code> <br />
|
||||
<strong>Content:</strong> <code>{"error": "You are unauthorized to make this request." }</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Sample Call:</strong></p>
|
||||
<ul>
|
||||
<li>
|
||||
<pre><code>curl -X POST --url http://localhost:8000/api/v1/suppress_by_rule --data "hash=82ab8b2193b3cfb1c737e3a786be363a&type=manifest&rule=app_allowbackup" -H "Authorization:{{ api_key}}"
|
||||
</code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
<p>OR</p>
|
||||
<ul>
|
||||
<li>
|
||||
<pre><code>curl -X POST --url http://localhost:8000/api/v1/suppress_by_rule --data "hash=82ab8b2193b3cfb1c737e3a786be363a&type=code&rule=android_logging" -H "X-Mobsf-Api-Key:{{ api_key}}"
|
||||
</code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
<h2><a id="suppress-by-files-api"></a><strong>Suppress by Files</strong></h2>
|
||||
<p>Suppress findings by files.</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><strong>URL:</strong> <code>/api/v1/suppress_by_files</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Method:</strong> <code>POST</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Header:</strong> <code>Authorization:<api_key></code> <strong>Or</strong> <code>X-Mobsf-Api-Key:<api_key></code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Data Params</strong></p>
|
||||
</li>
|
||||
</ul>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Param Name</th>
|
||||
<th>Param Value</th>
|
||||
<th>Required</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>hash</td>
|
||||
<td>hash of the scan</td>
|
||||
<td>Yes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>type</td>
|
||||
<td>code</td>
|
||||
<td>Yes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>rule</td>
|
||||
<td>rule id</td>
|
||||
<td>Yes</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br />
|
||||
<ul>
|
||||
<li>
|
||||
<p><strong>Success Response:</strong></p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Code:</strong> <code>200</code><br />
|
||||
<strong>Content-Type:</strong> <code>application/json; charset=utf-8</code> <br />
|
||||
<strong>Content:</strong> <code>JSON Contents</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Error Response:</strong></p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Code:</strong> <code>500 Internal Server Error</code> or <code>405 Method Not Allowed</code> or <code>422 Unprocessable Entity</code> <br />
|
||||
<strong>Content-Type:</strong> <code>application/json; charset=utf-8</code> <br />
|
||||
<strong>Content:</strong> <code>{"error": <error message> }</code>
|
||||
</li>
|
||||
</ul>
|
||||
<p>OR</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Code:</strong> <code>401 Unauthorized</code> <br />
|
||||
<strong>Content-Type:</strong> <code>application/json; charset=utf-8</code> <br />
|
||||
<strong>Content:</strong> <code>{"error": "You are unauthorized to make this request." }</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Sample Call:</strong></p>
|
||||
<ul>
|
||||
<li>
|
||||
<pre><code>curl -X POST --url http://localhost:8000/api/v1/suppress_by_files --data "hash=82ab8b2193b3cfb1c737e3a786be363a&type=code&rule=app_allowbackup" -H "Authorization:{{ api_key}}"
|
||||
</code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
<p>OR</p>
|
||||
<ul>
|
||||
<li>
|
||||
<pre><code>curl -X POST --url http://localhost:8000/api/v1/suppress_by_files --data "hash=82ab8b2193b3cfb1c737e3a786be363a&type=code&rule=android_logging" -H "X-Mobsf-Api-Key:{{ api_key}}"
|
||||
</code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
<h2><a id="list-suppressions-api"></a><strong>View Suppressions</strong></h2>
|
||||
<p>View suppressions associated with a scan.</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><strong>URL:</strong> <code>/api/v1/list_suppressions</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Method:</strong> <code>POST</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Header:</strong> <code>Authorization:<api_key></code> <strong>Or</strong> <code>X-Mobsf-Api-Key:<api_key></code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Data Params</strong></p>
|
||||
</li>
|
||||
</ul>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Param Name</th>
|
||||
<th>Param Value</th>
|
||||
<th>Required</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>hash</td>
|
||||
<td>hash of the scan</td>
|
||||
<td>Yes</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br />
|
||||
<ul>
|
||||
<li>
|
||||
<p><strong>Success Response:</strong></p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Code:</strong> <code>200</code><br />
|
||||
<strong>Content-Type:</strong> <code>application/json; charset=utf-8</code> <br />
|
||||
<strong>Content:</strong> <code>JSON Contents</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Error Response:</strong></p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Code:</strong> <code>500 Internal Server Error</code> or <code>405 Method Not Allowed</code> or <code>422 Unprocessable Entity</code> <br />
|
||||
<strong>Content-Type:</strong> <code>application/json; charset=utf-8</code> <br />
|
||||
<strong>Content:</strong> <code>{"error": <error message> }</code>
|
||||
</li>
|
||||
</ul>
|
||||
<p>OR</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Code:</strong> <code>401 Unauthorized</code> <br />
|
||||
<strong>Content-Type:</strong> <code>application/json; charset=utf-8</code> <br />
|
||||
<strong>Content:</strong> <code>{"error": "You are unauthorized to make this request." }</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Sample Call:</strong></p>
|
||||
<ul>
|
||||
<li>
|
||||
<pre><code>curl -X POST --url http://localhost:8000/api/v1/list_suppressions --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "Authorization:{{ api_key}}"
|
||||
</code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
<p>OR</p>
|
||||
<ul>
|
||||
<li>
|
||||
<pre><code>curl -X POST --url http://localhost:8000/api/v1/list_suppressions --data "hash=82ab8b2193b3cfb1c737e3a786be363a" -H "X-Mobsf-Api-Key:{{ api_key}}"
|
||||
</code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
<h2><a id="delete-suppression-api"></a><strong>Delete Suppressions</strong></h2>
|
||||
<p>Delete suppressions.</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p><strong>URL:</strong> <code>/api/v1/delete_suppression</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Method:</strong> <code>POST</code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Header:</strong> <code>Authorization:<api_key></code> <strong>Or</strong> <code>X-Mobsf-Api-Key:<api_key></code></p>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Data Params</strong></p>
|
||||
</li>
|
||||
</ul>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Param Name</th>
|
||||
<th>Param Value</th>
|
||||
<th>Required</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>hash</td>
|
||||
<td>hash of the scan</td>
|
||||
<td>Yes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>type</td>
|
||||
<td>code or manifest</td>
|
||||
<td>Yes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>rule</td>
|
||||
<td>rule id</td>
|
||||
<td>Yes</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>kind</td>
|
||||
<td>rule or file</td>
|
||||
<td>Yes</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<br />
|
||||
<ul>
|
||||
<li>
|
||||
<p><strong>Success Response:</strong></p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Code:</strong> <code>200</code><br />
|
||||
<strong>Content-Type:</strong> <code>application/json; charset=utf-8</code> <br />
|
||||
<strong>Content:</strong> <code>JSON Contents</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Error Response:</strong></p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Code:</strong> <code>500 Internal Server Error</code> or <code>405 Method Not Allowed</code> or <code>422 Unprocessable Entity</code> <br />
|
||||
<strong>Content-Type:</strong> <code>application/json; charset=utf-8</code> <br />
|
||||
<strong>Content:</strong> <code>{"error": <error message> }</code>
|
||||
</li>
|
||||
</ul>
|
||||
<p>OR</p>
|
||||
<ul>
|
||||
<li>
|
||||
<strong>Code:</strong> <code>401 Unauthorized</code> <br />
|
||||
<strong>Content-Type:</strong> <code>application/json; charset=utf-8</code> <br />
|
||||
<strong>Content:</strong> <code>{"error": "You are unauthorized to make this request." }</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p><strong>Sample Call:</strong></p>
|
||||
<ul>
|
||||
<li>
|
||||
<pre><code>curl -X POST --url http://localhost:8000/api/v1/delete_suppression --data "hash=82ab8b2193b3cfb1c737e3a786be363a&kind=file&type=code&rule=android_sql_raw_query" -H "Authorization:{{ api_key}}"
|
||||
</code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
<p>OR</p>
|
||||
<ul>
|
||||
<li>
|
||||
<pre><code>curl -X POST --url http://localhost:8000/api/v1/delete_suppression --data "hash=82ab8b2193b3cfb1c737e3a786be363a&kind=rule&type=manifest&rule=receiver_exported_intent_filter_exists" -H "X-Mobsf-Api-Key:{{ api_key}}"
|
||||
</code></pre>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
<h2><a id="get-apps-api"></a><strong>Get Apps API</strong></h2>
|
||||
<p>Get Apps available for Dynamic Analysis. You must perform static analysis before attempting dynamic analysis.</p>
|
||||
|
||||
@@ -488,17 +488,17 @@
|
||||
</td>
|
||||
<td>
|
||||
|
||||
{% if item|key:"stat" == "high" %}
|
||||
{% if item|key:"severity" == "high" %}
|
||||
<span class="danger">high</span>
|
||||
{% elif item|key:"stat" == "info" %}
|
||||
{% elif item|key:"severity" == "info" %}
|
||||
<span class="info">info</span>
|
||||
{% elif item|key:"stat" == "warning" %}
|
||||
{% elif item|key:"severity" == "warning" %}
|
||||
<span class="warning">warning</span>
|
||||
{% endif %}
|
||||
|
||||
</td>
|
||||
<td>
|
||||
{{item|key:"desc"}}
|
||||
{{item|key:"description"}}
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
sidebar-mini
|
||||
{% endblock %}
|
||||
{% block extra_css %}
|
||||
<link href="{% static "adminlte/plugins/sweetalert2/sweetalert2.min.css" %}" rel="stylesheet">
|
||||
<!-- DataTables -->
|
||||
<link rel="stylesheet" href="{% static "adminlte/plugins/datatables-bs4/dataTables.bootstrap4.min.css" %}">
|
||||
<link rel="stylesheet" href="{% static "others/css/spinner.css" %}">
|
||||
@@ -624,7 +625,9 @@
|
||||
<p>
|
||||
<strong><i class="fas fa-cog"></i> SCAN OPTIONS</strong>
|
||||
</p>
|
||||
<p> <a href="../static_analyzer/?checksum={{ md5 }}&name={{ file_name }}&type=apk&rescan=1" class="btn btn-info" role="button"><i class="fa fa-sync"></i> Rescan</a></p>
|
||||
<p> <a href="../static_analyzer/?checksum={{ md5 }}&name={{ file_name }}&type=apk&rescan=1" class="btn btn-info" role="button"><i class="fa fa-sync"></i> Rescan</a>
|
||||
<a id="supbtn" onclick="list_suppressions()" role="button" class="btn btn-primary" data-target="#sup_list" data-toggle="modal" href="#"><i class="fa fa-list"></i> Manage Suppressions</a>
|
||||
</p>
|
||||
<p>
|
||||
<a onclick="dynamic_loader()" href="../android_dynamic/{{md5}}" class="btn btn-success" role="button"><i class="fa fa-play-circle"></i> Start Dynamic Analysis</a>
|
||||
</p>
|
||||
@@ -899,7 +902,7 @@
|
||||
<strong><i class="fas fa-lock"></i> NETWORK SECURITY</strong>
|
||||
</p>
|
||||
<div class="table-responsive">
|
||||
<table id="table_manifest" class="table table-bordered table-hover table-striped">
|
||||
<table id="table_network" class="table table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>NO</th>
|
||||
@@ -962,6 +965,7 @@
|
||||
<th>ISSUE</th>
|
||||
<th>SEVERITY</th>
|
||||
<th>DESCRIPTION</th>
|
||||
<th>OPTIONS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -974,19 +978,29 @@
|
||||
</td>
|
||||
<td>
|
||||
|
||||
{% if item|key:"stat" == "high" %}
|
||||
{% if item|key:"severity" == "high" %}
|
||||
<span class="badge bg-danger">high</span>
|
||||
{% elif item|key:"stat" == "info" %}
|
||||
{% elif item|key:"severity" == "info" %}
|
||||
<span class="badge bg-info">info</span>
|
||||
{% elif item|key:"stat" == "warning" %}
|
||||
{% elif item|key:"severity" == "warning" %}
|
||||
<span class="badge bg-warning">warning</span>
|
||||
{% endif %}
|
||||
|
||||
</td>
|
||||
<td>
|
||||
{{item|key:"desc"}}
|
||||
{{item|key:"description"}}
|
||||
</td>
|
||||
<td>
|
||||
<div class="dropdown show">
|
||||
<a class="btn btn-secondary dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fa fa-eye-slash"></i>
|
||||
</a>
|
||||
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
|
||||
<a style="cursor:pointer;" class="dropdown-item" onclick="suppress('{{ item|key:"title" | android_component}}{{item|key:'rule' | escapejs }}', false, $(this.closest('tr')), true)">Suppression the rule <b>{{ item|key:"title" | android_component}}{{ item|key:'rule' }}</b> in <b>{{package_name}}</b></a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@@ -1019,6 +1033,7 @@
|
||||
<th>SEVERITY</th>
|
||||
<th>STANDARDS</th>
|
||||
<th>FILES</th>
|
||||
<th>OPTIONS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -1068,6 +1083,20 @@
|
||||
<br/>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td>
|
||||
|
||||
<div class="dropdown show">
|
||||
<a class="btn btn-secondary dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fa fa-eye-slash"></i>
|
||||
</a>
|
||||
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
|
||||
<a style="cursor:pointer;" class="dropdown-item" onclick="suppress('{{rule | escapejs }}', false, $(this.closest('tr')))">Suppress the rule <b>{{ rule }}</b> in <b>{{package_name}}</b></a>
|
||||
<a style="cursor:pointer;" class="dropdown-item" onclick="suppress('{{rule | escapejs }}', true, $(this.closest('tr')))">Suppress the rule <b>{{ rule }}</b> in <b>{{package_name}}</b> only from these files</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@@ -1903,6 +1932,36 @@
|
||||
<!-- end of contents -->
|
||||
</div>
|
||||
|
||||
<!--Modals-->
|
||||
<div class="modal" id="sup_list" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Suppression Rules</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<table id="sup_table" class="table table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Suppression Type</th>
|
||||
<th>By Rule ID</th>
|
||||
<th>By Files</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
<!--end new UI-->
|
||||
{% block extra_scripts %}
|
||||
@@ -1913,7 +1972,21 @@
|
||||
<script src="{% static "amcharts/maps.js" %}"></script>
|
||||
<script src="{% static "amcharts/worldIndiaLow.js" %}"></script>
|
||||
<script src="{% static "amcharts/animated.js" %}"></script>
|
||||
<script src="{% static "adminlte/plugins/sweetalert2/sweetalert2.min.js" %}"></script>
|
||||
<script>
|
||||
var list_suppressions_url = '{% url 'list_suppressions' %}';
|
||||
var suppress_by_files_url = '{% url 'suppress_by_files' %}';
|
||||
var suppress_by_rule_url = '{% url 'suppress_by_rule' %}';
|
||||
var delete_suppression_url = '{% url 'delete_suppression' %}';
|
||||
</script>
|
||||
<script src="{% static "others/js/suppression.js" %}"></script>
|
||||
<script>
|
||||
|
||||
// Globals
|
||||
const csrf = '{{ csrf_token }}'
|
||||
const pkg = '{{ package_name }}'
|
||||
const hash = '{{md5}}'
|
||||
|
||||
$(function () {
|
||||
// Datatable
|
||||
$('table').DataTable({
|
||||
@@ -1931,6 +2004,7 @@
|
||||
$(".nav-link").removeClass("active");
|
||||
$(this).addClass("active");
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
sidebar-mini
|
||||
{% endblock %}
|
||||
{% block extra_css %}
|
||||
<link href="{% static "adminlte/plugins/sweetalert2/sweetalert2.min.css" %}" rel="stylesheet">
|
||||
<!-- DataTables -->
|
||||
<link rel="stylesheet" href="{% static "adminlte/plugins/datatables-bs4/dataTables.bootstrap4.min.css" %}">
|
||||
<style type="text/css" media="print">
|
||||
@@ -533,6 +534,7 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="../static_analyzer/?checksum={{ md5 }}&name={{ file_name }}&type=zip&rescan=1" class="btn btn-info" role="button"><i class="fa fa-sync"></i> Rescan</a>
|
||||
<a id="supbtn" onclick="list_suppressions()" role="button" class="btn btn-primary" data-target="#sup_list" data-toggle="modal" href="#"><i class="fa fa-list"></i> Manage Suppressions</a>
|
||||
<a target="_blank" href="../manifest_view/?md5={{ md5 }}&type={{ app_type }}&bin=0" role="button" class="btn btn-primary"><i class="fa fa-eye"></i> View AndroidManifest.xml</a>
|
||||
<a href="{% url "tree_view" %}?md5={{ md5 }}&type=java" class="btn btn-info" role="button"><i class="fa fa-code"></i> View Source</a>
|
||||
</p>
|
||||
@@ -732,7 +734,7 @@
|
||||
<strong><i class="fas fa-lock"></i> NETWORK SECURITY</strong>
|
||||
</p>
|
||||
<div class="table-responsive">
|
||||
<table id="table_manifest" class="table table-bordered table-hover table-striped">
|
||||
<table id="table_network" class="table table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>NO</th>
|
||||
@@ -797,6 +799,7 @@
|
||||
<th>ISSUE</th>
|
||||
<th>SEVERITY</th>
|
||||
<th>DESCRIPTION</th>
|
||||
<th>OPTIONS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -809,19 +812,29 @@
|
||||
</td>
|
||||
<td>
|
||||
|
||||
{% if item|key:"stat" == "high" %}
|
||||
{% if item|key:"severity" == "high" %}
|
||||
<span class="badge bg-danger">high</span>
|
||||
{% elif item|key:"stat" == "info" %}
|
||||
{% elif item|key:"severity" == "info" %}
|
||||
<span class="badge bg-info">info</span>
|
||||
{% elif item|key:"stat" == "warning" %}
|
||||
{% elif item|key:"severity" == "warning" %}
|
||||
<span class="badge bg-warning">warning</span>
|
||||
{% endif %}
|
||||
|
||||
</td>
|
||||
<td>
|
||||
{{item|key:"desc"}}
|
||||
{{item|key:"description"}}
|
||||
</td>
|
||||
<td>
|
||||
<div class="dropdown show">
|
||||
<a class="btn btn-secondary dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fa fa-eye-slash"></i>
|
||||
</a>
|
||||
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
|
||||
<a style="cursor:pointer;" class="dropdown-item" onclick="suppress('{{ item|key:"title" | android_component}}{{item|key:'rule' | escapejs }}', false, $(this.closest('tr')), true)">Suppress the rule <b>{{ item|key:"title" | android_component}}{{ item|key:'rule' }}</b> in <b>{{package_name}}</b></a>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@@ -854,6 +867,7 @@
|
||||
<th>SEVERITY</th>
|
||||
<th>STANDARDS</th>
|
||||
<th>FILES</th>
|
||||
<th>OPTIONS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -903,6 +917,20 @@
|
||||
<br/>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td>
|
||||
|
||||
<div class="dropdown show">
|
||||
<a class="btn btn-secondary dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fa fa-eye-slash"></i>
|
||||
</a>
|
||||
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
|
||||
<a style="cursor:pointer;" class="dropdown-item" onclick="suppress('{{rule | escapejs }}', false, $(this.closest('tr')))">Suppress the rule <b>{{ rule }}</b> in <b>{{package_name}}</b></a>
|
||||
<a style="cursor:pointer;" class="dropdown-item" onclick="suppress('{{rule | escapejs }}', true, $(this.closest('tr')))">Suppress the rule <b>{{ rule }}</b> in <b>{{package_name}}</b> only from these files</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@@ -1460,9 +1488,42 @@
|
||||
<!-- ===========================end files ================================== -->
|
||||
<!-- end of contents -->
|
||||
</div>
|
||||
|
||||
|
||||
<!--Modals-->
|
||||
<div class="modal" id="sup_list" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Suppression Rules</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<table id="sup_table" class="table table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Suppression Type</th>
|
||||
<th>By Rule ID</th>
|
||||
<th>By Files</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
<!--end new UI-->
|
||||
{% block extra_scripts %}
|
||||
<script src="{% static "adminlte/plugins/sweetalert2/sweetalert2.min.js" %}"></script>
|
||||
<!-- DataTables -->
|
||||
<script src="{% static "adminlte/plugins/datatables/jquery.dataTables.min.js" %} "></script>
|
||||
<script src="{% static "adminlte/plugins/datatables-bs4/dataTables.bootstrap4.min.js" %}"></script>
|
||||
@@ -1471,6 +1532,19 @@
|
||||
<script src="{% static "amcharts/worldIndiaLow.js" %}"></script>
|
||||
<script src="{% static "amcharts/animated.js" %}"></script>
|
||||
<script>
|
||||
var list_suppressions_url = '{% url 'list_suppressions' %}';
|
||||
var suppress_by_files_url = '{% url 'suppress_by_files' %}';
|
||||
var suppress_by_rule_url = '{% url 'suppress_by_rule' %}';
|
||||
var delete_suppression_url = '{% url 'delete_suppression' %}';
|
||||
</script>
|
||||
<script src="{% static "others/js/suppression.js" %}"></script>
|
||||
<script>
|
||||
|
||||
// Globals
|
||||
const csrf = '{{ csrf_token }}'
|
||||
const pkg = '{{ package_name }}'
|
||||
const hash = '{{md5}}'
|
||||
|
||||
$(function () {
|
||||
// Datatable
|
||||
$('table').DataTable({
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
{% block extra_css %}
|
||||
<!-- DataTables -->
|
||||
<link rel="stylesheet" href="{% static "adminlte/plugins/datatables-bs4/dataTables.bootstrap4.min.css" %}">
|
||||
<link href="{% static "adminlte/plugins/sweetalert2/sweetalert2.min.css" %}" rel="stylesheet">
|
||||
<style type="text/css" media="print">
|
||||
@page { size: landscape; }
|
||||
@media print {
|
||||
@@ -396,6 +397,7 @@
|
||||
<strong><i class="fas fa-cog"></i> SCAN OPTIONS</strong>
|
||||
</p>
|
||||
<a role="button" class="btn btn-info" href="../static_analyzer_ios/?checksum={{ md5 }}&name={{ file_name }}&type=ipa&rescan=1" ><i class="fa fa-sync"></i> Rescan</a>
|
||||
<a id="supbtn" onclick="list_suppressions()" role="button" class="btn btn-primary" data-target="#sup_list" data-toggle="modal" href="#"><i class="fa fa-list"></i> Manage Suppressions</a>
|
||||
</p>
|
||||
</div>
|
||||
</div><!-- /.card -->
|
||||
@@ -618,7 +620,7 @@
|
||||
<strong><i class="fa fa-code"></i> IPA BINARY CODE ANALYSIS</strong>
|
||||
</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-hover table-striped">
|
||||
<table id="table_code" class="table table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>NO</th>
|
||||
@@ -626,7 +628,7 @@
|
||||
<th>SEVERITY</th>
|
||||
<th>STANDARDS</th>
|
||||
<th>DESCRIPTION</th>
|
||||
|
||||
<th>OPTIONS</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -669,6 +671,18 @@
|
||||
<td>
|
||||
{{ details|key:"detailed_desc" }}
|
||||
</td>
|
||||
<td>
|
||||
|
||||
<div class="dropdown show">
|
||||
<a class="btn btn-secondary dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fa fa-eye-slash"></i>
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
|
||||
<a style="cursor:pointer;" class="dropdown-item" onclick="suppress('{{issue | escapejs }}', false, $(this.closest('tr')))">Suppress the rule <b>{{ issue }}</b> in <b>{{bundle_id}}</b></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@@ -1266,6 +1280,35 @@
|
||||
</div>
|
||||
|
||||
|
||||
<!--Modals-->
|
||||
<div class="modal" id="sup_list" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Suppression Rules</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<table id="sup_table" class="table table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Suppression Type</th>
|
||||
<th>By Rule ID</th>
|
||||
<th>By Files</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
<!--end new UI-->
|
||||
@@ -1277,7 +1320,20 @@
|
||||
<script src="{% static "amcharts/maps.js" %}"></script>
|
||||
<script src="{% static "amcharts/worldIndiaLow.js" %}"></script>
|
||||
<script src="{% static "amcharts/animated.js" %}"></script>
|
||||
<script src="{% static "adminlte/plugins/sweetalert2/sweetalert2.min.js" %}"></script>
|
||||
<script>
|
||||
var list_suppressions_url = '{% url 'list_suppressions' %}';
|
||||
var suppress_by_files_url = '{% url 'suppress_by_files' %}';
|
||||
var suppress_by_rule_url = '{% url 'suppress_by_rule' %}';
|
||||
var delete_suppression_url = '{% url 'delete_suppression' %}';
|
||||
</script>
|
||||
<script src="{% static "others/js/suppression.js" %}"></script>
|
||||
<script>
|
||||
// Globals
|
||||
const csrf = '{{ csrf_token }}'
|
||||
const pkg = '{{ bundle_id }}'
|
||||
const hash = '{{ md5 }}'
|
||||
|
||||
$(function () {
|
||||
// Datatable
|
||||
$('table').DataTable({
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
{% endblock %}
|
||||
{% block extra_css %}
|
||||
<!-- DataTables -->
|
||||
<link href="{% static "adminlte/plugins/sweetalert2/sweetalert2.min.css" %}" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{% static "adminlte/plugins/datatables-bs4/dataTables.bootstrap4.min.css" %}">
|
||||
<style type="text/css" media="print">
|
||||
|
||||
<style type="text/css" media="print">
|
||||
@page { size: landscape; }
|
||||
@media print {
|
||||
.btn, #scan_options {
|
||||
@@ -360,6 +362,7 @@
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="../static_analyzer_ios/?checksum={{ md5 }}&name={{ file_name }}&type=ios&rescan=1" class="btn btn-info" role="button"><i class="fa fa-sync"></i> Rescan</a>
|
||||
<a id="supbtn" onclick="list_suppressions()" role="button" class="btn btn-primary" data-target="#sup_list" data-toggle="modal" href="#"><i class="fa fa-list"></i> Manage Suppressions</a>
|
||||
<a data-target="#mplist" role="button" class="btn btn-primary" data-toggle="modal" href="#"><i class="fa fa-list"></i> View Info.plist</a>
|
||||
</p>
|
||||
</div>
|
||||
@@ -612,7 +615,7 @@
|
||||
<strong><i class="fa fa-code"></i> CODE ANALYSIS</strong>
|
||||
</p>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered table-hover table-striped">
|
||||
<table id="table_code" class="table table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>NO</th>
|
||||
@@ -620,6 +623,7 @@
|
||||
<th>SEVERITY</th>
|
||||
<th>STANDARDS</th>
|
||||
<th>FILES</th>
|
||||
<th>OPTIONS</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -670,6 +674,19 @@
|
||||
</br>
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td>
|
||||
|
||||
<div class="dropdown show">
|
||||
<a class="btn btn-secondary dropdown-toggle" href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<i class="fa fa-eye-slash"></i>
|
||||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
|
||||
<a style="cursor:pointer;" class="dropdown-item" onclick="suppress('{{rule | escapejs }}', false, $(this.closest('tr')))">Suppress the rule <b>{{ rule }}</b> in <b>{{bundle_id}}</b></a>
|
||||
<a style="cursor:pointer;" class="dropdown-item" onclick="suppress('{{rule | escapejs }}', true, $(this.closest('tr')))">Suppress the rule <b>{{ rule }}</b> in <b>{{bundle_id}}</b> only from these files</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@@ -1081,6 +1098,34 @@
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
|
||||
<!--Modals-->
|
||||
<div class="modal" id="sup_list" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Suppression Rules</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<table id="sup_table" class="table table-bordered table-hover table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Suppression Type</th>
|
||||
<th>By Rule ID</th>
|
||||
<th>By Files</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /.modal-content -->
|
||||
</div>
|
||||
<!-- /.modal-dialog -->
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
<!--end new UI-->
|
||||
@@ -1092,7 +1137,20 @@
|
||||
<script src="{% static "amcharts/maps.js" %}"></script>
|
||||
<script src="{% static "amcharts/worldIndiaLow.js" %}"></script>
|
||||
<script src="{% static "amcharts/animated.js" %}"></script>
|
||||
<script src="{% static "adminlte/plugins/sweetalert2/sweetalert2.min.js" %}"></script>
|
||||
<script>
|
||||
var list_suppressions_url = '{% url 'list_suppressions' %}';
|
||||
var suppress_by_files_url = '{% url 'suppress_by_files' %}';
|
||||
var suppress_by_rule_url = '{% url 'suppress_by_rule' %}';
|
||||
var delete_suppression_url = '{% url 'delete_suppression' %}';
|
||||
</script>
|
||||
<script src="{% static "others/js/suppression.js" %}"></script>
|
||||
<script>
|
||||
// Globals
|
||||
const csrf = '{{ csrf_token }}'
|
||||
const pkg = '{{ bundle_id }}'
|
||||
const hash = '{{ md5 }}'
|
||||
|
||||
$(function () {
|
||||
// Datatable
|
||||
$('table').DataTable({
|
||||
|
||||
@@ -25,6 +25,7 @@ apkid==2.1.4
|
||||
quark-engine==22.6.1
|
||||
frida==15.2.2
|
||||
tldextract==3.3.1
|
||||
openstep-parser==1.5.3
|
||||
# For semgrep & mitmproxy
|
||||
ruamel.yaml==0.16.13 # pyup: ignore
|
||||
click==8.0.1 # pyup: ignore
|
||||
|
||||
Reference in New Issue
Block a user