[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:
Ajin Abraham
2022-10-03 19:25:29 -07:00
committed by GitHub
parent 41dc607733
commit 277e74d485
33 changed files with 132894 additions and 117025 deletions
+61
View File
@@ -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 -1
View File
@@ -1,5 +1,5 @@
# Mobile Security Framework (MobSF)
Version: v3.5 beta
Version: v3.6 beta
![](https://cloud.githubusercontent.com/assets/4301109/20019521/cc61f7fc-a2f2-11e6-95f3-407030d9fdde.png)
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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', '*']
+19
View File
@@ -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),
+36
View File
@@ -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
+70 -1
View File
@@ -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
+2 -6
View File
@@ -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):
+7
View File
@@ -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]'),
},
}
+24 -20
View File
@@ -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='
+8 -5
View File
@@ -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
File diff suppressed because it is too large Load Diff
+153
View File
@@ -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, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
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")
}
});
}
+364 -1
View File
@@ -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:&lt;api_key&gt;</code> <strong>Or</strong> <code>X-Mobsf-Api-Key:&lt;api_key&gt;</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>{&quot;error&quot;: &lt;error message&gt; }</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>{&quot;error&quot;: &quot;You are unauthorized to make this request.&quot; }</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&amp;type=manifest&amp;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&amp;type=code&amp;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:&lt;api_key&gt;</code> <strong>Or</strong> <code>X-Mobsf-Api-Key:&lt;api_key&gt;</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>{&quot;error&quot;: &lt;error message&gt; }</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>{&quot;error&quot;: &quot;You are unauthorized to make this request.&quot; }</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&amp;type=code&amp;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&amp;type=code&amp;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:&lt;api_key&gt;</code> <strong>Or</strong> <code>X-Mobsf-Api-Key:&lt;api_key&gt;</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>{&quot;error&quot;: &lt;error message&gt; }</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>{&quot;error&quot;: &quot;You are unauthorized to make this request.&quot; }</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:&lt;api_key&gt;</code> <strong>Or</strong> <code>X-Mobsf-Api-Key:&lt;api_key&gt;</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>{&quot;error&quot;: &lt;error message&gt; }</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>{&quot;error&quot;: &quot;You are unauthorized to make this request.&quot; }</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&amp;kind=file&amp;type=code&amp;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&amp;kind=rule&amp;type=manifest&amp;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>
+4 -4
View File
@@ -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 }}&amp;name={{ file_name }}&amp;type=apk&amp;rescan=1" class="btn btn-info" role="button"><i class="fa fa-sync"></i> Rescan</a></p>
<p> <a href="../static_analyzer/?checksum={{ md5 }}&amp;name={{ file_name }}&amp;type=apk&amp;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 }}&amp;name={{ file_name }}&amp;type=zip&amp;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 }}&amp;type={{ app_type }}&amp;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 }}&amp;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 }}&amp;name={{ file_name }}&amp;type=ipa&amp;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 }}&amp;name={{ file_name }}&amp;type=ios&amp;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({
+1
View File
@@ -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