From bfd8bc2df5c8fd5ac42163538990c7c2352c8664 Mon Sep 17 00:00:00 2001
From: "Ajin.Abraham"
Date: Tue, 5 Mar 2024 17:09:17 -0800
Subject: [PATCH 01/19] HOTFIX: Frida Logs API response code + Dependency bump
---
.../views/api/api_android_dynamic_analysis.py | 2 +-
poetry.lock | 139 +++++++++---------
2 files changed, 71 insertions(+), 70 deletions(-)
diff --git a/mobsf/MobSF/views/api/api_android_dynamic_analysis.py b/mobsf/MobSF/views/api/api_android_dynamic_analysis.py
index 3e6332a2..bf5cbac0 100644
--- a/mobsf/MobSF/views/api/api_android_dynamic_analysis.py
+++ b/mobsf/MobSF/views/api/api_android_dynamic_analysis.py
@@ -224,7 +224,7 @@ def api_frida_logs(request):
{'error': 'Missing Parameters'}, 422)
resp = frida.frida_logs(request, True)
# frida logs can be json or html
- if resp.get('data'):
+ if resp.get('data') or resp.get('message'):
return make_api_response(resp, 200)
return make_api_response(resp, 500)
diff --git a/poetry.lock b/poetry.lock
index 03abf208..67b21f26 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -785,13 +785,13 @@ files = [
[[package]]
name = "django"
-version = "5.0.2"
+version = "5.0.3"
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
optional = false
python-versions = ">=3.10"
files = [
- {file = "Django-5.0.2-py3-none-any.whl", hash = "sha256:56ab63a105e8bb06ee67381d7b65fe6774f057e41a8bab06c8020c8882d8ecd4"},
- {file = "Django-5.0.2.tar.gz", hash = "sha256:b5bb1d11b2518a5f91372a282f24662f58f66749666b0a286ab057029f728080"},
+ {file = "Django-5.0.3-py3-none-any.whl", hash = "sha256:5c7d748ad113a81b2d44750ccc41edc14e933f56581683db548c9257e078cc83"},
+ {file = "Django-5.0.3.tar.gz", hash = "sha256:5fb37580dcf4a262f9258c1f4373819aacca906431f505e4688e37f3a99195df"},
]
[package.dependencies]
@@ -1121,13 +1121,13 @@ files = [
[[package]]
name = "ipython"
-version = "8.22.1"
+version = "8.22.2"
description = "IPython: Productive Interactive Computing"
optional = false
python-versions = ">=3.10"
files = [
- {file = "ipython-8.22.1-py3-none-any.whl", hash = "sha256:869335e8cded62ffb6fac8928e5287a05433d6462e3ebaac25f4216474dd6bc4"},
- {file = "ipython-8.22.1.tar.gz", hash = "sha256:39c6f9efc079fb19bfb0f17eee903978fe9a290b1b82d68196c641cecb76ea22"},
+ {file = "ipython-8.22.2-py3-none-any.whl", hash = "sha256:3c86f284c8f3d8f2b6c662f885c4889a91df7cd52056fd02b7d8d6195d7f56e9"},
+ {file = "ipython-8.22.2.tar.gz", hash = "sha256:2dcaad9049f9056f1fef63514f176c7d41f930daa78d05b82a176202818f2c14"},
]
[package.dependencies]
@@ -1730,67 +1730,68 @@ files = [
[[package]]
name = "msgpack"
-version = "1.0.7"
+version = "1.0.8"
description = "MessagePack serializer"
optional = false
python-versions = ">=3.8"
files = [
- {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862"},
- {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329"},
- {file = "msgpack-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b"},
- {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6"},
- {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee"},
- {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d"},
- {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d"},
- {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1"},
- {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681"},
- {file = "msgpack-1.0.7-cp310-cp310-win32.whl", hash = "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9"},
- {file = "msgpack-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415"},
- {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84"},
- {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93"},
- {file = "msgpack-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8"},
- {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46"},
- {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b"},
- {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e"},
- {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002"},
- {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c"},
- {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e"},
- {file = "msgpack-1.0.7-cp311-cp311-win32.whl", hash = "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1"},
- {file = "msgpack-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82"},
- {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b"},
- {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4"},
- {file = "msgpack-1.0.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee"},
- {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5"},
- {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672"},
- {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075"},
- {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba"},
- {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c"},
- {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5"},
- {file = "msgpack-1.0.7-cp312-cp312-win32.whl", hash = "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9"},
- {file = "msgpack-1.0.7-cp312-cp312-win_amd64.whl", hash = "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf"},
- {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5b6ccc0c85916998d788b295765ea0e9cb9aac7e4a8ed71d12e7d8ac31c23c95"},
- {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:235a31ec7db685f5c82233bddf9858748b89b8119bf4538d514536c485c15fe0"},
- {file = "msgpack-1.0.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cab3db8bab4b7e635c1c97270d7a4b2a90c070b33cbc00c99ef3f9be03d3e1f7"},
- {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bfdd914e55e0d2c9e1526de210f6fe8ffe9705f2b1dfcc4aecc92a4cb4b533d"},
- {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36e17c4592231a7dbd2ed09027823ab295d2791b3b1efb2aee874b10548b7524"},
- {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38949d30b11ae5f95c3c91917ee7a6b239f5ec276f271f28638dec9156f82cfc"},
- {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ff1d0899f104f3921d94579a5638847f783c9b04f2d5f229392ca77fba5b82fc"},
- {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:dc43f1ec66eb8440567186ae2f8c447d91e0372d793dfe8c222aec857b81a8cf"},
- {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dd632777ff3beaaf629f1ab4396caf7ba0bdd075d948a69460d13d44357aca4c"},
- {file = "msgpack-1.0.7-cp38-cp38-win32.whl", hash = "sha256:4e71bc4416de195d6e9b4ee93ad3f2f6b2ce11d042b4d7a7ee00bbe0358bd0c2"},
- {file = "msgpack-1.0.7-cp38-cp38-win_amd64.whl", hash = "sha256:8f5b234f567cf76ee489502ceb7165c2a5cecec081db2b37e35332b537f8157c"},
- {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfef2bb6ef068827bbd021017a107194956918ab43ce4d6dc945ffa13efbc25f"},
- {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:484ae3240666ad34cfa31eea7b8c6cd2f1fdaae21d73ce2974211df099a95d81"},
- {file = "msgpack-1.0.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3967e4ad1aa9da62fd53e346ed17d7b2e922cba5ab93bdd46febcac39be636fc"},
- {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dd178c4c80706546702c59529ffc005681bd6dc2ea234c450661b205445a34d"},
- {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6ffbc252eb0d229aeb2f9ad051200668fc3a9aaa8994e49f0cb2ffe2b7867e7"},
- {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:822ea70dc4018c7e6223f13affd1c5c30c0f5c12ac1f96cd8e9949acddb48a61"},
- {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:384d779f0d6f1b110eae74cb0659d9aa6ff35aaf547b3955abf2ab4c901c4819"},
- {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f64e376cd20d3f030190e8c32e1c64582eba56ac6dc7d5b0b49a9d44021b52fd"},
- {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ed82f5a7af3697b1c4786053736f24a0efd0a1b8a130d4c7bfee4b9ded0f08f"},
- {file = "msgpack-1.0.7-cp39-cp39-win32.whl", hash = "sha256:f26a07a6e877c76a88e3cecac8531908d980d3d5067ff69213653649ec0f60ad"},
- {file = "msgpack-1.0.7-cp39-cp39-win_amd64.whl", hash = "sha256:1dc93e8e4653bdb5910aed79f11e165c85732067614f180f70534f056da97db3"},
- {file = "msgpack-1.0.7.tar.gz", hash = "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87"},
+ {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868"},
+ {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c"},
+ {file = "msgpack-1.0.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659"},
+ {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2"},
+ {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982"},
+ {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa"},
+ {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128"},
+ {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d"},
+ {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653"},
+ {file = "msgpack-1.0.8-cp310-cp310-win32.whl", hash = "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693"},
+ {file = "msgpack-1.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a"},
+ {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836"},
+ {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad"},
+ {file = "msgpack-1.0.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b"},
+ {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba"},
+ {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85"},
+ {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950"},
+ {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a"},
+ {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b"},
+ {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce"},
+ {file = "msgpack-1.0.8-cp311-cp311-win32.whl", hash = "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305"},
+ {file = "msgpack-1.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e"},
+ {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee"},
+ {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b"},
+ {file = "msgpack-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8"},
+ {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3"},
+ {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc"},
+ {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58"},
+ {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f"},
+ {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04"},
+ {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543"},
+ {file = "msgpack-1.0.8-cp312-cp312-win32.whl", hash = "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c"},
+ {file = "msgpack-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd"},
+ {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40"},
+ {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151"},
+ {file = "msgpack-1.0.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24"},
+ {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d"},
+ {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db"},
+ {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77"},
+ {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13"},
+ {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2"},
+ {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a"},
+ {file = "msgpack-1.0.8-cp38-cp38-win32.whl", hash = "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c"},
+ {file = "msgpack-1.0.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480"},
+ {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a"},
+ {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596"},
+ {file = "msgpack-1.0.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d"},
+ {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f"},
+ {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228"},
+ {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18"},
+ {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8"},
+ {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746"},
+ {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"},
+ {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"},
+ {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"},
+ {file = "msgpack-1.0.8-py3-none-any.whl", hash = "sha256:24f727df1e20b9876fa6e95f840a2a2651e34c0ad147676356f4bf5fbb0206ca"},
+ {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"},
]
[[package]]
@@ -2408,13 +2409,13 @@ files = [
[[package]]
name = "python-dateutil"
-version = "2.8.2"
+version = "2.9.0.post0"
description = "Extensions to the standard Python datetime module"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
files = [
- {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
- {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
+ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
+ {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
]
[package.dependencies]
@@ -3013,13 +3014,13 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,
[[package]]
name = "typing-extensions"
-version = "4.9.0"
+version = "4.10.0"
description = "Backported and Experimental Type Hints for Python 3.8+"
optional = false
python-versions = ">=3.8"
files = [
- {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"},
- {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"},
+ {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"},
+ {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"},
]
[[package]]
From 1e932050fca9ac767a731f4232485f4f8be2044f Mon Sep 17 00:00:00 2001
From: "Ajin.Abraham"
Date: Wed, 6 Mar 2024 18:55:42 -0800
Subject: [PATCH 02/19] HOTFIX: Bump deps + expose Corellium stop app api
---
.../views/ios/corellium_instance.py | 28 +++++++
mobsf/MobSF/init.py | 2 +-
mobsf/MobSF/urls.py | 2 +
.../views/api/api_ios_dynamic_analysis.py | 14 ++++
mobsf/templates/general/apidocs.html | 77 +++++++++++++++++++
poetry.lock | 6 +-
pyproject.toml | 2 +-
7 files changed, 126 insertions(+), 5 deletions(-)
diff --git a/mobsf/DynamicAnalyzer/views/ios/corellium_instance.py b/mobsf/DynamicAnalyzer/views/ios/corellium_instance.py
index 1d239924..4dd4df2b 100644
--- a/mobsf/DynamicAnalyzer/views/ios/corellium_instance.py
+++ b/mobsf/DynamicAnalyzer/views/ios/corellium_instance.py
@@ -462,6 +462,34 @@ def run_app(request, api=False):
# AJAX
+@require_http_methods(['POST'])
+def stop_app(request, api=False):
+ """Stop an App."""
+ data = {
+ 'status': 'failed',
+ 'message': 'Failed to stop the app'}
+ try:
+ instance_id = request.POST['instance_id']
+ bundle_id = request.POST['bundle_id']
+ failed = common_check(instance_id)
+ if failed:
+ return send_response(failed, api)
+ if not strict_package_check(bundle_id):
+ data['message'] = 'Invalid iOS Bundle id'
+ return send_response(data, api)
+ ca = CorelliumAgentAPI(instance_id)
+ if (ca.agent_ready()
+ and ca.unlock_device()
+ and ca.stop_app(bundle_id) == OK):
+ data['status'] = OK
+ data['message'] = 'App Killed'
+ except Exception as exp:
+ logger.exception('Failed to stop the app')
+ data['message'] = str(exp)
+ return send_response(data, api)
+# AJAX
+
+
@require_http_methods(['POST'])
def remove_app(request, api=False):
"""Remove an app from the device."""
diff --git a/mobsf/MobSF/init.py b/mobsf/MobSF/init.py
index 55f0c200..6b448598 100644
--- a/mobsf/MobSF/init.py
+++ b/mobsf/MobSF/init.py
@@ -10,7 +10,7 @@ from mobsf.install.windows.setup import windows_config_local
logger = logging.getLogger(__name__)
-VERSION = '3.9.4'
+VERSION = '3.9.5'
BANNER = """
__ __ _ ____ _____ _____ ___
| \/ | ___ | |__/ ___|| ___|_ _|___ // _ \
diff --git a/mobsf/MobSF/urls.py b/mobsf/MobSF/urls.py
index 02cbffb8..808f8599 100755
--- a/mobsf/MobSF/urls.py
+++ b/mobsf/MobSF/urls.py
@@ -115,6 +115,8 @@ urlpatterns = [
api_idz.api_ios_dynamic_analyzer),
re_path(r'^api/v1/ios/run_app$',
api_idz.api_run_app),
+ re_path(r'^api/v1/ios/stop_app$',
+ api_idz.api_stop_app),
re_path(r'^api/v1/ios/remove_app$',
api_idz.api_remove_app),
re_path(r'^api/v1/ios/take_screenshot$',
diff --git a/mobsf/MobSF/views/api/api_ios_dynamic_analysis.py b/mobsf/MobSF/views/api/api_ios_dynamic_analysis.py
index 62d61f53..0c99b375 100644
--- a/mobsf/MobSF/views/api/api_ios_dynamic_analysis.py
+++ b/mobsf/MobSF/views/api/api_ios_dynamic_analysis.py
@@ -192,6 +192,20 @@ def api_run_app(request):
return make_api_response(resp, 200)
+@request_method(['POST'])
+@csrf_exempt
+def api_stop_app(request):
+ """POST - Run an App."""
+ params = {INSTANCE_ID, BUNDLE_ID}
+ if set(request.POST) < params:
+ return make_api_response(
+ {'error': 'Missing Parameters'}, 422)
+ resp = corellium_instance.stop_app(request, True)
+ if resp.get('status') == FAILED:
+ return make_api_response(resp, 500)
+ return make_api_response(resp, 200)
+
+
@request_method(['POST'])
@csrf_exempt
def api_remove_app(request):
diff --git a/mobsf/templates/general/apidocs.html b/mobsf/templates/general/apidocs.html
index 7e265f36..93496811 100644
--- a/mobsf/templates/general/apidocs.html
+++ b/mobsf/templates/general/apidocs.html
@@ -81,6 +81,7 @@
api/v1/ios/setup_environment - Setup iOS Dynamic Analysis Environment
api/v1/ios/dynamic_analyzer - iOS Dynamic Analyzer
api/v1/ios/run_app - Run App
+ api/v1/ios/stop_app - Stop App
api/v1/ios/remove_app - Remove App
api/v1/ios/take_screenshot - Take Screenshot
api/v1/ios/get_app_container_path - Get App Container Path
@@ -3733,6 +3734,82 @@
+
+ Stop App API
+ Kill the app in the Corellium VM.
+
+
+
+
+ | Param Name |
+ Param Value |
+ Required |
+
+
+
+
+ | instance_id |
+ iOS instance id (Available from /api/v1/ios/dynamic_analysis) |
+ Yes |
+
+
+ | bundle_id |
+ iOS app bundle id (Available from /api/v1/ios/dynamic_analysis) |
+ Yes |
+
+
+
+
+
+ -
+
Success Response:
+
+ -
+ Code:
200
+ Content-Type: application/json; charset=utf-8
+ Content: {"status": "ok", "message": "App Killed"}
+
+
+
+ -
+
Error Response:
+
+ -
+ Code:
500 Internal Server Error or 405 Method Not Allowed or 422 Unprocessable Entity
+ Content-Type: application/json; charset=utf-8
+ Content: {"error": <error message> }
+
+
+ OR
+
+ -
+ Code:
401 Unauthorized
+ Content-Type: application/json; charset=utf-8
+ Content: {"error": "You are unauthorized to make this request." }
+
+
+
+ -
+
Sample Call:
+
+
+
+
Remove App API
Remove an app from the Corellium VM.
diff --git a/poetry.lock b/poetry.lock
index 67b21f26..5e783404 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2344,13 +2344,13 @@ test = ["flaky", "pretend", "pytest (>=3.0.1)"]
[[package]]
name = "pyparsing"
-version = "3.1.1"
+version = "3.1.2"
description = "pyparsing module - Classes and methods to define and execute parsing grammars"
optional = false
python-versions = ">=3.6.8"
files = [
- {file = "pyparsing-3.1.1-py3-none-any.whl", hash = "sha256:32c7c0b711493c72ff18a981d24f28aaf9c1fb7ed5e9667c9e84e3db623bdbfb"},
- {file = "pyparsing-3.1.1.tar.gz", hash = "sha256:ede28a1a32462f5a9705e07aea48001a08f7cf81a021585011deba701581a0db"},
+ {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"},
+ {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"},
]
[package.extras]
diff --git a/pyproject.toml b/pyproject.toml
index 1cfd53d0..6c7f3515 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "mobsf"
-version = "3.9.4"
+version = "3.9.5"
description = "Mobile Security Framework (MobSF) is an automated, all-in-one mobile application (Android/iOS/Windows) pen-testing, malware analysis and security assessment framework capable of performing static and dynamic analysis."
keywords = ["mobsf", "mobile security framework", "mobile security", "security tool", "static analysis", "dynamic analysis", "malware analysis"]
authors = ["Ajin Abraham "]
From 5c351b2314096267c26819a1e31335a4c0eec3f9 Mon Sep 17 00:00:00 2001
From: "Ajin.Abraham"
Date: Sat, 9 Mar 2024 12:55:07 -0800
Subject: [PATCH 03/19] Fix #2343
---
mobsf/DynamicAnalyzer/views/ios/corellium_apis.py | 3 +--
mobsf/StaticAnalyzer/views/android/manifest_analysis.py | 5 ++++-
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/mobsf/DynamicAnalyzer/views/ios/corellium_apis.py b/mobsf/DynamicAnalyzer/views/ios/corellium_apis.py
index ece1de15..70790bfa 100644
--- a/mobsf/DynamicAnalyzer/views/ios/corellium_apis.py
+++ b/mobsf/DynamicAnalyzer/views/ios/corellium_apis.py
@@ -531,11 +531,10 @@ class CorelliumAgentAPI(CorelliumInit):
def install_ipa(self):
"""Install IPA."""
- data = {'path': '/tmp/app.ipa'}
r = requests.post(
f'{self.api}/instances/{self.instance_id}/agent/v1/app/install',
headers=self.headers,
- json=data,
+ json={'path': '/tmp/app.ipa'},
proxies=self.proxies,
verify=self.verify)
if r.status_code in SUCCESS_RESP:
diff --git a/mobsf/StaticAnalyzer/views/android/manifest_analysis.py b/mobsf/StaticAnalyzer/views/android/manifest_analysis.py
index a76fd7cf..28f8c7fd 100755
--- a/mobsf/StaticAnalyzer/views/android/manifest_analysis.py
+++ b/mobsf/StaticAnalyzer/views/android/manifest_analysis.py
@@ -310,7 +310,10 @@ def manifest_analysis(mfxml, ns, man_data_dic, src_type, app_dir):
try:
target_sdk = int(man_data_dic['target_sdk'])
except Exception:
- target_sdk = ANDROID_8_0_LEVEL
+ if man_an_dic.get('min_sdk'):
+ target_sdk = int(man_an_dic['min_sdk'])
+ else:
+ target_sdk = ANDROID_8_0_LEVEL
if (target_sdk < ANDROID_9_0_LEVEL
and launchmode == 'singleTask'):
ret_list.append(('task_hijacking', (item,), (target_sdk,)))
From 641f4430bc7a216165a2077b7a6f841a5f2f19b7 Mon Sep 17 00:00:00 2001
From: "Ajin.Abraham"
Date: Sat, 9 Mar 2024 13:19:05 -0800
Subject: [PATCH 04/19] HOTFIX: target sdk bug
---
mobsf/StaticAnalyzer/views/android/manifest_analysis.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/mobsf/StaticAnalyzer/views/android/manifest_analysis.py b/mobsf/StaticAnalyzer/views/android/manifest_analysis.py
index 28f8c7fd..ae4497ab 100755
--- a/mobsf/StaticAnalyzer/views/android/manifest_analysis.py
+++ b/mobsf/StaticAnalyzer/views/android/manifest_analysis.py
@@ -310,9 +310,9 @@ def manifest_analysis(mfxml, ns, man_data_dic, src_type, app_dir):
try:
target_sdk = int(man_data_dic['target_sdk'])
except Exception:
- if man_an_dic.get('min_sdk'):
- target_sdk = int(man_an_dic['min_sdk'])
- else:
+ try:
+ target_sdk = int(man_data_dic['min_sdk'])
+ except Exception:
target_sdk = ANDROID_8_0_LEVEL
if (target_sdk < ANDROID_9_0_LEVEL
and launchmode == 'singleTask'):
From a5338ee6826439c605b388aad08e84eb7109e224 Mon Sep 17 00:00:00 2001
From: "Ajin.Abraham"
Date: Wed, 13 Mar 2024 00:57:54 -0700
Subject: [PATCH 05/19] HOTFIX: Bump androguard + remove quark
---
mobsf/StaticAnalyzer/views/android/app.py | 7 +-
.../views/android/icon_analysis.py | 5 +-
poetry.lock | 466 ++++++++++++++----
pyproject.toml | 3 +-
4 files changed, 372 insertions(+), 109 deletions(-)
diff --git a/mobsf/StaticAnalyzer/views/android/app.py b/mobsf/StaticAnalyzer/views/android/app.py
index 3f1ea68b..caf30aab 100644
--- a/mobsf/StaticAnalyzer/views/android/app.py
+++ b/mobsf/StaticAnalyzer/views/android/app.py
@@ -5,8 +5,13 @@ import re
import logging
from pathlib import Path
-from androguard.core.bytecodes import apk
+from androguard.core import (
+ androconf,
+ apk,
+)
+
+androconf.logger.remove()
logger = logging.getLogger(__name__)
logging.getLogger('androguard').setLevel(logging.ERROR)
diff --git a/mobsf/StaticAnalyzer/views/android/icon_analysis.py b/mobsf/StaticAnalyzer/views/android/icon_analysis.py
index 073a50b1..476a5a9e 100755
--- a/mobsf/StaticAnalyzer/views/android/icon_analysis.py
+++ b/mobsf/StaticAnalyzer/views/android/icon_analysis.py
@@ -11,7 +11,8 @@ import subprocess
from lxml import etree
-from androguard.core.bytecodes import (
+from androguard.core import (
+ androconf,
axml,
)
@@ -23,7 +24,7 @@ from mobsf.MobSF.utils import (
is_path_traversal,
)
-
+androconf.logger.remove()
logger = logging.getLogger(__name__)
logging.getLogger('androguard').setLevel(logging.ERROR)
diff --git a/poetry.lock b/poetry.lock
index 5e783404..7726ada7 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -20,6 +20,25 @@ service-identity = ">=23.1.0"
[package.extras]
dev = ["coverage[toml] (>=7.2.2)"]
+[[package]]
+name = "alembic"
+version = "1.13.1"
+description = "A database migration tool for SQLAlchemy."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"},
+ {file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"},
+]
+
+[package.dependencies]
+Mako = "*"
+SQLAlchemy = ">=1.3.0"
+typing-extensions = ">=4"
+
+[package.extras]
+tz = ["backports.zoneinfo"]
+
[[package]]
name = "altgraph"
version = "0.17.4"
@@ -33,31 +52,33 @@ files = [
[[package]]
name = "androguard"
-version = "3.4.0a1"
+version = "4.1.0"
description = "Androguard is a full python tool to play with Android files."
optional = false
-python-versions = ">=3.5"
+python-versions = ">=3.9,<4.0"
files = [
- {file = "androguard-3.4.0a1-py3-none-any.whl", hash = "sha256:1292b6631878ccd75c02426f68c159edee3d3716b212428397800b5b328537a2"},
- {file = "androguard-3.4.0a1.tar.gz", hash = "sha256:2bdd9583321b14bbc0c5bd92fd330f0efe7fb6abe1f334b658ded5f93849e898"},
+ {file = "androguard-4.1.0-py3-none-any.whl", hash = "sha256:71523c5cd24dbafb5054aedb99d4076a9d3bf4e2dcd2b9c6993c49412122e0cb"},
+ {file = "androguard-4.1.0.tar.gz", hash = "sha256:26eceafb62fe0e44750f63729e9d9dab2475d54cc2ae90430ec9134757c4f54a"},
]
[package.dependencies]
+apkInspector = ">=1.1.7"
asn1crypto = ">=0.24.0"
click = ">=7.0"
colorama = ">=0.4.1"
+dataset = "*"
+frida = "*"
ipython = ">=5.0.0"
+loguru = "*"
lxml = ">=4.3.0"
-matplotlib = ">=3.0.2"
-networkx = ">=2.2"
+matplotlib = "*"
+mutf8 = "*"
+networkx = "*"
pydot = ">=1.4.1"
pygments = ">=2.3.1"
-
-[package.extras]
-docs = ["sphinx", "sphinx-rtd-theme", "sphinxcontrib-programoutput (>0.8)"]
-gui = ["PyQt5", "pyperclip"]
-magic = ["python-magic-bin (>=0.4.14)"]
-tests = ["codecov", "coverage", "mock (>=2.0)", "nose", "nose-timer"]
+PyQt5 = "*"
+PyQt5-Qt5 = ">=5.15,<5.15.11 || >5.15.11,<5.15.12 || >5.15.12,<6.0"
+pyyaml = "*"
[[package]]
name = "apkid"
@@ -77,6 +98,17 @@ yara-python-dex = ">=1.0.1"
dev = ["mypy", "pypandoc", "twine"]
test = ["delayed-assert", "factory-boy", "mock", "pytest", "pytest-cov", "pytest-factoryboy", "pytest-flask", "pytest-runner", "tox"]
+[[package]]
+name = "apkinspector"
+version = "1.2.1"
+description = "apkInspector is a tool designed to provide detailed insights into the zip structure of APK files, offering the capability to extract content and decode the AndroidManifest.xml file."
+optional = false
+python-versions = ">=3.5,<4.0"
+files = [
+ {file = "apkinspector-1.2.1-py3-none-any.whl", hash = "sha256:4736719884e92dfbe3203103ad46deb22b4683e208ae66972236a052c5d23e37"},
+ {file = "apkinspector-1.2.1.tar.gz", hash = "sha256:6c1fd678246760e75f8386e6f4d9686b640cc6bd88256f0865dfacbd49e8e0dd"},
+]
+
[[package]]
name = "apksigcopier"
version = "1.1.1"
@@ -182,6 +214,20 @@ docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "zope.interface"]
tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six"]
+[[package]]
+name = "banal"
+version = "1.0.6"
+description = "Commons of banal micro-functions for Python."
+optional = false
+python-versions = "*"
+files = [
+ {file = "banal-1.0.6-py2.py3-none-any.whl", hash = "sha256:877aacb16b17f8fa4fd29a7c44515c5a23dc1a7b26078bc41dd34829117d85e1"},
+ {file = "banal-1.0.6.tar.gz", hash = "sha256:2fe02c9305f53168441948f4a03dfbfa2eacc73db30db4a93309083cb0e250a5"},
+]
+
+[package.extras]
+dev = ["mypy", "wheel"]
+
[[package]]
name = "bcrypt"
version = "4.1.2"
@@ -750,6 +796,25 @@ files = [
docs = ["ipython", "matplotlib", "numpydoc", "sphinx"]
tests = ["pytest", "pytest-cov", "pytest-xdist"]
+[[package]]
+name = "dataset"
+version = "1.6.2"
+description = "Toolkit for Python-based database access."
+optional = false
+python-versions = "*"
+files = [
+ {file = "dataset-1.6.2-py2.py3-none-any.whl", hash = "sha256:dcca9ba7658473d3082b1adf87a650252a1cd665705b73fa7d4ee32116a107b9"},
+ {file = "dataset-1.6.2.tar.gz", hash = "sha256:77d362118f67a8cbb4848dbd30ab362b9fa7cfebdbfaf426c9c500cb38969a99"},
+]
+
+[package.dependencies]
+alembic = ">=0.6.2"
+banal = ">=1.0.1"
+sqlalchemy = ">=1.3.2,<2.0.0"
+
+[package.extras]
+dev = ["PyMySQL", "coverage", "cryptography", "flake8", "pip", "psycopg2-binary", "pytest", "wheel"]
+
[[package]]
name = "decorator"
version = "5.1.1"
@@ -1000,20 +1065,75 @@ files = [
]
[[package]]
-name = "graphviz"
-version = "0.20.1"
-description = "Simple Python interface for Graphviz"
+name = "greenlet"
+version = "3.0.3"
+description = "Lightweight in-process concurrent programming"
optional = false
python-versions = ">=3.7"
files = [
- {file = "graphviz-0.20.1-py3-none-any.whl", hash = "sha256:587c58a223b51611c0cf461132da386edd896a029524ca61a1462b880bf97977"},
- {file = "graphviz-0.20.1.zip", hash = "sha256:8c58f14adaa3b947daf26c19bc1e98c4e0702cdc31cf99153e6f06904d492bf8"},
+ {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"},
+ {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"},
+ {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"},
+ {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"},
+ {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"},
+ {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"},
+ {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"},
+ {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"},
+ {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"},
+ {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"},
+ {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"},
+ {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"},
+ {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"},
+ {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"},
+ {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"},
+ {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"},
+ {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"},
+ {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"},
+ {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"},
+ {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"},
+ {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"},
+ {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"},
+ {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"},
+ {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"},
+ {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"},
+ {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"},
+ {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"},
+ {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"},
+ {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"},
+ {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"},
+ {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"},
+ {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"},
+ {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"},
+ {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"},
+ {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"},
+ {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"},
+ {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"},
+ {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"},
+ {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"},
+ {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"},
+ {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"},
+ {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"},
+ {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"},
+ {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"},
+ {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"},
+ {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"},
+ {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"},
+ {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"},
+ {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"},
+ {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"},
+ {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"},
+ {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"},
+ {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"},
+ {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"},
+ {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"},
+ {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"},
+ {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"},
+ {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"},
]
[package.extras]
-dev = ["flake8", "pep8-naming", "tox (>=3)", "twine", "wheel"]
-docs = ["sphinx (>=5)", "sphinx-autodoc-typehints", "sphinx-rtd-theme"]
-test = ["coverage", "mock (>=4)", "pytest (>=7)", "pytest-cov", "pytest-mock (>=3)"]
+docs = ["Sphinx", "furo"]
+test = ["objgraph", "psutil"]
[[package]]
name = "gunicorn"
@@ -1414,6 +1534,24 @@ files = [
{file = "lief-0.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:2db3eb282a35daf51f89c6509226668a08fb6a6d1f507dd549dd9f077585db11"},
]
+[[package]]
+name = "loguru"
+version = "0.7.2"
+description = "Python logging made (stupidly) simple"
+optional = false
+python-versions = ">=3.5"
+files = [
+ {file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"},
+ {file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"},
+]
+
+[package.dependencies]
+colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
+win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
+
+[package.extras]
+dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "mypy (==v1.5.1)", "pre-commit (==3.4.0)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"]
+
[[package]]
name = "lxml"
version = "5.1.0"
@@ -1517,6 +1655,25 @@ files = [
[package.dependencies]
altgraph = ">=0.17"
+[[package]]
+name = "mako"
+version = "1.3.2"
+description = "A super-fast templating language that borrows the best ideas from the existing templating languages."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "Mako-1.3.2-py3-none-any.whl", hash = "sha256:32a99d70754dfce237019d17ffe4a282d2d3351b9c476e90d8a60e63f133b80c"},
+ {file = "Mako-1.3.2.tar.gz", hash = "sha256:2a0c8ad7f6274271b3bb7467dd37cf9cc6dab4bc19cb69a4ef10669402de698e"},
+]
+
+[package.dependencies]
+MarkupSafe = ">=0.9.2"
+
+[package.extras]
+babel = ["Babel"]
+lingua = ["lingua"]
+testing = ["pytest"]
+
[[package]]
name = "markupsafe"
version = "2.1.5"
@@ -1794,6 +1951,31 @@ files = [
{file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"},
]
+[[package]]
+name = "mutf8"
+version = "1.0.6"
+description = "Fast MUTF-8 encoder & decoder"
+optional = false
+python-versions = "*"
+files = [
+ {file = "mutf8-1.0.6-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:74ae69cd9790fa4f0f6a7b0db503c459c955b8235551baf683cb4f3f31677063"},
+ {file = "mutf8-1.0.6-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcf20045263ce8ebd6c47e94c9477ab0d388ed169a69ad2d8f19bcbf0b87f401"},
+ {file = "mutf8-1.0.6-cp36-cp36m-win_amd64.whl", hash = "sha256:83c38555db263e369e95533d80848d8e4296e302303b72082b98c3124cba504d"},
+ {file = "mutf8-1.0.6-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:e09f4a19e5500699bb42074890b463b785ab9a8d95c7d793e590405f3b4b29d7"},
+ {file = "mutf8-1.0.6-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f4f497f20e3ea7968496c1eb1e1cb259c53ad040879e1e83ffb755a12112a04"},
+ {file = "mutf8-1.0.6-cp37-cp37m-win_amd64.whl", hash = "sha256:1925f5490fabca5c34138ed6644a1a093b0d935252207a5e89664097ff14114c"},
+ {file = "mutf8-1.0.6-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:018ceda7cdb66a1d3e9c07a71a1a35b92570fbb1230887a34ad784ff4d349981"},
+ {file = "mutf8-1.0.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a67e88534a7641c513dad13f2f7913239808df4a5d0b822eda0ff9024431e0b"},
+ {file = "mutf8-1.0.6-cp38-cp38-win_amd64.whl", hash = "sha256:0d1325d42806b31901a0ddd4ef199144e508fd9f6f3c75a8305d5979365b66c3"},
+ {file = "mutf8-1.0.6-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:3207a071ead14d928213019f12b5554b179f61a16a8094ed660b755990db3652"},
+ {file = "mutf8-1.0.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6172b5babc0c819636830fc79ca9c3a82662ef1ee764c82c1b59fbf6ea54d82f"},
+ {file = "mutf8-1.0.6-cp39-cp39-win_amd64.whl", hash = "sha256:4f7a24b55c53d508a7ecb2e8c6fe14e4fcefaa4c48100b446e73217ade7875a0"},
+ {file = "mutf8-1.0.6.tar.gz", hash = "sha256:1bbbefb67c2e5a57104750bb04b0912200b57b2fa9841be245279e83859cb346"},
+]
+
+[package.extras]
+test = ["pytest", "pytest-benchmark"]
+
[[package]]
name = "networkx"
version = "3.2.1"
@@ -2056,38 +2238,6 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa
typing = ["typing-extensions"]
xmp = ["defusedxml"]
-[[package]]
-name = "plotly"
-version = "5.19.0"
-description = "An open-source, interactive data visualization library for Python"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "plotly-5.19.0-py3-none-any.whl", hash = "sha256:906abcc5f15945765328c5d47edaa884bc99f5985fbc61e8cd4dc361f4ff8f5a"},
- {file = "plotly-5.19.0.tar.gz", hash = "sha256:5ea91a56571292ade3e3bc9bf712eba0b95a1fb0a941375d978cc79432e055f4"},
-]
-
-[package.dependencies]
-packaging = "*"
-tenacity = ">=6.2.0"
-
-[[package]]
-name = "prettytable"
-version = "3.10.0"
-description = "A simple Python library for easily displaying tabular data in a visually appealing ASCII table format"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "prettytable-3.10.0-py3-none-any.whl", hash = "sha256:6536efaf0757fdaa7d22e78b3aac3b69ea1b7200538c2c6995d649365bddab92"},
- {file = "prettytable-3.10.0.tar.gz", hash = "sha256:9665594d137fb08a1117518c25551e0ede1687197cf353a4fdc78d27e1073568"},
-]
-
-[package.dependencies]
-wcwidth = "*"
-
-[package.extras]
-tests = ["pytest", "pytest-cov", "pytest-lazy-fixtures"]
-
[[package]]
name = "prompt-toolkit"
version = "3.0.43"
@@ -2366,6 +2516,68 @@ files = [
{file = "pyperclip-1.8.2.tar.gz", hash = "sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57"},
]
+[[package]]
+name = "pyqt5"
+version = "5.15.10"
+description = "Python bindings for the Qt cross platform application toolkit"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "PyQt5-5.15.10-cp37-abi3-macosx_10_13_x86_64.whl", hash = "sha256:93288d62ebd47b1933d80c27f5d43c7c435307b84d480af689cef2474e87e4c8"},
+ {file = "PyQt5-5.15.10-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:862cea3be95b4b0a2b9678003b3a18edf7bd5eafd673860f58820f246d4bf616"},
+ {file = "PyQt5-5.15.10-cp37-abi3-manylinux_2_17_x86_64.whl", hash = "sha256:b89478d16d4118664ff58ed609e0a804d002703c9420118de7e4e70fa1cb5486"},
+ {file = "PyQt5-5.15.10-cp37-abi3-win32.whl", hash = "sha256:ff99b4f91aa8eb60510d5889faad07116d3340041916e46c07d519f7cad344e1"},
+ {file = "PyQt5-5.15.10-cp37-abi3-win_amd64.whl", hash = "sha256:501355f327e9a2c38db0428e1a236d25ebcb99304cd6e668c05d1188d514adec"},
+ {file = "PyQt5-5.15.10.tar.gz", hash = "sha256:d46b7804b1b10a4ff91753f8113e5b5580d2b4462f3226288e2d84497334898a"},
+]
+
+[package.dependencies]
+PyQt5-Qt5 = ">=5.15.2"
+PyQt5-sip = ">=12.13,<13"
+
+[[package]]
+name = "pyqt5-qt5"
+version = "5.15.2"
+description = "The subset of a Qt installation needed by PyQt5."
+optional = false
+python-versions = "*"
+files = [
+ {file = "PyQt5_Qt5-5.15.2-py3-none-macosx_10_13_intel.whl", hash = "sha256:76980cd3d7ae87e3c7a33bfebfaee84448fd650bad6840471d6cae199b56e154"},
+ {file = "PyQt5_Qt5-5.15.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:1988f364ec8caf87a6ee5d5a3a5210d57539988bf8e84714c7d60972692e2f4a"},
+ {file = "PyQt5_Qt5-5.15.2-py3-none-win32.whl", hash = "sha256:9cc7a768b1921f4b982ebc00a318ccb38578e44e45316c7a4a850e953e1dd327"},
+ {file = "PyQt5_Qt5-5.15.2-py3-none-win_amd64.whl", hash = "sha256:750b78e4dba6bdf1607febedc08738e318ea09e9b10aea9ff0d73073f11f6962"},
+]
+
+[[package]]
+name = "pyqt5-sip"
+version = "12.13.0"
+description = "The sip module support for PyQt5"
+optional = false
+python-versions = ">=3.7"
+files = [
+ {file = "PyQt5_sip-12.13.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a7e3623b2c743753625c4650ec7696362a37fb36433b61824cf257f6d3d43cca"},
+ {file = "PyQt5_sip-12.13.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6e4ac714252370ca037c7d609da92388057165edd4f94e63354f6d65c3ed9d53"},
+ {file = "PyQt5_sip-12.13.0-cp310-cp310-win32.whl", hash = "sha256:d5032da3fff62da055104926ffe76fd6044c1221f8ad35bb60804bcb422fe866"},
+ {file = "PyQt5_sip-12.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:9a8cdd6cb66adcbe5c941723ed1544eba05cf19b6c961851b58ccdae1c894afb"},
+ {file = "PyQt5_sip-12.13.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f85fb633a522f04e48008de49dce1ff1d947011b48885b8428838973fbca412"},
+ {file = "PyQt5_sip-12.13.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ec60162e034c42fb99859206d62b83b74f987d58937b3a82bdc07b5c3d190dec"},
+ {file = "PyQt5_sip-12.13.0-cp311-cp311-win32.whl", hash = "sha256:205cd449d08a2b024a468fb6100cd7ed03e946b4f49706f508944006f955ae1a"},
+ {file = "PyQt5_sip-12.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:1c8371682f77852256f1f2d38c41e2e684029f43330f0635870895ab01c02f6c"},
+ {file = "PyQt5_sip-12.13.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7fe3375b508c5bc657d73b9896bba8a768791f1f426c68053311b046bcebdddf"},
+ {file = "PyQt5_sip-12.13.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:773731b1b5ab1a7cf5621249f2379c95e3d2905e9bd96ff3611b119586daa876"},
+ {file = "PyQt5_sip-12.13.0-cp312-cp312-win32.whl", hash = "sha256:fb4a5271fa3f6bc2feb303269a837a95a6d8dd16be553aa40e530de7fb81bfdf"},
+ {file = "PyQt5_sip-12.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:3a4498f3b1b15f43f5d12963accdce0fd652b0bcaae6baf8008663365827444c"},
+ {file = "PyQt5_sip-12.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9b984c2620a7a7eaf049221b09ae50a345317add2624c706c7d2e9e6632a9587"},
+ {file = "PyQt5_sip-12.13.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3188a06956aef86f604fb0d14421a110fad70d2a9e943dbacbfc3303f651dade"},
+ {file = "PyQt5_sip-12.13.0-cp38-cp38-win32.whl", hash = "sha256:108a15f603e1886988c4b0d9d41cb74c9f9815bf05cefc843d559e8c298a10ce"},
+ {file = "PyQt5_sip-12.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:db228cd737f5cbfc66a3c3e50042140cb80b30b52edc5756dbbaa2346ec73137"},
+ {file = "PyQt5_sip-12.13.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5338773bbaedaa4f16a73c142fb23cc18c327be6c338813af70260b756c7bc92"},
+ {file = "PyQt5_sip-12.13.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:29fa9cc964517c9fc3f94f072b9a2aeef4e7a2eda1879cb835d9e06971161cdf"},
+ {file = "PyQt5_sip-12.13.0-cp39-cp39-win32.whl", hash = "sha256:96414c93f3d33963887cf562d50d88b955121fbfd73f937c8eca46643e77bf61"},
+ {file = "PyQt5_sip-12.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:bbc7cd498bf19e0862097be1ad2243e824dea56726f00c11cff1b547c2d31d01"},
+ {file = "PyQt5_sip-12.13.0.tar.gz", hash = "sha256:7f321daf84b9c9dbca61b80e1ef37bdaffc0e93312edae2cd7da25b953971d91"},
+]
+
[[package]]
name = "pyrsistent"
version = "0.20.0"
@@ -2497,28 +2709,6 @@ files = [
{file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"},
]
-[[package]]
-name = "quark-engine"
-version = "23.7.1"
-description = "An Obfuscation-Neglect Android Malware Scoring System"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "quark-engine-23.7.1.tar.gz", hash = "sha256:bd9478cd5575a7a2a8655e09568b875edff59e0421c3fb97df10b703f2cab857"},
- {file = "quark_engine-23.7.1-py3-none-any.whl", hash = "sha256:2473f5f3155e5e930f6a4f2078e6486abff048be697edb2bbea98cda6ba47771"},
-]
-
-[package.dependencies]
-androguard = "3.4.0a1"
-click = "*"
-colorama = "*"
-graphviz = "*"
-plotly = "*"
-prettytable = ">=1.0.0"
-prompt-toolkit = "*"
-rzpipe = "*"
-tqdm = "*"
-
[[package]]
name = "requests"
version = "2.31.0"
@@ -2645,17 +2835,6 @@ files = [
{file = "ruamel.yaml.clib-0.2.8.tar.gz", hash = "sha256:beb2e0404003de9a4cab9753a8805a8fe9320ee6673136ed7f04255fe60bb512"},
]
-[[package]]
-name = "rzpipe"
-version = "0.6.0"
-description = "Pipe interface for rizin"
-optional = false
-python-versions = "*"
-files = [
- {file = "rzpipe-0.6.0-py3-none-any.whl", hash = "sha256:6bb842d9d709ee9ca67feabe18d73e8c2d2644304dee1a7131061477e4a11ffd"},
- {file = "rzpipe-0.6.0.tar.gz", hash = "sha256:a72e2888da7e59470e1879f601d1f2229815f01b08f00d60b498b68e88b55b17"},
-]
-
[[package]]
name = "semgrep"
version = "0.117.0"
@@ -2866,6 +3045,85 @@ files = [
{file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"},
]
+[[package]]
+name = "sqlalchemy"
+version = "1.4.52"
+description = "Database Abstraction Library"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
+files = [
+ {file = "SQLAlchemy-1.4.52-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:f68016f9a5713684c1507cc37133c28035f29925c75c0df2f9d0f7571e23720a"},
+ {file = "SQLAlchemy-1.4.52-cp310-cp310-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24bb0f81fbbb13d737b7f76d1821ec0b117ce8cbb8ee5e8641ad2de41aa916d3"},
+ {file = "SQLAlchemy-1.4.52-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e93983cc0d2edae253b3f2141b0a3fb07e41c76cd79c2ad743fc27eb79c3f6db"},
+ {file = "SQLAlchemy-1.4.52-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:84e10772cfc333eb08d0b7ef808cd76e4a9a30a725fb62a0495877a57ee41d81"},
+ {file = "SQLAlchemy-1.4.52-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:427988398d2902de042093d17f2b9619a5ebc605bf6372f7d70e29bde6736842"},
+ {file = "SQLAlchemy-1.4.52-cp310-cp310-win32.whl", hash = "sha256:1296f2cdd6db09b98ceb3c93025f0da4835303b8ac46c15c2136e27ee4d18d94"},
+ {file = "SQLAlchemy-1.4.52-cp310-cp310-win_amd64.whl", hash = "sha256:80e7f697bccc56ac6eac9e2df5c98b47de57e7006d2e46e1a3c17c546254f6ef"},
+ {file = "SQLAlchemy-1.4.52-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2f251af4c75a675ea42766880ff430ac33291c8d0057acca79710f9e5a77383d"},
+ {file = "SQLAlchemy-1.4.52-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb8f9e4c4718f111d7b530c4e6fb4d28f9f110eb82e7961412955b3875b66de0"},
+ {file = "SQLAlchemy-1.4.52-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afb1672b57f58c0318ad2cff80b384e816735ffc7e848d8aa51e0b0fc2f4b7bb"},
+ {file = "SQLAlchemy-1.4.52-cp311-cp311-win32.whl", hash = "sha256:6e41cb5cda641f3754568d2ed8962f772a7f2b59403b95c60c89f3e0bd25f15e"},
+ {file = "SQLAlchemy-1.4.52-cp311-cp311-win_amd64.whl", hash = "sha256:5bed4f8c3b69779de9d99eb03fd9ab67a850d74ab0243d1be9d4080e77b6af12"},
+ {file = "SQLAlchemy-1.4.52-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:49e3772eb3380ac88d35495843daf3c03f094b713e66c7d017e322144a5c6b7c"},
+ {file = "SQLAlchemy-1.4.52-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:618827c1a1c243d2540314c6e100aee7af09a709bd005bae971686fab6723554"},
+ {file = "SQLAlchemy-1.4.52-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de9acf369aaadb71a725b7e83a5ef40ca3de1cf4cdc93fa847df6b12d3cd924b"},
+ {file = "SQLAlchemy-1.4.52-cp312-cp312-win32.whl", hash = "sha256:763bd97c4ebc74136ecf3526b34808c58945023a59927b416acebcd68d1fc126"},
+ {file = "SQLAlchemy-1.4.52-cp312-cp312-win_amd64.whl", hash = "sha256:f12aaf94f4d9679ca475975578739e12cc5b461172e04d66f7a3c39dd14ffc64"},
+ {file = "SQLAlchemy-1.4.52-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:853fcfd1f54224ea7aabcf34b227d2b64a08cbac116ecf376907968b29b8e763"},
+ {file = "SQLAlchemy-1.4.52-cp36-cp36m-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f98dbb8fcc6d1c03ae8ec735d3c62110949a3b8bc6e215053aa27096857afb45"},
+ {file = "SQLAlchemy-1.4.52-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e135fff2e84103bc15c07edd8569612ce317d64bdb391f49ce57124a73f45c5"},
+ {file = "SQLAlchemy-1.4.52-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5b5de6af8852500d01398f5047d62ca3431d1e29a331d0b56c3e14cb03f8094c"},
+ {file = "SQLAlchemy-1.4.52-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3491c85df263a5c2157c594f54a1a9c72265b75d3777e61ee13c556d9e43ffc9"},
+ {file = "SQLAlchemy-1.4.52-cp36-cp36m-win32.whl", hash = "sha256:427c282dd0deba1f07bcbf499cbcc9fe9a626743f5d4989bfdfd3ed3513003dd"},
+ {file = "SQLAlchemy-1.4.52-cp36-cp36m-win_amd64.whl", hash = "sha256:ca5ce82b11731492204cff8845c5e8ca1a4bd1ade85e3b8fcf86e7601bfc6a39"},
+ {file = "SQLAlchemy-1.4.52-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:29d4247313abb2015f8979137fe65f4eaceead5247d39603cc4b4a610936cd2b"},
+ {file = "SQLAlchemy-1.4.52-cp37-cp37m-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a752bff4796bf22803d052d4841ebc3c55c26fb65551f2c96e90ac7c62be763a"},
+ {file = "SQLAlchemy-1.4.52-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7ea11727feb2861deaa293c7971a4df57ef1c90e42cb53f0da40c3468388000"},
+ {file = "SQLAlchemy-1.4.52-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d913f8953e098ca931ad7f58797f91deed26b435ec3756478b75c608aa80d139"},
+ {file = "SQLAlchemy-1.4.52-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a251146b921725547ea1735b060a11e1be705017b568c9f8067ca61e6ef85f20"},
+ {file = "SQLAlchemy-1.4.52-cp37-cp37m-win32.whl", hash = "sha256:1f8e1c6a6b7f8e9407ad9afc0ea41c1f65225ce505b79bc0342159de9c890782"},
+ {file = "SQLAlchemy-1.4.52-cp37-cp37m-win_amd64.whl", hash = "sha256:346ed50cb2c30f5d7a03d888e25744154ceac6f0e6e1ab3bc7b5b77138d37710"},
+ {file = "SQLAlchemy-1.4.52-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:4dae6001457d4497736e3bc422165f107ecdd70b0d651fab7f731276e8b9e12d"},
+ {file = "SQLAlchemy-1.4.52-cp38-cp38-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5d2e08d79f5bf250afb4a61426b41026e448da446b55e4770c2afdc1e200fce"},
+ {file = "SQLAlchemy-1.4.52-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bbce5dd7c7735e01d24f5a60177f3e589078f83c8a29e124a6521b76d825b85"},
+ {file = "SQLAlchemy-1.4.52-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bdb7b4d889631a3b2a81a3347c4c3f031812eb4adeaa3ee4e6b0d028ad1852b5"},
+ {file = "SQLAlchemy-1.4.52-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c294ae4e6bbd060dd79e2bd5bba8b6274d08ffd65b58d106394cb6abbf35cf45"},
+ {file = "SQLAlchemy-1.4.52-cp38-cp38-win32.whl", hash = "sha256:bcdfb4b47fe04967669874fb1ce782a006756fdbebe7263f6a000e1db969120e"},
+ {file = "SQLAlchemy-1.4.52-cp38-cp38-win_amd64.whl", hash = "sha256:7d0dbc56cb6af5088f3658982d3d8c1d6a82691f31f7b0da682c7b98fa914e91"},
+ {file = "SQLAlchemy-1.4.52-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:a551d5f3dc63f096ed41775ceec72fdf91462bb95abdc179010dc95a93957800"},
+ {file = "SQLAlchemy-1.4.52-cp39-cp39-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ab773f9ad848118df7a9bbabca53e3f1002387cdbb6ee81693db808b82aaab0"},
+ {file = "SQLAlchemy-1.4.52-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2de46f5d5396d5331127cfa71f837cca945f9a2b04f7cb5a01949cf676db7d1"},
+ {file = "SQLAlchemy-1.4.52-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7027be7930a90d18a386b25ee8af30514c61f3852c7268899f23fdfbd3107181"},
+ {file = "SQLAlchemy-1.4.52-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99224d621affbb3c1a4f72b631f8393045f4ce647dd3262f12fe3576918f8bf3"},
+ {file = "SQLAlchemy-1.4.52-cp39-cp39-win32.whl", hash = "sha256:c124912fd4e1bb9d1e7dc193ed482a9f812769cb1e69363ab68e01801e859821"},
+ {file = "SQLAlchemy-1.4.52-cp39-cp39-win_amd64.whl", hash = "sha256:2c286fab42e49db23c46ab02479f328b8bdb837d3e281cae546cc4085c83b680"},
+ {file = "SQLAlchemy-1.4.52.tar.gz", hash = "sha256:80e63bbdc5217dad3485059bdf6f65a7d43f33c8bde619df5c220edf03d87296"},
+]
+
+[package.dependencies]
+greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"}
+
+[package.extras]
+aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"]
+aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"]
+asyncio = ["greenlet (!=0.4.17)"]
+asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"]
+mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)"]
+mssql = ["pyodbc"]
+mssql-pymssql = ["pymssql"]
+mssql-pyodbc = ["pyodbc"]
+mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"]
+mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"]
+mysql-connector = ["mysql-connector-python"]
+oracle = ["cx_oracle (>=7)", "cx_oracle (>=7,<8)"]
+postgresql = ["psycopg2 (>=2.7)"]
+postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"]
+postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"]
+postgresql-psycopg2binary = ["psycopg2-binary"]
+postgresql-psycopg2cffi = ["psycopg2cffi"]
+pymysql = ["pymysql", "pymysql (<1)"]
+sqlcipher = ["sqlcipher3_binary"]
+
[[package]]
name = "sqlparse"
version = "0.4.4"
@@ -2915,20 +3173,6 @@ files = [
[package.dependencies]
lxml = "*"
-[[package]]
-name = "tenacity"
-version = "8.2.3"
-description = "Retry code until it succeeds"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "tenacity-8.2.3-py3-none-any.whl", hash = "sha256:ce510e327a630c9e1beaf17d42e6ffacc88185044ad85cf74c0a8887c6a0f88c"},
- {file = "tenacity-8.2.3.tar.gz", hash = "sha256:5398ef0d78e63f40007c1fb4c0bff96e1911394d2fa8d194f77619c05ff6cc8a"},
-]
-
-[package.extras]
-doc = ["reno", "sphinx", "tornado (>=4.5)"]
-
[[package]]
name = "tldextract"
version = "3.4.4"
@@ -2999,18 +3243,18 @@ telegram = ["requests"]
[[package]]
name = "traitlets"
-version = "5.14.1"
+version = "5.14.2"
description = "Traitlets Python configuration system"
optional = false
python-versions = ">=3.8"
files = [
- {file = "traitlets-5.14.1-py3-none-any.whl", hash = "sha256:2e5a030e6eff91737c643231bfcf04a65b0132078dad75e4936700b213652e74"},
- {file = "traitlets-5.14.1.tar.gz", hash = "sha256:8585105b371a04b8316a43d5ce29c098575c2e477850b62b848b964f1444527e"},
+ {file = "traitlets-5.14.2-py3-none-any.whl", hash = "sha256:fcdf85684a772ddeba87db2f398ce00b40ff550d1528c03c14dbf6a02003cd80"},
+ {file = "traitlets-5.14.2.tar.gz", hash = "sha256:8cdd83c040dab7d1dee822678e5f5d100b514f7b72b01615b26fc5718916fdf9"},
]
[package.extras]
docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
-test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<7.5)", "pytest-mock", "pytest-mypy-testing"]
+test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.1)", "pytest-mock", "pytest-mypy-testing"]
[[package]]
name = "typing-extensions"
@@ -3211,6 +3455,20 @@ files = [
[package.extras]
brotli = ["Brotli"]
+[[package]]
+name = "win32-setctime"
+version = "1.1.0"
+description = "A small Python utility to set file creation time on Windows"
+optional = false
+python-versions = ">=3.5"
+files = [
+ {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"},
+ {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"},
+]
+
+[package.extras]
+dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
+
[[package]]
name = "wsproto"
version = "1.2.0"
@@ -3373,4 +3631,4 @@ cffi = ["cffi (>=1.11)"]
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
-content-hash = "e8daa22be0ec4957f7268f3d2dec0dca434b99433d0143cc750be770b9179019"
+content-hash = "ac9146cb36bfc2e8e726e78144e601bdc51baa8fdae71cec1c615c7f4d5ed514"
diff --git a/pyproject.toml b/pyproject.toml
index 6c7f3515..fc68e6b9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -44,9 +44,8 @@ lief = ">=0.12.3"
http-tools = ">=4.0.0"
pdfkit = ">=0.6.1"
google-play-scraper = ">=0.1.2"
-androguard = "3.4.0a1"
+androguard = ">=4.1.0"
apkid = "2.1.5"
-quark-engine = "23.7.1"
frida = ">=16.1.4"
tldextract = "3.4.4"
openstep-parser = "1.5.4"
From 18931bc79983dc99699d3b5a56a197b959f93c04 Mon Sep 17 00:00:00 2001
From: "Ajin.Abraham"
Date: Thu, 14 Mar 2024 17:36:20 -0700
Subject: [PATCH 06/19] HOTFIX: androguard bump
---
poetry.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/poetry.lock b/poetry.lock
index 7726ada7..e69a4106 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -52,13 +52,13 @@ files = [
[[package]]
name = "androguard"
-version = "4.1.0"
+version = "4.1.1"
description = "Androguard is a full python tool to play with Android files."
optional = false
python-versions = ">=3.9,<4.0"
files = [
- {file = "androguard-4.1.0-py3-none-any.whl", hash = "sha256:71523c5cd24dbafb5054aedb99d4076a9d3bf4e2dcd2b9c6993c49412122e0cb"},
- {file = "androguard-4.1.0.tar.gz", hash = "sha256:26eceafb62fe0e44750f63729e9d9dab2475d54cc2ae90430ec9134757c4f54a"},
+ {file = "androguard-4.1.1-py3-none-any.whl", hash = "sha256:6c457b5e0c007d2e32c5ac7041b625bf71f1ec39613ec4153837005a988f86a6"},
+ {file = "androguard-4.1.1.tar.gz", hash = "sha256:dffc138046f8c3aea061203afff490c77f2000345f685e1dad1780d478f3c5f6"},
]
[package.dependencies]
From d4e67866e4ab0243fd39d1ba006a33b749169774 Mon Sep 17 00:00:00 2001
From: Ajin Abraham
Date: Mon, 18 Mar 2024 18:28:11 -0700
Subject: [PATCH 07/19] Fix #2349
---
mobsf/MobSF/security.py | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/mobsf/MobSF/security.py b/mobsf/MobSF/security.py
index 27cef023..97edc7cf 100644
--- a/mobsf/MobSF/security.py
+++ b/mobsf/MobSF/security.py
@@ -137,9 +137,16 @@ def store_exec_hashes_at_first_run():
def subprocess_hook(oldfunc, *args, **kwargs):
global EXECUTABLE_HASH_MAP
- exec1 = args[0][0] # executable
+ if isinstance(args[0], str):
+ # arg is a string
+ agmtz = args[0].split()
+ exec1 = agmtz[0]
+ else:
+ # list of args
+ agmtz = args[0]
+ exec1 = agmtz[0] # executable
exec2 = None # secondary executable
- for arg in args[0]:
+ for arg in agmtz:
if arg.endswith('.jar'):
exec2 = Path(arg).as_posix()
if '/' in exec1 or '\\' in exec1:
From 34473d3dac4704ab1940e7339efe08165d192d42 Mon Sep 17 00:00:00 2001
From: Ajin Abraham
Date: Tue, 19 Mar 2024 17:31:05 -0700
Subject: [PATCH 08/19] HOTFIX: Individual image publish
---
.github/workflows/docker-latest.yml | 23 ++++++++++++++++-------
1 file changed, 16 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/docker-latest.yml b/.github/workflows/docker-latest.yml
index aa01ac6d..930b1f5e 100644
--- a/.github/workflows/docker-latest.yml
+++ b/.github/workflows/docker-latest.yml
@@ -14,22 +14,31 @@ jobs:
uses: actions/checkout@v3
-
name: Set up QEMU
- uses: docker/setup-qemu-action@v2
+ uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
+ uses: docker/setup-buildx-action@v3
-
name: Login to DockerHub
- uses: docker/login-action@v2
+ uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
-
- name: Build and push
- id: docker_build
- uses: docker/build-push-action@v4
+ name: Build and push AMD64 image
+ id: docker_build_amd
+ uses: docker/build-push-action@v5
with:
push: true
context: .
- platforms: linux/amd64,linux/arm64
+ platforms: linux/amd64
+ tags: opensecurity/mobile-security-framework-mobsf:latest
+ -
+ name: Build and push ARM64 image
+ id: docker_build_arm
+ uses: docker/build-push-action@v5
+ with:
+ push: true
+ context: .
+ platforms: linux/arm64
tags: opensecurity/mobile-security-framework-mobsf:latest
From 5a8eeee73c5f504a6c3abdf2a139a13804efdb77 Mon Sep 17 00:00:00 2001
From: "Ajin.Abraham"
Date: Fri, 22 Mar 2024 11:49:27 -0700
Subject: [PATCH 09/19] HOTFIX:[SECURITY] Fix GHSA-wfgj-wrgh-h3r3, dep bump,
docker build qa
---
.github/workflows/docker-release.yml | 23 ++--
.github/workflows/docker-test.yml | 21 ++--
mobsf/MobSF/init.py | 2 +-
mobsf/MobSF/utils.py | 40 +++++++
.../views/android/manifest_analysis.py | 8 +-
poetry.lock | 100 +++++++++---------
pyproject.toml | 2 +-
7 files changed, 128 insertions(+), 68 deletions(-)
diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml
index ca6335de..a6de4b50 100644
--- a/.github/workflows/docker-release.yml
+++ b/.github/workflows/docker-release.yml
@@ -13,13 +13,13 @@ jobs:
uses: actions/checkout@v3
-
name: Set up QEMU
- uses: docker/setup-qemu-action@v2
+ uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
+ uses: docker/setup-buildx-action@v3
-
name: Login to DockerHub
- uses: docker/login-action@v2
+ uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
@@ -34,11 +34,20 @@ jobs:
fi
echo "tag=${TAG}" >> $GITHUB_OUTPUT
-
- name: Build and push release
- id: docker_build
- uses: docker/build-push-action@v4
+ name: Build and push AMD64 image release
+ id: docker_build_amd
+ uses: docker/build-push-action@v5
with:
push: true
context: .
- platforms: linux/amd64,linux/arm64
+ platforms: linux/amd64
+ tags: ${{ steps.release.outputs.tag }}
+ -
+ name: Build and push ARM64 image release
+ id: docker_build_arm
+ uses: docker/build-push-action@v5
+ with:
+ push: true
+ context: .
+ platforms: linux/arm64
tags: ${{ steps.release.outputs.tag }}
diff --git a/.github/workflows/docker-test.yml b/.github/workflows/docker-test.yml
index 328fde56..4f3b42dd 100644
--- a/.github/workflows/docker-test.yml
+++ b/.github/workflows/docker-test.yml
@@ -13,16 +13,25 @@ jobs:
uses: actions/checkout@v3
-
name: Set up QEMU
- uses: docker/setup-qemu-action@v2
+ uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
+ uses: docker/setup-buildx-action@v3
-
- name: Build and push
- id: docker_build
- uses: docker/build-push-action@v4
+ name: Build and push AMD64 image
+ id: docker_build_amd
+ uses: docker/build-push-action@v5
with:
push: false
context: .
- platforms: linux/amd64,linux/arm64
+ platforms: linux/amd64
+ tags: opensecurity/mobile-security-framework-mobsf:latest
+ -
+ name: Build and push ARM64 image
+ id: docker_build_arm
+ uses: docker/build-push-action@v5
+ with:
+ push: false
+ context: .
+ platforms: linux/arm64
tags: opensecurity/mobile-security-framework-mobsf:latest
diff --git a/mobsf/MobSF/init.py b/mobsf/MobSF/init.py
index 6b448598..db9d1adb 100644
--- a/mobsf/MobSF/init.py
+++ b/mobsf/MobSF/init.py
@@ -10,7 +10,7 @@ from mobsf.install.windows.setup import windows_config_local
logger = logging.getLogger(__name__)
-VERSION = '3.9.5'
+VERSION = '3.9.6'
BANNER = """
__ __ _ ____ _____ _____ ___
| \/ | ___ | |__/ ___|| ___|_ _|___ // _ \
diff --git a/mobsf/MobSF/utils.py b/mobsf/MobSF/utils.py
index 5a646302..358fdce4 100755
--- a/mobsf/MobSF/utils.py
+++ b/mobsf/MobSF/utils.py
@@ -16,9 +16,11 @@ import signal
import string
import subprocess
import stat
+import socket
import sqlite3
import unicodedata
import threading
+from urllib.parse import urlparse
from pathlib import Path
from distutils.version import StrictVersion
@@ -848,3 +850,41 @@ def settings_enabled(attr):
def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
"""Generate random string."""
return ''.join(random.choice(chars) for _ in range(size))
+
+
+def valid_host(host):
+ """Check if host is valid."""
+ try:
+ prefixs = ('http://', 'https://')
+ if not host.startswith(prefixs):
+ host = f'http://{host}'
+ parsed = urlparse(host)
+ domain = parsed.netloc
+ path = parsed.path
+ if len(domain) == 0:
+ # No valid domain
+ return False
+ if len(path) > 0:
+ # Only host is allowed
+ return False
+ if ':' in domain:
+ # IPv6
+ return False
+ # Local network
+ invalid_prefix = (
+ '127.',
+ '192.',
+ '10.',
+ '172.',
+ '169',
+ '0.',
+ 'localhost')
+ if domain.startswith(invalid_prefix):
+ return False
+ ip = socket.gethostbyname(domain)
+ if ip.startswith(invalid_prefix):
+ # Resolve dns to get IP
+ return False
+ return True
+ except Exception:
+ return False
diff --git a/mobsf/StaticAnalyzer/views/android/manifest_analysis.py b/mobsf/StaticAnalyzer/views/android/manifest_analysis.py
index ae4497ab..fd347c21 100755
--- a/mobsf/StaticAnalyzer/views/android/manifest_analysis.py
+++ b/mobsf/StaticAnalyzer/views/android/manifest_analysis.py
@@ -7,7 +7,9 @@ import requests
from concurrent.futures import ThreadPoolExecutor
from mobsf.MobSF.utils import (
+ is_number,
upstream_proxy,
+ valid_host,
)
from mobsf.StaticAnalyzer.views.android import (
android_manifest_desc,
@@ -148,9 +150,11 @@ def get_browsable_activities(node, ns):
and scheme in ('http', 'https')
and host
and host != '*'):
- host = host.replace('*.', '')
+ host = host.replace('*.', '').replace('#', '')
+ if not valid_host(host):
+ continue
shost = f'{scheme}://{host}'
- if port:
+ if port and is_number(port):
c_url = f'{shost}:{port}{well_known_path}'
else:
c_url = f'{shost}{well_known_path}'
diff --git a/poetry.lock b/poetry.lock
index e69a4106..b563163a 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -100,13 +100,13 @@ test = ["delayed-assert", "factory-boy", "mock", "pytest", "pytest-cov", "pytest
[[package]]
name = "apkinspector"
-version = "1.2.1"
+version = "1.2.2"
description = "apkInspector is a tool designed to provide detailed insights into the zip structure of APK files, offering the capability to extract content and decode the AndroidManifest.xml file."
optional = false
python-versions = ">=3.5,<4.0"
files = [
- {file = "apkinspector-1.2.1-py3-none-any.whl", hash = "sha256:4736719884e92dfbe3203103ad46deb22b4683e208ae66972236a052c5d23e37"},
- {file = "apkinspector-1.2.1.tar.gz", hash = "sha256:6c1fd678246760e75f8386e6f4d9686b640cc6bd88256f0865dfacbd49e8e0dd"},
+ {file = "apkinspector-1.2.2-py3-none-any.whl", hash = "sha256:ad55200bd72eb1edeeb5fb114e3bb576ec825feef54fb8727173f1f02fa12bb7"},
+ {file = "apkinspector-1.2.2.tar.gz", hash = "sha256:ea7e56090e95eb791b593e9beedf4f1056eb269c4483558e13d5d5ef4b479c60"},
]
[[package]]
@@ -950,53 +950,53 @@ dotenv = ["python-dotenv"]
[[package]]
name = "fonttools"
-version = "4.49.0"
+version = "4.50.0"
description = "Tools to manipulate font files"
optional = false
python-versions = ">=3.8"
files = [
- {file = "fonttools-4.49.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d970ecca0aac90d399e458f0b7a8a597e08f95de021f17785fb68e2dc0b99717"},
- {file = "fonttools-4.49.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac9a745b7609f489faa65e1dc842168c18530874a5f5b742ac3dd79e26bca8bc"},
- {file = "fonttools-4.49.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ba0e00620ca28d4ca11fc700806fd69144b463aa3275e1b36e56c7c09915559"},
- {file = "fonttools-4.49.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdee3ab220283057e7840d5fb768ad4c2ebe65bdba6f75d5d7bf47f4e0ed7d29"},
- {file = "fonttools-4.49.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ce7033cb61f2bb65d8849658d3786188afd80f53dad8366a7232654804529532"},
- {file = "fonttools-4.49.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:07bc5ea02bb7bc3aa40a1eb0481ce20e8d9b9642a9536cde0218290dd6085828"},
- {file = "fonttools-4.49.0-cp310-cp310-win32.whl", hash = "sha256:86eef6aab7fd7c6c8545f3ebd00fd1d6729ca1f63b0cb4d621bccb7d1d1c852b"},
- {file = "fonttools-4.49.0-cp310-cp310-win_amd64.whl", hash = "sha256:1fac1b7eebfce75ea663e860e7c5b4a8831b858c17acd68263bc156125201abf"},
- {file = "fonttools-4.49.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:edc0cce355984bb3c1d1e89d6a661934d39586bb32191ebff98c600f8957c63e"},
- {file = "fonttools-4.49.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:83a0d9336de2cba86d886507dd6e0153df333ac787377325a39a2797ec529814"},
- {file = "fonttools-4.49.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36c8865bdb5cfeec88f5028e7e592370a0657b676c6f1d84a2108e0564f90e22"},
- {file = "fonttools-4.49.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33037d9e56e2562c710c8954d0f20d25b8386b397250d65581e544edc9d6b942"},
- {file = "fonttools-4.49.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8fb022d799b96df3eaa27263e9eea306bd3d437cc9aa981820850281a02b6c9a"},
- {file = "fonttools-4.49.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:33c584c0ef7dc54f5dd4f84082eabd8d09d1871a3d8ca2986b0c0c98165f8e86"},
- {file = "fonttools-4.49.0-cp311-cp311-win32.whl", hash = "sha256:cbe61b158deb09cffdd8540dc4a948d6e8f4d5b4f3bf5cd7db09bd6a61fee64e"},
- {file = "fonttools-4.49.0-cp311-cp311-win_amd64.whl", hash = "sha256:fc11e5114f3f978d0cea7e9853627935b30d451742eeb4239a81a677bdee6bf6"},
- {file = "fonttools-4.49.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:d647a0e697e5daa98c87993726da8281c7233d9d4ffe410812a4896c7c57c075"},
- {file = "fonttools-4.49.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f3bbe672df03563d1f3a691ae531f2e31f84061724c319652039e5a70927167e"},
- {file = "fonttools-4.49.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bebd91041dda0d511b0d303180ed36e31f4f54b106b1259b69fade68413aa7ff"},
- {file = "fonttools-4.49.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4145f91531fd43c50f9eb893faa08399816bb0b13c425667c48475c9f3a2b9b5"},
- {file = "fonttools-4.49.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ea329dafb9670ffbdf4dbc3b0e5c264104abcd8441d56de77f06967f032943cb"},
- {file = "fonttools-4.49.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c076a9e548521ecc13d944b1d261ff3d7825048c338722a4bd126d22316087b7"},
- {file = "fonttools-4.49.0-cp312-cp312-win32.whl", hash = "sha256:b607ea1e96768d13be26d2b400d10d3ebd1456343eb5eaddd2f47d1c4bd00880"},
- {file = "fonttools-4.49.0-cp312-cp312-win_amd64.whl", hash = "sha256:a974c49a981e187381b9cc2c07c6b902d0079b88ff01aed34695ec5360767034"},
- {file = "fonttools-4.49.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b85ec0bdd7bdaa5c1946398cbb541e90a6dfc51df76dfa88e0aaa41b335940cb"},
- {file = "fonttools-4.49.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:af20acbe198a8a790618ee42db192eb128afcdcc4e96d99993aca0b60d1faeb4"},
- {file = "fonttools-4.49.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d418b1fee41a1d14931f7ab4b92dc0bc323b490e41d7a333eec82c9f1780c75"},
- {file = "fonttools-4.49.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b44a52b8e6244b6548851b03b2b377a9702b88ddc21dcaf56a15a0393d425cb9"},
- {file = "fonttools-4.49.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7c7125068e04a70739dad11857a4d47626f2b0bd54de39e8622e89701836eabd"},
- {file = "fonttools-4.49.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:29e89d0e1a7f18bc30f197cfadcbef5a13d99806447c7e245f5667579a808036"},
- {file = "fonttools-4.49.0-cp38-cp38-win32.whl", hash = "sha256:9d95fa0d22bf4f12d2fb7b07a46070cdfc19ef5a7b1c98bc172bfab5bf0d6844"},
- {file = "fonttools-4.49.0-cp38-cp38-win_amd64.whl", hash = "sha256:768947008b4dc552d02772e5ebd49e71430a466e2373008ce905f953afea755a"},
- {file = "fonttools-4.49.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:08877e355d3dde1c11973bb58d4acad1981e6d1140711230a4bfb40b2b937ccc"},
- {file = "fonttools-4.49.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fdb54b076f25d6b0f0298dc706acee5052de20c83530fa165b60d1f2e9cbe3cb"},
- {file = "fonttools-4.49.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0af65c720520710cc01c293f9c70bd69684365c6015cc3671db2b7d807fe51f2"},
- {file = "fonttools-4.49.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f255ce8ed7556658f6d23f6afd22a6d9bbc3edb9b96c96682124dc487e1bf42"},
- {file = "fonttools-4.49.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d00af0884c0e65f60dfaf9340e26658836b935052fdd0439952ae42e44fdd2be"},
- {file = "fonttools-4.49.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:263832fae27481d48dfafcc43174644b6706639661e242902ceb30553557e16c"},
- {file = "fonttools-4.49.0-cp39-cp39-win32.whl", hash = "sha256:0404faea044577a01bb82d47a8fa4bc7a54067fa7e324785dd65d200d6dd1133"},
- {file = "fonttools-4.49.0-cp39-cp39-win_amd64.whl", hash = "sha256:b050d362df50fc6e38ae3954d8c29bf2da52be384649ee8245fdb5186b620836"},
- {file = "fonttools-4.49.0-py3-none-any.whl", hash = "sha256:af281525e5dd7fa0b39fb1667b8d5ca0e2a9079967e14c4bfe90fd1cd13e0f18"},
- {file = "fonttools-4.49.0.tar.gz", hash = "sha256:ebf46e7f01b7af7861310417d7c49591a85d99146fc23a5ba82fdb28af156321"},
+ {file = "fonttools-4.50.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:effd303fb422f8ce06543a36ca69148471144c534cc25f30e5be752bc4f46736"},
+ {file = "fonttools-4.50.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7913992ab836f621d06aabac118fc258b9947a775a607e1a737eb3a91c360335"},
+ {file = "fonttools-4.50.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e0a1c5bd2f63da4043b63888534b52c5a1fd7ae187c8ffc64cbb7ae475b9dab"},
+ {file = "fonttools-4.50.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d40fc98540fa5360e7ecf2c56ddf3c6e7dd04929543618fd7b5cc76e66390562"},
+ {file = "fonttools-4.50.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fff65fbb7afe137bac3113827855e0204482727bddd00a806034ab0d3951d0d"},
+ {file = "fonttools-4.50.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1aeae3dd2ee719074a9372c89ad94f7c581903306d76befdaca2a559f802472"},
+ {file = "fonttools-4.50.0-cp310-cp310-win32.whl", hash = "sha256:e9623afa319405da33b43c85cceb0585a6f5d3a1d7c604daf4f7e1dd55c03d1f"},
+ {file = "fonttools-4.50.0-cp310-cp310-win_amd64.whl", hash = "sha256:778c5f43e7e654ef7fe0605e80894930bc3a7772e2f496238e57218610140f54"},
+ {file = "fonttools-4.50.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3dfb102e7f63b78c832e4539969167ffcc0375b013080e6472350965a5fe8048"},
+ {file = "fonttools-4.50.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9e58fe34cb379ba3d01d5d319d67dd3ce7ca9a47ad044ea2b22635cd2d1247fc"},
+ {file = "fonttools-4.50.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c673ab40d15a442a4e6eb09bf007c1dda47c84ac1e2eecbdf359adacb799c24"},
+ {file = "fonttools-4.50.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b3ac35cdcd1a4c90c23a5200212c1bb74fa05833cc7c14291d7043a52ca2aaa"},
+ {file = "fonttools-4.50.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8844e7a2c5f7ecf977e82eb6b3014f025c8b454e046d941ece05b768be5847ae"},
+ {file = "fonttools-4.50.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f849bd3c5c2249b49c98eca5aaebb920d2bfd92b3c69e84ca9bddf133e9f83f0"},
+ {file = "fonttools-4.50.0-cp311-cp311-win32.whl", hash = "sha256:39293ff231b36b035575e81c14626dfc14407a20de5262f9596c2cbb199c3625"},
+ {file = "fonttools-4.50.0-cp311-cp311-win_amd64.whl", hash = "sha256:c33d5023523b44d3481624f840c8646656a1def7630ca562f222eb3ead16c438"},
+ {file = "fonttools-4.50.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b4a886a6dbe60100ba1cd24de962f8cd18139bd32808da80de1fa9f9f27bf1dc"},
+ {file = "fonttools-4.50.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b2ca1837bfbe5eafa11313dbc7edada79052709a1fffa10cea691210af4aa1fa"},
+ {file = "fonttools-4.50.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0493dd97ac8977e48ffc1476b932b37c847cbb87fd68673dee5182004906828"},
+ {file = "fonttools-4.50.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77844e2f1b0889120b6c222fc49b2b75c3d88b930615e98893b899b9352a27ea"},
+ {file = "fonttools-4.50.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3566bfb8c55ed9100afe1ba6f0f12265cd63a1387b9661eb6031a1578a28bad1"},
+ {file = "fonttools-4.50.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:35e10ddbc129cf61775d58a14f2d44121178d89874d32cae1eac722e687d9019"},
+ {file = "fonttools-4.50.0-cp312-cp312-win32.whl", hash = "sha256:cc8140baf9fa8f9b903f2b393a6c413a220fa990264b215bf48484f3d0bf8710"},
+ {file = "fonttools-4.50.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ccc85fd96373ab73c59833b824d7a73846670a0cb1f3afbaee2b2c426a8f931"},
+ {file = "fonttools-4.50.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e270a406219af37581d96c810172001ec536e29e5593aa40d4c01cca3e145aa6"},
+ {file = "fonttools-4.50.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac2463de667233372e9e1c7e9de3d914b708437ef52a3199fdbf5a60184f190c"},
+ {file = "fonttools-4.50.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47abd6669195abe87c22750dbcd366dc3a0648f1b7c93c2baa97429c4dc1506e"},
+ {file = "fonttools-4.50.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:074841375e2e3d559aecc86e1224caf78e8b8417bb391e7d2506412538f21adc"},
+ {file = "fonttools-4.50.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0743fd2191ad7ab43d78cd747215b12033ddee24fa1e088605a3efe80d6984de"},
+ {file = "fonttools-4.50.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3d7080cce7be5ed65bee3496f09f79a82865a514863197ff4d4d177389e981b0"},
+ {file = "fonttools-4.50.0-cp38-cp38-win32.whl", hash = "sha256:a467ba4e2eadc1d5cc1a11d355abb945f680473fbe30d15617e104c81f483045"},
+ {file = "fonttools-4.50.0-cp38-cp38-win_amd64.whl", hash = "sha256:f77e048f805e00870659d6318fd89ef28ca4ee16a22b4c5e1905b735495fc422"},
+ {file = "fonttools-4.50.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b6245eafd553c4e9a0708e93be51392bd2288c773523892fbd616d33fd2fda59"},
+ {file = "fonttools-4.50.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a4062cc7e8de26f1603323ef3ae2171c9d29c8a9f5e067d555a2813cd5c7a7e0"},
+ {file = "fonttools-4.50.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34692850dfd64ba06af61e5791a441f664cb7d21e7b544e8f385718430e8f8e4"},
+ {file = "fonttools-4.50.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678dd95f26a67e02c50dcb5bf250f95231d455642afbc65a3b0bcdacd4e4dd38"},
+ {file = "fonttools-4.50.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4f2ce7b0b295fe64ac0a85aef46a0f2614995774bd7bc643b85679c0283287f9"},
+ {file = "fonttools-4.50.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d346f4dc2221bfb7ab652d1e37d327578434ce559baf7113b0f55768437fe6a0"},
+ {file = "fonttools-4.50.0-cp39-cp39-win32.whl", hash = "sha256:a51eeaf52ba3afd70bf489be20e52fdfafe6c03d652b02477c6ce23c995222f4"},
+ {file = "fonttools-4.50.0-cp39-cp39-win_amd64.whl", hash = "sha256:8639be40d583e5d9da67795aa3eeeda0488fb577a1d42ae11a5036f18fb16d93"},
+ {file = "fonttools-4.50.0-py3-none-any.whl", hash = "sha256:48fa36da06247aa8282766cfd63efff1bb24e55f020f29a335939ed3844d20d3"},
+ {file = "fonttools-4.50.0.tar.gz", hash = "sha256:fa5cf61058c7dbb104c2ac4e782bf1b2016a8cf2f69de6e4dd6a865d2c969bb5"},
]
[package.extras]
@@ -2537,15 +2537,13 @@ PyQt5-sip = ">=12.13,<13"
[[package]]
name = "pyqt5-qt5"
-version = "5.15.2"
+version = "5.15.13"
description = "The subset of a Qt installation needed by PyQt5."
optional = false
python-versions = "*"
files = [
- {file = "PyQt5_Qt5-5.15.2-py3-none-macosx_10_13_intel.whl", hash = "sha256:76980cd3d7ae87e3c7a33bfebfaee84448fd650bad6840471d6cae199b56e154"},
- {file = "PyQt5_Qt5-5.15.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:1988f364ec8caf87a6ee5d5a3a5210d57539988bf8e84714c7d60972692e2f4a"},
- {file = "PyQt5_Qt5-5.15.2-py3-none-win32.whl", hash = "sha256:9cc7a768b1921f4b982ebc00a318ccb38578e44e45316c7a4a850e953e1dd327"},
- {file = "PyQt5_Qt5-5.15.2-py3-none-win_amd64.whl", hash = "sha256:750b78e4dba6bdf1607febedc08738e318ea09e9b10aea9ff0d73073f11f6962"},
+ {file = "PyQt5_Qt5-5.15.13-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:92575a9e96a27c4ed67c56c7048ded7461a1655d5d21f0e05064664e6e9fcbdf"},
+ {file = "PyQt5_Qt5-5.15.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:141859f2ffe04cc6c5db970e2b6ad9f98897805d886a14c52614e3799daab6d6"},
]
[[package]]
diff --git a/pyproject.toml b/pyproject.toml
index fc68e6b9..2dcdc6a8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "mobsf"
-version = "3.9.5"
+version = "3.9.6"
description = "Mobile Security Framework (MobSF) is an automated, all-in-one mobile application (Android/iOS/Windows) pen-testing, malware analysis and security assessment framework capable of performing static and dynamic analysis."
keywords = ["mobsf", "mobile security framework", "mobile security", "security tool", "static analysis", "dynamic analysis", "malware analysis"]
authors = ["Ajin Abraham "]
From 4417da03ba8ec23adae91a5d2c78ec1287e56173 Mon Sep 17 00:00:00 2001
From: Ajin Abraham
Date: Fri, 22 Mar 2024 13:52:04 -0700
Subject: [PATCH 10/19] poetry pyqt5 fixes (#2362)
* poetry pyqt5 fixes
* QA
* fix
* Cert analysis qa
* QA
* pin pyqt5
---
.../views/android/cert_analysis.py | 62 +++++++++++++++++--
poetry.lock | 10 +--
pyproject.toml | 3 +-
3 files changed, 66 insertions(+), 9 deletions(-)
diff --git a/mobsf/StaticAnalyzer/views/android/cert_analysis.py b/mobsf/StaticAnalyzer/views/android/cert_analysis.py
index aa0944b7..bb0df088 100755
--- a/mobsf/StaticAnalyzer/views/android/cert_analysis.py
+++ b/mobsf/StaticAnalyzer/views/android/cert_analysis.py
@@ -8,9 +8,7 @@ import re
import subprocess
from pathlib import Path
-from androguard.util import get_certificate_name_string
-
-from asn1crypto import x509
+import asn1crypto
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
@@ -40,6 +38,62 @@ HASH_FUNCS = {
}
+def get_certificate_name_string(name, short=False, delimiter=', '):
+ """
+ Function from androguard.
+
+ licensed under the Apache License, Version 2.0.
+ https://github.com/androguard/androguard/blob/master/androguard/util.py
+ Format the Name type of a X509 Certificate in a human readable form.
+
+ :param name: Name object to return the DN from
+ :param short: Use short form (default: False)
+ :param delimiter: Delimiter string or character between
+ two parts (default: ', ')
+
+ :type name: dict or :class:`asn1crypto.x509.Name`
+ :type short: boolean
+ :type delimiter: str
+
+ :rtype: str
+ """
+ if isinstance(name, asn1crypto.x509.Name):
+ name = name.native
+
+ # For the shortform, we have a lookup table
+ # See RFC4514 for more details
+ _ = {
+ 'business_category': ('businessCategory', 'businessCategory'),
+ 'serial_number': ('serialNumber', 'serialNumber'),
+ 'country_name': ('C', 'countryName'),
+ 'postal_code': ('postalCode', 'postalCode'),
+ 'state_or_province_name': ('ST', 'stateOrProvinceName'),
+ 'locality_name': ('L', 'localityName'),
+ 'street_address': ('street', 'streetAddress'),
+ 'organization_name': ('O', 'organizationName'),
+ 'organizational_unit_name': ('OU', 'organizationalUnitName'),
+ 'title': ('title', 'title'),
+ 'common_name': ('CN', 'commonName'),
+ 'initials': ('initials', 'initials'),
+ 'generation_qualifier': ('generationQualifier', 'generationQualifier'),
+ 'surname': ('SN', 'surname'),
+ 'given_name': ('GN', 'givenName'),
+ 'name': ('name', 'name'),
+ 'pseudonym': ('pseudonym', 'pseudonym'),
+ 'dn_qualifier': ('dnQualifier', 'dnQualifier'),
+ 'telephone_number': ('telephoneNumber', 'telephoneNumber'),
+ 'email_address': ('E', 'emailAddress'),
+ 'domain_component': ('DC', 'domainComponent'),
+ 'name_distinguisher': ('nameDistinguisher', 'nameDistinguisher'),
+ 'organization_identifier': (
+ 'organizationIdentifier', 'organizationIdentifier'),
+ }
+ return delimiter.join(
+ ['{}={}'.format(
+ _.get(attr, (attr, attr))[0 if short else 1],
+ name[attr]) for attr in name])
+
+
def get_hardcoded_cert_keystore(files):
"""Returns the hardcoded certificate keystore."""
try:
@@ -70,7 +124,7 @@ def get_hardcoded_cert_keystore(files):
def get_cert_details(data):
"""Get certificate details."""
certlist = []
- x509_cert = x509.Certificate.load(data)
+ x509_cert = asn1crypto.x509.Certificate.load(data)
subject = get_certificate_name_string(x509_cert.subject, short=True)
certlist.append(f'X.509 Subject: {subject}')
certlist.append(f'Signature Algorithm: {x509_cert.signature_algo}')
diff --git a/poetry.lock b/poetry.lock
index b563163a..25e5d02b 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -2537,13 +2537,15 @@ PyQt5-sip = ">=12.13,<13"
[[package]]
name = "pyqt5-qt5"
-version = "5.15.13"
+version = "5.15.2"
description = "The subset of a Qt installation needed by PyQt5."
optional = false
python-versions = "*"
files = [
- {file = "PyQt5_Qt5-5.15.13-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:92575a9e96a27c4ed67c56c7048ded7461a1655d5d21f0e05064664e6e9fcbdf"},
- {file = "PyQt5_Qt5-5.15.13-py3-none-macosx_11_0_arm64.whl", hash = "sha256:141859f2ffe04cc6c5db970e2b6ad9f98897805d886a14c52614e3799daab6d6"},
+ {file = "PyQt5_Qt5-5.15.2-py3-none-macosx_10_13_intel.whl", hash = "sha256:76980cd3d7ae87e3c7a33bfebfaee84448fd650bad6840471d6cae199b56e154"},
+ {file = "PyQt5_Qt5-5.15.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:1988f364ec8caf87a6ee5d5a3a5210d57539988bf8e84714c7d60972692e2f4a"},
+ {file = "PyQt5_Qt5-5.15.2-py3-none-win32.whl", hash = "sha256:9cc7a768b1921f4b982ebc00a318ccb38578e44e45316c7a4a850e953e1dd327"},
+ {file = "PyQt5_Qt5-5.15.2-py3-none-win_amd64.whl", hash = "sha256:750b78e4dba6bdf1607febedc08738e318ea09e9b10aea9ff0d73073f11f6962"},
]
[[package]]
@@ -3629,4 +3631,4 @@ cffi = ["cffi (>=1.11)"]
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
-content-hash = "ac9146cb36bfc2e8e726e78144e601bdc51baa8fdae71cec1c615c7f4d5ed514"
+content-hash = "e077f3a528867790025d5079bc7af606f8a2d71b365a488ba4159ad31bae20dc"
diff --git a/pyproject.toml b/pyproject.toml
index 2dcdc6a8..740cf37f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -55,7 +55,8 @@ apksigtool = "0.1.0"
tzdata = "^2023.3"
libsast = "^2.0.0"
paramiko = "^3.3.1"
-
+# For androguard/apkinspector
+pyqt5-qt5 = {version = "=5.15.2"}
[build-system]
requires = ["poetry-core"]
From 5056a7d660e9b749d1251ec180066822bc25f14c Mon Sep 17 00:00:00 2001
From: Ajin Abraham
Date: Fri, 22 Mar 2024 15:30:05 -0700
Subject: [PATCH 11/19] HOTFIX: Remove Androguard dependency use only features
required by MobSF (#2363)
This PR strips out androguard and it's dependencies from MobSF.
Extract androguard related functions used by MobSF.
Some dependencies such as pyQt5 from apkinspector is breaking the ARM64 docker image.
This should address that issue.
In future, we will have to copy over any fixes to axml, apk, public, types from androguard and ZipEntry from apkinspector.
We won't be adding linting to these files. The extracted functions will be considered as an external tool.
---
LICENSES/apkinspector.txt | 201 ++
mobsf/MobSF/init.py | 2 +-
mobsf/StaticAnalyzer/tools/androguard4/apk.py | 2201 ++++++++++++
.../StaticAnalyzer/tools/androguard4/axml.py | 3016 +++++++++++++++++
.../tools/androguard4/resources/public.py | 34 +
.../tools/androguard4/resources/public.xml | 2861 ++++++++++++++++
.../StaticAnalyzer/tools/androguard4/types.py | 52 +
.../tools/androguard4/zipfile.py | 566 ++++
mobsf/StaticAnalyzer/views/android/app.py | 6 +-
.../views/android/cert_analysis.py | 59 +-
.../views/android/icon_analysis.py | 11 +-
poetry.lock | 1123 +-----
pyproject.toml | 6 +-
13 files changed, 8944 insertions(+), 1194 deletions(-)
create mode 100644 LICENSES/apkinspector.txt
create mode 100644 mobsf/StaticAnalyzer/tools/androguard4/apk.py
create mode 100644 mobsf/StaticAnalyzer/tools/androguard4/axml.py
create mode 100644 mobsf/StaticAnalyzer/tools/androguard4/resources/public.py
create mode 100644 mobsf/StaticAnalyzer/tools/androguard4/resources/public.xml
create mode 100644 mobsf/StaticAnalyzer/tools/androguard4/types.py
create mode 100644 mobsf/StaticAnalyzer/tools/androguard4/zipfile.py
diff --git a/LICENSES/apkinspector.txt b/LICENSES/apkinspector.txt
new file mode 100644
index 00000000..f49a4e16
--- /dev/null
+++ b/LICENSES/apkinspector.txt
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
diff --git a/mobsf/MobSF/init.py b/mobsf/MobSF/init.py
index db9d1adb..b91daf7f 100644
--- a/mobsf/MobSF/init.py
+++ b/mobsf/MobSF/init.py
@@ -10,7 +10,7 @@ from mobsf.install.windows.setup import windows_config_local
logger = logging.getLogger(__name__)
-VERSION = '3.9.6'
+VERSION = '3.9.7'
BANNER = """
__ __ _ ____ _____ _____ ___
| \/ | ___ | |__/ ___|| ___|_ _|___ // _ \
diff --git a/mobsf/StaticAnalyzer/tools/androguard4/apk.py b/mobsf/StaticAnalyzer/tools/androguard4/apk.py
new file mode 100644
index 00000000..dbb7f203
--- /dev/null
+++ b/mobsf/StaticAnalyzer/tools/androguard4/apk.py
@@ -0,0 +1,2201 @@
+# -*- coding: utf_8 -*-
+# flake8: noqa
+# Androguard
+
+from .axml import ARSCParser, AXMLPrinter, ARSCResTableConfig
+from .zipfile import ZipEntry
+
+# Python core
+import io
+from zlib import crc32
+import os
+import re
+import binascii
+import zipfile
+from struct import unpack
+import hashlib
+import asn1crypto
+import logging
+
+# External dependecies
+import lxml.sax
+from xml.dom.pulldom import SAX2DOM
+# Used for reading Certificates
+from asn1crypto import cms, x509, keys
+
+logger = logging.getLogger(__name__)
+logger.setLevel(level=logging.INFO)
+
+NS_ANDROID_URI = 'http://schemas.android.com/apk/res/android'
+NS_ANDROID = '{{{}}}'.format(NS_ANDROID_URI) # Namespace as used by etree
+
+
+def get_certificate_name_string(name, short=False, delimiter=', '):
+ """
+ Function from androguard.
+
+ licensed under the Apache License, Version 2.0.
+ https://github.com/androguard/androguard/blob/master/androguard/util.py
+ Format the Name type of a X509 Certificate in a human readable form.
+
+ :param name: Name object to return the DN from
+ :param short: Use short form (default: False)
+ :param delimiter: Delimiter string or character between
+ two parts (default: ', ')
+
+ :type name: dict or :class:`asn1crypto.x509.Name`
+ :type short: boolean
+ :type delimiter: str
+
+ :rtype: str
+ """
+ if isinstance(name, asn1crypto.x509.Name):
+ name = name.native
+
+ # For the shortform, we have a lookup table
+ # See RFC4514 for more details
+ _ = {
+ 'business_category': ('businessCategory', 'businessCategory'),
+ 'serial_number': ('serialNumber', 'serialNumber'),
+ 'country_name': ('C', 'countryName'),
+ 'postal_code': ('postalCode', 'postalCode'),
+ 'state_or_province_name': ('ST', 'stateOrProvinceName'),
+ 'locality_name': ('L', 'localityName'),
+ 'street_address': ('street', 'streetAddress'),
+ 'organization_name': ('O', 'organizationName'),
+ 'organizational_unit_name': ('OU', 'organizationalUnitName'),
+ 'title': ('title', 'title'),
+ 'common_name': ('CN', 'commonName'),
+ 'initials': ('initials', 'initials'),
+ 'generation_qualifier': ('generationQualifier', 'generationQualifier'),
+ 'surname': ('SN', 'surname'),
+ 'given_name': ('GN', 'givenName'),
+ 'name': ('name', 'name'),
+ 'pseudonym': ('pseudonym', 'pseudonym'),
+ 'dn_qualifier': ('dnQualifier', 'dnQualifier'),
+ 'telephone_number': ('telephoneNumber', 'telephoneNumber'),
+ 'email_address': ('E', 'emailAddress'),
+ 'domain_component': ('DC', 'domainComponent'),
+ 'name_distinguisher': ('nameDistinguisher', 'nameDistinguisher'),
+ 'organization_identifier': (
+ 'organizationIdentifier', 'organizationIdentifier'),
+ }
+ return delimiter.join(
+ ['{}={}'.format(
+ _.get(attr, (attr, attr))[0 if short else 1],
+ name[attr]) for attr in name])
+
+
+def parse_lxml_dom(tree):
+ handler = SAX2DOM()
+ lxml.sax.saxify(tree, handler)
+ return handler.document
+
+
+class Error(Exception):
+ """Base class for exceptions in this module."""
+ pass
+
+
+class FileNotPresent(Error):
+ pass
+
+
+class BrokenAPKError(Error):
+ pass
+
+
+def _dump_additional_attributes(additional_attributes):
+ """ try to parse additional attributes, but ends up to hexdump if the scheme is unknown """
+
+ attributes_raw = io.BytesIO(additional_attributes)
+ attributes_hex = binascii.hexlify(additional_attributes)
+
+ if not len(additional_attributes):
+ return attributes_hex
+
+ len_attribute, = unpack('= 0x7fffffff:
+ max_sdk_str = "0x%x" % self.maxSDK
+
+ return "\n".join([
+ 'signer minSDK : {:d}'.format(self.minSDK),
+ 'signer maxSDK : {:s}'.format(max_sdk_str),
+ base_str
+ ])
+
+
+class APKV2Signer:
+ """
+ This class holds all data associated with an APK V2 SigningBlock signer.
+ source : https://source.android.com/security/apksigning/v2.html
+ """
+
+ def __init__(self):
+ self._bytes = None
+ self.signed_data = None
+ self.signatures = None
+ self.public_key = None
+
+ def __str__(self):
+ return "\n".join([
+ '{:s}'.format(str(self.signed_data)),
+ 'signatures : {}'.format(_dump_digests_or_signatures(self.signatures)),
+ 'public key : {}'.format(binascii.hexlify(self.public_key)),
+ ])
+
+
+class APKV3Signer(APKV2Signer):
+ """
+ This class holds all data associated with an APK V3 SigningBlock signer.
+ source : https://source.android.com/security/apksigning/v3.html
+ """
+
+ def __init__(self):
+ super().__init__()
+ self.minSDK = None
+ self.maxSDK = None
+
+ def __str__(self):
+
+ base_str = super().__str__()
+
+ # maxSDK is set to a negative value if there is no upper bound on the sdk targeted
+ max_sdk_str = "%d" % self.maxSDK
+ if self.maxSDK >= 0x7fffffff:
+ max_sdk_str = "0x%x" % self.maxSDK
+
+ return "\n".join([
+ 'signer minSDK : {:d}'.format(self.minSDK),
+ 'signer maxSDK : {:s}'.format(max_sdk_str),
+ base_str
+ ])
+
+
+class APK:
+ # Constants in ZipFile
+ _PK_END_OF_CENTRAL_DIR = b"\x50\x4b\x05\x06"
+ _PK_CENTRAL_DIR = b"\x50\x4b\x01\x02"
+
+ # Constants in the APK Signature Block
+ _APK_SIG_MAGIC = b"APK Sig Block 42"
+ _APK_SIG_KEY_V2_SIGNATURE = 0x7109871a
+ _APK_SIG_KEY_V3_SIGNATURE = 0xf05368c0
+ _APK_SIG_ATTR_V2_STRIPPING_PROTECTION = 0xbeeff00d
+
+ _APK_SIG_ALGO_IDS = {
+ 0x0101 : "RSASSA-PSS with SHA2-256 digest, SHA2-256 MGF1, 32 bytes of salt, trailer: 0xbc",
+ 0x0102 : "RSASSA-PSS with SHA2-512 digest, SHA2-512 MGF1, 64 bytes of salt, trailer: 0xbc",
+ 0x0103 : "RSASSA-PKCS1-v1_5 with SHA2-256 digest.", # This is for build systems which require deterministic signatures.
+ 0x0104 : "RSASSA-PKCS1-v1_5 with SHA2-512 digest.", # This is for build systems which require deterministic signatures.
+ 0x0201 : "ECDSA with SHA2-256 digest",
+ 0x0202 : "ECDSA with SHA2-512 digest",
+ 0x0301 : "DSA with SHA2-256 digest",
+ }
+
+ __no_magic = False
+
+ def __init__(self, filename, raw=False, magic_file=None, skip_analysis=False, testzip=False):
+ """
+ This class can access to all elements in an APK file
+
+ example::
+
+ APK("myfile.apk")
+ APK(read("myfile.apk"), raw=True)
+
+ :param filename: specify the path of the file, or raw data
+ :param raw: specify if the filename is a path or raw data (optional)
+ :param magic_file: specify the magic file (not used anymore - legacy only)
+ :param skip_analysis: Skip the analysis, e.g. no manifest files are read. (default: False)
+ :param testzip: Test the APK for integrity, e.g. if the ZIP file is broken. Throw an exception on failure (default False)
+
+ :type filename: string
+ :type raw: boolean
+ :type magic_file: string
+ :type skip_analysis: boolean
+ :type testzip: boolean
+
+ """
+ if magic_file:
+ logger.warning("You set magic_file but this parameter is actually unused. You should remove it.")
+
+ self.filename = filename
+
+ self.xml = {}
+ self.axml = {}
+ self.arsc = {}
+
+ self.package = ""
+ self.androidversion = {}
+ self.permissions = []
+ self.uses_permissions = []
+ self.declared_permissions = {}
+ self.valid_apk = False
+
+ self._is_signed_v2 = None
+ self._is_signed_v3 = None
+ self._v2_blocks = {}
+ self._v2_signing_data = None
+ self._v3_signing_data = None
+
+ self._files = {}
+ self.files_crc32 = {}
+
+ if raw is True:
+ self.__raw = filename
+ self._sha256 = hashlib.sha256(self.__raw).hexdigest()
+ # Set the filename to something sane
+ self.filename = "raw_apk_sha256:{}".format(self._sha256)
+ self.zip = ZipEntry.parse(io.BytesIO(self.__raw), True)
+ else:
+ self.zip = ZipEntry.parse(filename, False)
+ self.__raw = self.zip.zip.getvalue()
+
+ if testzip:
+ logger.info("Testing zip file integrity, this might take a while...")
+ # Test the zipfile for integrity before continuing.
+ # This process might be slow, as the whole file is read.
+ # Therefore it is possible to enable it as a separate feature.
+ #
+ # A short benchmark showed, that testing the zip takes about 10 times longer!
+ # e.g. normal zip loading (skip_analysis=True) takes about 0.01s, where
+ # testzip takes 0.1s!
+ test_zip = zipfile.ZipFile(io.BytesIO(self.__raw), mode="r")
+ ret = test_zip.testzip()
+ if ret is not None:
+ # we could print the filename here, but there are zip which are so broken
+ # That the filename is either very very long or does not make any sense.
+ # Thus we do not do it, the user might find out by using other tools.
+ raise BrokenAPKError("The APK is probably broken: testzip returned an error.")
+
+ if not skip_analysis:
+ self._apk_analysis()
+
+ @staticmethod
+ def _ns(name):
+ """
+ return the name including the Android namespace URI
+ """
+ return NS_ANDROID + name
+
+ def _apk_analysis(self):
+ """
+ Run analysis on the APK file.
+
+ This method is usually called by __init__ except if skip_analysis is False.
+ It will then parse the AndroidManifest.xml and set all fields in the APK class which can be
+ extracted from the Manifest.
+ """
+ i = "AndroidManifest.xml"
+ logger.info("Starting analysis on {}".format(i))
+ try:
+ manifest_data = self.zip.read(i)
+ except KeyError:
+ logger.warning("Missing AndroidManifest.xml. Is this an APK file?")
+ else:
+ ap = AXMLPrinter(manifest_data)
+
+ if not ap.is_valid():
+ logger.error("Error while parsing AndroidManifest.xml - is the file valid?")
+ return
+
+ self.axml[i] = ap
+ self.xml[i] = self.axml[i].get_xml_obj()
+
+ if self.axml[i].is_packed():
+ logger.warning("XML Seems to be packed, operations on the AndroidManifest.xml might fail.")
+
+ if self.xml[i] is not None:
+ if self.xml[i].tag != "manifest":
+ logger.error("AndroidManifest.xml does not start with a tag! Is this a valid APK?")
+ return
+
+ self.package = self.get_attribute_value("manifest", "package")
+ self.androidversion["Code"] = self.get_attribute_value("manifest", "versionCode")
+ self.androidversion["Name"] = self.get_attribute_value("manifest", "versionName")
+ permission = list(self.get_all_attribute_value("uses-permission", "name"))
+ self.permissions = list(set(self.permissions + permission))
+
+ for uses_permission in self.find_tags("uses-permission"):
+ self.uses_permissions.append([
+ self.get_value_from_tag(uses_permission, "name"),
+ self._get_permission_maxsdk(uses_permission)
+ ])
+
+ # getting details of the declared permissions
+ for d_perm_item in self.find_tags('permission'):
+ d_perm_name = self._get_res_string_value(
+ str(self.get_value_from_tag(d_perm_item, "name")))
+ d_perm_label = self._get_res_string_value(
+ str(self.get_value_from_tag(d_perm_item, "label")))
+ d_perm_description = self._get_res_string_value(
+ str(self.get_value_from_tag(d_perm_item, "description")))
+ d_perm_permissionGroup = self._get_res_string_value(
+ str(self.get_value_from_tag(d_perm_item, "permissionGroup")))
+ d_perm_protectionLevel = self._get_res_string_value(
+ str(self.get_value_from_tag(d_perm_item, "protectionLevel")))
+
+ d_perm_details = {
+ "label": d_perm_label,
+ "description": d_perm_description,
+ "permissionGroup": d_perm_permissionGroup,
+ "protectionLevel": d_perm_protectionLevel,
+ }
+ self.declared_permissions[d_perm_name] = d_perm_details
+
+ self.valid_apk = True
+ logger.debug("APK file was successfully validated!")
+
+ # self.permission_module = androconf.load_api_specific_resource_module(
+ # "aosp_permissions", self.get_target_sdk_version())
+ # self.permission_module_min_sdk = androconf.load_api_specific_resource_module(
+ # "aosp_permissions", self.get_min_sdk_version())
+
+ def __getstate__(self):
+ """
+ Function for pickling APK Objects.
+
+ We remove the zip from the Object, as it is not pickable
+ And it does not make any sense to pickle it anyways.
+
+ :returns: the picklable APK Object without zip.
+ """
+ # Upon pickling, we need to remove the ZipFile
+ x = self.__dict__
+ x['axml'] = str(x['axml'])
+ x['xml'] = str(x['xml'])
+ del x['zip']
+
+ return x
+
+ def __setstate__(self, state):
+ """
+ Load a pickled APK Object and restore the state
+
+ We load the zip file back by reading __raw from the Object.
+
+ :param state: pickled state
+ """
+ self.__dict__ = state
+
+ self.zip = zipfile.ZipFile(io.BytesIO(self.get_raw()), mode="r")
+
+ def _get_res_string_value(self, string):
+ if not string.startswith('@string/'):
+ return string
+ string_key = string[9:]
+
+ res_parser = self.get_android_resources()
+ if not res_parser:
+ return ''
+ string_value = ''
+ for package_name in res_parser.get_packages_names():
+ extracted_values = res_parser.get_string(package_name, string_key)
+ if extracted_values:
+ string_value = extracted_values[1]
+ break
+ return string_value
+
+ def _get_permission_maxsdk(self, item):
+ maxSdkVersion = None
+ try:
+ maxSdkVersion = int(self.get_value_from_tag(item, "maxSdkVersion"))
+ except ValueError:
+ logger.warning(str(maxSdkVersion) + ' is not a valid value for maxSdkVersion')
+ except TypeError:
+ pass
+ return maxSdkVersion
+
+ def is_valid_APK(self):
+ """
+ Return true if the APK is valid, false otherwise.
+ An APK is seen as valid, if the AndroidManifest.xml could be successful parsed.
+ This does not mean that the APK has a valid signature nor that the APK
+ can be installed on an Android system.
+
+ :rtype: boolean
+ """
+ return self.valid_apk
+
+ def get_filename(self):
+ """
+ Return the filename of the APK
+
+ :rtype: :class:`str`
+ """
+ return self.filename
+
+ def get_app_name(self):
+ """
+ Return the appname of the APK
+
+ This name is read from the AndroidManifest.xml
+ using the application android:label.
+ If no label exists, the android:label of the main activity is used.
+
+ If there is also no main activity label, an empty string is returned.
+
+ :rtype: :class:`str`
+ """
+
+ app_name = self.get_attribute_value('application', 'label')
+ if app_name is None:
+ activities = self.get_main_activities()
+ main_activity_name = None
+ if len(activities) > 0:
+ main_activity_name = activities.pop()
+
+ # FIXME: would need to use _format_value inside get_attribute_value for each returned name!
+ # For example, as the activity name might be foobar.foo.bar but inside the activity it is only .bar
+ app_name = self.get_attribute_value('activity', 'label', name=main_activity_name)
+
+ if app_name is None:
+ # No App name set
+ # TODO return packagename instead?
+ logger.warning("It looks like that no app name is set for the main activity!")
+ return ""
+
+ if app_name.startswith("@"):
+ res_parser = self.get_android_resources()
+ if not res_parser:
+ # TODO: What should be the correct return value here?
+ return app_name
+
+ res_id, package = res_parser.parse_id(app_name)
+
+ # If the package name is the same as the APK package,
+ # we should be able to resolve the ID.
+ if package and package != self.get_package():
+ if package == 'android':
+ # TODO: we can not resolve this, as we lack framework-res.apk
+ # one exception would be when parsing framework-res.apk directly.
+ logger.warning("Resource ID with android package name encountered! "
+ "Will not resolve, framework-res.apk would be required.")
+ return app_name
+ else:
+ # TODO should look this up, might be in the resources
+ logger.warning("Resource ID with Package name '{}' encountered! Will not resolve".format(package))
+ return app_name
+
+ try:
+ app_name = res_parser.get_resolved_res_configs(
+ res_id,
+ ARSCResTableConfig.default_config())[0][1]
+ except Exception as e:
+ logger.warning("Exception selecting app name: %s" % e)
+ return app_name
+
+ def get_app_icon(self, max_dpi=65536):
+ """
+ Return the first icon file name, which density is not greater than max_dpi,
+ unless exact icon resolution is set in the manifest, in which case
+ return the exact file.
+
+ This information is read from the AndroidManifest.xml
+
+ From https://developer.android.com/guide/practices/screens_support.html
+ and https://developer.android.com/ndk/reference/group___configuration.html
+
+ * DEFAULT 0dpi
+ * ldpi (low) 120dpi
+ * mdpi (medium) 160dpi
+ * TV 213dpi
+ * hdpi (high) 240dpi
+ * xhdpi (extra-high) 320dpi
+ * xxhdpi (extra-extra-high) 480dpi
+ * xxxhdpi (extra-extra-extra-high) 640dpi
+ * anydpi 65534dpi (0xFFFE)
+ * nodpi 65535dpi (0xFFFF)
+
+ There is a difference between nodpi and anydpi:
+ nodpi will be used if no other density is specified. Or the density does not match.
+ nodpi is the fallback for everything else. If there is a resource that matches the DPI,
+ this is used.
+ anydpi is also valid for all densities but in this case, anydpi will overrule all other files!
+ Therefore anydpi is usually used with vector graphics and with constraints on the API level.
+ For example adaptive icons are usually marked as anydpi.
+
+ When it comes now to selecting an icon, there is the following flow:
+
+ 1. is there an anydpi icon?
+ 2. is there an icon for the dpi of the device?
+ 3. is there a nodpi icon?
+ 4. (only on very old devices) is there a icon with dpi 0 (the default)
+
+ For more information read here: https://stackoverflow.com/a/34370735/446140
+
+ :rtype: :class:`str`
+ """
+ main_activity_name = self.get_main_activity()
+
+ app_icon = self.get_attribute_value(
+ 'activity', 'icon', name=main_activity_name)
+
+ if not app_icon:
+ app_icon = self.get_attribute_value('application', 'icon')
+
+ res_parser = self.get_android_resources()
+ if not res_parser:
+ # Can not do anything below this point to resolve...
+ return None
+
+ if not app_icon:
+ res_id = res_parser.get_res_id_by_key(self.package, 'mipmap', 'ic_launcher')
+ if res_id:
+ app_icon = "@%x" % res_id
+
+ if not app_icon:
+ res_id = res_parser.get_res_id_by_key(self.package, 'drawable', 'ic_launcher')
+ if res_id:
+ app_icon = "@%x" % res_id
+
+ if not app_icon:
+ # If the icon can not be found, return now
+ return None
+
+ if app_icon.startswith("@"):
+ app_icon_id = app_icon[1:]
+ app_icon_id = app_icon_id.split(':')[-1]
+ res_id = int(app_icon_id, 16)
+ candidates = res_parser.get_resolved_res_configs(res_id)
+
+ app_icon = None
+ current_dpi = -1
+
+ try:
+ for config, file_name in candidates:
+ dpi = config.get_density()
+ if current_dpi < dpi <= max_dpi:
+ app_icon = file_name
+ current_dpi = dpi
+ except Exception as e:
+ logger.warning("Exception selecting app icon: %s" % e)
+
+ return app_icon
+
+ def get_package(self):
+ """
+ Return the name of the package
+
+ This information is read from the AndroidManifest.xml
+
+ :rtype: :class:`str`
+ """
+ return self.package
+
+ def get_androidversion_code(self):
+ """
+ Return the android version code
+
+ This information is read from the AndroidManifest.xml
+
+ :rtype: :class:`str`
+ """
+ return self.androidversion["Code"]
+
+ def get_androidversion_name(self):
+ """
+ Return the android version name
+
+ This information is read from the AndroidManifest.xml
+
+ :rtype: :class:`str`
+ """
+ return self.androidversion["Name"]
+
+ def get_files(self):
+ """
+ Return the file names inside the APK.
+
+ :rtype: a list of :class:`str`
+ """
+ return self.zip.namelist()
+
+ # def _get_file_magic_name(self, buffer):
+ # """
+ # Return the filetype guessed for a buffer
+ # :param buffer: bytes
+ # :returns: str of filetype
+ # """
+ # default = "Unknown"
+
+ # # Faster way, test once, return default.
+ # if self.__no_magic:
+ # return default
+
+ # try:
+ # # Magic is optional
+ # import magic
+ # except ImportError:
+ # self.__no_magic = True
+ # logger.warning("No Magic library was found on your system.")
+ # return default
+ # except TypeError as e:
+ # self.__no_magic = True
+ # logger.warning("It looks like you have the magic python package installed but not the magic library itself!")
+ # logger.warning("Error from magic library: %s", e)
+ # logger.warning("Please follow the installation instructions at https://github.com/ahupp/python-magic/#installation")
+ # logger.warning("You can also install the 'python-magic-bin' package on Windows and MacOS")
+ # return default
+
+ try:
+ # There are several implementations of magic,
+ # unfortunately all called magic
+ # We use this one: https://github.com/ahupp/python-magic/
+ # You can also use python-magic-bin on Windows or MacOS
+ getattr(magic, "MagicException")
+ except AttributeError:
+ self.__no_magic = True
+ logger.warning("Not the correct Magic library was found on your "
+ "system. Please install python-magic or python-magic-bin!")
+ return default
+
+ try:
+ # 1024 byte are usually enough to test the magic
+ ftype = magic.from_buffer(buffer[:1024])
+ except magic.MagicException as e:
+ logger.exception("Error getting the magic type: %s", e)
+ return default
+
+ if not ftype:
+ return default
+ else:
+ return self._patch_magic(buffer, ftype)
+
+ @property
+ def files(self):
+ """
+ Returns a dictionary of filenames and detected magic type
+
+ :returns: dictionary of files and their mime type
+ """
+ return self.get_files_types()
+
+ # def get_files_types(self):
+ # """
+ # Return the files inside the APK with their associated types (by using python-magic)
+
+ # At the same time, the CRC32 are calculated for the files.
+
+ # :rtype: a dictionnary
+ # """
+ # if self._files == {}:
+ # # Generate File Types / CRC List
+ # for i in self.get_files():
+ # buffer = self._get_crc32(i)
+ # self._files[i] = self._get_file_magic_name(buffer)
+
+ # return self._files
+
+ # def _patch_magic(self, buffer, orig):
+ # """
+ # Overwrite some probably wrong detections by mime libraries
+
+ # :param buffer: bytes of the file to detect
+ # :param orig: guess by mime libary
+ # :returns: corrected guess
+ # """
+ # if ("Zip" in orig) or ('(JAR)' in orig) and androconf.is_android_raw(buffer) == 'APK':
+ # return "Android application package file"
+
+ # return orig
+
+ def _get_crc32(self, filename):
+ """
+ Calculates and compares the CRC32 and returns the raw buffer.
+
+ The CRC32 is added to `files_crc32` dictionary, if not present.
+
+ :param filename: filename inside the zipfile
+ :rtype: bytes
+ """
+ buffer = self.zip.read(filename)
+ if filename not in self.files_crc32:
+ self.files_crc32[filename] = crc32(buffer)
+ if self.files_crc32[filename] != self.zip.infolist()[filename].crc32_of_uncompressed_data:
+ logger.error("File '{}' has different CRC32 after unpacking! "
+ "Declared: {:08x}, Calculated: {:08x}".format(filename,
+ self.zip.infolist()[filename].crc32_of_uncompressed_data,
+ self.files_crc32[filename]))
+ return buffer
+
+ def get_files_crc32(self):
+ """
+ Calculates and returns a dictionary of filenames and CRC32
+
+ :returns: dict of filename: CRC32
+ """
+ if self.files_crc32 == {}:
+ for i in self.get_files():
+ self._get_crc32(i)
+
+ return self.files_crc32
+
+ # def get_files_information(self):
+ # """
+ # Return the files inside the APK with their associated types and crc32
+
+ # :rtype: str, str, int
+ # """
+ # for k in self.get_files():
+ # yield k, self.get_files_types()[k], self.get_files_crc32()[k]
+
+ def get_raw(self):
+ """
+ Return raw bytes of the APK
+
+ :rtype: bytes
+ """
+
+ if self.__raw:
+ return self.__raw
+ else:
+ with open(self.filename, 'rb') as f:
+ self.__raw = bytearray(f.read())
+ return self.__raw
+
+ def get_file(self, filename):
+ """
+ Return the raw data of the specified filename
+ inside the APK
+
+ :rtype: bytes
+ """
+ try:
+ return self.zip.read(filename)
+ except KeyError:
+ raise FileNotPresent(filename)
+
+ def get_dex(self):
+ """
+ Return the raw data of the classes dex file
+
+ This will give you the data of the file called `classes.dex`
+ inside the APK. If the APK has multiple DEX files, you need to use :func:`~APK.get_all_dex`.
+
+ :rtype: bytes
+ """
+ try:
+ return self.get_file("classes.dex")
+ except FileNotPresent:
+ # TODO is this a good idea to return an empty string?
+ return b""
+
+ def get_dex_names(self):
+ """
+ Return the names of all DEX files found in the APK.
+ This method only accounts for "offical" dex files, i.e. all files
+ in the root directory of the APK named classes.dex or classes[0-9]+.dex
+
+ :rtype: a list of str
+ """
+ dexre = re.compile(r"^classes(\d*).dex$")
+ return filter(lambda x: dexre.match(x), self.get_files())
+
+ def get_all_dex(self):
+ """
+ Return the raw data of all classes dex files
+
+ :rtype: a generator of bytes
+ """
+ for dex_name in self.get_dex_names():
+ yield self.get_file(dex_name)
+
+ def is_multidex(self):
+ """
+ Test if the APK has multiple DEX files
+
+ :returns: True if multiple dex found, otherwise False
+ """
+ dexre = re.compile(r"^classes(\d+)?.dex$")
+ return len([instance for instance in self.get_files() if dexre.search(instance)]) > 1
+
+ def _format_value(self, value):
+ """
+ Format a value with packagename, if not already set.
+ For example, the name :code:`'.foobar'` will be transformed into :code:`'package.name.foobar'`.
+
+ Names which do not contain any dots are assumed to be packagename-less as well:
+ :code:`foobar` will also transform into :code:`package.name.foobar`.
+
+ :param value:
+ :returns:
+ """
+ if value and self.package:
+ v_dot = value.find(".")
+ if v_dot == 0:
+ # Dot at the start
+ value = self.package + value
+ elif v_dot == -1:
+ # Not a single dot
+ value = self.package + "." + value
+ return value
+
+ def get_all_attribute_value(
+ self, tag_name, attribute, format_value=True, **attribute_filter
+ ):
+ """
+ Yields all the attribute values in xml files which match with the tag name and the specific attribute
+
+ :param str tag_name: specify the tag name
+ :param str attribute: specify the attribute
+ :param bool format_value: specify if the value needs to be formatted with packagename
+ """
+ tags = self.find_tags(tag_name, **attribute_filter)
+ for tag in tags:
+ value = tag.get(self._ns(attribute)) or tag.get(attribute)
+ if value is not None:
+ if format_value:
+ yield self._format_value(value)
+ else:
+ yield value
+
+ def get_attribute_value(
+ self, tag_name, attribute, format_value=False, **attribute_filter
+ ):
+ """
+ Return the attribute value in xml files which matches the tag name and the specific attribute
+
+ :param str tag_name: specify the tag name
+ :param str attribute: specify the attribute
+ :param bool format_value: specify if the value needs to be formatted with packagename
+ """
+
+ for value in self.get_all_attribute_value(
+ tag_name, attribute, format_value, **attribute_filter):
+ if value is not None:
+ return value
+
+ def get_value_from_tag(self, tag, attribute):
+ """
+ Return the value of the android prefixed attribute in a specific tag.
+
+ This function will always try to get the attribute with a android: prefix first,
+ and will try to return the attribute without the prefix, if the attribute could not be found.
+ This is useful for some broken AndroidManifest.xml, where no android namespace is set,
+ but could also indicate malicious activity (i.e. wrongly repackaged files).
+ A warning is printed if the attribute is found without a namespace prefix.
+
+ If you require to get the exact result you need to query the tag directly:
+
+ example::
+ >>> from lxml.etree import Element
+ >>> tag = Element('bar', nsmap={'android': 'http://schemas.android.com/apk/res/android'})
+ >>> tag.set('{http://schemas.android.com/apk/res/android}foobar', 'barfoo')
+ >>> tag.set('name', 'baz')
+ # Assume that `a` is some APK object
+ >>> a.get_value_from_tag(tag, 'name')
+ 'baz'
+ >>> tag.get('name')
+ 'baz'
+ >>> tag.get('foobar')
+ None
+ >>> a.get_value_from_tag(tag, 'foobar')
+ 'barfoo'
+
+ :param lxml.etree.Element tag: specify the tag element
+ :param str attribute: specify the attribute name
+ :returns: the attribute's value, or None if the attribute is not present
+ """
+
+ # TODO: figure out if both android:name and name tag exist which one to give preference:
+ # currently we give preference for the namespace one and fallback to the un-namespaced
+ value = tag.get(self._ns(attribute))
+ if value is None:
+ value = tag.get(attribute)
+
+ if value:
+ # If value is still None, the attribute could not be found, thus is not present
+ logger.warning("Failed to get the attribute '{}' on tag '{}' with namespace. "
+ "But found the same attribute without namespace!".format(attribute, tag.tag))
+ return value
+
+ def find_tags(self, tag_name, **attribute_filter):
+ """
+ Return a list of all the matched tags in all available xml
+
+ :param str tag: specify the tag name
+ """
+ all_tags = [
+ self.find_tags_from_xml(
+ i, tag_name, **attribute_filter
+ )
+ for i in self.xml
+ ]
+ return [tag for tag_list in all_tags for tag in tag_list]
+
+ def find_tags_from_xml(
+ self, xml_name, tag_name, **attribute_filter
+ ):
+ """
+ Return a list of all the matched tags in a specific xml
+ w
+ :param str xml_name: specify from which xml to pick the tag from
+ :param str tag_name: specify the tag name
+ """
+ xml = self.xml[xml_name]
+ if xml is None:
+ return []
+ if xml.tag == tag_name:
+ if self.is_tag_matched(
+ xml.tag, **attribute_filter
+ ):
+ return [xml]
+ return []
+ tags = xml.findall(".//" + tag_name)
+ return [
+ tag for tag in tags if self.is_tag_matched(
+ tag, **attribute_filter
+ )
+ ]
+
+ def is_tag_matched(self, tag, **attribute_filter):
+ r"""
+ Return true if the attributes matches in attribute filter.
+
+ An attribute filter is a dictionary containing: {attribute_name: value}.
+ This function will return True if and only if all attributes have the same value.
+ This function allows to set the dictionary via kwargs, thus you can filter like this:
+
+ example::
+ a.is_tag_matched(tag, name="foobar", other="barfoo")
+
+ This function uses a fallback for attribute searching. It will by default use
+ the namespace variant but fall back to the non-namespace variant.
+ Thus specifiying :code:`{"name": "foobar"}` will match on :code:``
+ as well as on :code:``.
+
+ :param lxml.etree.Element tag: specify the tag element
+ :param attribute_filter: specify the attribute filter as dictionary
+ """
+ if len(attribute_filter) <= 0:
+ return True
+ for attr, value in attribute_filter.items():
+ _value = self.get_value_from_tag(tag, attr)
+ if _value != value:
+ return False
+ return True
+
+ def get_main_activities(self):
+ """
+ Return names of the main activities
+
+ These values are read from the AndroidManifest.xml
+
+ :rtype: a set of str
+ """
+ x = set()
+ y = set()
+
+ for i in self.xml:
+ if self.xml[i] is None:
+ continue
+ activities_and_aliases = self.xml[i].findall(".//activity") + \
+ self.xml[i].findall(".//activity-alias")
+
+ for item in activities_and_aliases:
+ # Some applications have more than one MAIN activity.
+ # For example: paid and free content
+ activityEnabled = item.get(self._ns("enabled"))
+ if activityEnabled == "false":
+ continue
+
+ for sitem in item.findall(".//action"):
+ val = sitem.get(self._ns("name"))
+ if val == "android.intent.action.MAIN":
+ activity = item.get(self._ns("name"))
+ if activity is not None:
+ x.add(item.get(self._ns("name")))
+ else:
+ logger.warning('Main activity without name')
+
+ for sitem in item.findall(".//category"):
+ val = sitem.get(self._ns("name"))
+ if val == "android.intent.category.LAUNCHER":
+ activity = item.get(self._ns("name"))
+ if activity is not None:
+ y.add(item.get(self._ns("name")))
+ else:
+ logger.warning('Launcher activity without name')
+
+ return x.intersection(y)
+
+ def get_main_activity(self):
+ """
+ Return the name of the main activity
+
+ This value is read from the AndroidManifest.xml
+
+ :rtype: str
+ """
+ activities = self.get_main_activities()
+ if len(activities) == 1:
+ return self._format_value(activities.pop())
+ elif len(activities) > 1:
+ main_activities = {self._format_value(ma) for ma in activities}
+ # sorted is necessary
+ # 9fc7d3e8225f6b377f9181a92c551814317b77e1aa0df4c6d508d24b18f0f633
+ good_main_activities = sorted(
+ main_activities.intersection(self.get_activities()))
+ if good_main_activities:
+ return good_main_activities[0]
+ return sorted(main_activities)[0]
+ return None
+
+ def get_activities(self):
+ """
+ Return the android:name attribute of all activities
+
+ :rtype: a list of str
+ """
+ return list(self.get_all_attribute_value("activity", "name"))
+
+ def get_activity_aliases(self):
+ """
+ Return the android:name and android:targetActivity attribute of all activity aliases.
+
+ :rtype: a list of dict
+ """
+ ali = []
+ for alias in self.find_tags('activity-alias'):
+ activity_alias = {}
+ for attribute in ['name', 'targetActivity']:
+ value = (alias.get(attribute) or
+ alias.get(self._ns(attribute)))
+ if not value:
+ continue
+ activity_alias[attribute] = self._format_value(value)
+ if activity_alias:
+ ali.append(activity_alias)
+ return ali
+
+ def get_services(self):
+ """
+ Return the android:name attribute of all services
+
+ :rtype: a list of str
+ """
+ return list(self.get_all_attribute_value("service", "name"))
+
+ def get_receivers(self):
+ """
+ Return the android:name attribute of all receivers
+
+ :rtype: a list of string
+ """
+ return list(self.get_all_attribute_value("receiver", "name"))
+
+ def get_providers(self):
+ """
+ Return the android:name attribute of all providers
+
+ :rtype: a list of string
+ """
+ return list(self.get_all_attribute_value("provider", "name"))
+
+ def get_res_value(self, name):
+ """
+ Return the literal value with a resource id
+ :rtype: str
+ """
+
+ res_parser = self.get_android_resources()
+ if not res_parser:
+ return name
+
+ res_id = res_parser.parse_id(name)[0]
+ try:
+ value = res_parser.get_resolved_res_configs(
+ res_id,
+ ARSCResTableConfig.default_config())[0][1]
+ except Exception as e:
+ logger.warning("Exception get resolved resource id: %s" % e)
+ return name
+
+ return value
+
+ def get_intent_filters(self, itemtype, name):
+ """
+ Find intent filters for a given item and name.
+
+ Intent filter are attached to activities, services or receivers.
+ You can search for the intent filters of such items and get a dictionary of all
+ attached actions and intent categories.
+
+ :param itemtype: the type of parent item to look for, e.g. `activity`, `service` or `receiver`
+ :param name: the `android:name` of the parent item, e.g. activity name
+ :returns: a dictionary with the keys `action` and `category` containing the `android:name` of those items
+ """
+ attributes = {"action": ["name"], "category": ["name"], "data": ['scheme', 'host', 'port', 'path', 'pathPattern', 'pathPrefix', 'mimeType']}
+
+ d = {}
+ for element in attributes.keys():
+ d[element] = []
+
+ for i in self.xml:
+ # TODO: this can probably be solved using a single xpath
+ for item in self.xml[i].findall(".//" + itemtype):
+ if self._format_value(item.get(self._ns("name"))) == name:
+ for sitem in item.findall(".//intent-filter"):
+ for element in d.keys():
+ for ssitem in sitem.findall(element):
+ if element == 'data': # multiple attributes
+ values = {}
+ for attribute in attributes[element]:
+ value = ssitem.get(self._ns(attribute))
+ if value:
+ if value.startswith('@'):
+ value = self.get_res_value(value)
+ values[attribute] = value
+
+ if values:
+ d[element].append(values)
+ else:
+ for attribute in attributes[element]:
+ value = ssitem.get(self._ns(attribute))
+ if value.startswith('@'):
+ value = self.get_res_value(value)
+
+ if value not in d[element]:
+ d[element].append(value)
+
+ for element in list(d.keys()):
+ if not d[element]:
+ del d[element]
+
+ return d
+
+ def get_permissions(self):
+ """
+ Return permissions names declared in the AndroidManifest.xml.
+
+ It is possible that permissions are returned multiple times,
+ as this function does not filter the permissions, i.e. it shows you
+ exactly what was defined in the AndroidManifest.xml.
+
+ Implied permissions, which are granted automatically, are not returned
+ here. Use :meth:`get_uses_implied_permission_list` if you need a list
+ of implied permissions.
+
+ :returns: A list of permissions
+ :rtype: list
+ """
+ return self.permissions
+
+ def get_uses_implied_permission_list(self):
+ """
+ Return all permissions implied by the target SDK or other permissions.
+
+ :rtype: list of string
+ """
+ target_sdk_version = self.get_effective_target_sdk_version()
+
+ READ_CALL_LOG = 'android.permission.READ_CALL_LOG'
+ READ_CONTACTS = 'android.permission.READ_CONTACTS'
+ READ_EXTERNAL_STORAGE = 'android.permission.READ_EXTERNAL_STORAGE'
+ READ_PHONE_STATE = 'android.permission.READ_PHONE_STATE'
+ WRITE_CALL_LOG = 'android.permission.WRITE_CALL_LOG'
+ WRITE_CONTACTS = 'android.permission.WRITE_CONTACTS'
+ WRITE_EXTERNAL_STORAGE = 'android.permission.WRITE_EXTERNAL_STORAGE'
+
+ implied = []
+
+ implied_WRITE_EXTERNAL_STORAGE = False
+ if target_sdk_version < 4:
+ if WRITE_EXTERNAL_STORAGE not in self.permissions:
+ implied.append([WRITE_EXTERNAL_STORAGE, None])
+ implied_WRITE_EXTERNAL_STORAGE = True
+ if READ_PHONE_STATE not in self.permissions:
+ implied.append([READ_PHONE_STATE, None])
+
+ if (WRITE_EXTERNAL_STORAGE in self.permissions or implied_WRITE_EXTERNAL_STORAGE) \
+ and READ_EXTERNAL_STORAGE not in self.permissions:
+ maxSdkVersion = None
+ for name, version in self.uses_permissions:
+ if name == WRITE_EXTERNAL_STORAGE:
+ maxSdkVersion = version
+ break
+ implied.append([READ_EXTERNAL_STORAGE, maxSdkVersion])
+
+ if target_sdk_version < 16:
+ if READ_CONTACTS in self.permissions \
+ and READ_CALL_LOG not in self.permissions:
+ implied.append([READ_CALL_LOG, None])
+ if WRITE_CONTACTS in self.permissions \
+ and WRITE_CALL_LOG not in self.permissions:
+ implied.append([WRITE_CALL_LOG, None])
+
+ return implied
+
+ def _update_permission_protection_level(self, protection_level, sdk_version):
+ if not sdk_version or int(sdk_version) <= 15:
+ return protection_level.replace('Or', '|').lower()
+ return protection_level
+
+ def _fill_deprecated_permissions(self, permissions):
+ min_sdk = self.get_min_sdk_version()
+ target_sdk = self.get_target_sdk_version()
+ filled_permissions = permissions.copy()
+ for permission in filled_permissions:
+ protection_level, label, description = filled_permissions[permission]
+ if ((not label or not description)
+ and permission in self.permission_module_min_sdk):
+ x = self.permission_module_min_sdk[permission]
+ protection_level = self._update_permission_protection_level(
+ x['protectionLevel'], min_sdk)
+ filled_permissions[permission] = [
+ protection_level, x['label'], x['description']]
+ else:
+ filled_permissions[permission] = [
+ self._update_permission_protection_level(
+ protection_level, target_sdk),
+ label, description]
+ return filled_permissions
+
+ def get_details_permissions(self):
+ """
+ Return permissions with details.
+
+ THis can only return details about the permission, if the permission is
+ defined in the AOSP.
+
+ :rtype: dict of {permission: [protectionLevel, label, description]}
+ """
+ l = {}
+
+ for i in self.permissions:
+ if i in self.permission_module:
+ x = self.permission_module[i]
+ l[i] = [x["protectionLevel"], x["label"], x["description"]]
+ else:
+ # FIXME: the permission might be signature, if it is defined by the app itself!
+ l[i] = ["normal", "Unknown permission from android reference",
+ "Unknown permission from android reference"]
+ return self._fill_deprecated_permissions(l)
+
+ def get_requested_aosp_permissions(self):
+ """
+ Returns requested permissions declared within AOSP project.
+
+ This includes several other permissions as well, which are in the platform apps.
+
+ :rtype: list of str
+ """
+ aosp_permissions = []
+ all_permissions = self.get_permissions()
+ for perm in all_permissions:
+ if perm in list(self.permission_module.keys()):
+ aosp_permissions.append(perm)
+ return aosp_permissions
+
+ def get_requested_aosp_permissions_details(self):
+ """
+ Returns requested aosp permissions with details.
+
+ :rtype: dictionary
+ """
+ l = {}
+ for i in self.permissions:
+ try:
+ l[i] = self.permission_module[i]
+ except KeyError:
+ # if we have not found permission do nothing
+ continue
+ return l
+
+ def get_requested_third_party_permissions(self):
+ """
+ Returns list of requested permissions not declared within AOSP project.
+
+ :rtype: list of strings
+ """
+ third_party_permissions = []
+ all_permissions = self.get_permissions()
+ for perm in all_permissions:
+ if perm not in list(self.permission_module.keys()):
+ third_party_permissions.append(perm)
+ return third_party_permissions
+
+ def get_declared_permissions(self):
+ """
+ Returns list of the declared permissions.
+
+ :rtype: list of strings
+ """
+ return list(self.declared_permissions.keys())
+
+ def get_declared_permissions_details(self):
+ """
+ Returns declared permissions with the details.
+
+ :rtype: dict
+ """
+ return self.declared_permissions
+
+ def get_max_sdk_version(self):
+ """
+ Return the android:maxSdkVersion attribute
+
+ :rtype: string
+ """
+ return self.get_attribute_value("uses-sdk", "maxSdkVersion")
+
+ def get_min_sdk_version(self):
+ """
+ Return the android:minSdkVersion attribute
+
+ :rtype: string
+ """
+ return self.get_attribute_value("uses-sdk", "minSdkVersion")
+
+ def get_target_sdk_version(self):
+ """
+ Return the android:targetSdkVersion attribute
+
+ :rtype: string
+ """
+ return self.get_attribute_value("uses-sdk", "targetSdkVersion")
+
+ def get_effective_target_sdk_version(self):
+ """
+ Return the effective targetSdkVersion, always returns int > 0.
+
+ If the targetSdkVersion is not set, it defaults to 1. This is
+ set based on defaults as defined in:
+ https://developer.android.com/guide/topics/manifest/uses-sdk-element.html
+
+ :rtype: int
+ """
+ target_sdk_version = self.get_target_sdk_version()
+ if not target_sdk_version:
+ target_sdk_version = self.get_min_sdk_version()
+ try:
+ return int(target_sdk_version)
+ except (ValueError, TypeError):
+ return 1
+
+ def get_libraries(self):
+ """
+ Return the android:name attributes for libraries
+
+ :rtype: list
+ """
+ return list(self.get_all_attribute_value("uses-library", "name"))
+
+ def get_features(self):
+ """
+ Return a list of all android:names found for the tag uses-feature
+ in the AndroidManifest.xml
+
+ :returns: list
+ """
+ return list(self.get_all_attribute_value("uses-feature", "name"))
+
+ def is_wearable(self):
+ """
+ Checks if this application is build for wearables by
+ checking if it uses the feature 'android.hardware.type.watch'
+ See: https://developer.android.com/training/wearables/apps/creating.html for more information.
+
+ Not every app is setting this feature (not even the example Google provides),
+ so it might be wise to not 100% rely on this feature.
+
+ :returns: True if wearable, False otherwise
+ """
+ return 'android.hardware.type.watch' in self.get_features()
+
+ def is_leanback(self):
+ """
+ Checks if this application is build for TV (Leanback support)
+ by checkin if it uses the feature 'android.software.leanback'
+
+ :returns: True if leanback feature is used, false otherwise
+ """
+ return 'android.software.leanback' in self.get_features()
+
+ def is_androidtv(self):
+ """
+ Checks if this application does not require a touchscreen,
+ as this is the rule to get into the TV section of the Play Store
+ See: https://developer.android.com/training/tv/start/start.html for more information.
+
+ :returns: True if 'android.hardware.touchscreen' is not required, False otherwise
+ """
+ return self.get_attribute_value('uses-feature', 'name', required="false", name="android.hardware.touchscreen") == "android.hardware.touchscreen"
+
+ def get_certificate_der(self, filename):
+ """
+ Return the DER coded X.509 certificate from the signature file.
+
+ :param filename: Signature filename in APK
+ :returns: DER coded X.509 certificate as binary
+ """
+ pkcs7message = self.get_file(filename)
+
+ pkcs7obj = cms.ContentInfo.load(pkcs7message)
+ cert = pkcs7obj['content']['certificates'][0].chosen.dump()
+ return cert
+
+ def get_certificate(self, filename):
+ """
+ Return a X.509 certificate object by giving the name in the apk file
+
+ :param filename: filename of the signature file in the APK
+ :returns: a :class:`Certificate` certificate
+ """
+ cert = self.get_certificate_der(filename)
+ certificate = x509.Certificate.load(cert)
+
+ return certificate
+
+ def new_zip(self, filename, deleted_files=None, new_files={}):
+ """
+ Create a new zip file
+
+ :param filename: the output filename of the zip
+ :param deleted_files: a regex pattern to remove specific file
+ :param new_files: a dictionnary of new files
+
+ :type filename: string
+ :type deleted_files: None or a string
+ :type new_files: a dictionnary (key:filename, value:content of the file)
+ """
+ zout = zipfile.ZipFile(filename, 'w')
+
+ for item in self.zip.infolist():
+ # Block one: deleted_files, or deleted_files and new_files
+ if deleted_files is not None:
+ if re.match(deleted_files, item) is None:
+ # if the regex of deleted_files doesn't match the filename
+ if new_files is not False:
+ if item in new_files:
+ # and if the filename is in new_files
+ zout.writestr(item, new_files[item])
+ continue
+ # Otherwise, write the original file.
+ buffer = self.zip.read(item)
+ zout.writestr(item, buffer)
+ # Block two: deleted_files is None, new_files is not empty
+ elif new_files is not False:
+ if item in new_files:
+ zout.writestr(item, new_files[item])
+ else:
+ buffer = self.zip.read(item)
+ zout.writestr(item, buffer)
+ # Block three: deleted_files is None, new_files is empty.
+ # Just write out the default zip
+ else:
+ buffer = self.zip.read(item)
+ zout.writestr(item, buffer)
+ zout.close()
+
+ def get_android_manifest_axml(self):
+ """
+ Return the :class:`AXMLPrinter` object which corresponds to the AndroidManifest.xml file
+
+ :rtype: :class:`~androguard.core.bytecodes.axml.AXMLPrinter`
+ """
+ try:
+ return self.axml["AndroidManifest.xml"]
+ except KeyError:
+ return None
+
+ def get_android_manifest_xml(self):
+ """
+ Return the parsed xml object which corresponds to the AndroidManifest.xml file
+
+ :rtype: :class:`~lxml.etree.Element`
+ """
+ try:
+ return self.xml["AndroidManifest.xml"]
+ except KeyError:
+ return None
+
+ def get_android_resources(self):
+ """
+ Return the :class:`~androguard.core.bytecodes.axml.ARSCParser` object which corresponds to the resources.arsc file
+
+ :rtype: :class:`~androguard.core.bytecodes.axml.ARSCParser`
+ """
+ try:
+ return self.arsc["resources.arsc"]
+ except KeyError:
+ if "resources.arsc" not in self.zip.namelist():
+ # There is a rare case, that no resource file is supplied.
+ # Maybe it was added manually, thus we check here
+ return None
+ self.arsc["resources.arsc"] = ARSCParser(self.zip.read("resources.arsc"))
+ return self.arsc["resources.arsc"]
+
+ def is_signed(self):
+ """
+ Returns true if any of v1, v2, or v3 signatures were found.
+ """
+ return self.is_signed_v1() or self.is_signed_v2() or self.is_signed_v3()
+
+ def is_signed_v1(self):
+ """
+ Returns true if a v1 / JAR signature was found.
+
+ Returning `True` does not mean that the file is properly signed!
+ It just says that there is a signature file which needs to be validated.
+ """
+ return self.get_signature_name() is not None
+
+ def is_signed_v2(self):
+ """
+ Returns true of a v2 / APK signature was found.
+
+ Returning `True` does not mean that the file is properly signed!
+ It just says that there is a signature file which needs to be validated.
+ """
+ if self._is_signed_v2 is None:
+ self.parse_v2_v3_signature()
+
+ return self._is_signed_v2
+
+ def is_signed_v3(self):
+ """
+ Returns true of a v3 / APK signature was found.
+
+ Returning `True` does not mean that the file is properly signed!
+ It just says that there is a signature file which needs to be validated.
+ """
+ if self._is_signed_v3 is None:
+ self.parse_v2_v3_signature()
+
+ return self._is_signed_v3
+
+ def read_uint32_le(self, io_stream):
+ value, = unpack(' 0:
+ f.seek(-1, io.SEEK_CUR)
+ r, = unpack('<4s', f.read(4))
+ if r == self._PK_END_OF_CENTRAL_DIR:
+ # Read central dir
+ this_disk, disk_central, this_entries, total_entries, \
+ size_central, offset_central = unpack('> 4) & 3]
+
+
+class StringBlock:
+ """
+ StringBlock is a CHUNK inside an AXML File: `ResStringPool_header`
+ It contains all strings, which are used by referecing to ID's
+
+ See http://androidxref.com/9.0.0_r3/xref/frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h#436
+ """
+ def __init__(self, buff, header):
+ """
+ :param buff: buffer which holds the string block
+ :param header: a instance of :class:`~ARSCHeader`
+ """
+ self._cache = {}
+ self.header = header
+ # We already read the header (which was chunk_type and chunk_size
+ # Now, we read the string_count:
+ self.stringCount = unpack(' 0:
+ logger.info("Styles Offset given, but styleCount is zero. "
+ "This is not a problem but could indicate packers.")
+
+ self.m_stringOffsets = []
+ self.m_styleOffsets = []
+ self.m_charbuff = ""
+ self.m_styles = []
+
+ # Next, there is a list of string following.
+ # This is only a list of offsets (4 byte each)
+ for i in range(self.stringCount):
+ self.m_stringOffsets.append(unpack('".format(self.stringCount, self.styleCount, self.m_isUTF8)
+
+ def __getitem__(self, idx):
+ """
+ Returns the string at the index in the string table
+ """
+ return self.getString(idx)
+
+ def __len__(self):
+ """
+ Get the number of strings stored in this table
+ """
+ return self.stringCount
+
+ def __iter__(self):
+ """
+ Iterable over all strings
+ """
+ for i in range(self.stringCount):
+ yield self.getString(i)
+
+ def getString(self, idx):
+ """
+ Return the string at the index in the string table
+
+ :param idx: index in the string table
+ :return: str
+ """
+ if idx in self._cache:
+ return self._cache[idx]
+
+ if idx < 0 or not self.m_stringOffsets or idx > self.stringCount:
+ return ""
+
+ offset = self.m_stringOffsets[idx]
+
+ if self.m_isUTF8:
+ self._cache[idx] = self._decode8(offset)
+ else:
+ self._cache[idx] = self._decode16(offset)
+
+ return self._cache[idx]
+
+ def getStyle(self, idx):
+ """
+ Return the style associated with the index
+
+ :param idx: index of the style
+ :return:
+ """
+ return self.m_styles[idx]
+
+ def _decode8(self, offset):
+ """
+ Decode an UTF-8 String at the given offset
+
+ :param offset: offset of the string inside the data
+ :return: str
+ """
+ # UTF-8 Strings contain two lengths, as they might differ:
+ # 1) the UTF-16 length
+ str_len, skip = self._decode_length(offset, 1)
+ offset += skip
+
+ # 2) the utf-8 string length
+ encoded_bytes, skip = self._decode_length(offset, 1)
+ offset += skip
+
+ data = self.m_charbuff[offset: offset + encoded_bytes]
+
+ if self.m_charbuff[offset + encoded_bytes] != 0:
+ raise ResParserError("UTF-8 String is not null terminated! At offset={}".format(offset))
+
+ return self._decode_bytes(data, 'utf-8', str_len)
+
+ def _decode16(self, offset):
+ """
+ Decode an UTF-16 String at the given offset
+
+ :param offset: offset of the string inside the data
+ :return: str
+ """
+ str_len, skip = self._decode_length(offset, 2)
+ offset += skip
+
+ # The len is the string len in utf-16 units
+ encoded_bytes = str_len * 2
+
+ data = self.m_charbuff[offset: offset + encoded_bytes]
+
+ if self.m_charbuff[offset + encoded_bytes:offset + encoded_bytes + 2] != b"\x00\x00":
+ raise ResParserError("UTF-16 String is not null terminated! At offset={}".format(offset))
+
+ return self._decode_bytes(data, 'utf-16', str_len)
+
+ @staticmethod
+ def _decode_bytes(data, encoding, str_len):
+ """
+ Generic decoding with length check.
+ The string is decoded from bytes with the given encoding, then the length
+ of the string is checked.
+ The string is decoded using the "replace" method.
+
+ :param data: bytes
+ :param encoding: encoding name ("utf-8" or "utf-16")
+ :param str_len: length of the decoded string
+ :return: str
+ """
+ string = data.decode(encoding, 'replace')
+ if len(string) != str_len:
+ logger.warning("invalid decoded string length")
+ return string
+
+ def _decode_length(self, offset, sizeof_char):
+ """
+ Generic Length Decoding at offset of string
+
+ The method works for both 8 and 16 bit Strings.
+ Length checks are enforced:
+ * 8 bit strings: maximum of 0x7FFF bytes (See
+ http://androidxref.com/9.0.0_r3/xref/frameworks/base/libs/androidfw/ResourceTypes.cpp#692)
+ * 16 bit strings: maximum of 0x7FFFFFF bytes (See
+ http://androidxref.com/9.0.0_r3/xref/frameworks/base/libs/androidfw/ResourceTypes.cpp#670)
+
+ :param offset: offset into the string data section of the beginning of
+ the string
+ :param sizeof_char: number of bytes per char (1 = 8bit, 2 = 16bit)
+ :returns: tuple of (length, read bytes)
+ """
+ sizeof_2chars = sizeof_char << 1
+ fmt = "<2{}".format('B' if sizeof_char == 1 else 'H')
+ highbit = 0x80 << (8 * (sizeof_char - 1))
+
+ length1, length2 = unpack(fmt, self.m_charbuff[offset:(offset + sizeof_2chars)])
+
+ if (length1 & highbit) != 0:
+ length = ((length1 & ~highbit) << (8 * sizeof_char)) | length2
+ size = sizeof_2chars
+ else:
+ length = length1
+ size = sizeof_char
+
+ # These are true asserts, as the size should never be less than the values
+ if sizeof_char == 1:
+ assert length <= 0x7FFF, "length of UTF-8 string is too large! At offset={}".format(offset)
+ else:
+ assert length <= 0x7FFFFFFF, "length of UTF-16 string is too large! At offset={}".format(offset)
+
+ return length, size
+
+ def show(self):
+ """
+ Print some information on stdout about the string table
+ """
+ print("StringBlock(stringsCount=0x%x, "
+ "stringsOffset=0x%x, "
+ "stylesCount=0x%x, "
+ "stylesOffset=0x%x, "
+ "flags=0x%x"
+ ")" % (self.stringCount,
+ self.stringsOffset,
+ self.styleCount,
+ self.stylesOffset,
+ self.flags))
+
+ if self.stringCount > 0:
+ print()
+ print("String Table: ")
+ for i, s in enumerate(self):
+ print("{:08d} {}".format(i, repr(s)))
+
+ if self.styleCount > 0:
+ print()
+ print("Styles Table: ")
+ for i in range(self.styleCount):
+ print("{:08d} {}".format(i, repr(self.getStyle(i))))
+
+
+class AXMLParser:
+ """
+ AXMLParser reads through all chunks in the AXML file
+ and implements a state machine to return information about
+ the current chunk, which can then be read by :class:`~AXMLPrinter`.
+
+ An AXML file is a file which contains multiple chunks of data, defined
+ by the `ResChunk_header`.
+ There is no real file magic but as the size of the first header is fixed
+ and the `type` of the `ResChunk_header` is set to `RES_XML_TYPE`, a file
+ will usually start with `0x03000800`.
+ But there are several examples where the `type` is set to something
+ else, probably in order to fool parsers.
+
+ Typically the AXMLParser is used in a loop which terminates if `m_event` is set to `END_DOCUMENT`.
+ You can use the `next()` function to get the next chunk.
+ Note that not all chunk types are yielded from the iterator! Some chunks are processed in
+ the AXMLParser only.
+ The parser will set `is_valid()` to False if it parses something not valid.
+ Messages what is wrong are logged.
+
+ See http://androidxref.com/9.0.0_r3/xref/frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h#563
+ """
+ def __init__(self, raw_buff):
+ logger.debug("AXMLParser")
+
+ self._reset()
+
+ self._valid = True
+ self.axml_tampered = False
+ self.buff = io.BufferedReader(io.BytesIO(raw_buff))
+ self.buff_size = self.buff.raw.getbuffer().nbytes
+
+ # Minimum is a single ARSCHeader, which would be a strange edge case...
+ if self.buff_size < 8:
+ logger.error("Filesize is too small to be a valid AXML file! Filesize: {}".format(self.buff_size))
+ self._valid = False
+ return
+
+ # This would be even stranger, if an AXML file is larger than 4GB...
+ # But this is not possible as the maximum chunk size is a unsigned 4 byte int.
+ if self.buff_size > 0xFFFFFFFF:
+ logger.error("Filesize is too large to be a valid AXML file! Filesize: {}".format(self.buff_size))
+ self._valid = False
+ return
+
+ try:
+ axml_header = ARSCHeader(self.buff)
+ logger.debug("FIRST HEADER {}".format(axml_header))
+ except ResParserError as e:
+ logger.error("Error parsing first resource header: %s", e)
+ self._valid = False
+ return
+
+ self.filesize = axml_header.size
+
+ if axml_header.header_size == 28024:
+ # Can be a common error: the file is not an AXML but a plain XML
+ # The file will then usually start with ' self.buff_size:
+ logger.error("This does not look like an AXML file. Declared filesize does not match real size: {} vs {}".format(self.filesize, self.buff_size))
+ self._valid = False
+ return
+
+ if self.filesize < self.buff_size:
+ # The file can still be parsed up to the point where the chunk should end.
+ self.axml_tampered = True
+ logger.warning("Declared filesize ({}) is smaller than total file size ({}). "
+ "Was something appended to the file? Trying to parse it anyways.".format(self.filesize, self.buff.size()))
+
+ # Not that severe of an error, we have plenty files where this is not
+ # set correctly
+ if axml_header.type != RES_XML_TYPE:
+ self.axml_tampered = True
+ logger.warning("AXML file has an unusual resource type! "
+ "Malware likes to to such stuff to anti androguard! "
+ "But we try to parse it anyways. Resource Type: 0x{:04x}".format(axml_header.type))
+
+ # Now we parse the STRING POOL
+ try:
+ header = ARSCHeader(self.buff, expected_type=RES_STRING_POOL_TYPE)
+ logger.debug("STRING_POOL {}".format(header))
+ except ResParserError as e:
+ logger.error("Error parsing resource header of string pool: {}".format(e))
+ self._valid = False
+ return
+
+ if header.header_size != 0x1C:
+ logger.error("This does not look like an AXML file. String chunk header size does not equal 28! header size = {}".format(header.header_size))
+ self._valid = False
+ return
+
+ self.sb = StringBlock(self.buff, header)
+
+ self.buff.seek(axml_header.header_size + header.size)
+
+ # Stores resource ID mappings, if any
+ self.m_resourceIDs = []
+
+ # Store a list of prefix/uri mappings encountered
+ self.namespaces = []
+
+ def is_valid(self):
+ """
+ Get the state of the AXMLPrinter.
+ if an error happend somewhere in the process of parsing the file,
+ this flag is set to False.
+ """
+ logger.debug(self._valid)
+ return self._valid
+
+ def _reset(self):
+ self.m_event = -1
+ self.m_lineNumber = -1
+ self.m_name = -1
+ self.m_namespaceUri = -1
+ self.m_attributes = []
+ self.m_idAttribute = -1
+ self.m_classAttribute = -1
+ self.m_styleAttribute = -1
+
+ def __next__(self):
+ self._do_next()
+ return self.m_event
+
+ def _do_next(self):
+ logger.debug("M_EVENT {}".format(self.m_event))
+
+ if self.m_event == END_DOCUMENT:
+ return
+
+ self._reset()
+ while self._valid:
+ # Stop at the declared filesize or at the end of the file
+ if self.buff.tell() == self.filesize:
+ self.m_event = END_DOCUMENT
+ break
+
+ # Again, we read an ARSCHeader
+ try:
+ possible_types = {256, 257, 258, 259, 260, 384}
+ h = ARSCHeader(self.buff, possible_types=possible_types)
+ logger.debug("NEXT HEADER {}".format(h))
+ except ResParserError as e:
+ logger.error("Error parsing resource header: {}".format(e))
+ self._valid = False
+ return
+
+ # Special chunk: Resource Map. This chunk might be contained inside
+ # the file, after the string pool.
+ if h.type == RES_XML_RESOURCE_MAP_TYPE:
+ logger.debug("AXML contains a RESOURCE MAP")
+ # Check size: < 8 bytes mean that the chunk is not complete
+ # Should be aligned to 4 bytes.
+ if h.size < 8 or (h.size % 4) != 0:
+ logger.error("Invalid chunk size in chunk XML_RESOURCE_MAP")
+ self._valid = False
+ return
+
+ for i in range((h.size - h.header_size) // 4):
+ self.m_resourceIDs.append(unpack(' RES_XML_LAST_CHUNK_TYPE:
+ # h.size is the size of the whole chunk including the header.
+ # We read already 8 bytes of the header, thus we need to
+ # subtract them.
+ logger.error("Not a XML resource chunk type: 0x{:04x}. Skipping {} bytes".format(h.type, h.size))
+ self.buff.seek(h.end)
+ continue
+
+ # Check that we read a correct header
+ if h.header_size != 0x10:
+ logger.error("XML Resource Type Chunk header size does not match 16! " \
+ "At chunk type 0x{:04x}, declared header size=0x{:04x}, chunk size=0x{:04x}".format(h.type, h.header_size, h.size))
+ self.buff.seek(h.end)
+ continue
+
+ # Line Number of the source file, only used as meta information
+ self.m_lineNumber, = unpack(' uri {}: '{}'".format(prefix, s_prefix, uri, s_uri))
+
+ if s_uri == '':
+ logger.warning("Namespace prefix '{}' resolves to empty URI. "
+ "This might be a packer.".format(s_prefix))
+
+ if (prefix, uri) in self.namespaces:
+ logger.debug("Namespace mapping ({}, {}) already seen! "
+ "This is usually not a problem but could indicate packers or broken AXML compilers.".format(prefix, uri))
+ self.namespaces.append((prefix, uri))
+
+ # We can continue with the next chunk, as we store the namespace
+ # mappings for each tag
+ continue
+
+ if h.type == RES_XML_END_NAMESPACE_TYPE:
+ # END_PREFIX contains again prefix and uri field
+ prefix, = unpack('> 16) - 1
+ self.m_attribute_count = attributeCount & 0xFFFF
+ self.m_styleAttribute = (self.m_classAttribute >> 16) - 1
+ self.m_classAttribute = (self.m_classAttribute & 0xFFFF) - 1
+
+ # Now, we parse the attributes.
+ # Each attribute has 5 fields of 4 byte
+ for i in range(0, self.m_attribute_count):
+ # Each field is linearly parsed into the array
+ # Each Attribute contains:
+ # * Namespace URI (String ID)
+ # * Name (String ID)
+ # * Value
+ # * Type
+ # * Data
+ for j in range(0, ATTRIBUTE_LENGTH):
+ self.m_attributes.append(unpack('> 24
+
+ self.m_event = START_TAG
+ break
+
+ if h.type == RES_XML_END_ELEMENT_TYPE:
+ self.m_namespaceUri, = unpack(' uint32_t index
+ self.m_name, = unpack(' always zero
+ # uint8_t dataType
+ # uint32_t data
+ # For now, we ingore these values
+ size, res0, dataType, data = unpack("= len(self.m_attributes):
+ logger.warning("Invalid attribute index")
+
+ return offset
+
+ def getAttributeCount(self):
+ """
+ Return the number of Attributes for a Tag
+ or -1 if not in a tag
+ """
+ if self.m_event != START_TAG:
+ return -1
+
+ return self.m_attribute_count
+
+ def getAttributeUri(self, index):
+ """
+ Returns the numeric ID for the namespace URI of an attribute
+ """
+ logger.debug(index)
+
+ offset = self._get_attribute_offset(index)
+ uri = self.m_attributes[offset + ATTRIBUTE_IX_NAMESPACE_URI]
+
+ return uri
+
+ def getAttributeNamespace(self, index):
+ """
+ Return the Namespace URI (if any) for the attribute
+ """
+ logger.debug(index)
+
+ uri = self.getAttributeUri(index)
+
+ # No Namespace
+ if uri == 0xFFFFFFFF:
+ return ''
+
+ return self.sb[uri]
+
+ def getAttributeName(self, index):
+ """
+ Returns the String which represents the attribute name
+ """
+ logger.debug(index)
+ offset = self._get_attribute_offset(index)
+ name = self.m_attributes[offset + ATTRIBUTE_IX_NAME]
+
+ res = self.sb[name]
+ # If the result is a (null) string, we need to look it up.
+ if not res or res == ":":
+ attr = self.m_resourceIDs[name]
+ if attr in public.SYSTEM_RESOURCES['attributes']['inverse']:
+ res = 'android:' + public.SYSTEM_RESOURCES['attributes']['inverse'][attr]
+ else:
+ # Attach the HEX Number, so for multiple missing attributes we do not run
+ # into problems.
+ res = 'android:UNKNOWN_SYSTEM_ATTRIBUTE_{:08x}'.format(attr)
+ return res
+
+ def getAttributeValueType(self, index):
+ """
+ Return the type of the attribute at the given index
+
+ :param index: index of the attribute
+ """
+ logger.debug(index)
+
+ offset = self._get_attribute_offset(index)
+ return self.m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]
+
+ def getAttributeValueData(self, index):
+ """
+ Return the data of the attribute at the given index
+
+ :param index: index of the attribute
+ """
+ logger.debug(index)
+
+ offset = self._get_attribute_offset(index)
+ return self.m_attributes[offset + ATTRIBUTE_IX_VALUE_DATA]
+
+ def getAttributeValue(self, index):
+ """
+ This function is only used to look up strings
+ All other work is done by
+ :func:`~androguard.core.bytecodes.axml.format_value`
+ # FIXME should unite those functions
+ :param index: index of the attribute
+ :return:
+ """
+ logger.debug(index)
+
+ offset = self._get_attribute_offset(index)
+ valueType = self.m_attributes[offset + ATTRIBUTE_IX_VALUE_TYPE]
+ if valueType == TYPE_STRING:
+ valueString = self.m_attributes[offset + ATTRIBUTE_IX_VALUE_STRING]
+ return self.sb[valueString]
+ return ''
+
+
+def format_value(_type, _data, lookup_string=lambda ix: ""):
+ """
+ Format a value based on type and data.
+ By default, no strings are looked up and "" is returned.
+ You need to define `lookup_string` in order to actually lookup strings from
+ the string table.
+
+ :param _type: The numeric type of the value
+ :param _data: The numeric data of the value
+ :param lookup_string: A function how to resolve strings from integer IDs
+ """
+
+ # Function to prepend android prefix for attributes/references from the
+ # android library
+ fmt_package = lambda x: "android:" if x >> 24 == 1 else ""
+
+ # Function to represent integers
+ fmt_int = lambda x: (0x7FFFFFFF & x) - 0x80000000 if x > 0x7FFFFFFF else x
+
+ if _type == TYPE_STRING:
+ return lookup_string(_data)
+
+ elif _type == TYPE_ATTRIBUTE:
+ return "?{}{:08X}".format(fmt_package(_data), _data)
+
+ elif _type == TYPE_REFERENCE:
+ return "@{}{:08X}".format(fmt_package(_data), _data)
+
+ elif _type == TYPE_FLOAT:
+ return "%f" % unpack("=f", pack("=L", _data))[0]
+
+ elif _type == TYPE_INT_HEX:
+ return "0x%08X" % _data
+
+ elif _type == TYPE_INT_BOOLEAN:
+ if _data == 0:
+ return "false"
+ return "true"
+
+ elif _type == TYPE_DIMENSION:
+ return "{:f}{}".format(complexToFloat(_data), DIMENSION_UNITS[_data & COMPLEX_UNIT_MASK])
+
+ elif _type == TYPE_FRACTION:
+ return "{:f}{}".format(complexToFloat(_data) * 100, FRACTION_UNITS[_data & COMPLEX_UNIT_MASK])
+
+ elif TYPE_FIRST_COLOR_INT <= _type <= TYPE_LAST_COLOR_INT:
+ return "#%08X" % _data
+
+ elif TYPE_FIRST_INT <= _type <= TYPE_LAST_INT:
+ return "%d" % fmt_int(_data)
+
+ return "<0x{:X}, type 0x{:02X}>".format(_data, _type)
+
+
+class AXMLPrinter:
+ """
+ Converter for AXML Files into a lxml ElementTree, which can easily be
+ converted into XML.
+
+ A Reference Implementation can be found at http://androidxref.com/9.0.0_r3/xref/frameworks/base/tools/aapt/XMLNode.cpp
+ """
+ __charrange = None
+ __replacement = None
+
+ def __init__(self, raw_buff):
+ logger.debug("AXMLPrinter")
+
+ self.axml = AXMLParser(raw_buff)
+
+ self.root = None
+ self.packerwarning = False
+ cur = []
+
+ while self.axml.is_valid():
+ _type = next(self.axml)
+ logger.debug("DEBUG ARSC TYPE {}".format(_type))
+
+ if _type == START_TAG:
+ uri = self._print_namespace(self.axml.namespace)
+ uri, name = self._fix_name(uri, self.axml.name)
+ tag = "{}{}".format(uri, name)
+
+ comment = self.axml.comment
+ if comment:
+ if self.root is None:
+ logger.warning("Can not attach comment with content '{}' without root!".format(comment))
+ else:
+ cur[-1].append(etree.Comment(comment))
+
+ logger.debug("START_TAG: {} (line={})".format(tag, self.axml.m_lineNumber))
+ elem = etree.Element(tag, nsmap=self.axml.nsmap)
+
+ for i in range(self.axml.getAttributeCount()):
+ uri = self._print_namespace(self.axml.getAttributeNamespace(i))
+ uri, name = self._fix_name(uri, self.axml.getAttributeName(i))
+ value = self._fix_value(self._get_attribute_value(i))
+
+ logger.debug("found an attribute: {}{}='{}'".format(uri, name, value.encode("utf-8")))
+ if "{}{}".format(uri, name) in elem.attrib:
+ logger.warning("Duplicate attribute '{}{}'! Will overwrite!".format(uri, name))
+ elem.set("{}{}".format(uri, name), value)
+
+ if self.root is None:
+ self.root = elem
+ else:
+ if not cur:
+ # looks like we lost the root?
+ logger.error("No more elements available to attach to! Is the XML malformed?")
+ break
+ cur[-1].append(elem)
+ cur.append(elem)
+
+ if _type == END_TAG:
+ if not cur:
+ logger.warning("Too many END_TAG! No more elements available to attach to!")
+
+ name = self.axml.name
+ uri = self._print_namespace(self.axml.namespace)
+ tag = "{}{}".format(uri, name)
+ if cur[-1].tag != tag:
+ logger.warning("Closing tag '{}' does not match current stack! At line number: {}. Is the XML malformed?".format(self.axml.name, self.axml.m_lineNumber))
+ cur.pop()
+ if _type == TEXT:
+ logger.debug("TEXT for {}".format(cur[-1]))
+ cur[-1].text = self.axml.text
+ if _type == END_DOCUMENT:
+ # Check if all namespace mappings are closed
+ if len(self.axml.namespaces) > 0:
+ logger.warning("Not all namespace mappings were closed! Malformed AXML?")
+ break
+
+ def get_buff(self):
+ """
+ Returns the raw XML file without prettification applied.
+
+ :returns: bytes, encoded as UTF-8
+ """
+ return self.get_xml(pretty=False)
+
+ def get_xml(self, pretty=True):
+ """
+ Get the XML as an UTF-8 string
+
+ :returns: bytes encoded as UTF-8
+ """
+ return etree.tostring(self.root, encoding="utf-8", pretty_print=pretty)
+
+ def get_xml_obj(self):
+ """
+ Get the XML as an ElementTree object
+
+ :returns: :class:`lxml.etree.Element`
+ """
+ return self.root
+
+ def is_valid(self):
+ """
+ Return the state of the AXMLParser.
+ If this flag is set to False, the parsing has failed, thus
+ the resulting XML will not work or will even be empty.
+ """
+ return self.axml.is_valid()
+
+ def is_packed(self):
+ """
+ Returns True if the AXML is likely to be packed
+
+ Packers do some weird stuff and we try to detect it.
+ Sometimes the files are not packed but simply broken or compiled with
+ some broken version of a tool.
+ Some file corruption might also be appear to be a packed file.
+
+ :returns: True if packer detected, False otherwise
+ """
+ return self.packerwarning
+
+ def _get_attribute_value(self, index):
+ """
+ Wrapper function for format_value to resolve the actual value of an attribute in a tag
+ :param index: index of the current attribute
+ :return: formatted value
+ """
+ _type = self.axml.getAttributeValueType(index)
+ _data = self.axml.getAttributeValueData(index)
+
+ return format_value(_type, _data, lambda _: self.axml.getAttributeValue(index))
+
+ def _fix_name(self, prefix, name):
+ """
+ Apply some fixes to element named and attribute names.
+ Try to get conform to:
+ > Like element names, attribute names are case-sensitive and must start with a letter or underscore.
+ > The rest of the name can contain letters, digits, hyphens, underscores, and periods.
+ See: https://msdn.microsoft.com/en-us/library/ms256152(v=vs.110).aspx
+
+ This function tries to fix some broken namespace mappings.
+ In some cases, the namespace prefix is inside the name and not in the prefix field.
+ Then, the tag name will usually look like 'android:foobar'.
+ If and only if the namespace prefix is inside the namespace mapping and the actual prefix field is empty,
+ we will strip the prefix from the attribute name and return the fixed prefix URI instead.
+ Otherwise replacement rules will be applied.
+
+ The replacement rules work in that way, that all unwanted characters are replaced by underscores.
+ In other words, all characters except the ones listed above are replaced.
+
+ :param name: Name of the attribute or tag
+ :param prefix: The existing prefix uri as found in the AXML chunk
+ :return: a fixed version of prefix and name
+ :rtype: tuple
+ """
+ if not name[0].isalpha() and name[0] != "_":
+ logger.warning("Invalid start for name '{}'. "
+ "XML name must start with a letter.".format(name))
+ self.packerwarning = True
+ name = "_{}".format(name)
+ if name.startswith("android:") and prefix == '' and 'android' in self.axml.nsmap:
+ # Seems be a common thing...
+ logger.info("Name '{}' starts with 'android:' prefix but 'android' is a known prefix. Replacing prefix.".format(name))
+ prefix = self._print_namespace(self.axml.nsmap['android'])
+ name = name[len("android:"):]
+ # It looks like this is some kind of packer... Not sure though.
+ self.packerwarning = True
+ elif ":" in name and prefix == '':
+ self.packerwarning = True
+ embedded_prefix, new_name = name.split(":", 1)
+ if embedded_prefix in self.axml.nsmap:
+ logger.info("Prefix '{}' is in namespace mapping, assume that it is a prefix.")
+ prefix = self._print_namespace(self.axml.nsmap[embedded_prefix])
+ name = new_name
+ else:
+ # Print out an extra warning
+ logger.warning("Confused: name contains a unknown namespace prefix: '{}'. "
+ "This is either a broken AXML file or some attempt to break stuff.".format(name))
+ if not re.match(r"^[a-zA-Z0-9._-]*$", name):
+ logger.warning("Name '{}' contains invalid characters!".format(name))
+ self.packerwarning = True
+ name = re.sub(r"[^a-zA-Z0-9._-]", "_", name)
+
+ return prefix, name
+
+ def _fix_value(self, value):
+ """
+ Return a cleaned version of a value
+ according to the specification:
+ > Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
+
+ See https://www.w3.org/TR/xml/#charsets
+
+ :param value: a value to clean
+ :return: the cleaned value
+ """
+ if not self.__charrange or not self.__replacement:
+ self.__charrange = re.compile('^[\u0020-\uD7FF\u0009\u000A\u000D\uE000-\uFFFD\U00010000-\U0010FFFF]*$')
+ self.__replacement = re.compile('[^\u0020-\uD7FF\u0009\u000A\u000D\uE000-\uFFFD\U00010000-\U0010FFFF]')
+
+ # Reading string until \x00. This is the same as aapt does.
+ if "\x00" in value:
+ self.packerwarning = True
+ logger.warning("Null byte found in attribute value at position {}: "
+ "Value(hex): '{}'".format(
+ value.find("\x00"),
+ binascii.hexlify(value.encode("utf-8"))))
+ value = value[:value.find("\x00")]
+
+ if not self.__charrange.match(value):
+ logger.warning("Invalid character in value found. Replacing with '_'.")
+ self.packerwarning = True
+ value = self.__replacement.sub('_', value)
+ return value
+
+ def _print_namespace(self, uri):
+ if uri != "":
+ uri = "{{{}}}".format(uri)
+ return uri
+
+# See http://aospxref.com/android-13.0.0_r3/xref/frameworks/native/include/android/configuration.h#56
+
+ACONFIGURATION_ORIENTATION_ANY = 0x0000
+ACONFIGURATION_ORIENTATION_PORT = 0x0001
+ACONFIGURATION_ORIENTATION_LAND = 0x0002
+ACONFIGURATION_ORIENTATION_SQUARE = 0x0003
+ACONFIGURATION_TOUCHSCREEN_ANY = 0x0000
+ACONFIGURATION_TOUCHSCREEN_NOTOUCH = 0x0001
+ACONFIGURATION_TOUCHSCREEN_STYLUS = 0x0002
+ACONFIGURATION_TOUCHSCREEN_FINGER = 0x0003
+ACONFIGURATION_DENSITY_DEFAULT = 0
+ACONFIGURATION_DENSITY_LOW = 120
+ACONFIGURATION_DENSITY_MEDIUM = 160
+ACONFIGURATION_DENSITY_TV = 213
+ACONFIGURATION_DENSITY_HIGH = 240
+ACONFIGURATION_DENSITY_XHIGH = 320
+ACONFIGURATION_DENSITY_XXHIGH = 480
+ACONFIGURATION_DENSITY_XXXHIGH = 640
+ACONFIGURATION_DENSITY_ANY = 0xfffe
+ACONFIGURATION_DENSITY_NONE = 0xffff
+ACONFIGURATION_KEYBOARD_ANY = 0x0000
+ACONFIGURATION_KEYBOARD_NOKEYS = 0x0001
+ACONFIGURATION_KEYBOARD_QWERTY = 0x0002
+ACONFIGURATION_KEYBOARD_12KEY = 0x0003
+ACONFIGURATION_NAVIGATION_ANY = 0x0000
+ACONFIGURATION_NAVIGATION_NONAV = 0x0001
+ACONFIGURATION_NAVIGATION_DPAD = 0x0002
+ACONFIGURATION_NAVIGATION_TRACKBALL = 0x0003
+ACONFIGURATION_NAVIGATION_WHEEL = 0x0004
+ACONFIGURATION_KEYSHIDDEN_ANY = 0x0000
+ACONFIGURATION_KEYSHIDDEN_NO = 0x0001
+ACONFIGURATION_KEYSHIDDEN_YES = 0x0002
+ACONFIGURATION_KEYSHIDDEN_SOFT = 0x0003
+ACONFIGURATION_NAVHIDDEN_ANY = 0x0000
+ACONFIGURATION_NAVHIDDEN_NO = 0x0001
+ACONFIGURATION_NAVHIDDEN_YES = 0x0002
+ACONFIGURATION_SCREENSIZE_ANY = 0x00
+ACONFIGURATION_SCREENSIZE_SMALL = 0x01
+ACONFIGURATION_SCREENSIZE_NORMAL = 0x02
+ACONFIGURATION_SCREENSIZE_LARGE = 0x03
+ACONFIGURATION_SCREENSIZE_XLARGE = 0x04
+ACONFIGURATION_SCREENLONG_ANY = 0x00
+ACONFIGURATION_SCREENLONG_NO = 0x1
+ACONFIGURATION_SCREENLONG_YES = 0x2
+ACONFIGURATION_SCREENROUND_ANY = 0x00
+ACONFIGURATION_SCREENROUND_NO = 0x1
+ACONFIGURATION_SCREENROUND_YES = 0x2
+ACONFIGURATION_WIDE_COLOR_GAMUT_ANY = 0x00
+ACONFIGURATION_WIDE_COLOR_GAMUT_NO = 0x1
+ACONFIGURATION_WIDE_COLOR_GAMUT_YES = 0x2
+ACONFIGURATION_HDR_ANY = 0x00
+ACONFIGURATION_HDR_NO = 0x1
+ACONFIGURATION_HDR_YES = 0x2
+ACONFIGURATION_UI_MODE_TYPE_ANY = 0x00
+ACONFIGURATION_UI_MODE_TYPE_NORMAL = 0x01
+ACONFIGURATION_UI_MODE_TYPE_DESK = 0x02
+ACONFIGURATION_UI_MODE_TYPE_CAR = 0x03
+ACONFIGURATION_UI_MODE_TYPE_TELEVISION = 0x04
+ACONFIGURATION_UI_MODE_TYPE_APPLIANCE = 0x05
+ACONFIGURATION_UI_MODE_TYPE_WATCH = 0x06
+ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET = 0x07
+ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00
+ACONFIGURATION_UI_MODE_NIGHT_NO = 0x1
+ACONFIGURATION_UI_MODE_NIGHT_YES = 0x2
+ACONFIGURATION_SCREEN_WIDTH_DP_ANY = 0x0000
+ACONFIGURATION_SCREEN_HEIGHT_DP_ANY = 0x0000
+ACONFIGURATION_SMALLEST_SCREEN_WIDTH_DP_ANY = 0x0000
+ACONFIGURATION_LAYOUTDIR_ANY = 0x00
+ACONFIGURATION_LAYOUTDIR_LTR = 0x01
+ACONFIGURATION_LAYOUTDIR_RTL = 0x02
+ACONFIGURATION_MCC = 0x0001
+ACONFIGURATION_MNC = 0x0002
+ACONFIGURATION_LOCALE = 0x0004
+ACONFIGURATION_TOUCHSCREEN = 0x0008
+ACONFIGURATION_KEYBOARD = 0x0010
+ACONFIGURATION_KEYBOARD_HIDDEN = 0x0020
+ACONFIGURATION_NAVIGATION = 0x0040
+ACONFIGURATION_ORIENTATION = 0x0080
+ACONFIGURATION_DENSITY = 0x0100
+ACONFIGURATION_SCREEN_SIZE = 0x0200
+ACONFIGURATION_VERSION = 0x0400
+ACONFIGURATION_SCREEN_LAYOUT = 0x0800
+ACONFIGURATION_UI_MODE = 0x1000
+ACONFIGURATION_SMALLEST_SCREEN_SIZE = 0x2000
+ACONFIGURATION_LAYOUTDIR = 0x4000
+ACONFIGURATION_SCREEN_ROUND = 0x8000
+ACONFIGURATION_COLOR_MODE = 0x10000
+ACONFIGURATION_MNC_ZERO = 0xffff
+
+# See http://aospxref.com/android-13.0.0_r3/xref/frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h#946
+
+ORIENTATION_ANY = ACONFIGURATION_ORIENTATION_ANY
+ORIENTATION_PORT = ACONFIGURATION_ORIENTATION_PORT
+ORIENTATION_LAND = ACONFIGURATION_ORIENTATION_LAND
+ORIENTATION_SQUARE = ACONFIGURATION_ORIENTATION_SQUARE
+
+TOUCHSCREEN_ANY = ACONFIGURATION_TOUCHSCREEN_ANY
+TOUCHSCREEN_NOTOUCH = ACONFIGURATION_TOUCHSCREEN_NOTOUCH
+TOUCHSCREEN_STYLUS = ACONFIGURATION_TOUCHSCREEN_STYLUS
+TOUCHSCREEN_FINGER = ACONFIGURATION_TOUCHSCREEN_FINGER
+
+DENSITY_DEFAULT = ACONFIGURATION_DENSITY_DEFAULT
+DENSITY_LOW = ACONFIGURATION_DENSITY_LOW
+DENSITY_MEDIUM = ACONFIGURATION_DENSITY_MEDIUM
+DENSITY_TV = ACONFIGURATION_DENSITY_TV
+DENSITY_HIGH = ACONFIGURATION_DENSITY_HIGH
+DENSITY_XHIGH = ACONFIGURATION_DENSITY_XHIGH
+DENSITY_XXHIGH = ACONFIGURATION_DENSITY_XXHIGH
+DENSITY_XXXHIGH = ACONFIGURATION_DENSITY_XXXHIGH
+DENSITY_ANY = ACONFIGURATION_DENSITY_ANY
+DENSITY_NONE = ACONFIGURATION_DENSITY_NONE
+
+KEYBOARD_ANY = ACONFIGURATION_KEYBOARD_ANY
+KEYBOARD_NOKEYS = ACONFIGURATION_KEYBOARD_NOKEYS
+KEYBOARD_QWERTY = ACONFIGURATION_KEYBOARD_QWERTY
+KEYBOARD_12KEY = ACONFIGURATION_KEYBOARD_12KEY
+
+NAVIGATION_ANY = ACONFIGURATION_NAVIGATION_ANY
+NAVIGATION_NONAV = ACONFIGURATION_NAVIGATION_NONAV
+NAVIGATION_DPAD = ACONFIGURATION_NAVIGATION_DPAD
+NAVIGATION_TRACKBALL = ACONFIGURATION_NAVIGATION_TRACKBALL
+NAVIGATION_WHEEL = ACONFIGURATION_NAVIGATION_WHEEL
+
+MASK_KEYSHIDDEN = 0x0003
+KEYSHIDDEN_ANY = ACONFIGURATION_KEYSHIDDEN_ANY
+KEYSHIDDEN_NO = ACONFIGURATION_KEYSHIDDEN_NO
+KEYSHIDDEN_YES = ACONFIGURATION_KEYSHIDDEN_YES
+KEYSHIDDEN_SOFT = ACONFIGURATION_KEYSHIDDEN_SOFT
+
+MASK_NAVHIDDEN = 0x000c
+SHIFT_NAVHIDDEN = 2
+NAVHIDDEN_ANY = ACONFIGURATION_NAVHIDDEN_ANY << SHIFT_NAVHIDDEN
+NAVHIDDEN_NO = ACONFIGURATION_NAVHIDDEN_NO << SHIFT_NAVHIDDEN
+NAVHIDDEN_YES = ACONFIGURATION_NAVHIDDEN_YES << SHIFT_NAVHIDDEN
+
+SCREENWIDTH_ANY = 0
+SCREENHEIGHT_ANY = 0
+SDKVERSION_ANY = 0
+MINORVERSION_ANY = 0
+
+MASK_SCREENSIZE = 0x0f
+SCREENSIZE_ANY = ACONFIGURATION_SCREENSIZE_ANY
+SCREENSIZE_SMALL = ACONFIGURATION_SCREENSIZE_SMALL
+SCREENSIZE_NORMAL = ACONFIGURATION_SCREENSIZE_NORMAL
+SCREENSIZE_LARGE = ACONFIGURATION_SCREENSIZE_LARGE
+SCREENSIZE_XLARGE = ACONFIGURATION_SCREENSIZE_XLARGE
+
+MASK_SCREENLONG = 0x30
+SHIFT_SCREENLONG = 4
+SCREENLONG_ANY = ACONFIGURATION_SCREENLONG_ANY << SHIFT_SCREENLONG
+SCREENLONG_NO = ACONFIGURATION_SCREENLONG_NO << SHIFT_SCREENLONG
+SCREENLONG_YES = ACONFIGURATION_SCREENLONG_YES << SHIFT_SCREENLONG
+
+MASK_LAYOUTDIR = 0xC0
+SHIFT_LAYOUTDIR = 6
+LAYOUTDIR_ANY = ACONFIGURATION_LAYOUTDIR_ANY << SHIFT_LAYOUTDIR
+LAYOUTDIR_LTR = ACONFIGURATION_LAYOUTDIR_LTR << SHIFT_LAYOUTDIR
+LAYOUTDIR_RTL = ACONFIGURATION_LAYOUTDIR_RTL << SHIFT_LAYOUTDIR
+
+MASK_UI_MODE_TYPE = 0x0f
+UI_MODE_TYPE_ANY = ACONFIGURATION_UI_MODE_TYPE_ANY
+UI_MODE_TYPE_NORMAL = ACONFIGURATION_UI_MODE_TYPE_NORMAL
+UI_MODE_TYPE_DESK = ACONFIGURATION_UI_MODE_TYPE_DESK
+UI_MODE_TYPE_CAR = ACONFIGURATION_UI_MODE_TYPE_CAR
+UI_MODE_TYPE_TELEVISION = ACONFIGURATION_UI_MODE_TYPE_TELEVISION
+UI_MODE_TYPE_APPLIANCE = ACONFIGURATION_UI_MODE_TYPE_APPLIANCE
+UI_MODE_TYPE_WATCH = ACONFIGURATION_UI_MODE_TYPE_WATCH
+UI_MODE_TYPE_VR_HEADSET = ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET
+
+MASK_UI_MODE_NIGHT = 0x30
+SHIFT_UI_MODE_NIGHT = 4
+UI_MODE_NIGHT_ANY = ACONFIGURATION_UI_MODE_NIGHT_ANY << SHIFT_UI_MODE_NIGHT
+UI_MODE_NIGHT_NO = ACONFIGURATION_UI_MODE_NIGHT_NO << SHIFT_UI_MODE_NIGHT
+UI_MODE_NIGHT_YES = ACONFIGURATION_UI_MODE_NIGHT_YES << SHIFT_UI_MODE_NIGHT
+
+MASK_SCREENROUND = 0x03
+SCREENROUND_ANY = ACONFIGURATION_SCREENROUND_ANY
+SCREENROUND_NO = ACONFIGURATION_SCREENROUND_NO
+SCREENROUND_YES = ACONFIGURATION_SCREENROUND_YES
+
+MASK_WIDE_COLOR_GAMUT = 0x03
+WIDE_COLOR_GAMUT_ANY = ACONFIGURATION_WIDE_COLOR_GAMUT_ANY
+WIDE_COLOR_GAMUT_NO = ACONFIGURATION_WIDE_COLOR_GAMUT_NO
+WIDE_COLOR_GAMUT_YES = ACONFIGURATION_WIDE_COLOR_GAMUT_YES
+
+MASK_HDR = 0x0c
+SHIFT_HDR = 2
+HDR_ANY = ACONFIGURATION_HDR_ANY << SHIFT_HDR
+HDR_NO = ACONFIGURATION_HDR_NO << SHIFT_HDR
+HDR_YES = ACONFIGURATION_HDR_YES << SHIFT_HDR
+
+
+class ARSCParser:
+ """
+ Parser for resource.arsc files
+
+ The ARSC File is, like the binary XML format, a chunk based format.
+ Both formats are actually identical but use different chunks in order to store the data.
+
+ The most outer chunk in the ARSC file is a chunk of type RES_TABLE_TYPE.
+ Inside this chunk is a StringPool and at least one package.
+
+ Each package is a chunk of type RES_TABLE_PACKAGE_TYPE.
+ It contains again many more chunks.
+ """
+ def __init__(self, raw_buff):
+ """
+ :param bytes raw_buff: the raw bytes of the file
+ """
+ self.buff = io.BufferedReader(io.BytesIO(raw_buff))
+ self.buff_size = self.buff.raw.getbuffer().nbytes
+
+ if self.buff_size < 8 or self.buff_size > 0xFFFFFFFF:
+ raise ResParserError("Invalid file size {} for a resources.arsc file!".format(self.buff.size()))
+
+ self.analyzed = False
+ self._resolved_strings = None
+ self.packages = defaultdict(list)
+ self.values = {}
+ self.resource_values = defaultdict(defaultdict)
+ self.resource_configs = defaultdict(lambda: defaultdict(set))
+ self.resource_keys = defaultdict(lambda: defaultdict(defaultdict))
+ self.stringpool_main = None
+
+ # First, there is a ResTable_header.
+ self.header = ARSCHeader(self.buff, expected_type=RES_TABLE_TYPE)
+
+ # More sanity checks...
+ if self.header.header_size != 12:
+ logger.warning("The ResTable_header has an unexpected header size! Expected 12 bytes, got {}.".format(self.header.header_size))
+
+ if self.header.size > self.buff_size:
+ raise ResParserError("The file seems to be truncated. Refuse to parse the file! Filesize: {}, declared size: {}".format(self.buff_size, self.header.size))
+
+ if self.header.size < self.buff_size:
+ logger.warning("The Resource file seems to have data appended to it. Filesize: {}, declared size: {}".format(self.buff_size, self.header.size))
+
+ # The ResTable_header contains the packageCount, i.e. the number of ResTable_package
+ self.packageCount = unpack(' self.header.end:
+ # this inner chunk crosses the boundary of the table chunk
+ logger.warning("Invalid chunk found! It is larger than the outer chunk: %s", res_header)
+ break
+
+ if res_header.type == RES_STRING_POOL_TYPE:
+ # There should be only one StringPool per resource table.
+ if self.stringpool_main:
+ logger.warning("Already found a ResStringPool_header, but there should be only one! Will not parse the Pool again.")
+ else:
+ self.stringpool_main = StringBlock(self.buff, res_header)
+ logger.debug("Found the main string pool: %s", self.stringpool_main)
+
+ elif res_header.type == RES_TABLE_PACKAGE_TYPE:
+ if len(self.packages) > self.packageCount:
+ raise ResParserError("Got more packages ({}) than expected ({})".format(len(self.packages), self.packageCount))
+
+ current_package = ARSCResTablePackage(self.buff, res_header)
+ package_name = current_package.get_name()
+
+ # After the Header, we have the resource type symbol table
+ self.buff.seek(current_package.header.start + current_package.typeStrings)
+ type_sp_header = ARSCHeader(self.buff, expected_type=RES_STRING_POOL_TYPE)
+ mTableStrings = StringBlock(self.buff, type_sp_header)
+
+ # Next, we should have the resource key symbol table
+ self.buff.seek(current_package.header.start + current_package.keyStrings)
+ key_sp_header = ARSCHeader(self.buff, expected_type=RES_STRING_POOL_TYPE)
+ mKeyStrings = StringBlock(self.buff, key_sp_header)
+
+ # Add them to the dict of read packages
+ self.packages[package_name].append(current_package)
+ self.packages[package_name].append(mTableStrings)
+ self.packages[package_name].append(mKeyStrings)
+
+ pc = PackageContext(current_package, self.stringpool_main, mTableStrings, mKeyStrings)
+ logger.debug("Constructed a PackageContext: %s", pc)
+
+ # skip to the first header in this table package chunk
+ # FIXME is this correct? We have already read the first two sections!
+ # self.buff.set_idx(res_header.start + res_header.header_size)
+ # this looks more like we want: (???)
+ # FIXME it looks like that the two string pools we have read might not be concatenated to each other,
+ # thus jumping to the sum of the sizes might not be correct...
+ next_idx = res_header.start + res_header.header_size + type_sp_header.size + key_sp_header.size
+
+ if next_idx != self.buff.tell():
+ # If this happens, we have a testfile ;)
+ logger.error("This looks like an odd resources.arsc file!")
+ logger.error("Please report this error including the file you have parsed!")
+ logger.error("next_idx = {}, current buffer position = {}".format(next_idx, self.buff.tell()))
+ logger.error("Please open a issue at https://github.com/androguard/androguard/issues")
+ logger.error("Thank you!")
+
+ self.buff.seek(next_idx)
+
+ # Read all other headers
+ while self.buff.tell() <= res_header.end - ARSCHeader.SIZE:
+ pkg_chunk_header = ARSCHeader(self.buff)
+ logger.debug("Found a header: {}".format(pkg_chunk_header))
+ if pkg_chunk_header.start + pkg_chunk_header.size > res_header.end:
+ # we are way off the package chunk; bail out
+ break
+
+ self.packages[package_name].append(pkg_chunk_header)
+
+ if pkg_chunk_header.type == RES_TABLE_TYPE_SPEC_TYPE:
+ self.packages[package_name].append(ARSCResTypeSpec(self.buff, pc))
+
+ elif pkg_chunk_header.type == RES_TABLE_TYPE_TYPE:
+ # Parse a RES_TABLE_TYPE
+ # http://androidxref.com/9.0.0_r3/xref/frameworks/base/tools/aapt2/format/binary/BinaryResourceParser.cpp#311
+ a_res_type = ARSCResType(self.buff, pc)
+ self.packages[package_name].append(a_res_type)
+ self.resource_configs[package_name][a_res_type].add(a_res_type.config)
+
+ logger.debug("Config: {}".format(a_res_type.config))
+
+ entries = []
+ for i in range(0, a_res_type.entryCount):
+ current_package.mResId = current_package.mResId & 0xffff0000 | i
+ entries.append((unpack('> 24) & 0xFF),
+ ((entry_data >> 16) & 0xFF),
+ ((entry_data >> 8) & 0xFF),
+ (entry_data & 0xFF))
+ ]
+
+ def get_resource_dimen(self, ate):
+ try:
+ return [
+ ate.get_value(), "{}{}".format(
+ complexToFloat(ate.key.get_data()),
+ DIMENSION_UNITS[ate.key.get_data() & COMPLEX_UNIT_MASK])
+ ]
+ except IndexError:
+ logger.debug("Out of range dimension unit index for {}: {}".format(
+ complexToFloat(ate.key.get_data()),
+ ate.key.get_data() & COMPLEX_UNIT_MASK))
+ return [ate.get_value(), ate.key.get_data()]
+
+ # FIXME
+ def get_resource_style(self, ate):
+ return ["", ""]
+
+ def get_packages_names(self):
+ """
+ Retrieve a list of all package names, which are available
+ in the given resources.arsc.
+ """
+ return list(self.packages.keys())
+
+ def get_locales(self, package_name):
+ """
+ Retrieve a list of all available locales in a given packagename.
+
+ :param package_name: the package name to get locales of
+ """
+ self._analyse()
+ return list(self.values[package_name].keys())
+
+ def get_types(self, package_name, locale='\x00\x00'):
+ """
+ Retrieve a list of all types which are available in the given
+ package and locale.
+
+ :param package_name: the package name to get types of
+ :param locale: the locale to get types of (default: '\x00\x00')
+ """
+ self._analyse()
+ return list(self.values[package_name][locale].keys())
+
+ def get_public_resources(self, package_name, locale='\x00\x00'):
+ """
+ Get the XML (as string) of all resources of type 'public'.
+
+ The public resources table contains the IDs for each item.
+
+ :param package_name: the package name to get the resources for
+ :param locale: the locale to get the resources for (default: '\x00\x00')
+ """
+
+ self._analyse()
+
+ buff = '\n'
+ buff += '\n'
+
+ try:
+ for i in self.values[package_name][locale]["public"]:
+ buff += '\n'.format(
+ i[0], i[1], i[2])
+ except KeyError:
+ pass
+
+ buff += '\n'
+
+ return buff.encode('utf-8')
+
+ def get_string_resources(self, package_name, locale='\x00\x00'):
+ """
+ Get the XML (as string) of all resources of type 'string'.
+
+ Read more about string resources:
+ https://developer.android.com/guide/topics/resources/string-resource.html
+
+ :param package_name: the package name to get the resources for
+ :param locale: the locale to get the resources for (default: '\x00\x00')
+ """
+ self._analyse()
+
+ buff = '\n'
+ buff += '\n'
+
+ try:
+ for i in self.values[package_name][locale]["string"]:
+ if any(map(i[1].__contains__, '<&>')):
+ value = '' % i[1]
+ else:
+ value = i[1]
+ buff += '{}\n'.format(i[0], value)
+ except KeyError:
+ pass
+
+ buff += '\n'
+
+ return buff.encode('utf-8')
+
+ def get_strings_resources(self):
+ """
+ Get the XML (as string) of all resources of type 'string'.
+ This is a combined variant, which has all locales and all package names
+ stored.
+ """
+ self._analyse()
+
+ buff = '\n'
+
+ buff += "\n"
+ for package_name in self.get_packages_names():
+ buff += "\n" % package_name
+
+ for locale in self.get_locales(package_name):
+ buff += "\n" % repr(locale)
+
+ buff += '\n'
+ try:
+ for i in self.values[package_name][locale]["string"]:
+ buff += '{}\n'.format(i[0], escape(i[1]))
+ except KeyError:
+ pass
+
+ buff += '\n'
+ buff += '\n'
+
+ buff += "\n"
+
+ buff += "\n"
+
+ return buff.encode('utf-8')
+
+ def get_id_resources(self, package_name, locale='\x00\x00'):
+ """
+ Get the XML (as string) of all resources of type 'id'.
+
+ Read more about ID resources:
+ https://developer.android.com/guide/topics/resources/more-resources.html#Id
+
+ :param package_name: the package name to get the resources for
+ :param locale: the locale to get the resources for (default: '\x00\x00')
+ """
+ self._analyse()
+
+ buff = '\n'
+ buff += '\n'
+
+ try:
+ for i in self.values[package_name][locale]["id"]:
+ if len(i) == 1:
+ buff += ' \n' % (i[0])
+ else:
+ buff += '- {}
\n'.format(i[0],
+ escape(i[1]))
+ except KeyError:
+ pass
+
+ buff += '\n'
+
+ return buff.encode('utf-8')
+
+ def get_bool_resources(self, package_name, locale='\x00\x00'):
+ """
+ Get the XML (as string) of all resources of type 'bool'.
+
+ Read more about bool resources:
+ https://developer.android.com/guide/topics/resources/more-resources.html#Bool
+
+ :param package_name: the package name to get the resources for
+ :param locale: the locale to get the resources for (default: '\x00\x00')
+ """
+ self._analyse()
+
+ buff = '\n'
+ buff += '\n'
+
+ try:
+ for i in self.values[package_name][locale]["bool"]:
+ buff += '{}\n'.format(i[0], i[1])
+ except KeyError:
+ pass
+
+ buff += '\n'
+
+ return buff.encode('utf-8')
+
+ def get_integer_resources(self, package_name, locale='\x00\x00'):
+ """
+ Get the XML (as string) of all resources of type 'integer'.
+
+ Read more about integer resources:
+ https://developer.android.com/guide/topics/resources/more-resources.html#Integer
+
+ :param package_name: the package name to get the resources for
+ :param locale: the locale to get the resources for (default: '\x00\x00')
+ """
+ self._analyse()
+
+ buff = '\n'
+ buff += '\n'
+
+ try:
+ for i in self.values[package_name][locale]["integer"]:
+ buff += '{}\n'.format(i[0], i[1])
+ except KeyError:
+ pass
+
+ buff += '\n'
+
+ return buff.encode('utf-8')
+
+ def get_color_resources(self, package_name, locale='\x00\x00'):
+ """
+ Get the XML (as string) of all resources of type 'color'.
+
+ Read more about color resources:
+ https://developer.android.com/guide/topics/resources/more-resources.html#Color
+
+ :param package_name: the package name to get the resources for
+ :param locale: the locale to get the resources for (default: '\x00\x00')
+ """
+ self._analyse()
+
+ buff = '\n'
+ buff += '\n'
+
+ try:
+ for i in self.values[package_name][locale]["color"]:
+ buff += '{}\n'.format(i[0], i[1])
+ except KeyError:
+ pass
+
+ buff += '\n'
+
+ return buff.encode('utf-8')
+
+ def get_dimen_resources(self, package_name, locale='\x00\x00'):
+ """
+ Get the XML (as string) of all resources of type 'dimen'.
+
+ Read more about Dimension resources:
+ https://developer.android.com/guide/topics/resources/more-resources.html#Dimension
+
+ :param package_name: the package name to get the resources for
+ :param locale: the locale to get the resources for (default: '\x00\x00')
+ """
+ self._analyse()
+
+ buff = '\n'
+ buff += '\n'
+
+ try:
+ for i in self.values[package_name][locale]["dimen"]:
+ buff += '{}\n'.format(i[0], i[1])
+ except KeyError:
+ pass
+
+ buff += '\n'
+
+ return buff.encode('utf-8')
+
+ def get_id(self, package_name, rid, locale='\x00\x00'):
+ """
+ Returns the tuple (resource_type, resource_name, resource_id)
+ for the given resource_id.
+
+ :param package_name: package name to query
+ :param rid: the resource_id
+ :param locale: specific locale
+ :return: tuple of (resource_type, resource_name, resource_id)
+ """
+ self._analyse()
+
+ try:
+ for i in self.values[package_name][locale]["public"]:
+ if i[2] == rid:
+ return i
+ except KeyError:
+ pass
+ return None, None, None
+
+ class ResourceResolver:
+ """
+ Resolves resources by ID and configuration.
+ This resolver deals with complex resources as well as with references.
+ """
+ def __init__(self, android_resources, config=None):
+ """
+ :param ARSCParser android_resources: A resource parser
+ :param ARSCResTableConfig config: The desired configuration or None to resolve all.
+ """
+ self.resources = android_resources
+ self.wanted_config = config
+
+ def resolve(self, res_id):
+ """
+ the given ID into the Resource and returns a list of matching resources.
+
+ :param int res_id: numerical ID of the resource
+ :return: a list of tuples of (ARSCResTableConfig, str)
+ """
+ result = []
+ self._resolve_into_result(result, res_id, self.wanted_config)
+ return result
+
+ def _resolve_into_result(self, result, res_id, config):
+ # First: Get all candidates
+ configs = self.resources.get_res_configs(res_id, config)
+
+ for config, ate in configs:
+ # deconstruct them and check if more candidates are generated
+ self.put_ate_value(result, ate, config)
+
+ def put_ate_value(self, result, ate, config):
+ """
+ Put a ResTableEntry into the list of results
+ :param list result: results array
+ :param ARSCResTableEntry ate:
+ :param ARSCResTableConfig config:
+ :return:
+ """
+ if ate.is_complex():
+ complex_array = []
+ result.append((config, complex_array))
+ for _, item in ate.item.items:
+ self.put_item_value(complex_array, item, config, ate, complex_=True)
+ else:
+ self.put_item_value(result, ate.key, config, ate, complex_=False)
+
+ def put_item_value(self, result, item, config, parent, complex_):
+ """
+ Put the tuple (ARSCResTableConfig, resolved string) into the result set
+
+ :param list result: the result set
+ :param ARSCResStringPoolRef item:
+ :param ARSCResTableConfig config:
+ :param ARSCResTableEntry parent: the originating entry
+ :param bool complex_: True if the originating :class:`ARSCResTableEntry` was complex
+ :return:
+ """
+ if item.is_reference():
+ res_id = item.get_data()
+ if res_id:
+ # Infinite loop detection:
+ # TODO should this stay here or should be detect the loop much earlier?
+ if res_id == parent.mResId:
+ logger.warning("Infinite loop detected at resource item {}. It references itself!".format(parent))
+ return
+
+ self._resolve_into_result(result, item.get_data(), self.wanted_config)
+ else:
+ if complex_:
+ result.append(item.format_value())
+ else:
+ result.append((config, item.format_value()))
+
+ def get_resolved_res_configs(self, rid, config=None):
+ """
+ Return a list of resolved resource IDs with their corresponding configuration.
+ It has a similar return type as :meth:`get_res_configs` but also handles complex entries
+ and references.
+ Also instead of returning :class:`ARSCResTableEntry` in the tuple, the actual values are resolved.
+
+ This is the preferred way of resolving resource IDs to their resources.
+
+ :param int rid: the numerical ID of the resource
+ :param ARSCTableResConfig config: the desired configuration or None to retrieve all
+ :return: A list of tuples of (ARSCResTableConfig, str)
+ """
+ resolver = ARSCParser.ResourceResolver(self, config)
+ return resolver.resolve(rid)
+
+ def get_resolved_strings(self):
+ self._analyse()
+ if self._resolved_strings:
+ return self._resolved_strings
+
+ r = {}
+ for package_name in self.get_packages_names():
+ r[package_name] = {}
+ k = {}
+
+ for locale in self.values[package_name]:
+ v_locale = locale
+ if v_locale == '\x00\x00':
+ v_locale = 'DEFAULT'
+
+ r[package_name][v_locale] = {}
+
+ try:
+ for i in self.values[package_name][locale]["public"]:
+ if i[0] == 'string':
+ r[package_name][v_locale][i[2]] = None
+ k[i[1]] = i[2]
+ except KeyError:
+ pass
+
+ try:
+ for i in self.values[package_name][locale]["string"]:
+ if i[0] in k:
+ r[package_name][v_locale][k[i[0]]] = i[1]
+ except KeyError:
+ pass
+
+ self._resolved_strings = r
+ return r
+
+ def get_res_configs(self, rid, config=None, fallback=True):
+ """
+ Return the resources found with the ID `rid` and select
+ the right one based on the configuration, or return all if no configuration was set.
+
+ But we try to be generous here and at least try to resolve something:
+ This method uses a fallback to return at least one resource (the first one in the list)
+ if more than one items are found and the default config is used and no default entry could be found.
+
+ This is usually a bad sign (i.e. the developer did not follow the android documentation:
+ https://developer.android.com/guide/topics/resources/localization.html#failing2)
+ In practise an app might just be designed to run on a single locale and thus only has those locales set.
+
+ You can disable this fallback behaviour, to just return exactly the given result.
+
+ :param rid: resource id as int
+ :param config: a config to resolve from, or None to get all results
+ :param fallback: Enable the fallback for resolving default configuration (default: True)
+ :return: a list of ARSCResTableConfig: ARSCResTableEntry
+ """
+ self._analyse()
+
+ if not rid:
+ raise ValueError("'rid' should be set")
+ if not isinstance(rid, int):
+ raise ValueError("'rid' must be an int")
+
+ if rid not in self.resource_values:
+ logger.warning("The requested rid '0x{:08x}' could not be found in the list of resources.".format(rid))
+ return []
+
+ res_options = self.resource_values[rid]
+ if len(res_options) > 1 and config:
+ if config in res_options:
+ return [(config, res_options[config])]
+ elif fallback and config == ARSCResTableConfig.default_config():
+ logger.warning("No default resource config could be found for the given rid '0x{:08x}', using fallback!".format(rid))
+ return [list(self.resource_values[rid].items())[0]]
+ else:
+ return []
+ else:
+ return list(res_options.items())
+
+ def get_string(self, package_name, name, locale='\x00\x00'):
+ self._analyse()
+
+ try:
+ for i in self.values[package_name][locale]["string"]:
+ if i[0] == name:
+ return i
+ except KeyError:
+ return None
+
+ def get_res_id_by_key(self, package_name, resource_type, key):
+ try:
+ return self.resource_keys[package_name][resource_type][key]
+ except KeyError:
+ return None
+
+ def get_items(self, package_name):
+ self._analyse()
+ return self.packages[package_name]
+
+ def get_type_configs(self, package_name, type_name=None):
+ if package_name is None:
+ package_name = self.get_packages_names()[0]
+ result = collections.defaultdict(list)
+
+ for res_type, configs in list(self.resource_configs[package_name].items()):
+ if res_type.get_package_name() == package_name and (
+ type_name is None or res_type.get_type() == type_name):
+ result[res_type.get_type()].extend(configs)
+
+ return result
+
+ @staticmethod
+ def parse_id(name):
+ """
+ Resolves an id from a binary XML file in the form "@[package:]DEADBEEF"
+ and returns a tuple of package name and resource id.
+ If no package name was given, i.e. the ID has the form "@DEADBEEF",
+ the package name is set to None.
+
+ Raises a ValueError if the id is malformed.
+
+ :param name: the string of the resource, as in the binary XML file
+ :return: a tuple of (resource_id, package_name).
+ """
+
+ if not name.startswith('@'):
+ raise ValueError("Not a valid resource ID, must start with @: '{}'".format(name))
+
+ # remove @
+ name = name[1:]
+
+ package = None
+ if ':' in name:
+ package, res_id = name.split(':', 1)
+ else:
+ res_id = name
+
+ if len(res_id) != 8:
+ raise ValueError("Numerical ID is not 8 characters long: '{}'".format(res_id))
+
+ try:
+ return int(res_id, 16), package
+ except ValueError:
+ raise ValueError("ID is not a hex ID: '{}'".format(res_id))
+
+ def get_resource_xml_name(self, r_id, package=None):
+ """
+ Returns the XML name for a resource, including the package name if package is None.
+ A full name might look like `@com.example:string/foobar`
+ Otherwise the name is only looked up in the specified package and is returned without
+ the package name.
+ The same example from about without the package name will read as `@string/foobar`.
+
+ If the ID could not be found, `None` is returned.
+
+ A description of the XML name can be found here:
+ https://developer.android.com/guide/topics/resources/providing-resources#ResourcesFromXml
+
+ :param r_id: numerical ID if the resource
+ :param package: package name
+ :return: XML name identifier
+ """
+ if package:
+ resource, name, i_id = self.get_id(package, r_id)
+ if not i_id:
+ return None
+ return "@{}/{}".format(resource, name)
+ else:
+ for p in self.get_packages_names():
+ r, n, i_id = self.get_id(p, r_id)
+ if i_id:
+ # found the resource in this package
+ package = p
+ resource = r
+ name = n
+ break
+ if not package:
+ return None
+ else:
+ return "@{}:{}/{}".format(package, resource, name)
+
+
+class PackageContext:
+ def __init__(self, current_package, stringpool_main, mTableStrings, mKeyStrings):
+ """
+ :param ARSCResTablePackage current_package:
+ :param StringBlock stringpool_main:
+ :param StringBlock mTableStrings:
+ :param StringBlock mKeyStrings:
+ """
+ self.stringpool_main = stringpool_main
+ self.mTableStrings = mTableStrings
+ self.mKeyStrings = mKeyStrings
+ self.current_package = current_package
+
+ def get_mResId(self):
+ return self.current_package.mResId
+
+ def set_mResId(self, mResId):
+ self.current_package.mResId = mResId
+
+ def get_package_name(self):
+ return self.current_package.get_name()
+
+ def __repr__(self):
+ return "".format(self.current_package,
+ self.stringpool_main,
+ self.mTableStrings,
+ self.mKeyStrings)
+
+
+class ARSCHeader:
+ """
+ Object which contains a Resource Chunk.
+ This is an implementation of the `ResChunk_header`.
+
+ It will throw an :class:`ResParserError` if the header could not be read successfully.
+
+ It is not checked if the data is outside the buffer size nor if the current
+ chunk fits into the parent chunk (if any)!
+
+ The parameter `expected_type` can be used to immediately check the header for the type or raise a :class:`ResParserError`.
+ This is useful if you know what type of chunk must follow.
+
+ See http://androidxref.com/9.0.0_r3/xref/frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h#196
+ :raises: ResParserError
+ """
+
+ # This is the minimal size such a header must have. There might be other header data too!
+ SIZE = 2 + 2 + 4
+
+ def __init__(self, buff, expected_type=None, possible_types=None):
+ """
+ :param androguard.core.bytecode.BuffHandle buff: the buffer set to the position where the header starts.
+ :param int expected_type: the type of the header which is expected.
+ """
+ self.start = buff.tell()
+ # Make sure we do not read over the buffer:
+ if buff.raw.getbuffer().nbytes < self.start + self.SIZE:
+ raise ResParserError("Can not read over the buffer size! Offset={}".format(self.start))
+
+ # Checking for dummy data between elements
+ if possible_types:
+ while True:
+ cur_pos = buff.tell()
+ self._type, self._header_size, self._size = unpack('= self.SIZE and self._size > self.SIZE):
+ break
+ buff.seek(cur_pos)
+ buff.read(1)
+ logger.warning("Appears that dummy data are found between elements!")
+ else:
+ self._type, self._header_size, self._size = unpack('".format(self.start,
+ self.type,
+ self.header_size,
+ self.size)
+
+
+class ARSCResTablePackage:
+ """
+ A `ResTable_package`
+
+ See http://androidxref.com/9.0.0_r3/xref/frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h#861
+ """
+ def __init__(self, buff, header):
+ self.header = header
+ self.start = buff.tell()
+ self.id = unpack('" % (
+ self.start,
+ self.id,
+ self.flags,
+ self.entryCount,
+ self.entriesStart,
+ self.mResId,
+ "table:" + self.parent.mTableStrings.getString(self.id - 1)
+ )
+
+
+class ARSCResTableConfig:
+ """
+ ARSCResTableConfig contains the configuration for specific resource selection.
+ This is used on the device to determine which resources should be loaded
+ based on different properties of the device like locale or displaysize.
+
+ See the definition of `ResTable_config` in
+ http://androidxref.com/9.0.0_r3/xref/frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h#911
+ """
+ @classmethod
+ def default_config(cls):
+ if not hasattr(cls, 'DEFAULT'):
+ cls.DEFAULT = ARSCResTableConfig(None)
+ return cls.DEFAULT
+
+ def __init__(self, buff=None, **kwargs):
+ if buff is not None:
+ self.start = buff.tell()
+
+ # uint32_t
+ self.size = unpack('= 32:
+ # struct of
+ # uint8_t screenLayout
+ # uint8_t uiMode
+ # uint16_t smallestScreenWidthDp
+ self.screenConfig, = unpack('= 36:
+ # struct of
+ # uint16_t screenWidthDp
+ # uint16_t screenHeightDp
+ self.screenSizeDp, = unpack('= 40:
+ self.localeScript = buff.read(4)
+
+ if self.size >= 44:
+ self.localeVariant = buff.read(8)
+
+ if self.size >= 52:
+ # struct of
+ # uint8_t screenLayout2
+ # uint8_t colorMode
+ # uint16_t screenConfigPad2
+ self.screenConfig2, = unpack(" 0:
+ logger.debug("Skipping padding bytes!")
+ self.padding = buff.read(self.exceedingSize)
+
+ else:
+ self.start = 0
+ self.size = 0
+ self.imsi = \
+ ((kwargs.pop('mcc', 0) & 0xffff) << 0) + \
+ ((kwargs.pop('mnc', 0) & 0xffff) << 16)
+
+ self.locale = 0
+ for char_ix, char in kwargs.pop('locale', "")[0:4]:
+ self.locale += (ord(char) << (char_ix * 8))
+
+ self.screenType = \
+ ((kwargs.pop('orientation', 0) & 0xff) << 0) + \
+ ((kwargs.pop('touchscreen', 0) & 0xff) << 8) + \
+ ((kwargs.pop('density', 0) & 0xffff) << 16)
+
+ self.input = \
+ ((kwargs.pop('keyboard', 0) & 0xff) << 0) + \
+ ((kwargs.pop('navigation', 0) & 0xff) << 8) + \
+ ((kwargs.pop('inputFlags', 0) & 0xff) << 16) + \
+ ((kwargs.pop('inputPad0', 0) & 0xff) << 24)
+
+ self.screenSize = \
+ ((kwargs.pop('screenWidth', 0) & 0xffff) << 0) + \
+ ((kwargs.pop('screenHeight', 0) & 0xffff) << 16)
+
+ self.version = \
+ ((kwargs.pop('sdkVersion', 0) & 0xffff) << 0) + \
+ ((kwargs.pop('minorVersion', 0) & 0xffff) << 16)
+
+ self.screenConfig = \
+ ((kwargs.pop('screenLayout', 0) & 0xff) << 0) + \
+ ((kwargs.pop('uiMode', 0) & 0xff) << 8) + \
+ ((kwargs.pop('smallestScreenWidthDp', 0) & 0xffff) << 16)
+
+ self.screenSizeDp = \
+ ((kwargs.pop('screenWidthDp', 0) & 0xffff) << 0) + \
+ ((kwargs.pop('screenHeightDp', 0) & 0xffff) << 16)
+
+ # TODO add this some day...
+ self.screenConfig2 = 0
+
+ self.exceedingSize = 0
+
+ def _unpack_language_or_region(self, char_in, char_base):
+ char_out = ""
+ if char_in[0] & 0x80:
+ first = char_in[1] & 0x1f
+ second = ((char_in[1] & 0xe0) >> 5) + ((char_in[0] & 0x03) << 3)
+ third = (char_in[0] & 0x7c) >> 2
+ char_out += chr(first + char_base)
+ char_out += chr(second + char_base)
+ char_out += chr(third + char_base)
+ else:
+ if char_in[0]:
+ char_out += chr(char_in[0])
+ if char_in[1]:
+ char_out += chr(char_in[1])
+ return char_out
+
+ def get_language_and_region(self):
+ """
+ Returns the combined language+region string or \x00\x00 for the default locale
+ :return:
+ """
+ if self.locale != 0:
+ _language = self._unpack_language_or_region([self.locale & 0xff, (self.locale & 0xff00) >> 8, ], ord('a'))
+ _region = self._unpack_language_or_region([(self.locale & 0xff0000) >> 16, (self.locale & 0xff000000) >> 24, ], ord('0'))
+ return (_language + "-r" + _region) if _region else _language
+ return "\x00\x00"
+
+ def get_config_name_friendly(self):
+ """
+ Here for legacy reasons.
+
+ use :meth:`~get_qualifier` instead.
+ """
+ return self.get_qualifier()
+
+ def get_qualifier(self):
+ """
+ Return resource name qualifier for the current configuration.
+ for example
+ * `ldpi-v4`
+ * `hdpi-v4`
+
+ All possible qualifiers are listed in table 2 of https://developer.android.com/guide/topics/resources/providing-resources
+
+ You can find how android process this at http://aospxref.com/android-13.0.0_r3/xref/frameworks/base/libs/androidfw/ResourceTypes.cpp#3243
+
+ :return: str
+ """
+ res = []
+
+ mcc = self.imsi & 0xFFFF
+ mnc = (self.imsi & 0xFFFF0000) >> 16
+ if mcc != 0:
+ res.append("mcc%d" % mcc)
+ if mnc != 0:
+ res.append("mnc%d" % mnc)
+
+ if self.locale != 0:
+ res.append(self.get_language_and_region())
+
+ screenLayout = self.screenConfig & 0xff
+ if (screenLayout & MASK_LAYOUTDIR) != 0:
+ if screenLayout & MASK_LAYOUTDIR == LAYOUTDIR_LTR:
+ res.append("ldltr")
+ elif screenLayout & MASK_LAYOUTDIR == LAYOUTDIR_RTL:
+ res.append("ldrtl")
+ else:
+ res.append("layoutDir_%d" % (screenLayout & MASK_LAYOUTDIR))
+
+ smallestScreenWidthDp = (self.screenConfig & 0xFFFF0000) >> 16
+ if smallestScreenWidthDp != 0:
+ res.append("sw%ddp" % smallestScreenWidthDp)
+
+ screenWidthDp = self.screenSizeDp & 0xFFFF
+ screenHeightDp = (self.screenSizeDp & 0xFFFF0000) >> 16
+ if screenWidthDp != 0:
+ res.append("w%ddp" % screenWidthDp)
+ if screenHeightDp != 0:
+ res.append("h%ddp" % screenHeightDp)
+
+ if (screenLayout & MASK_SCREENSIZE) != SCREENSIZE_ANY:
+ if screenLayout & MASK_SCREENSIZE == SCREENSIZE_SMALL:
+ res.append("small")
+ elif screenLayout & MASK_SCREENSIZE == SCREENSIZE_NORMAL:
+ res.append("normal")
+ elif screenLayout & MASK_SCREENSIZE == SCREENSIZE_LARGE:
+ res.append("large")
+ elif screenLayout & MASK_SCREENSIZE == SCREENSIZE_XLARGE:
+ res.append("xlarge")
+ else:
+ res.append("screenLayoutSize_%d" % (screenLayout & MASK_SCREENSIZE))
+ if (screenLayout & MASK_SCREENLONG) != 0:
+ if screenLayout & MASK_SCREENLONG == SCREENLONG_NO:
+ res.append("notlong")
+ elif screenLayout & MASK_SCREENLONG == SCREENLONG_YES:
+ res.append("long")
+ else:
+ res.append("screenLayoutLong_%d" % (screenLayout & MASK_SCREENLONG))
+
+ screenLayout2 = self.screenConfig2 & 0xff
+ if (screenLayout2 & MASK_SCREENROUND) != 0:
+ if screenLayout2 & MASK_SCREENROUND == SCREENROUND_NO:
+ res.append("notround")
+ elif screenLayout2 & MASK_SCREENROUND == SCREENROUND_YES:
+ res.append("round")
+ else:
+ res.append("screenRound_%d" % (screenLayout2 & MASK_SCREENROUND))
+
+ colorMode = (self.screenConfig2 & 0xff00) >> 8
+ if (colorMode & MASK_WIDE_COLOR_GAMUT) != 0:
+ if colorMode & MASK_WIDE_COLOR_GAMUT == WIDE_COLOR_GAMUT_NO:
+ res.append("nowidecg")
+ elif colorMode & MASK_WIDE_COLOR_GAMUT == WIDE_COLOR_GAMUT_YES:
+ res.append("widecg")
+ else:
+ res.append("wideColorGamut_%d" % (colorMode & MASK_WIDE_COLOR_GAMUT))
+
+ if (colorMode & MASK_HDR) != 0:
+ if colorMode & MASK_HDR == HDR_NO:
+ res.append("lowdr")
+ elif colorMode & MASK_HDR == HDR_YES:
+ res.append("highdr")
+ else:
+ res.append("hdr_%d" % (colorMode & MASK_HDR))
+
+ orientation = self.screenType & 0xff
+ if orientation != ORIENTATION_ANY:
+ if orientation == ORIENTATION_PORT:
+ res.append("port")
+ elif orientation == ORIENTATION_LAND:
+ res.append("land")
+ elif orientation == ORIENTATION_SQUARE:
+ res.append("square")
+ else:
+ res.append("orientation_%d" % orientation)
+
+ uiMode = (self.screenConfig & 0xff00) >> 8
+ if (uiMode & MASK_UI_MODE_TYPE) != UI_MODE_TYPE_ANY:
+ ui_mode = uiMode & MASK_UI_MODE_TYPE
+ if ui_mode == UI_MODE_TYPE_DESK:
+ res.append("desk")
+ elif ui_mode == UI_MODE_TYPE_CAR:
+ res.append("car")
+ elif ui_mode == UI_MODE_TYPE_TELEVISION:
+ res.append("television")
+ elif ui_mode == UI_MODE_TYPE_APPLIANCE:
+ res.append("appliance")
+ elif ui_mode == UI_MODE_TYPE_WATCH:
+ res.append("watch")
+ elif ui_mode == UI_MODE_TYPE_VR_HEADSET:
+ res.append("vrheadset")
+ else:
+ res.append("uiModeType_%d" % ui_mode)
+
+ if (uiMode & MASK_UI_MODE_NIGHT) != 0:
+ if uiMode & MASK_UI_MODE_NIGHT == UI_MODE_NIGHT_NO:
+ res.append("notnight")
+ elif uiMode & MASK_UI_MODE_NIGHT == UI_MODE_NIGHT_YES:
+ res.append("night")
+ else:
+ res.append("uiModeNight_%d" % (uiMode & MASK_UI_MODE_NIGHT))
+
+ density = (self.screenType & 0xffff0000) >> 16
+ if density != DENSITY_DEFAULT:
+ if density == DENSITY_LOW:
+ res.append("ldpi")
+ elif density == DENSITY_MEDIUM:
+ res.append("mdpi")
+ elif density == DENSITY_TV:
+ res.append("tvdpi")
+ elif density == DENSITY_HIGH:
+ res.append("hdpi")
+ elif density == DENSITY_XHIGH:
+ res.append("xhdpi")
+ elif density == DENSITY_XXHIGH:
+ res.append("xxhdpi")
+ elif density == DENSITY_XXXHIGH:
+ res.append("xxxhdpi")
+ elif density == DENSITY_NONE:
+ res.append("nodpi")
+ elif density == DENSITY_ANY:
+ res.append("anydpi")
+ else:
+ res.append("%ddpi" % (density))
+
+ touchscreen = (self.screenType & 0xff00) >> 8
+ if touchscreen != TOUCHSCREEN_ANY:
+ if touchscreen == TOUCHSCREEN_NOTOUCH:
+ res.append("notouch")
+ elif touchscreen == TOUCHSCREEN_FINGER:
+ res.append("finger")
+ elif touchscreen == TOUCHSCREEN_STYLUS:
+ res.append("stylus")
+ else:
+ res.append("touchscreen_%d" % touchscreen)
+
+ keyboard = self.input & 0xff
+ navigation = (self.input & 0xff00) >> 8
+ inputFlags = (self.input & 0xff0000) >> 16
+
+ if inputFlags & MASK_KEYSHIDDEN != 0:
+ input_flags = inputFlags & MASK_KEYSHIDDEN
+ if input_flags == KEYSHIDDEN_NO:
+ res.append("keysexposed")
+ elif input_flags == KEYSHIDDEN_YES:
+ res.append("keyshidden")
+ elif input_flags == KEYSHIDDEN_SOFT:
+ res.append("keyssoft")
+
+ if keyboard != KEYBOARD_ANY:
+ if keyboard == KEYBOARD_NOKEYS:
+ res.append("nokeys")
+ elif keyboard == KEYBOARD_QWERTY:
+ res.append("qwerty")
+ elif keyboard == KEYBOARD_12KEY:
+ res.append("12key")
+ else:
+ res.append("keyboard_%d" % keyboard)
+
+ if inputFlags & MASK_NAVHIDDEN != 0:
+ input_flags = inputFlags & MASK_NAVHIDDEN
+ if input_flags == NAVHIDDEN_NO:
+ res.append("navexposed")
+ elif input_flags == NAVHIDDEN_YES:
+ res.append("navhidden")
+ else:
+ res.append("inputFlagsNavHidden_%d" % input_flags)
+
+ if navigation != NAVIGATION_ANY:
+ if navigation == NAVIGATION_NONAV:
+ res.append("nonav")
+ elif navigation == NAVIGATION_DPAD:
+ res.append("dpad")
+ elif navigation == NAVIGATION_TRACKBALL:
+ res.append("trackball")
+ elif navigation == NAVIGATION_WHEEL:
+ res.append("wheel")
+ else:
+ res.append("navigation_%d" % navigation)
+
+ screenSize = self.screenSize
+ if screenSize != 0:
+ screenWidth = self.screenSize & 0xffff
+ screenHeight = (self.screenSize & 0xffff0000) >> 16
+ res.append("%dx%d" % (screenWidth, screenHeight))
+
+ version = self.version
+ if version != 0:
+ sdkVersion = self.version & 0xffff
+ minorVersion = (self.version & 0xffff0000) >> 16
+ res.append("v%d" % sdkVersion)
+ if minorVersion != 0:
+ res.append(".%d" % minorVersion)
+
+ return "-".join(res)
+
+ def get_language(self):
+ x = self.locale & 0x0000ffff
+ return chr(x & 0x00ff) + chr((x & 0xff00) >> 8)
+
+ def get_country(self):
+ x = (self.locale & 0xffff0000) >> 16
+ return chr(x & 0x00ff) + chr((x & 0xff00) >> 8)
+
+ def get_density(self):
+ x = ((self.screenType >> 16) & 0xffff)
+ return x
+
+ def is_default(self):
+ """
+ Test if this is a default resource, which matches all
+
+ This is indicated that all fields are zero.
+ :return: True if default, False otherwise
+ """
+ return all(map(lambda x: x == 0, self._get_tuple()))
+
+ def _get_tuple(self):
+ return (
+ self.imsi,
+ self.locale,
+ self.screenType,
+ self.input,
+ self.screenSize,
+ self.version,
+ self.screenConfig,
+ self.screenSizeDp,
+ self.screenConfig2,
+ )
+
+ def __hash__(self):
+ return hash(self._get_tuple())
+
+ def __eq__(self, other):
+ return self._get_tuple() == other._get_tuple()
+
+ def __repr__(self):
+ return "".format(self.get_qualifier(), repr(self._get_tuple()))
+
+
+class ARSCResTableEntry:
+ """
+ A `ResTable_entry`.
+
+ See http://androidxref.com/9.0.0_r3/xref/frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h#1458
+ """
+ # If set, this is a complex entry, holding a set of name/value
+ # mappings. It is followed by an array of ResTable_map structures.
+ FLAG_COMPLEX = 1
+
+ # If set, this resource has been declared public, so libraries
+ # are allowed to reference it.
+ FLAG_PUBLIC = 2
+
+ # If set, this is a weak resource and may be overriden by strong
+ # resources of the same name/type. This is only useful during
+ # linking with other resource tables.
+ FLAG_WEAK = 4
+
+ def __init__(self, buff, mResId, parent=None):
+ self.start = buff.tell()
+ self.mResId = mResId
+ self.parent = parent
+
+ self.size = unpack('".format(
+ self.start,
+ self.mResId,
+ self.size,
+ self.flags,
+ self.index,
+ self.item if self.is_complex() else self.key)
+
+
+class ARSCComplex:
+ """
+ This is actually a `ResTable_map_entry`
+
+ It contains a set of {name: value} mappings, which are of type `ResTable_map`.
+ A `ResTable_map` contains two items: `ResTable_ref` and `Res_value`.
+
+ See http://androidxref.com/9.0.0_r3/xref/frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h#1485 for `ResTable_map_entry`
+ and http://androidxref.com/9.0.0_r3/xref/frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h#1498 for `ResTable_map`
+ """
+ def __init__(self, buff, parent=None):
+ self.start = buff.tell()
+ self.parent = parent
+
+ self.id_parent = unpack('".format(self.start, self.id_parent, self.count)
+
+
+class ARSCResStringPoolRef:
+ """
+ This is actually a `Res_value`
+ It holds information about the stored resource value
+
+ See: http://androidxref.com/9.0.0_r3/xref/frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h#262
+ """
+ def __init__(self, buff, parent=None):
+ self.start = buff.tell()
+ self.parent = parent
+
+ self.size, = unpack("".format(
+ self.start,
+ self.size,
+ TYPE_TABLE.get(self.data_type, "0x%x" % self.data_type),
+ self.data)
+
+
+def get_arsc_info(arscobj):
+ """
+ Return a string containing all resources packages ordered by packagename, locale and type.
+
+ :param arscobj: :class:`~ARSCParser`
+ :return: a string
+ """
+ buff = ""
+ for package in arscobj.get_packages_names():
+ buff += package + ":\n"
+ for locale in arscobj.get_locales(package):
+ buff += "\t" + repr(locale) + ":\n"
+ for ttype in arscobj.get_types(package, locale):
+ buff += "\t\t" + ttype + ":\n"
+ try:
+ tmp_buff = getattr(arscobj, "get_" + ttype + "_resources")(
+ package, locale).decode("utf-8", 'replace').split("\n")
+ for i in tmp_buff:
+ buff += "\t\t\t" + i + "\n"
+ except AttributeError:
+ pass
+ return buff
\ No newline at end of file
diff --git a/mobsf/StaticAnalyzer/tools/androguard4/resources/public.py b/mobsf/StaticAnalyzer/tools/androguard4/resources/public.py
new file mode 100644
index 00000000..e54b24e0
--- /dev/null
+++ b/mobsf/StaticAnalyzer/tools/androguard4/resources/public.py
@@ -0,0 +1,34 @@
+# -*- coding: utf_8 -*-
+# flake8: noqa
+import os
+from xml.dom import minidom
+
+_public_res = None
+# copy the newest sdk/platforms/android-?/data/res/values/public.xml here
+if _public_res is None:
+ _public_res = {}
+ root = os.path.dirname(os.path.realpath(__file__))
+ xmlfile = os.path.join(root, "public.xml")
+ if os.path.isfile(xmlfile):
+ with open(xmlfile, "r") as fp:
+ _xml = minidom.parseString(fp.read())
+ for element in _xml.getElementsByTagName("public"):
+ _type = element.getAttribute('type')
+ _name = element.getAttribute('name')
+ _id = int(element.getAttribute('id'), 16)
+ if _type not in _public_res:
+ _public_res[_type] = {}
+ _public_res[_type][_name] = _id
+ else:
+ raise Exception("need to copy the sdk/platforms/android-?/data/res/values/public.xml here")
+
+SYSTEM_RESOURCES = {
+ "attributes": {
+ "forward": {k: v for k, v in _public_res['attr'].items()},
+ "inverse": {v: k for k, v in _public_res['attr'].items()}
+ },
+ "styles": {
+ "forward": {k: v for k, v in _public_res['style'].items()},
+ "inverse": {v: k for k, v in _public_res['style'].items()}
+ }
+}
diff --git a/mobsf/StaticAnalyzer/tools/androguard4/resources/public.xml b/mobsf/StaticAnalyzer/tools/androguard4/resources/public.xml
new file mode 100644
index 00000000..5db0f455
--- /dev/null
+++ b/mobsf/StaticAnalyzer/tools/androguard4/resources/public.xml
@@ -0,0 +1,2861 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mobsf/StaticAnalyzer/tools/androguard4/types.py b/mobsf/StaticAnalyzer/tools/androguard4/types.py
new file mode 100644
index 00000000..33348577
--- /dev/null
+++ b/mobsf/StaticAnalyzer/tools/androguard4/types.py
@@ -0,0 +1,52 @@
+# -*- coding: utf_8 -*-
+# flake8: noqa
+# Type definiton for (type, data) tuples representing a value
+# See http://androidxref.com/9.0.0_r3/xref/frameworks/base/libs/androidfw/include/androidfw/ResourceTypes.h#262
+
+# The 'data' is either 0 or 1, specifying this resource is either
+# undefined or empty, respectively.
+TYPE_NULL = 0x00
+# The 'data' holds a ResTable_ref, a reference to another resource
+# table entry.
+TYPE_REFERENCE = 0x01
+# The 'data' holds an attribute resource identifier.
+TYPE_ATTRIBUTE = 0x02
+# The 'data' holds an index into the containing resource table's
+# global value string pool.
+TYPE_STRING = 0x03
+# The 'data' holds a single-precision floating point number.
+TYPE_FLOAT = 0x04
+# The 'data' holds a complex number encoding a dimension value
+# such as "100in".
+TYPE_DIMENSION = 0x05
+# The 'data' holds a complex number encoding a fraction of a
+# container.
+TYPE_FRACTION = 0x06
+# The 'data' holds a dynamic ResTable_ref, which needs to be
+# resolved before it can be used like a TYPE_REFERENCE.
+TYPE_DYNAMIC_REFERENCE = 0x07
+# The 'data' holds an attribute resource identifier, which needs to be resolved
+# before it can be used like a TYPE_ATTRIBUTE.
+TYPE_DYNAMIC_ATTRIBUTE = 0x08
+# Beginning of integer flavors...
+TYPE_FIRST_INT = 0x10
+# The 'data' is a raw integer value of the form n..n.
+TYPE_INT_DEC = 0x10
+# The 'data' is a raw integer value of the form 0xn..n.
+TYPE_INT_HEX = 0x11
+# The 'data' is either 0 or 1, for input "false" or "true" respectively.
+TYPE_INT_BOOLEAN = 0x12
+# Beginning of color integer flavors...
+TYPE_FIRST_COLOR_INT = 0x1c
+# The 'data' is a raw integer value of the form #aarrggbb.
+TYPE_INT_COLOR_ARGB8 = 0x1c
+# The 'data' is a raw integer value of the form #rrggbb.
+TYPE_INT_COLOR_RGB8 = 0x1d
+# The 'data' is a raw integer value of the form #argb.
+TYPE_INT_COLOR_ARGB4 = 0x1e
+# The 'data' is a raw integer value of the form #rgb.
+TYPE_INT_COLOR_RGB4 = 0x1f
+# ...end of integer flavors.
+TYPE_LAST_COLOR_INT = 0x1f
+# ...end of integer flavors.
+TYPE_LAST_INT = 0x1f
diff --git a/mobsf/StaticAnalyzer/tools/androguard4/zipfile.py b/mobsf/StaticAnalyzer/tools/androguard4/zipfile.py
new file mode 100644
index 00000000..2ce878e6
--- /dev/null
+++ b/mobsf/StaticAnalyzer/tools/androguard4/zipfile.py
@@ -0,0 +1,566 @@
+# -*- coding: utf_8 -*-
+# flake8: noqa
+"""This file is from apkinspector licensed under the Apache License 2.0."""
+import io
+import zlib
+import struct
+from typing import Dict
+
+
+def extract_file_based_on_header_info(apk_file, local_header_info, central_directory_info):
+ """
+ Extracts a single file from the apk_file based on the information provided from the offset and the header_info.
+ It takes into account that the compression method provided might not be STORED or DEFLATED! The returned
+ 'indicator', shows what compression method was used. Besides the standard STORED/DEFLATE it may return
+ 'DEFLATED_TAMPERED', which means that the compression method found was not DEFLATED(8) but it should have been,
+ and 'STORED_TAMPERED' which means that the compression method found was not STORED(0) but should have been.
+
+ :param apk_file: The APK file e.g. with open('test.apk', 'rb') as apk_file
+ :type apk_file: bytesIO
+ :param local_header_info: The local header dictionary info for that specific filename
+ :type local_header_info: dict
+ :param central_directory_info: The central directory entry for that specific filename
+ :type central_directory_info: dict
+ :return: Returns the actual extracted data for that file along with an indication of whether a static analysis evasion technique was used or not.
+ :rtype: set(bytes, str)
+ """
+ filename_length = local_header_info["file_name_length"]
+ if local_header_info["compressed_size"] == 0 or local_header_info["uncompressed_size"] == 0:
+ compressed_size = central_directory_info["compressed_size"]
+ uncompressed_size = central_directory_info["uncompressed_size"]
+ else:
+ compressed_size = local_header_info["compressed_size"]
+ uncompressed_size = local_header_info["uncompressed_size"]
+
+ extra_field_length = local_header_info["extra_field_length"]
+ compression_method = local_header_info["compression_method"]
+ # Skip the offset + local header to reach the compressed data
+ local_header_size = 30 # Size of the local header in bytes
+ offset = central_directory_info["relative_offset_of_local_file_header"]
+ apk_file.seek(offset + local_header_size + filename_length + extra_field_length)
+ if compression_method == 0: # Stored (no compression)
+ uncompressed_data = apk_file.read(uncompressed_size)
+ extracted_data = uncompressed_data
+ indicator = 'STORED'
+ elif compression_method == 8:
+ compressed_data = apk_file.read(compressed_size)
+ # -15 for windows size due to raw stream with no header or trailer
+ extracted_data = zlib.decompress(compressed_data, -15)
+ indicator = 'DEFLATED'
+ else:
+ try:
+ cur_loc = apk_file.tell()
+ compressed_data = apk_file.read(compressed_size)
+ extracted_data = zlib.decompress(compressed_data, -15)
+ indicator = 'DEFLATED_TAMPERED'
+ except:
+ apk_file.seek(cur_loc)
+ compressed_data = apk_file.read(uncompressed_size)
+ extracted_data = compressed_data
+ indicator = 'STORED_TAMPERED'
+ return extracted_data, indicator
+
+
+class EndOfCentralDirectoryRecord:
+ """
+ A class to provide details about the end of central directory record.
+ """
+ def __init__(self, signature, number_of_this_disk, disk_where_central_directory_starts,
+ number_of_central_directory_records_on_this_disk,
+ total_number_of_central_directory_records, size_of_central_directory,
+ offset_of_start_of_central_directory, comment_length, comment):
+ self.signature = signature
+ self.number_of_this_disk = number_of_this_disk
+ self.disk_where_central_directory_starts = disk_where_central_directory_starts
+ self.number_of_central_directory_records_on_this_disk = number_of_central_directory_records_on_this_disk
+ self.total_number_of_central_directory_records = total_number_of_central_directory_records
+ self.size_of_central_directory = size_of_central_directory
+ self.offset_of_start_of_central_directory = offset_of_start_of_central_directory
+ self.comment_length = comment_length
+ self.comment = comment
+
+ @classmethod
+ def parse(cls, apk_file):
+ """
+ Method to locate the "end of central directory record signature" as the first step of the correct process of
+ reading a ZIP archive. Should be noted that certain APKs do not follow the zip specification and declare multiple
+ "end of central directory records". For this reason the search for the corresponding signature of the eocd starts
+ from the end of the apk.
+
+ :param apk_file: The already read/loaded data of the APK file e.g. with open('test.apk', 'rb') as apk_file
+ :type apk_file: bytesIO
+ :return: Returns the end of central directory record with all the information available if the corresponding signature is found. If not, then it returns None.
+ :rtype: EndOfCentralDirectoryRecord or None
+ """
+ chunk_size = 1024
+ offset = 0
+ signature_offset = -1
+ file_size = apk_file.seek(0, 2)
+ while offset < file_size:
+ position = file_size - offset - chunk_size
+ if position < 0:
+ position = 0
+ apk_file.seek(position)
+ chunk = apk_file.read(chunk_size)
+ if not chunk:
+ break
+ signature_offset = chunk.rfind(b'\x50\x4b\x05\x06') # end of Central Directory File Header signature
+ if signature_offset != -1:
+ eo_central_directory_offset = position + signature_offset
+ break # Found End of central directory record (EOCD) signature
+ offset += chunk_size
+ if signature_offset == -1:
+ raise ValueError("End of central directory record (EOCD) signature not found")
+ apk_file.seek(eo_central_directory_offset)
+
+ signature = apk_file.read(4)
+ number_of_this_disk = struct.unpack(' Dict[str, CentralDirectoryEntry]:
+ """
+ List of information about the entries in the central directory.
+
+ :return: returns a dictionary where the keys are the filenames and the values are each an instance of the CentralDirectoryEntry
+ :rtype: dict
+ """
+ return self.central_directory.entries
+
+ def namelist(self):
+ """
+ List of the filenames included in the central directory.
+
+ :return: returns the list of the filenames
+ :rtype: list
+ """
+ return [vl for vl in self.central_directory.to_dict()]
diff --git a/mobsf/StaticAnalyzer/views/android/app.py b/mobsf/StaticAnalyzer/views/android/app.py
index caf30aab..acb29140 100644
--- a/mobsf/StaticAnalyzer/views/android/app.py
+++ b/mobsf/StaticAnalyzer/views/android/app.py
@@ -5,15 +5,11 @@ import re
import logging
from pathlib import Path
-from androguard.core import (
- androconf,
+from mobsf.StaticAnalyzer.tools.androguard4 import (
apk,
)
-
-androconf.logger.remove()
logger = logging.getLogger(__name__)
-logging.getLogger('androguard').setLevel(logging.ERROR)
def parse_apk(app_path):
diff --git a/mobsf/StaticAnalyzer/views/android/cert_analysis.py b/mobsf/StaticAnalyzer/views/android/cert_analysis.py
index bb0df088..d8f2843c 100755
--- a/mobsf/StaticAnalyzer/views/android/cert_analysis.py
+++ b/mobsf/StaticAnalyzer/views/android/cert_analysis.py
@@ -24,6 +24,9 @@ from mobsf.MobSF.utils import (
find_java_binary,
gen_sha256_hash,
)
+from mobsf.StaticAnalyzer.tools.androguard4.apk import (
+ get_certificate_name_string,
+)
logger = logging.getLogger(__name__)
ANDROID_8_1_LEVEL = 27
@@ -38,62 +41,6 @@ HASH_FUNCS = {
}
-def get_certificate_name_string(name, short=False, delimiter=', '):
- """
- Function from androguard.
-
- licensed under the Apache License, Version 2.0.
- https://github.com/androguard/androguard/blob/master/androguard/util.py
- Format the Name type of a X509 Certificate in a human readable form.
-
- :param name: Name object to return the DN from
- :param short: Use short form (default: False)
- :param delimiter: Delimiter string or character between
- two parts (default: ', ')
-
- :type name: dict or :class:`asn1crypto.x509.Name`
- :type short: boolean
- :type delimiter: str
-
- :rtype: str
- """
- if isinstance(name, asn1crypto.x509.Name):
- name = name.native
-
- # For the shortform, we have a lookup table
- # See RFC4514 for more details
- _ = {
- 'business_category': ('businessCategory', 'businessCategory'),
- 'serial_number': ('serialNumber', 'serialNumber'),
- 'country_name': ('C', 'countryName'),
- 'postal_code': ('postalCode', 'postalCode'),
- 'state_or_province_name': ('ST', 'stateOrProvinceName'),
- 'locality_name': ('L', 'localityName'),
- 'street_address': ('street', 'streetAddress'),
- 'organization_name': ('O', 'organizationName'),
- 'organizational_unit_name': ('OU', 'organizationalUnitName'),
- 'title': ('title', 'title'),
- 'common_name': ('CN', 'commonName'),
- 'initials': ('initials', 'initials'),
- 'generation_qualifier': ('generationQualifier', 'generationQualifier'),
- 'surname': ('SN', 'surname'),
- 'given_name': ('GN', 'givenName'),
- 'name': ('name', 'name'),
- 'pseudonym': ('pseudonym', 'pseudonym'),
- 'dn_qualifier': ('dnQualifier', 'dnQualifier'),
- 'telephone_number': ('telephoneNumber', 'telephoneNumber'),
- 'email_address': ('E', 'emailAddress'),
- 'domain_component': ('DC', 'domainComponent'),
- 'name_distinguisher': ('nameDistinguisher', 'nameDistinguisher'),
- 'organization_identifier': (
- 'organizationIdentifier', 'organizationIdentifier'),
- }
- return delimiter.join(
- ['{}={}'.format(
- _.get(attr, (attr, attr))[0 if short else 1],
- name[attr]) for attr in name])
-
-
def get_hardcoded_cert_keystore(files):
"""Returns the hardcoded certificate keystore."""
try:
diff --git a/mobsf/StaticAnalyzer/views/android/icon_analysis.py b/mobsf/StaticAnalyzer/views/android/icon_analysis.py
index 476a5a9e..9e17ec01 100755
--- a/mobsf/StaticAnalyzer/views/android/icon_analysis.py
+++ b/mobsf/StaticAnalyzer/views/android/icon_analysis.py
@@ -11,11 +11,6 @@ import subprocess
from lxml import etree
-from androguard.core import (
- androconf,
- axml,
-)
-
from django.conf import settings
from mobsf.MobSF.utils import (
@@ -23,10 +18,12 @@ from mobsf.MobSF.utils import (
is_file_exists,
is_path_traversal,
)
+from mobsf.StaticAnalyzer.tools.androguard4 import (
+ axml,
+)
+
-androconf.logger.remove()
logger = logging.getLogger(__name__)
-logging.getLogger('androguard').setLevel(logging.ERROR)
# relative to res folder
diff --git a/poetry.lock b/poetry.lock
index 25e5d02b..347a7901 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -20,25 +20,6 @@ service-identity = ">=23.1.0"
[package.extras]
dev = ["coverage[toml] (>=7.2.2)"]
-[[package]]
-name = "alembic"
-version = "1.13.1"
-description = "A database migration tool for SQLAlchemy."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "alembic-1.13.1-py3-none-any.whl", hash = "sha256:2edcc97bed0bd3272611ce3a98d98279e9c209e7186e43e75bbb1b2bdfdbcc43"},
- {file = "alembic-1.13.1.tar.gz", hash = "sha256:4932c8558bf68f2ee92b9bbcb8218671c627064d5b08939437af6d77dc05e595"},
-]
-
-[package.dependencies]
-Mako = "*"
-SQLAlchemy = ">=1.3.0"
-typing-extensions = ">=4"
-
-[package.extras]
-tz = ["backports.zoneinfo"]
-
[[package]]
name = "altgraph"
version = "0.17.4"
@@ -50,36 +31,6 @@ files = [
{file = "altgraph-0.17.4.tar.gz", hash = "sha256:1b5afbb98f6c4dcadb2e2ae6ab9fa994bbb8c1d75f4fa96d340f9437ae454406"},
]
-[[package]]
-name = "androguard"
-version = "4.1.1"
-description = "Androguard is a full python tool to play with Android files."
-optional = false
-python-versions = ">=3.9,<4.0"
-files = [
- {file = "androguard-4.1.1-py3-none-any.whl", hash = "sha256:6c457b5e0c007d2e32c5ac7041b625bf71f1ec39613ec4153837005a988f86a6"},
- {file = "androguard-4.1.1.tar.gz", hash = "sha256:dffc138046f8c3aea061203afff490c77f2000345f685e1dad1780d478f3c5f6"},
-]
-
-[package.dependencies]
-apkInspector = ">=1.1.7"
-asn1crypto = ">=0.24.0"
-click = ">=7.0"
-colorama = ">=0.4.1"
-dataset = "*"
-frida = "*"
-ipython = ">=5.0.0"
-loguru = "*"
-lxml = ">=4.3.0"
-matplotlib = "*"
-mutf8 = "*"
-networkx = "*"
-pydot = ">=1.4.1"
-pygments = ">=2.3.1"
-PyQt5 = "*"
-PyQt5-Qt5 = ">=5.15,<5.15.11 || >5.15.11,<5.15.12 || >5.15.12,<6.0"
-pyyaml = "*"
-
[[package]]
name = "apkid"
version = "2.1.5"
@@ -98,17 +49,6 @@ yara-python-dex = ">=1.0.1"
dev = ["mypy", "pypandoc", "twine"]
test = ["delayed-assert", "factory-boy", "mock", "pytest", "pytest-cov", "pytest-factoryboy", "pytest-flask", "pytest-runner", "tox"]
-[[package]]
-name = "apkinspector"
-version = "1.2.2"
-description = "apkInspector is a tool designed to provide detailed insights into the zip structure of APK files, offering the capability to extract content and decode the AndroidManifest.xml file."
-optional = false
-python-versions = ">=3.5,<4.0"
-files = [
- {file = "apkinspector-1.2.2-py3-none-any.whl", hash = "sha256:ad55200bd72eb1edeeb5fb114e3bb576ec825feef54fb8727173f1f02fa12bb7"},
- {file = "apkinspector-1.2.2.tar.gz", hash = "sha256:ea7e56090e95eb791b593e9beedf4f1056eb269c4483558e13d5d5ef4b479c60"},
-]
-
[[package]]
name = "apksigcopier"
version = "1.1.1"
@@ -179,24 +119,6 @@ files = [
{file = "asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c"},
]
-[[package]]
-name = "asttokens"
-version = "2.4.1"
-description = "Annotate AST trees with source code positions"
-optional = false
-python-versions = "*"
-files = [
- {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"},
- {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"},
-]
-
-[package.dependencies]
-six = ">=1.12.0"
-
-[package.extras]
-astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"]
-test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"]
-
[[package]]
name = "attrs"
version = "21.4.0"
@@ -214,20 +136,6 @@ docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"]
tests = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "zope.interface"]
tests-no-zope = ["cloudpickle", "coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six"]
-[[package]]
-name = "banal"
-version = "1.0.6"
-description = "Commons of banal micro-functions for Python."
-optional = false
-python-versions = "*"
-files = [
- {file = "banal-1.0.6-py2.py3-none-any.whl", hash = "sha256:877aacb16b17f8fa4fd29a7c44515c5a23dc1a7b26078bc41dd34829117d85e1"},
- {file = "banal-1.0.6.tar.gz", hash = "sha256:2fe02c9305f53168441948f4a03dfbfa2eacc73db30db4a93309083cb0e250a5"},
-]
-
-[package.extras]
-dev = ["mypy", "wheel"]
-
[[package]]
name = "bcrypt"
version = "4.1.2"
@@ -673,69 +581,6 @@ colorama = {version = "*", markers = "sys_platform == \"win32\""}
[package.extras]
development = ["black", "flake8", "mypy", "pytest", "types-colorama"]
-[[package]]
-name = "contourpy"
-version = "1.2.0"
-description = "Python library for calculating contours of 2D quadrilateral grids"
-optional = false
-python-versions = ">=3.9"
-files = [
- {file = "contourpy-1.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0274c1cb63625972c0c007ab14dd9ba9e199c36ae1a231ce45d725cbcbfd10a8"},
- {file = "contourpy-1.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ab459a1cbbf18e8698399c595a01f6dcc5c138220ca3ea9e7e6126232d102bb4"},
- {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fdd887f17c2f4572ce548461e4f96396681212d858cae7bd52ba3310bc6f00f"},
- {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d16edfc3fc09968e09ddffada434b3bf989bf4911535e04eada58469873e28e"},
- {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c203f617abc0dde5792beb586f827021069fb6d403d7f4d5c2b543d87edceb9"},
- {file = "contourpy-1.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b69303ceb2e4d4f146bf82fda78891ef7bcd80c41bf16bfca3d0d7eb545448aa"},
- {file = "contourpy-1.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:884c3f9d42d7218304bc74a8a7693d172685c84bd7ab2bab1ee567b769696df9"},
- {file = "contourpy-1.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4a1b1208102be6e851f20066bf0e7a96b7d48a07c9b0cfe6d0d4545c2f6cadab"},
- {file = "contourpy-1.2.0-cp310-cp310-win32.whl", hash = "sha256:34b9071c040d6fe45d9826cbbe3727d20d83f1b6110d219b83eb0e2a01d79488"},
- {file = "contourpy-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:bd2f1ae63998da104f16a8b788f685e55d65760cd1929518fd94cd682bf03e41"},
- {file = "contourpy-1.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dd10c26b4eadae44783c45ad6655220426f971c61d9b239e6f7b16d5cdaaa727"},
- {file = "contourpy-1.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5c6b28956b7b232ae801406e529ad7b350d3f09a4fde958dfdf3c0520cdde0dd"},
- {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebeac59e9e1eb4b84940d076d9f9a6cec0064e241818bcb6e32124cc5c3e377a"},
- {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:139d8d2e1c1dd52d78682f505e980f592ba53c9f73bd6be102233e358b401063"},
- {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e9dc350fb4c58adc64df3e0703ab076f60aac06e67d48b3848c23647ae4310e"},
- {file = "contourpy-1.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18fc2b4ed8e4a8fe849d18dce4bd3c7ea637758c6343a1f2bae1e9bd4c9f4686"},
- {file = "contourpy-1.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:16a7380e943a6d52472096cb7ad5264ecee36ed60888e2a3d3814991a0107286"},
- {file = "contourpy-1.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8d8faf05be5ec8e02a4d86f616fc2a0322ff4a4ce26c0f09d9f7fb5330a35c95"},
- {file = "contourpy-1.2.0-cp311-cp311-win32.whl", hash = "sha256:67b7f17679fa62ec82b7e3e611c43a016b887bd64fb933b3ae8638583006c6d6"},
- {file = "contourpy-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:99ad97258985328b4f207a5e777c1b44a83bfe7cf1f87b99f9c11d4ee477c4de"},
- {file = "contourpy-1.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:575bcaf957a25d1194903a10bc9f316c136c19f24e0985a2b9b5608bdf5dbfe0"},
- {file = "contourpy-1.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9e6c93b5b2dbcedad20a2f18ec22cae47da0d705d454308063421a3b290d9ea4"},
- {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:464b423bc2a009088f19bdf1f232299e8b6917963e2b7e1d277da5041f33a779"},
- {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:68ce4788b7d93e47f84edd3f1f95acdcd142ae60bc0e5493bfd120683d2d4316"},
- {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d7d1f8871998cdff5d2ff6a087e5e1780139abe2838e85b0b46b7ae6cc25399"},
- {file = "contourpy-1.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e739530c662a8d6d42c37c2ed52a6f0932c2d4a3e8c1f90692ad0ce1274abe0"},
- {file = "contourpy-1.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:247b9d16535acaa766d03037d8e8fb20866d054d3c7fbf6fd1f993f11fc60ca0"},
- {file = "contourpy-1.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:461e3ae84cd90b30f8d533f07d87c00379644205b1d33a5ea03381edc4b69431"},
- {file = "contourpy-1.2.0-cp312-cp312-win32.whl", hash = "sha256:1c2559d6cffc94890b0529ea7eeecc20d6fadc1539273aa27faf503eb4656d8f"},
- {file = "contourpy-1.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:491b1917afdd8638a05b611a56d46587d5a632cabead889a5440f7c638bc6ed9"},
- {file = "contourpy-1.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5fd1810973a375ca0e097dee059c407913ba35723b111df75671a1976efa04bc"},
- {file = "contourpy-1.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:999c71939aad2780f003979b25ac5b8f2df651dac7b38fb8ce6c46ba5abe6ae9"},
- {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7caf9b241464c404613512d5594a6e2ff0cc9cb5615c9475cc1d9b514218ae8"},
- {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:266270c6f6608340f6c9836a0fb9b367be61dde0c9a9a18d5ece97774105ff3e"},
- {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbd50d0a0539ae2e96e537553aff6d02c10ed165ef40c65b0e27e744a0f10af8"},
- {file = "contourpy-1.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11f8d2554e52f459918f7b8e6aa20ec2a3bce35ce95c1f0ef4ba36fbda306df5"},
- {file = "contourpy-1.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ce96dd400486e80ac7d195b2d800b03e3e6a787e2a522bfb83755938465a819e"},
- {file = "contourpy-1.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6d3364b999c62f539cd403f8123ae426da946e142312a514162adb2addd8d808"},
- {file = "contourpy-1.2.0-cp39-cp39-win32.whl", hash = "sha256:1c88dfb9e0c77612febebb6ac69d44a8d81e3dc60f993215425b62c1161353f4"},
- {file = "contourpy-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:78e6ad33cf2e2e80c5dfaaa0beec3d61face0fb650557100ee36db808bfa6843"},
- {file = "contourpy-1.2.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:be16975d94c320432657ad2402f6760990cb640c161ae6da1363051805fa8108"},
- {file = "contourpy-1.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b95a225d4948b26a28c08307a60ac00fb8671b14f2047fc5476613252a129776"},
- {file = "contourpy-1.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0d7e03c0f9a4f90dc18d4e77e9ef4ec7b7bbb437f7f675be8e530d65ae6ef956"},
- {file = "contourpy-1.2.0.tar.gz", hash = "sha256:171f311cb758de7da13fc53af221ae47a5877be5a0843a9fe150818c51ed276a"},
-]
-
-[package.dependencies]
-numpy = ">=1.20,<2.0"
-
-[package.extras]
-bokeh = ["bokeh", "selenium"]
-docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"]
-mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.6.1)", "types-Pillow"]
-test = ["Pillow", "contourpy[test-no-images]", "matplotlib"]
-test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"]
-
[[package]]
name = "cryptography"
version = "41.0.7"
@@ -781,51 +626,6 @@ ssh = ["bcrypt (>=3.1.5)"]
test = ["pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"]
test-randomorder = ["pytest-randomly"]
-[[package]]
-name = "cycler"
-version = "0.12.1"
-description = "Composable style cycles"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"},
- {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"},
-]
-
-[package.extras]
-docs = ["ipython", "matplotlib", "numpydoc", "sphinx"]
-tests = ["pytest", "pytest-cov", "pytest-xdist"]
-
-[[package]]
-name = "dataset"
-version = "1.6.2"
-description = "Toolkit for Python-based database access."
-optional = false
-python-versions = "*"
-files = [
- {file = "dataset-1.6.2-py2.py3-none-any.whl", hash = "sha256:dcca9ba7658473d3082b1adf87a650252a1cd665705b73fa7d4ee32116a107b9"},
- {file = "dataset-1.6.2.tar.gz", hash = "sha256:77d362118f67a8cbb4848dbd30ab362b9fa7cfebdbfaf426c9c500cb38969a99"},
-]
-
-[package.dependencies]
-alembic = ">=0.6.2"
-banal = ">=1.0.1"
-sqlalchemy = ">=1.3.2,<2.0.0"
-
-[package.extras]
-dev = ["PyMySQL", "coverage", "cryptography", "flake8", "pip", "psycopg2-binary", "pytest", "wheel"]
-
-[[package]]
-name = "decorator"
-version = "5.1.1"
-description = "Decorators for Humans"
-optional = false
-python-versions = ">=3.5"
-files = [
- {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"},
- {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"},
-]
-
[[package]]
name = "defusedxml"
version = "0.7.1"
@@ -868,34 +668,6 @@ tzdata = {version = "*", markers = "sys_platform == \"win32\""}
argon2 = ["argon2-cffi (>=19.1.0)"]
bcrypt = ["bcrypt"]
-[[package]]
-name = "exceptiongroup"
-version = "1.2.0"
-description = "Backport of PEP 654 (exception groups)"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"},
- {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"},
-]
-
-[package.extras]
-test = ["pytest (>=6)"]
-
-[[package]]
-name = "executing"
-version = "2.0.1"
-description = "Get the currently executing AST node of a frame, and other information"
-optional = false
-python-versions = ">=3.5"
-files = [
- {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"},
- {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"},
-]
-
-[package.extras]
-tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"]
-
[[package]]
name = "face"
version = "22.0.0"
@@ -948,71 +720,6 @@ Werkzeug = ">=3.0.0"
async = ["asgiref (>=3.2)"]
dotenv = ["python-dotenv"]
-[[package]]
-name = "fonttools"
-version = "4.50.0"
-description = "Tools to manipulate font files"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "fonttools-4.50.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:effd303fb422f8ce06543a36ca69148471144c534cc25f30e5be752bc4f46736"},
- {file = "fonttools-4.50.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7913992ab836f621d06aabac118fc258b9947a775a607e1a737eb3a91c360335"},
- {file = "fonttools-4.50.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e0a1c5bd2f63da4043b63888534b52c5a1fd7ae187c8ffc64cbb7ae475b9dab"},
- {file = "fonttools-4.50.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d40fc98540fa5360e7ecf2c56ddf3c6e7dd04929543618fd7b5cc76e66390562"},
- {file = "fonttools-4.50.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9fff65fbb7afe137bac3113827855e0204482727bddd00a806034ab0d3951d0d"},
- {file = "fonttools-4.50.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1aeae3dd2ee719074a9372c89ad94f7c581903306d76befdaca2a559f802472"},
- {file = "fonttools-4.50.0-cp310-cp310-win32.whl", hash = "sha256:e9623afa319405da33b43c85cceb0585a6f5d3a1d7c604daf4f7e1dd55c03d1f"},
- {file = "fonttools-4.50.0-cp310-cp310-win_amd64.whl", hash = "sha256:778c5f43e7e654ef7fe0605e80894930bc3a7772e2f496238e57218610140f54"},
- {file = "fonttools-4.50.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3dfb102e7f63b78c832e4539969167ffcc0375b013080e6472350965a5fe8048"},
- {file = "fonttools-4.50.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9e58fe34cb379ba3d01d5d319d67dd3ce7ca9a47ad044ea2b22635cd2d1247fc"},
- {file = "fonttools-4.50.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c673ab40d15a442a4e6eb09bf007c1dda47c84ac1e2eecbdf359adacb799c24"},
- {file = "fonttools-4.50.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9b3ac35cdcd1a4c90c23a5200212c1bb74fa05833cc7c14291d7043a52ca2aaa"},
- {file = "fonttools-4.50.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8844e7a2c5f7ecf977e82eb6b3014f025c8b454e046d941ece05b768be5847ae"},
- {file = "fonttools-4.50.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f849bd3c5c2249b49c98eca5aaebb920d2bfd92b3c69e84ca9bddf133e9f83f0"},
- {file = "fonttools-4.50.0-cp311-cp311-win32.whl", hash = "sha256:39293ff231b36b035575e81c14626dfc14407a20de5262f9596c2cbb199c3625"},
- {file = "fonttools-4.50.0-cp311-cp311-win_amd64.whl", hash = "sha256:c33d5023523b44d3481624f840c8646656a1def7630ca562f222eb3ead16c438"},
- {file = "fonttools-4.50.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b4a886a6dbe60100ba1cd24de962f8cd18139bd32808da80de1fa9f9f27bf1dc"},
- {file = "fonttools-4.50.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b2ca1837bfbe5eafa11313dbc7edada79052709a1fffa10cea691210af4aa1fa"},
- {file = "fonttools-4.50.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0493dd97ac8977e48ffc1476b932b37c847cbb87fd68673dee5182004906828"},
- {file = "fonttools-4.50.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77844e2f1b0889120b6c222fc49b2b75c3d88b930615e98893b899b9352a27ea"},
- {file = "fonttools-4.50.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3566bfb8c55ed9100afe1ba6f0f12265cd63a1387b9661eb6031a1578a28bad1"},
- {file = "fonttools-4.50.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:35e10ddbc129cf61775d58a14f2d44121178d89874d32cae1eac722e687d9019"},
- {file = "fonttools-4.50.0-cp312-cp312-win32.whl", hash = "sha256:cc8140baf9fa8f9b903f2b393a6c413a220fa990264b215bf48484f3d0bf8710"},
- {file = "fonttools-4.50.0-cp312-cp312-win_amd64.whl", hash = "sha256:0ccc85fd96373ab73c59833b824d7a73846670a0cb1f3afbaee2b2c426a8f931"},
- {file = "fonttools-4.50.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e270a406219af37581d96c810172001ec536e29e5593aa40d4c01cca3e145aa6"},
- {file = "fonttools-4.50.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ac2463de667233372e9e1c7e9de3d914b708437ef52a3199fdbf5a60184f190c"},
- {file = "fonttools-4.50.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47abd6669195abe87c22750dbcd366dc3a0648f1b7c93c2baa97429c4dc1506e"},
- {file = "fonttools-4.50.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:074841375e2e3d559aecc86e1224caf78e8b8417bb391e7d2506412538f21adc"},
- {file = "fonttools-4.50.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0743fd2191ad7ab43d78cd747215b12033ddee24fa1e088605a3efe80d6984de"},
- {file = "fonttools-4.50.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3d7080cce7be5ed65bee3496f09f79a82865a514863197ff4d4d177389e981b0"},
- {file = "fonttools-4.50.0-cp38-cp38-win32.whl", hash = "sha256:a467ba4e2eadc1d5cc1a11d355abb945f680473fbe30d15617e104c81f483045"},
- {file = "fonttools-4.50.0-cp38-cp38-win_amd64.whl", hash = "sha256:f77e048f805e00870659d6318fd89ef28ca4ee16a22b4c5e1905b735495fc422"},
- {file = "fonttools-4.50.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b6245eafd553c4e9a0708e93be51392bd2288c773523892fbd616d33fd2fda59"},
- {file = "fonttools-4.50.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a4062cc7e8de26f1603323ef3ae2171c9d29c8a9f5e067d555a2813cd5c7a7e0"},
- {file = "fonttools-4.50.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34692850dfd64ba06af61e5791a441f664cb7d21e7b544e8f385718430e8f8e4"},
- {file = "fonttools-4.50.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678dd95f26a67e02c50dcb5bf250f95231d455642afbc65a3b0bcdacd4e4dd38"},
- {file = "fonttools-4.50.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4f2ce7b0b295fe64ac0a85aef46a0f2614995774bd7bc643b85679c0283287f9"},
- {file = "fonttools-4.50.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d346f4dc2221bfb7ab652d1e37d327578434ce559baf7113b0f55768437fe6a0"},
- {file = "fonttools-4.50.0-cp39-cp39-win32.whl", hash = "sha256:a51eeaf52ba3afd70bf489be20e52fdfafe6c03d652b02477c6ce23c995222f4"},
- {file = "fonttools-4.50.0-cp39-cp39-win_amd64.whl", hash = "sha256:8639be40d583e5d9da67795aa3eeeda0488fb577a1d42ae11a5036f18fb16d93"},
- {file = "fonttools-4.50.0-py3-none-any.whl", hash = "sha256:48fa36da06247aa8282766cfd63efff1bb24e55f020f29a335939ed3844d20d3"},
- {file = "fonttools-4.50.0.tar.gz", hash = "sha256:fa5cf61058c7dbb104c2ac4e782bf1b2016a8cf2f69de6e4dd6a865d2c969bb5"},
-]
-
-[package.extras]
-all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"]
-graphite = ["lz4 (>=1.7.4.2)"]
-interpolatable = ["munkres", "pycairo", "scipy"]
-lxml = ["lxml (>=4.0)"]
-pathops = ["skia-pathops (>=0.5.0)"]
-plot = ["matplotlib"]
-repacker = ["uharfbuzz (>=0.23.0)"]
-symfont = ["sympy"]
-type1 = ["xattr"]
-ufo = ["fs (>=2.2.0,<3)"]
-unicode = ["unicodedata2 (>=15.1.0)"]
-woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"]
-
[[package]]
name = "frida"
version = "16.2.1"
@@ -1064,77 +771,6 @@ files = [
{file = "google_play_scraper-1.2.6.tar.gz", hash = "sha256:b05b0572e7a7a5ddc5d1a9c06f4410bda5a69d86808446b9dd74e5cdcdf5865f"},
]
-[[package]]
-name = "greenlet"
-version = "3.0.3"
-description = "Lightweight in-process concurrent programming"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"},
- {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"},
- {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"},
- {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"},
- {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"},
- {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"},
- {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"},
- {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"},
- {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"},
- {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"},
- {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"},
- {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"},
- {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"},
- {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"},
- {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"},
- {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"},
- {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"},
- {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"},
- {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"},
- {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"},
- {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"},
- {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"},
- {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"},
- {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"},
- {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"},
- {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"},
- {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"},
- {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"},
- {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"},
- {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"},
- {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"},
- {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"},
- {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"},
- {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"},
- {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"},
- {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"},
- {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"},
- {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"},
- {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"},
- {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"},
- {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"},
- {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"},
- {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"},
- {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"},
- {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"},
- {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"},
- {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"},
- {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"},
- {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"},
- {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"},
- {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"},
- {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"},
- {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"},
- {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"},
- {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"},
- {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"},
- {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"},
- {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"},
-]
-
-[package.extras]
-docs = ["Sphinx", "furo"]
-test = ["objgraph", "psutil"]
-
[[package]]
name = "gunicorn"
version = "21.2.0"
@@ -1239,42 +875,6 @@ files = [
{file = "IP2Location-8.10.0.tar.gz", hash = "sha256:f0eea26c655f701406fa9379a38a0b56cc0dc7d0c6a221d44bda5280978a75ab"},
]
-[[package]]
-name = "ipython"
-version = "8.22.2"
-description = "IPython: Productive Interactive Computing"
-optional = false
-python-versions = ">=3.10"
-files = [
- {file = "ipython-8.22.2-py3-none-any.whl", hash = "sha256:3c86f284c8f3d8f2b6c662f885c4889a91df7cd52056fd02b7d8d6195d7f56e9"},
- {file = "ipython-8.22.2.tar.gz", hash = "sha256:2dcaad9049f9056f1fef63514f176c7d41f930daa78d05b82a176202818f2c14"},
-]
-
-[package.dependencies]
-colorama = {version = "*", markers = "sys_platform == \"win32\""}
-decorator = "*"
-exceptiongroup = {version = "*", markers = "python_version < \"3.11\""}
-jedi = ">=0.16"
-matplotlib-inline = "*"
-pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""}
-prompt-toolkit = ">=3.0.41,<3.1.0"
-pygments = ">=2.4.0"
-stack-data = "*"
-traitlets = ">=5.13.0"
-
-[package.extras]
-all = ["ipython[black,doc,kernel,nbconvert,nbformat,notebook,parallel,qtconsole,terminal]", "ipython[test,test-extra]"]
-black = ["black"]
-doc = ["docrepr", "exceptiongroup", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "stack-data", "typing-extensions"]
-kernel = ["ipykernel"]
-nbconvert = ["nbconvert"]
-nbformat = ["nbformat"]
-notebook = ["ipywidgets", "notebook"]
-parallel = ["ipyparallel"]
-qtconsole = ["qtconsole"]
-test = ["pickleshare", "pytest (<8)", "pytest-asyncio (<0.22)", "testpath"]
-test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"]
-
[[package]]
name = "itsdangerous"
version = "2.1.2"
@@ -1286,25 +886,6 @@ files = [
{file = "itsdangerous-2.1.2.tar.gz", hash = "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a"},
]
-[[package]]
-name = "jedi"
-version = "0.19.1"
-description = "An autocompletion tool for Python that can be used for text editors."
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"},
- {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"},
-]
-
-[package.dependencies]
-parso = ">=0.8.3,<0.9.0"
-
-[package.extras]
-docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"]
-qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
-testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"]
-
[[package]]
name = "jinja2"
version = "3.1.3"
@@ -1352,119 +933,6 @@ files = [
{file = "kaitaistruct-0.10.tar.gz", hash = "sha256:a044dee29173d6afbacf27bcac39daf89b654dd418cfa009ab82d9178a9ae52a"},
]
-[[package]]
-name = "kiwisolver"
-version = "1.4.5"
-description = "A fast implementation of the Cassowary constraint solver"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af"},
- {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3"},
- {file = "kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4"},
- {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1"},
- {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff"},
- {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a"},
- {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa"},
- {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c"},
- {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b"},
- {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770"},
- {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0"},
- {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525"},
- {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b"},
- {file = "kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238"},
- {file = "kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276"},
- {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5"},
- {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90"},
- {file = "kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797"},
- {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9"},
- {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437"},
- {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9"},
- {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da"},
- {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e"},
- {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8"},
- {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d"},
- {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0"},
- {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f"},
- {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f"},
- {file = "kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac"},
- {file = "kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355"},
- {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a"},
- {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192"},
- {file = "kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45"},
- {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7"},
- {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db"},
- {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff"},
- {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228"},
- {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16"},
- {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9"},
- {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162"},
- {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4"},
- {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3"},
- {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a"},
- {file = "kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20"},
- {file = "kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9"},
- {file = "kiwisolver-1.4.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130"},
- {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898"},
- {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709"},
- {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b"},
- {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89"},
- {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985"},
- {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265"},
- {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a"},
- {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205"},
- {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb"},
- {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f"},
- {file = "kiwisolver-1.4.5-cp37-cp37m-win32.whl", hash = "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3"},
- {file = "kiwisolver-1.4.5-cp37-cp37m-win_amd64.whl", hash = "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a"},
- {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71"},
- {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93"},
- {file = "kiwisolver-1.4.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29"},
- {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712"},
- {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6"},
- {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb"},
- {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18"},
- {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333"},
- {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da"},
- {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b"},
- {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c"},
- {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc"},
- {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250"},
- {file = "kiwisolver-1.4.5-cp38-cp38-win32.whl", hash = "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e"},
- {file = "kiwisolver-1.4.5-cp38-cp38-win_amd64.whl", hash = "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced"},
- {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d"},
- {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9"},
- {file = "kiwisolver-1.4.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046"},
- {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0"},
- {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff"},
- {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54"},
- {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958"},
- {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3"},
- {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf"},
- {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901"},
- {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9"},
- {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342"},
- {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77"},
- {file = "kiwisolver-1.4.5-cp39-cp39-win32.whl", hash = "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f"},
- {file = "kiwisolver-1.4.5-cp39-cp39-win_amd64.whl", hash = "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635"},
- {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920"},
- {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390"},
- {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d"},
- {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523"},
- {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4"},
- {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892"},
- {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544"},
- {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126"},
- {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd"},
- {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929"},
- {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09"},
- {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7"},
- {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad"},
- {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea"},
- {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee"},
- {file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"},
-]
-
[[package]]
name = "ldap3"
version = "2.9.1"
@@ -1534,24 +1002,6 @@ files = [
{file = "lief-0.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:2db3eb282a35daf51f89c6509226668a08fb6a6d1f507dd549dd9f077585db11"},
]
-[[package]]
-name = "loguru"
-version = "0.7.2"
-description = "Python logging made (stupidly) simple"
-optional = false
-python-versions = ">=3.5"
-files = [
- {file = "loguru-0.7.2-py3-none-any.whl", hash = "sha256:003d71e3d3ed35f0f8984898359d65b79e5b21943f78af86aa5491210429b8eb"},
- {file = "loguru-0.7.2.tar.gz", hash = "sha256:e671a53522515f34fd406340ee968cb9ecafbc4b36c679da03c18fd8d0bd51ac"},
-]
-
-[package.dependencies]
-colorama = {version = ">=0.3.4", markers = "sys_platform == \"win32\""}
-win32-setctime = {version = ">=1.0.0", markers = "sys_platform == \"win32\""}
-
-[package.extras]
-dev = ["Sphinx (==7.2.5)", "colorama (==0.4.5)", "colorama (==0.4.6)", "exceptiongroup (==1.1.3)", "freezegun (==1.1.0)", "freezegun (==1.2.2)", "mypy (==v0.910)", "mypy (==v0.971)", "mypy (==v1.4.1)", "mypy (==v1.5.1)", "pre-commit (==3.4.0)", "pytest (==6.1.2)", "pytest (==7.4.0)", "pytest-cov (==2.12.1)", "pytest-cov (==4.1.0)", "pytest-mypy-plugins (==1.9.3)", "pytest-mypy-plugins (==3.0.0)", "sphinx-autobuild (==2021.3.14)", "sphinx-rtd-theme (==1.3.0)", "tox (==3.27.1)", "tox (==4.11.0)"]
-
[[package]]
name = "lxml"
version = "5.1.0"
@@ -1655,25 +1105,6 @@ files = [
[package.dependencies]
altgraph = ">=0.17"
-[[package]]
-name = "mako"
-version = "1.3.2"
-description = "A super-fast templating language that borrows the best ideas from the existing templating languages."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "Mako-1.3.2-py3-none-any.whl", hash = "sha256:32a99d70754dfce237019d17ffe4a282d2d3351b9c476e90d8a60e63f133b80c"},
- {file = "Mako-1.3.2.tar.gz", hash = "sha256:2a0c8ad7f6274271b3bb7467dd37cf9cc6dab4bc19cb69a4ef10669402de698e"},
-]
-
-[package.dependencies]
-MarkupSafe = ">=0.9.2"
-
-[package.extras]
-babel = ["Babel"]
-lingua = ["lingua"]
-testing = ["pytest"]
-
[[package]]
name = "markupsafe"
version = "2.1.5"
@@ -1743,68 +1174,6 @@ files = [
{file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
]
-[[package]]
-name = "matplotlib"
-version = "3.8.3"
-description = "Python plotting package"
-optional = false
-python-versions = ">=3.9"
-files = [
- {file = "matplotlib-3.8.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:cf60138ccc8004f117ab2a2bad513cc4d122e55864b4fe7adf4db20ca68a078f"},
- {file = "matplotlib-3.8.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f557156f7116be3340cdeef7f128fa99b0d5d287d5f41a16e169819dcf22357"},
- {file = "matplotlib-3.8.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f386cf162b059809ecfac3bcc491a9ea17da69fa35c8ded8ad154cd4b933d5ec"},
- {file = "matplotlib-3.8.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3c5f96f57b0369c288bf6f9b5274ba45787f7e0589a34d24bdbaf6d3344632f"},
- {file = "matplotlib-3.8.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:83e0f72e2c116ca7e571c57aa29b0fe697d4c6425c4e87c6e994159e0c008635"},
- {file = "matplotlib-3.8.3-cp310-cp310-win_amd64.whl", hash = "sha256:1c5c8290074ba31a41db1dc332dc2b62def469ff33766cbe325d32a3ee291aea"},
- {file = "matplotlib-3.8.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5184e07c7e1d6d1481862ee361905b7059f7fe065fc837f7c3dc11eeb3f2f900"},
- {file = "matplotlib-3.8.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d7e7e0993d0758933b1a241a432b42c2db22dfa37d4108342ab4afb9557cbe3e"},
- {file = "matplotlib-3.8.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:04b36ad07eac9740fc76c2aa16edf94e50b297d6eb4c081e3add863de4bb19a7"},
- {file = "matplotlib-3.8.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c42dae72a62f14982f1474f7e5c9959fc4bc70c9de11cc5244c6e766200ba65"},
- {file = "matplotlib-3.8.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bf5932eee0d428192c40b7eac1399d608f5d995f975cdb9d1e6b48539a5ad8d0"},
- {file = "matplotlib-3.8.3-cp311-cp311-win_amd64.whl", hash = "sha256:40321634e3a05ed02abf7c7b47a50be50b53ef3eaa3a573847431a545585b407"},
- {file = "matplotlib-3.8.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:09074f8057917d17ab52c242fdf4916f30e99959c1908958b1fc6032e2d0f6d4"},
- {file = "matplotlib-3.8.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5745f6d0fb5acfabbb2790318db03809a253096e98c91b9a31969df28ee604aa"},
- {file = "matplotlib-3.8.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97653d869a71721b639714b42d87cda4cfee0ee74b47c569e4874c7590c55c5"},
- {file = "matplotlib-3.8.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:242489efdb75b690c9c2e70bb5c6550727058c8a614e4c7716f363c27e10bba1"},
- {file = "matplotlib-3.8.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:83c0653c64b73926730bd9ea14aa0f50f202ba187c307a881673bad4985967b7"},
- {file = "matplotlib-3.8.3-cp312-cp312-win_amd64.whl", hash = "sha256:ef6c1025a570354297d6c15f7d0f296d95f88bd3850066b7f1e7b4f2f4c13a39"},
- {file = "matplotlib-3.8.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c4af3f7317f8a1009bbb2d0bf23dfaba859eb7dd4ccbd604eba146dccaaaf0a4"},
- {file = "matplotlib-3.8.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4c6e00a65d017d26009bac6808f637b75ceade3e1ff91a138576f6b3065eeeba"},
- {file = "matplotlib-3.8.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e7b49ab49a3bea17802df6872f8d44f664ba8f9be0632a60c99b20b6db2165b7"},
- {file = "matplotlib-3.8.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6728dde0a3997396b053602dbd907a9bd64ec7d5cf99e728b404083698d3ca01"},
- {file = "matplotlib-3.8.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:813925d08fb86aba139f2d31864928d67511f64e5945ca909ad5bc09a96189bb"},
- {file = "matplotlib-3.8.3-cp39-cp39-win_amd64.whl", hash = "sha256:cd3a0c2be76f4e7be03d34a14d49ded6acf22ef61f88da600a18a5cd8b3c5f3c"},
- {file = "matplotlib-3.8.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fa93695d5c08544f4a0dfd0965f378e7afc410d8672816aff1e81be1f45dbf2e"},
- {file = "matplotlib-3.8.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9764df0e8778f06414b9d281a75235c1e85071f64bb5d71564b97c1306a2afc"},
- {file = "matplotlib-3.8.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:5e431a09e6fab4012b01fc155db0ce6dccacdbabe8198197f523a4ef4805eb26"},
- {file = "matplotlib-3.8.3.tar.gz", hash = "sha256:7b416239e9ae38be54b028abbf9048aff5054a9aba5416bef0bd17f9162ce161"},
-]
-
-[package.dependencies]
-contourpy = ">=1.0.1"
-cycler = ">=0.10"
-fonttools = ">=4.22.0"
-kiwisolver = ">=1.3.1"
-numpy = ">=1.21,<2"
-packaging = ">=20.0"
-pillow = ">=8"
-pyparsing = ">=2.3.1"
-python-dateutil = ">=2.7"
-
-[[package]]
-name = "matplotlib-inline"
-version = "0.1.6"
-description = "Inline Matplotlib backend for Jupyter"
-optional = false
-python-versions = ">=3.5"
-files = [
- {file = "matplotlib-inline-0.1.6.tar.gz", hash = "sha256:f887e5f10ba98e8d2b150ddcf4702c1e5f8b3a20005eb0f74bfdbd360ee6f304"},
- {file = "matplotlib_inline-0.1.6-py3-none-any.whl", hash = "sha256:f1f41aab5328aa5aaea9b16d083b128102f8712542f819fe7e6a420ff581b311"},
-]
-
-[package.dependencies]
-traitlets = "*"
-
[[package]]
name = "mitmproxy"
version = "10.1.5"
@@ -1951,94 +1320,6 @@ files = [
{file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"},
]
-[[package]]
-name = "mutf8"
-version = "1.0.6"
-description = "Fast MUTF-8 encoder & decoder"
-optional = false
-python-versions = "*"
-files = [
- {file = "mutf8-1.0.6-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:74ae69cd9790fa4f0f6a7b0db503c459c955b8235551baf683cb4f3f31677063"},
- {file = "mutf8-1.0.6-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fcf20045263ce8ebd6c47e94c9477ab0d388ed169a69ad2d8f19bcbf0b87f401"},
- {file = "mutf8-1.0.6-cp36-cp36m-win_amd64.whl", hash = "sha256:83c38555db263e369e95533d80848d8e4296e302303b72082b98c3124cba504d"},
- {file = "mutf8-1.0.6-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:e09f4a19e5500699bb42074890b463b785ab9a8d95c7d793e590405f3b4b29d7"},
- {file = "mutf8-1.0.6-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f4f497f20e3ea7968496c1eb1e1cb259c53ad040879e1e83ffb755a12112a04"},
- {file = "mutf8-1.0.6-cp37-cp37m-win_amd64.whl", hash = "sha256:1925f5490fabca5c34138ed6644a1a093b0d935252207a5e89664097ff14114c"},
- {file = "mutf8-1.0.6-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:018ceda7cdb66a1d3e9c07a71a1a35b92570fbb1230887a34ad784ff4d349981"},
- {file = "mutf8-1.0.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a67e88534a7641c513dad13f2f7913239808df4a5d0b822eda0ff9024431e0b"},
- {file = "mutf8-1.0.6-cp38-cp38-win_amd64.whl", hash = "sha256:0d1325d42806b31901a0ddd4ef199144e508fd9f6f3c75a8305d5979365b66c3"},
- {file = "mutf8-1.0.6-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:3207a071ead14d928213019f12b5554b179f61a16a8094ed660b755990db3652"},
- {file = "mutf8-1.0.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6172b5babc0c819636830fc79ca9c3a82662ef1ee764c82c1b59fbf6ea54d82f"},
- {file = "mutf8-1.0.6-cp39-cp39-win_amd64.whl", hash = "sha256:4f7a24b55c53d508a7ecb2e8c6fe14e4fcefaa4c48100b446e73217ade7875a0"},
- {file = "mutf8-1.0.6.tar.gz", hash = "sha256:1bbbefb67c2e5a57104750bb04b0912200b57b2fa9841be245279e83859cb346"},
-]
-
-[package.extras]
-test = ["pytest", "pytest-benchmark"]
-
-[[package]]
-name = "networkx"
-version = "3.2.1"
-description = "Python package for creating and manipulating graphs and networks"
-optional = false
-python-versions = ">=3.9"
-files = [
- {file = "networkx-3.2.1-py3-none-any.whl", hash = "sha256:f18c69adc97877c42332c170849c96cefa91881c99a7cb3e95b7c659ebdc1ec2"},
- {file = "networkx-3.2.1.tar.gz", hash = "sha256:9f1bb5cf3409bf324e0a722c20bdb4c20ee39bf1c30ce8ae499c8502b0b5e0c6"},
-]
-
-[package.extras]
-default = ["matplotlib (>=3.5)", "numpy (>=1.22)", "pandas (>=1.4)", "scipy (>=1.9,!=1.11.0,!=1.11.1)"]
-developer = ["changelist (==0.4)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"]
-doc = ["nb2plots (>=0.7)", "nbconvert (<7.9)", "numpydoc (>=1.6)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.14)", "sphinx (>=7)", "sphinx-gallery (>=0.14)", "texext (>=0.6.7)"]
-extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.11)", "sympy (>=1.10)"]
-test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"]
-
-[[package]]
-name = "numpy"
-version = "1.26.4"
-description = "Fundamental package for array computing in Python"
-optional = false
-python-versions = ">=3.9"
-files = [
- {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"},
- {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"},
- {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"},
- {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"},
- {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"},
- {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"},
- {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"},
- {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"},
- {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"},
- {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"},
- {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"},
- {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"},
- {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"},
- {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"},
- {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"},
- {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"},
- {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"},
- {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"},
- {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"},
- {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"},
- {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"},
- {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"},
- {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"},
- {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"},
- {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"},
- {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"},
- {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"},
- {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"},
- {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"},
- {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"},
- {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"},
- {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"},
- {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"},
- {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"},
- {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"},
- {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"},
-]
-
[[package]]
name = "openstep-parser"
version = "1.5.4"
@@ -2085,21 +1366,6 @@ all = ["gssapi (>=1.4.1)", "invoke (>=2.0)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1
gssapi = ["gssapi (>=1.4.1)", "pyasn1 (>=0.1.7)", "pywin32 (>=2.1.8)"]
invoke = ["invoke (>=2.0)"]
-[[package]]
-name = "parso"
-version = "0.8.3"
-description = "A Python Parser"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"},
- {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"},
-]
-
-[package.extras]
-qa = ["flake8 (==3.8.3)", "mypy (==0.782)"]
-testing = ["docopt", "pytest (<6.0.0)"]
-
[[package]]
name = "passlib"
version = "1.7.4"
@@ -2139,119 +1405,6 @@ files = [
{file = "peewee-3.17.1.tar.gz", hash = "sha256:e009ac4227c4fdc0058a56e822ad5987684f0a1fbb20fed577200785102581c3"},
]
-[[package]]
-name = "pexpect"
-version = "4.9.0"
-description = "Pexpect allows easy control of interactive console applications."
-optional = false
-python-versions = "*"
-files = [
- {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"},
- {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"},
-]
-
-[package.dependencies]
-ptyprocess = ">=0.5"
-
-[[package]]
-name = "pillow"
-version = "10.2.0"
-description = "Python Imaging Library (Fork)"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "pillow-10.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:7823bdd049099efa16e4246bdf15e5a13dbb18a51b68fa06d6c1d4d8b99a796e"},
- {file = "pillow-10.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:83b2021f2ade7d1ed556bc50a399127d7fb245e725aa0113ebd05cfe88aaf588"},
- {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fad5ff2f13d69b7e74ce5b4ecd12cc0ec530fcee76356cac6742785ff71c452"},
- {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da2b52b37dad6d9ec64e653637a096905b258d2fc2b984c41ae7d08b938a67e4"},
- {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:47c0995fc4e7f79b5cfcab1fc437ff2890b770440f7696a3ba065ee0fd496563"},
- {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:322bdf3c9b556e9ffb18f93462e5f749d3444ce081290352c6070d014c93feb2"},
- {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:51f1a1bffc50e2e9492e87d8e09a17c5eea8409cda8d3f277eb6edc82813c17c"},
- {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69ffdd6120a4737710a9eee73e1d2e37db89b620f702754b8f6e62594471dee0"},
- {file = "pillow-10.2.0-cp310-cp310-win32.whl", hash = "sha256:c6dafac9e0f2b3c78df97e79af707cdc5ef8e88208d686a4847bab8266870023"},
- {file = "pillow-10.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:aebb6044806f2e16ecc07b2a2637ee1ef67a11840a66752751714a0d924adf72"},
- {file = "pillow-10.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:7049e301399273a0136ff39b84c3678e314f2158f50f517bc50285fb5ec847ad"},
- {file = "pillow-10.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35bb52c37f256f662abdfa49d2dfa6ce5d93281d323a9af377a120e89a9eafb5"},
- {file = "pillow-10.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c23f307202661071d94b5e384e1e1dc7dfb972a28a2310e4ee16103e66ddb67"},
- {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:773efe0603db30c281521a7c0214cad7836c03b8ccff897beae9b47c0b657d61"},
- {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11fa2e5984b949b0dd6d7a94d967743d87c577ff0b83392f17cb3990d0d2fd6e"},
- {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:716d30ed977be8b37d3ef185fecb9e5a1d62d110dfbdcd1e2a122ab46fddb03f"},
- {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a086c2af425c5f62a65e12fbf385f7c9fcb8f107d0849dba5839461a129cf311"},
- {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c8de2789052ed501dd829e9cae8d3dcce7acb4777ea4a479c14521c942d395b1"},
- {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:609448742444d9290fd687940ac0b57fb35e6fd92bdb65386e08e99af60bf757"},
- {file = "pillow-10.2.0-cp311-cp311-win32.whl", hash = "sha256:823ef7a27cf86df6597fa0671066c1b596f69eba53efa3d1e1cb8b30f3533068"},
- {file = "pillow-10.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:1da3b2703afd040cf65ec97efea81cfba59cdbed9c11d8efc5ab09df9509fc56"},
- {file = "pillow-10.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:edca80cbfb2b68d7b56930b84a0e45ae1694aeba0541f798e908a49d66b837f1"},
- {file = "pillow-10.2.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:1b5e1b74d1bd1b78bc3477528919414874748dd363e6272efd5abf7654e68bef"},
- {file = "pillow-10.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0eae2073305f451d8ecacb5474997c08569fb4eb4ac231ffa4ad7d342fdc25ac"},
- {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7c2286c23cd350b80d2fc9d424fc797575fb16f854b831d16fd47ceec078f2c"},
- {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e23412b5c41e58cec602f1135c57dfcf15482013ce6e5f093a86db69646a5aa"},
- {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:52a50aa3fb3acb9cf7213573ef55d31d6eca37f5709c69e6858fe3bc04a5c2a2"},
- {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:127cee571038f252a552760076407f9cff79761c3d436a12af6000cd182a9d04"},
- {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8d12251f02d69d8310b046e82572ed486685c38f02176bd08baf216746eb947f"},
- {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54f1852cd531aa981bc0965b7d609f5f6cc8ce8c41b1139f6ed6b3c54ab82bfb"},
- {file = "pillow-10.2.0-cp312-cp312-win32.whl", hash = "sha256:257d8788df5ca62c980314053197f4d46eefedf4e6175bc9412f14412ec4ea2f"},
- {file = "pillow-10.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:154e939c5f0053a383de4fd3d3da48d9427a7e985f58af8e94d0b3c9fcfcf4f9"},
- {file = "pillow-10.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:f379abd2f1e3dddb2b61bc67977a6b5a0a3f7485538bcc6f39ec76163891ee48"},
- {file = "pillow-10.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8373c6c251f7ef8bda6675dd6d2b3a0fcc31edf1201266b5cf608b62a37407f9"},
- {file = "pillow-10.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:870ea1ada0899fd0b79643990809323b389d4d1d46c192f97342eeb6ee0b8483"},
- {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4b6b1e20608493548b1f32bce8cca185bf0480983890403d3b8753e44077129"},
- {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3031709084b6e7852d00479fd1d310b07d0ba82765f973b543c8af5061cf990e"},
- {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:3ff074fc97dd4e80543a3e91f69d58889baf2002b6be64347ea8cf5533188213"},
- {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:cb4c38abeef13c61d6916f264d4845fab99d7b711be96c326b84df9e3e0ff62d"},
- {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b1b3020d90c2d8e1dae29cf3ce54f8094f7938460fb5ce8bc5c01450b01fbaf6"},
- {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:170aeb00224ab3dc54230c797f8404507240dd868cf52066f66a41b33169bdbe"},
- {file = "pillow-10.2.0-cp38-cp38-win32.whl", hash = "sha256:c4225f5220f46b2fde568c74fca27ae9771536c2e29d7c04f4fb62c83275ac4e"},
- {file = "pillow-10.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:0689b5a8c5288bc0504d9fcee48f61a6a586b9b98514d7d29b840143d6734f39"},
- {file = "pillow-10.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b792a349405fbc0163190fde0dc7b3fef3c9268292586cf5645598b48e63dc67"},
- {file = "pillow-10.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c570f24be1e468e3f0ce7ef56a89a60f0e05b30a3669a459e419c6eac2c35364"},
- {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8ecd059fdaf60c1963c58ceb8997b32e9dc1b911f5da5307aab614f1ce5c2fb"},
- {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c365fd1703040de1ec284b176d6af5abe21b427cb3a5ff68e0759e1e313a5e7e"},
- {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:70c61d4c475835a19b3a5aa42492409878bbca7438554a1f89d20d58a7c75c01"},
- {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b6f491cdf80ae540738859d9766783e3b3c8e5bd37f5dfa0b76abdecc5081f13"},
- {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d189550615b4948f45252d7f005e53c2040cea1af5b60d6f79491a6e147eef7"},
- {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:49d9ba1ed0ef3e061088cd1e7538a0759aab559e2e0a80a36f9fd9d8c0c21591"},
- {file = "pillow-10.2.0-cp39-cp39-win32.whl", hash = "sha256:babf5acfede515f176833ed6028754cbcd0d206f7f614ea3447d67c33be12516"},
- {file = "pillow-10.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:0304004f8067386b477d20a518b50f3fa658a28d44e4116970abfcd94fac34a8"},
- {file = "pillow-10.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:0fb3e7fc88a14eacd303e90481ad983fd5b69c761e9e6ef94c983f91025da869"},
- {file = "pillow-10.2.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:322209c642aabdd6207517e9739c704dc9f9db943015535783239022002f054a"},
- {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3eedd52442c0a5ff4f887fab0c1c0bb164d8635b32c894bc1faf4c618dd89df2"},
- {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb28c753fd5eb3dd859b4ee95de66cc62af91bcff5db5f2571d32a520baf1f04"},
- {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:33870dc4653c5017bf4c8873e5488d8f8d5f8935e2f1fb9a2208c47cdd66efd2"},
- {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3c31822339516fb3c82d03f30e22b1d038da87ef27b6a78c9549888f8ceda39a"},
- {file = "pillow-10.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a2b56ba36e05f973d450582fb015594aaa78834fefe8dfb8fcd79b93e64ba4c6"},
- {file = "pillow-10.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d8e6aeb9201e655354b3ad049cb77d19813ad4ece0df1249d3c793de3774f8c7"},
- {file = "pillow-10.2.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:2247178effb34a77c11c0e8ac355c7a741ceca0a732b27bf11e747bbc950722f"},
- {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15587643b9e5eb26c48e49a7b33659790d28f190fc514a322d55da2fb5c2950e"},
- {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753cd8f2086b2b80180d9b3010dd4ed147efc167c90d3bf593fe2af21265e5a5"},
- {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7c8f97e8e7a9009bcacbe3766a36175056c12f9a44e6e6f2d5caad06dcfbf03b"},
- {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d1b35bcd6c5543b9cb547dee3150c93008f8dd0f1fef78fc0cd2b141c5baf58a"},
- {file = "pillow-10.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe4c15f6c9285dc54ce6553a3ce908ed37c8f3825b5a51a15c91442bb955b868"},
- {file = "pillow-10.2.0.tar.gz", hash = "sha256:e87f0b2c78157e12d7686b27d63c070fd65d994e8ddae6f328e0dcf4a0cd007e"},
-]
-
-[package.extras]
-docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"]
-fpx = ["olefile"]
-mic = ["olefile"]
-tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
-typing = ["typing-extensions"]
-xmp = ["defusedxml"]
-
-[[package]]
-name = "prompt-toolkit"
-version = "3.0.43"
-description = "Library for building powerful interactive command lines in Python"
-optional = false
-python-versions = ">=3.7.0"
-files = [
- {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"},
- {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"},
-]
-
-[package.dependencies]
-wcwidth = "*"
-
[[package]]
name = "protobuf"
version = "4.25.3"
@@ -2300,17 +1453,6 @@ files = [
[package.extras]
test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"]
-[[package]]
-name = "ptyprocess"
-version = "0.7.0"
-description = "Run a subprocess in a pseudo terminal"
-optional = false
-python-versions = "*"
-files = [
- {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
- {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
-]
-
[[package]]
name = "publicsuffix2"
version = "2.20191221"
@@ -2322,20 +1464,6 @@ files = [
{file = "publicsuffix2-2.20191221.tar.gz", hash = "sha256:00f8cc31aa8d0d5592a5ced19cccba7de428ebca985db26ac852d920ddd6fe7b"},
]
-[[package]]
-name = "pure-eval"
-version = "0.2.2"
-description = "Safely evaluate AST nodes without side effects"
-optional = false
-python-versions = "*"
-files = [
- {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"},
- {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"},
-]
-
-[package.extras]
-tests = ["pytest"]
-
[[package]]
name = "pyasn1"
version = "0.5.1"
@@ -2387,40 +1515,6 @@ files = [
docs = ["sphinx (>=1.4.8)"]
test = ["codecov (>=2.0.5)", "hypothesis (>=3.5.3)", "mock (>=1.0.1)", "pytest (>=3.0.3)", "pytest-cov (>=2.2.1)", "pytest-faulthandler (>=1.3.0,<2)", "pytest-timeout (>=1.0.0,<2)", "wheel (>=0.29)"]
-[[package]]
-name = "pydot"
-version = "2.0.0"
-description = "Python interface to Graphviz's Dot"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "pydot-2.0.0-py3-none-any.whl", hash = "sha256:408a47913ea7bd5d2d34b274144880c1310c4aee901f353cf21fe2e526a4ea28"},
- {file = "pydot-2.0.0.tar.gz", hash = "sha256:60246af215123fa062f21cd791be67dda23a6f280df09f68919e637a1e4f3235"},
-]
-
-[package.dependencies]
-pyparsing = ">=3"
-
-[package.extras]
-dev = ["black", "chardet"]
-release = ["zest.releaser[recommended]"]
-tests = ["black", "chardet", "tox"]
-
-[[package]]
-name = "pygments"
-version = "2.17.2"
-description = "Pygments is a syntax highlighting package written in Python."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"},
- {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"},
-]
-
-[package.extras]
-plugins = ["importlib-metadata"]
-windows-terminal = ["colorama (>=0.4.6)"]
-
[[package]]
name = "pylsqpack"
version = "0.3.18"
@@ -2516,68 +1610,6 @@ files = [
{file = "pyperclip-1.8.2.tar.gz", hash = "sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57"},
]
-[[package]]
-name = "pyqt5"
-version = "5.15.10"
-description = "Python bindings for the Qt cross platform application toolkit"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "PyQt5-5.15.10-cp37-abi3-macosx_10_13_x86_64.whl", hash = "sha256:93288d62ebd47b1933d80c27f5d43c7c435307b84d480af689cef2474e87e4c8"},
- {file = "PyQt5-5.15.10-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:862cea3be95b4b0a2b9678003b3a18edf7bd5eafd673860f58820f246d4bf616"},
- {file = "PyQt5-5.15.10-cp37-abi3-manylinux_2_17_x86_64.whl", hash = "sha256:b89478d16d4118664ff58ed609e0a804d002703c9420118de7e4e70fa1cb5486"},
- {file = "PyQt5-5.15.10-cp37-abi3-win32.whl", hash = "sha256:ff99b4f91aa8eb60510d5889faad07116d3340041916e46c07d519f7cad344e1"},
- {file = "PyQt5-5.15.10-cp37-abi3-win_amd64.whl", hash = "sha256:501355f327e9a2c38db0428e1a236d25ebcb99304cd6e668c05d1188d514adec"},
- {file = "PyQt5-5.15.10.tar.gz", hash = "sha256:d46b7804b1b10a4ff91753f8113e5b5580d2b4462f3226288e2d84497334898a"},
-]
-
-[package.dependencies]
-PyQt5-Qt5 = ">=5.15.2"
-PyQt5-sip = ">=12.13,<13"
-
-[[package]]
-name = "pyqt5-qt5"
-version = "5.15.2"
-description = "The subset of a Qt installation needed by PyQt5."
-optional = false
-python-versions = "*"
-files = [
- {file = "PyQt5_Qt5-5.15.2-py3-none-macosx_10_13_intel.whl", hash = "sha256:76980cd3d7ae87e3c7a33bfebfaee84448fd650bad6840471d6cae199b56e154"},
- {file = "PyQt5_Qt5-5.15.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:1988f364ec8caf87a6ee5d5a3a5210d57539988bf8e84714c7d60972692e2f4a"},
- {file = "PyQt5_Qt5-5.15.2-py3-none-win32.whl", hash = "sha256:9cc7a768b1921f4b982ebc00a318ccb38578e44e45316c7a4a850e953e1dd327"},
- {file = "PyQt5_Qt5-5.15.2-py3-none-win_amd64.whl", hash = "sha256:750b78e4dba6bdf1607febedc08738e318ea09e9b10aea9ff0d73073f11f6962"},
-]
-
-[[package]]
-name = "pyqt5-sip"
-version = "12.13.0"
-description = "The sip module support for PyQt5"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "PyQt5_sip-12.13.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a7e3623b2c743753625c4650ec7696362a37fb36433b61824cf257f6d3d43cca"},
- {file = "PyQt5_sip-12.13.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6e4ac714252370ca037c7d609da92388057165edd4f94e63354f6d65c3ed9d53"},
- {file = "PyQt5_sip-12.13.0-cp310-cp310-win32.whl", hash = "sha256:d5032da3fff62da055104926ffe76fd6044c1221f8ad35bb60804bcb422fe866"},
- {file = "PyQt5_sip-12.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:9a8cdd6cb66adcbe5c941723ed1544eba05cf19b6c961851b58ccdae1c894afb"},
- {file = "PyQt5_sip-12.13.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0f85fb633a522f04e48008de49dce1ff1d947011b48885b8428838973fbca412"},
- {file = "PyQt5_sip-12.13.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ec60162e034c42fb99859206d62b83b74f987d58937b3a82bdc07b5c3d190dec"},
- {file = "PyQt5_sip-12.13.0-cp311-cp311-win32.whl", hash = "sha256:205cd449d08a2b024a468fb6100cd7ed03e946b4f49706f508944006f955ae1a"},
- {file = "PyQt5_sip-12.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:1c8371682f77852256f1f2d38c41e2e684029f43330f0635870895ab01c02f6c"},
- {file = "PyQt5_sip-12.13.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7fe3375b508c5bc657d73b9896bba8a768791f1f426c68053311b046bcebdddf"},
- {file = "PyQt5_sip-12.13.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:773731b1b5ab1a7cf5621249f2379c95e3d2905e9bd96ff3611b119586daa876"},
- {file = "PyQt5_sip-12.13.0-cp312-cp312-win32.whl", hash = "sha256:fb4a5271fa3f6bc2feb303269a837a95a6d8dd16be553aa40e530de7fb81bfdf"},
- {file = "PyQt5_sip-12.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:3a4498f3b1b15f43f5d12963accdce0fd652b0bcaae6baf8008663365827444c"},
- {file = "PyQt5_sip-12.13.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9b984c2620a7a7eaf049221b09ae50a345317add2624c706c7d2e9e6632a9587"},
- {file = "PyQt5_sip-12.13.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3188a06956aef86f604fb0d14421a110fad70d2a9e943dbacbfc3303f651dade"},
- {file = "PyQt5_sip-12.13.0-cp38-cp38-win32.whl", hash = "sha256:108a15f603e1886988c4b0d9d41cb74c9f9815bf05cefc843d559e8c298a10ce"},
- {file = "PyQt5_sip-12.13.0-cp38-cp38-win_amd64.whl", hash = "sha256:db228cd737f5cbfc66a3c3e50042140cb80b30b52edc5756dbbaa2346ec73137"},
- {file = "PyQt5_sip-12.13.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5338773bbaedaa4f16a73c142fb23cc18c327be6c338813af70260b756c7bc92"},
- {file = "PyQt5_sip-12.13.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:29fa9cc964517c9fc3f94f072b9a2aeef4e7a2eda1879cb835d9e06971161cdf"},
- {file = "PyQt5_sip-12.13.0-cp39-cp39-win32.whl", hash = "sha256:96414c93f3d33963887cf562d50d88b955121fbfd73f937c8eca46643e77bf61"},
- {file = "PyQt5_sip-12.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:bbc7cd498bf19e0862097be1ad2243e824dea56726f00c11cff1b547c2d31d01"},
- {file = "PyQt5_sip-12.13.0.tar.gz", hash = "sha256:7f321daf84b9c9dbca61b80e1ef37bdaffc0e93312edae2cd7da25b953971d91"},
-]
-
[[package]]
name = "pyrsistent"
version = "0.20.0"
@@ -2619,20 +1651,6 @@ files = [
{file = "pyrsistent-0.20.0.tar.gz", hash = "sha256:4c48f78f62ab596c679086084d0dd13254ae4f3d6c72a83ffdf5ebdef8f265a4"},
]
-[[package]]
-name = "python-dateutil"
-version = "2.9.0.post0"
-description = "Extensions to the standard Python datetime module"
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
-files = [
- {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
- {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
-]
-
-[package.dependencies]
-six = ">=1.5"
-
[[package]]
name = "python-lsp-jsonrpc"
version = "1.0.0"
@@ -3045,85 +2063,6 @@ files = [
{file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"},
]
-[[package]]
-name = "sqlalchemy"
-version = "1.4.52"
-description = "Database Abstraction Library"
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
-files = [
- {file = "SQLAlchemy-1.4.52-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:f68016f9a5713684c1507cc37133c28035f29925c75c0df2f9d0f7571e23720a"},
- {file = "SQLAlchemy-1.4.52-cp310-cp310-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24bb0f81fbbb13d737b7f76d1821ec0b117ce8cbb8ee5e8641ad2de41aa916d3"},
- {file = "SQLAlchemy-1.4.52-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e93983cc0d2edae253b3f2141b0a3fb07e41c76cd79c2ad743fc27eb79c3f6db"},
- {file = "SQLAlchemy-1.4.52-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:84e10772cfc333eb08d0b7ef808cd76e4a9a30a725fb62a0495877a57ee41d81"},
- {file = "SQLAlchemy-1.4.52-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:427988398d2902de042093d17f2b9619a5ebc605bf6372f7d70e29bde6736842"},
- {file = "SQLAlchemy-1.4.52-cp310-cp310-win32.whl", hash = "sha256:1296f2cdd6db09b98ceb3c93025f0da4835303b8ac46c15c2136e27ee4d18d94"},
- {file = "SQLAlchemy-1.4.52-cp310-cp310-win_amd64.whl", hash = "sha256:80e7f697bccc56ac6eac9e2df5c98b47de57e7006d2e46e1a3c17c546254f6ef"},
- {file = "SQLAlchemy-1.4.52-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2f251af4c75a675ea42766880ff430ac33291c8d0057acca79710f9e5a77383d"},
- {file = "SQLAlchemy-1.4.52-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb8f9e4c4718f111d7b530c4e6fb4d28f9f110eb82e7961412955b3875b66de0"},
- {file = "SQLAlchemy-1.4.52-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afb1672b57f58c0318ad2cff80b384e816735ffc7e848d8aa51e0b0fc2f4b7bb"},
- {file = "SQLAlchemy-1.4.52-cp311-cp311-win32.whl", hash = "sha256:6e41cb5cda641f3754568d2ed8962f772a7f2b59403b95c60c89f3e0bd25f15e"},
- {file = "SQLAlchemy-1.4.52-cp311-cp311-win_amd64.whl", hash = "sha256:5bed4f8c3b69779de9d99eb03fd9ab67a850d74ab0243d1be9d4080e77b6af12"},
- {file = "SQLAlchemy-1.4.52-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:49e3772eb3380ac88d35495843daf3c03f094b713e66c7d017e322144a5c6b7c"},
- {file = "SQLAlchemy-1.4.52-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:618827c1a1c243d2540314c6e100aee7af09a709bd005bae971686fab6723554"},
- {file = "SQLAlchemy-1.4.52-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de9acf369aaadb71a725b7e83a5ef40ca3de1cf4cdc93fa847df6b12d3cd924b"},
- {file = "SQLAlchemy-1.4.52-cp312-cp312-win32.whl", hash = "sha256:763bd97c4ebc74136ecf3526b34808c58945023a59927b416acebcd68d1fc126"},
- {file = "SQLAlchemy-1.4.52-cp312-cp312-win_amd64.whl", hash = "sha256:f12aaf94f4d9679ca475975578739e12cc5b461172e04d66f7a3c39dd14ffc64"},
- {file = "SQLAlchemy-1.4.52-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:853fcfd1f54224ea7aabcf34b227d2b64a08cbac116ecf376907968b29b8e763"},
- {file = "SQLAlchemy-1.4.52-cp36-cp36m-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f98dbb8fcc6d1c03ae8ec735d3c62110949a3b8bc6e215053aa27096857afb45"},
- {file = "SQLAlchemy-1.4.52-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e135fff2e84103bc15c07edd8569612ce317d64bdb391f49ce57124a73f45c5"},
- {file = "SQLAlchemy-1.4.52-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5b5de6af8852500d01398f5047d62ca3431d1e29a331d0b56c3e14cb03f8094c"},
- {file = "SQLAlchemy-1.4.52-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3491c85df263a5c2157c594f54a1a9c72265b75d3777e61ee13c556d9e43ffc9"},
- {file = "SQLAlchemy-1.4.52-cp36-cp36m-win32.whl", hash = "sha256:427c282dd0deba1f07bcbf499cbcc9fe9a626743f5d4989bfdfd3ed3513003dd"},
- {file = "SQLAlchemy-1.4.52-cp36-cp36m-win_amd64.whl", hash = "sha256:ca5ce82b11731492204cff8845c5e8ca1a4bd1ade85e3b8fcf86e7601bfc6a39"},
- {file = "SQLAlchemy-1.4.52-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:29d4247313abb2015f8979137fe65f4eaceead5247d39603cc4b4a610936cd2b"},
- {file = "SQLAlchemy-1.4.52-cp37-cp37m-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a752bff4796bf22803d052d4841ebc3c55c26fb65551f2c96e90ac7c62be763a"},
- {file = "SQLAlchemy-1.4.52-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7ea11727feb2861deaa293c7971a4df57ef1c90e42cb53f0da40c3468388000"},
- {file = "SQLAlchemy-1.4.52-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d913f8953e098ca931ad7f58797f91deed26b435ec3756478b75c608aa80d139"},
- {file = "SQLAlchemy-1.4.52-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a251146b921725547ea1735b060a11e1be705017b568c9f8067ca61e6ef85f20"},
- {file = "SQLAlchemy-1.4.52-cp37-cp37m-win32.whl", hash = "sha256:1f8e1c6a6b7f8e9407ad9afc0ea41c1f65225ce505b79bc0342159de9c890782"},
- {file = "SQLAlchemy-1.4.52-cp37-cp37m-win_amd64.whl", hash = "sha256:346ed50cb2c30f5d7a03d888e25744154ceac6f0e6e1ab3bc7b5b77138d37710"},
- {file = "SQLAlchemy-1.4.52-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:4dae6001457d4497736e3bc422165f107ecdd70b0d651fab7f731276e8b9e12d"},
- {file = "SQLAlchemy-1.4.52-cp38-cp38-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5d2e08d79f5bf250afb4a61426b41026e448da446b55e4770c2afdc1e200fce"},
- {file = "SQLAlchemy-1.4.52-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bbce5dd7c7735e01d24f5a60177f3e589078f83c8a29e124a6521b76d825b85"},
- {file = "SQLAlchemy-1.4.52-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bdb7b4d889631a3b2a81a3347c4c3f031812eb4adeaa3ee4e6b0d028ad1852b5"},
- {file = "SQLAlchemy-1.4.52-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c294ae4e6bbd060dd79e2bd5bba8b6274d08ffd65b58d106394cb6abbf35cf45"},
- {file = "SQLAlchemy-1.4.52-cp38-cp38-win32.whl", hash = "sha256:bcdfb4b47fe04967669874fb1ce782a006756fdbebe7263f6a000e1db969120e"},
- {file = "SQLAlchemy-1.4.52-cp38-cp38-win_amd64.whl", hash = "sha256:7d0dbc56cb6af5088f3658982d3d8c1d6a82691f31f7b0da682c7b98fa914e91"},
- {file = "SQLAlchemy-1.4.52-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:a551d5f3dc63f096ed41775ceec72fdf91462bb95abdc179010dc95a93957800"},
- {file = "SQLAlchemy-1.4.52-cp39-cp39-manylinux1_x86_64.manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_5_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ab773f9ad848118df7a9bbabca53e3f1002387cdbb6ee81693db808b82aaab0"},
- {file = "SQLAlchemy-1.4.52-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d2de46f5d5396d5331127cfa71f837cca945f9a2b04f7cb5a01949cf676db7d1"},
- {file = "SQLAlchemy-1.4.52-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7027be7930a90d18a386b25ee8af30514c61f3852c7268899f23fdfbd3107181"},
- {file = "SQLAlchemy-1.4.52-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99224d621affbb3c1a4f72b631f8393045f4ce647dd3262f12fe3576918f8bf3"},
- {file = "SQLAlchemy-1.4.52-cp39-cp39-win32.whl", hash = "sha256:c124912fd4e1bb9d1e7dc193ed482a9f812769cb1e69363ab68e01801e859821"},
- {file = "SQLAlchemy-1.4.52-cp39-cp39-win_amd64.whl", hash = "sha256:2c286fab42e49db23c46ab02479f328b8bdb837d3e281cae546cc4085c83b680"},
- {file = "SQLAlchemy-1.4.52.tar.gz", hash = "sha256:80e63bbdc5217dad3485059bdf6f65a7d43f33c8bde619df5c220edf03d87296"},
-]
-
-[package.dependencies]
-greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"}
-
-[package.extras]
-aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"]
-aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"]
-asyncio = ["greenlet (!=0.4.17)"]
-asyncmy = ["asyncmy (>=0.2.3,!=0.2.4)", "greenlet (!=0.4.17)"]
-mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2)"]
-mssql = ["pyodbc"]
-mssql-pymssql = ["pymssql"]
-mssql-pyodbc = ["pyodbc"]
-mypy = ["mypy (>=0.910)", "sqlalchemy2-stubs"]
-mysql = ["mysqlclient (>=1.4.0)", "mysqlclient (>=1.4.0,<2)"]
-mysql-connector = ["mysql-connector-python"]
-oracle = ["cx_oracle (>=7)", "cx_oracle (>=7,<8)"]
-postgresql = ["psycopg2 (>=2.7)"]
-postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"]
-postgresql-pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"]
-postgresql-psycopg2binary = ["psycopg2-binary"]
-postgresql-psycopg2cffi = ["psycopg2cffi"]
-pymysql = ["pymysql", "pymysql (<1)"]
-sqlcipher = ["sqlcipher3_binary"]
-
[[package]]
name = "sqlparse"
version = "0.4.4"
@@ -3140,25 +2079,6 @@ dev = ["build", "flake8"]
doc = ["sphinx"]
test = ["pytest", "pytest-cov"]
-[[package]]
-name = "stack-data"
-version = "0.6.3"
-description = "Extract data from python stack frames and tracebacks for informative displays"
-optional = false
-python-versions = "*"
-files = [
- {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"},
- {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"},
-]
-
-[package.dependencies]
-asttokens = ">=2.1.0"
-executing = ">=1.2.0"
-pure-eval = "*"
-
-[package.extras]
-tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"]
-
[[package]]
name = "svgutils"
version = "0.3.4"
@@ -3241,21 +2161,6 @@ notebook = ["ipywidgets (>=6)"]
slack = ["slack-sdk"]
telegram = ["requests"]
-[[package]]
-name = "traitlets"
-version = "5.14.2"
-description = "Traitlets Python configuration system"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "traitlets-5.14.2-py3-none-any.whl", hash = "sha256:fcdf85684a772ddeba87db2f398ce00b40ff550d1528c03c14dbf6a02003cd80"},
- {file = "traitlets-5.14.2.tar.gz", hash = "sha256:8cdd83c040dab7d1dee822678e5f5d100b514f7b72b01615b26fc5718916fdf9"},
-]
-
-[package.extras]
-docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
-test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.1)", "pytest-mock", "pytest-mypy-testing"]
-
[[package]]
name = "typing-extensions"
version = "4.10.0"
@@ -3413,17 +2318,6 @@ files = [
[package.dependencies]
bracex = ">=2.1.1"
-[[package]]
-name = "wcwidth"
-version = "0.2.13"
-description = "Measures the displayed width of unicode strings in a terminal"
-optional = false
-python-versions = "*"
-files = [
- {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"},
- {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"},
-]
-
[[package]]
name = "werkzeug"
version = "3.0.1"
@@ -3455,20 +2349,6 @@ files = [
[package.extras]
brotli = ["Brotli"]
-[[package]]
-name = "win32-setctime"
-version = "1.1.0"
-description = "A small Python utility to set file creation time on Windows"
-optional = false
-python-versions = ">=3.5"
-files = [
- {file = "win32_setctime-1.1.0-py3-none-any.whl", hash = "sha256:231db239e959c2fe7eb1d7dc129f11172354f98361c4fa2d6d2d7e278baa8aad"},
- {file = "win32_setctime-1.1.0.tar.gz", hash = "sha256:15cf5750465118d6929ae4de4eb46e8edae9a5634350c01ba582df868e932cb2"},
-]
-
-[package.extras]
-dev = ["black (>=19.3b0)", "pytest (>=4.6.2)"]
-
[[package]]
name = "wsproto"
version = "1.2.0"
@@ -3631,4 +2511,5 @@ cffi = ["cffi (>=1.11)"]
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
-content-hash = "e077f3a528867790025d5079bc7af606f8a2d71b365a488ba4159ad31bae20dc"
+content-hash = "8a363c742e66046c0dceaf5edfc74c6ad876c00e2f3447f8b466fb6d5058444b"
+
diff --git a/pyproject.toml b/pyproject.toml
index 740cf37f..53b35faa 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "mobsf"
-version = "3.9.6"
+version = "3.9.7"
description = "Mobile Security Framework (MobSF) is an automated, all-in-one mobile application (Android/iOS/Windows) pen-testing, malware analysis and security assessment framework capable of performing static and dynamic analysis."
keywords = ["mobsf", "mobile security framework", "mobile security", "security tool", "static analysis", "dynamic analysis", "malware analysis"]
authors = ["Ajin Abraham "]
@@ -44,7 +44,6 @@ lief = ">=0.12.3"
http-tools = ">=4.0.0"
pdfkit = ">=0.6.1"
google-play-scraper = ">=0.1.2"
-androguard = ">=4.1.0"
apkid = "2.1.5"
frida = ">=16.1.4"
tldextract = "3.4.4"
@@ -55,8 +54,7 @@ apksigtool = "0.1.0"
tzdata = "^2023.3"
libsast = "^2.0.0"
paramiko = "^3.3.1"
-# For androguard/apkinspector
-pyqt5-qt5 = {version = "=5.15.2"}
+six = ">=1.16.0"
[build-system]
requires = ["poetry-core"]
From 7645cee99e29af0dcedddcfba73b8d2c048a63a0 Mon Sep 17 00:00:00 2001
From: JPSxzy8 <147696419+JPSxzy8@users.noreply.github.com>
Date: Sat, 23 Mar 2024 00:37:55 +0100
Subject: [PATCH 12/19] Optimize rendering of big lists (#2351)
* Optimize rendering of big lists
* Dynamic rendering in browser to improve ux
Co-authored-by: Ajin Abraham
---
mobsf/templates/base/list.html | 9 +++
.../android_binary_analysis.html | 81 +++++--------------
.../android_source_analysis.html | 52 ++----------
.../static_analysis/ios_binary_analysis.html | 55 +++++++------
.../static_analysis/ios_source_analysis.html | 12 +--
5 files changed, 68 insertions(+), 141 deletions(-)
create mode 100644 mobsf/templates/base/list.html
diff --git a/mobsf/templates/base/list.html b/mobsf/templates/base/list.html
new file mode 100644
index 00000000..c1575cf0
--- /dev/null
+++ b/mobsf/templates/base/list.html
@@ -0,0 +1,9 @@
+
+{% if list|length != 0 %}
+
+ {% if list|length < limit %}Showing{% else %}Show{% endif %} all {{ list | length }} {{ type }}
+ {% for val in list %}
+ {{ val }}
+ {% endfor %}
+
+{% endif %}
\ No newline at end of file
diff --git a/mobsf/templates/static_analysis/android_binary_analysis.html b/mobsf/templates/static_analysis/android_binary_analysis.html
index 7266ed5e..3c954523 100755
--- a/mobsf/templates/static_analysis/android_binary_analysis.html
+++ b/mobsf/templates/static_analysis/android_binary_analysis.html
@@ -2118,11 +2118,7 @@
POSSIBLE HARDCODED SECRETS
-
- {% for val in secrets %}
- {{ val }}
- {% endfor %}
-
+ {% include 'base/list.html' with list=secrets type="secrets" limit=100 %}
@@ -2145,41 +2141,24 @@
{% if app_type not in 'so' %}
From APK Resource
-
- {% for key, val in strings.items %}
- {% if key == 'strings_apk_res' %}
- {% for v in val %}
- {{ v }}
- {% endfor %}
- {% endif %}
- {% endfor %}
-
-
From Code
-
- {% for key, val in strings.items %}
- {% if key == 'strings_code' %}
- {% for v in val %}
- {{ v }}
- {% endfor %}
- {% endif %}
- {% endfor %}
-
+ {% include 'base/list.html' with list=strings.strings_apk_res type="strings" limit=100 %}
+
From Code
+ {% include 'base/list.html' with list=strings.strings_code type="strings" limit=100 %}
{% endif %}
+
From Shared Objects
-
{% for key, val in strings.items %}
{% if key == 'strings_so' %}
{% for ls in val %}
{% for k,v in ls.items %}
-
{{ k }}
- {% for i in v %}
- {{ i }}
- {% endfor %}
+
{{ k }}
+
+ {% include 'base/list.html' with list=v type="strings" limit=5 %}
+
{% endfor %}
{% endfor %}
{% endif %}
{% endfor %}
-
@@ -2201,11 +2180,7 @@
SYMBOLS
-
- {% for val in file_analysis %}
- {{ val }}
- {% endfor %}
-
+ {% include 'base/list.html' with list=file_analysis type="symbols" limit=50 %}
@@ -2228,10 +2203,8 @@
ACTIVITIES
-
- {% for act in activities %}
- {{ act}}
- {% endfor %}
+
+ {% include 'base/list.html' with list=activities type="activities" limit=50 %}
@@ -2253,10 +2226,8 @@
SERVICES
-
- {% for srv in services %}
- {{ srv}}
- {% endfor %}
+
+ {% include 'base/list.html' with list=services type="services" limit=50 %}
@@ -2278,10 +2249,8 @@
RECEIVERS
-
- {% for rcv in receivers %}
- {{ rcv}}
- {% endfor %}
+
+ {% include 'base/list.html' with list=receivers type="receivers" limit=50 %}
@@ -2304,10 +2273,8 @@
PROVIDERS
-
- {% for prv in providers %}
- {{ prv }}
- {% endfor %}
+
+ {% include 'base/list.html' with list=providers type="providers" limit=50 %}
@@ -2329,11 +2296,7 @@
LIBRARIES
-
- {% for lib in libraries %}
- {{ lib }}
- {% endfor %}
-
+ {% include 'base/list.html' with list=libraries type="libraries" limit=100 %}
@@ -2354,11 +2317,7 @@
FILES
-
- {% for file in files %}
- {{ file}}
- {% endfor %}
-
+ {% include 'base/list.html' with list=files type="files" limit=200 %}
diff --git a/mobsf/templates/static_analysis/android_source_analysis.html b/mobsf/templates/static_analysis/android_source_analysis.html
index e13138e8..ec093128 100755
--- a/mobsf/templates/static_analysis/android_source_analysis.html
+++ b/mobsf/templates/static_analysis/android_source_analysis.html
@@ -1587,11 +1587,7 @@
POSSIBLE HARDCODED SECRETS
-
- {% for val in secrets %}
- {{ val }}
- {% endfor %}
-
+ {% include 'base/list.html' with list=secrets type="secrets" limit=100 %}
@@ -1613,15 +1609,7 @@
From Code
-
- {% for key, val in strings.items %}
- {% if key == 'strings_code' %}
- {% for v in val %}
- {{ v }}
- {% endfor %}
- {% endif %}
- {% endfor %}
-
+ {% include 'base/list.html' with list=strings.strings_code type="strings" limit=100 %}
@@ -1642,11 +1630,7 @@
ACTIVITIES
-
- {% for act in activities %}
- {{ act}}
- {% endfor %}
-
+ {% include 'base/list.html' with list=activities type="activities" limit=50 %}
@@ -1667,11 +1651,7 @@
SERVICES
-
- {% for srv in services %}
- {{ srv}}
- {% endfor %}
-
+ {% include 'base/list.html' with list=services type="services" limit=50 %}
@@ -1692,11 +1672,7 @@
RECEIVERS
-
- {% for rcv in receivers %}
- {{ rcv}}
- {% endfor %}
-
+ {% include 'base/list.html' with list=receivers type="receivers" limit=50%}
@@ -1718,11 +1694,7 @@
PROVIDERS
-
- {% for prv in providers %}
- {{ prv }}
- {% endfor %}
-
+ {% include 'base/list.html' with list=providers type="providers" limit=50 %}
@@ -1743,11 +1715,7 @@
LIBRARIES
-
- {% for lib in libraries %}
- {{ lib }}
- {% endfor %}
-
+ {% include 'base/list.html' with list=libraries type="libraries" limit=50 %}
@@ -1768,11 +1736,7 @@
FILES
-
- {% for file in files %}
- {{ file}}
- {% endfor %}
-
+ {% include 'base/list.html' with list=files type="files" limit=200 %}
diff --git a/mobsf/templates/static_analysis/ios_binary_analysis.html b/mobsf/templates/static_analysis/ios_binary_analysis.html
index 1ef5c4e3..73d3f7d2 100755
--- a/mobsf/templates/static_analysis/ios_binary_analysis.html
+++ b/mobsf/templates/static_analysis/ios_binary_analysis.html
@@ -1185,14 +1185,36 @@
{{ item.issue }}
- {% for file in item.files %}
+ {% if item.files|length < 4 %}
+ {% for file in item.files %}
+
{% if file.type %}
{{ file.file_path }}
{% else %}
{{ file.file_path }}
{% endif %}
-
+
+
+ {% endfor %}
+ {% else %}
+
+ Show Files
+
+
+ {% for file in item.files %}
+
+ {% if file.type %}
+ {{ file.file_path }}
+ {% else %}
+ {{ file.file_path }}
+ {% endif %}
+
+
{% endfor %}
+
+ {% endif %}
+
+
|
@@ -1572,11 +1594,7 @@
POSSIBLE HARDCODED SECRETS
-
- {% for val in secrets %}
- {{ val }}
- {% endfor %}
-
+ {% include 'base/list.html' with list=secrets type="secrets" limit=100 %}
@@ -1597,9 +1615,7 @@
STRINGS
- {% for string in strings %}
- {{string}}
- {% endfor %}
+ {% include 'base/list.html' with list=strings type="strings" limit=100 %}
@@ -1621,11 +1637,7 @@
SYMBOLS
-
- {% for val in file_analysis %}
- {{ val }}
- {% endfor %}
-
+ {% include 'base/list.html' with list=file_analysis type="symbols" limit=100 %}
@@ -1648,12 +1660,7 @@
LIBRARIES
-
- {% for lib in libraries %}
- {{ lib }}
-
- {% endfor %}
-
+ {% include 'base/list.html' with list=libraries type="libraries" limit=100 %}
@@ -1674,11 +1681,7 @@
FILES
-
- {% for file in files %}
- {{ file}}
- {% endfor %}
-
+ {% include 'base/list.html' with list=files type="files" limit=200 %}
diff --git a/mobsf/templates/static_analysis/ios_source_analysis.html b/mobsf/templates/static_analysis/ios_source_analysis.html
index 72174542..c831ef83 100755
--- a/mobsf/templates/static_analysis/ios_source_analysis.html
+++ b/mobsf/templates/static_analysis/ios_source_analysis.html
@@ -1190,11 +1190,7 @@
STRINGS
-
- {% for val in strings %}
- {{ val }}
- {% endfor %}
-
+ {% include 'base/list.html' with list=strings type="strings" limit=100 %}
@@ -1215,11 +1211,7 @@
FILES
-
- {% for file in files %}
- {{ file}}
- {% endfor %}
-
+ {% include 'base/list.html' with list=files type="files" limit=200 %}
From f22c584aa7d43527970c9da61eb678953cfc0a8e Mon Sep 17 00:00:00 2001
From: "Ajin.Abraham"
Date: Fri, 22 Mar 2024 16:43:43 -0700
Subject: [PATCH 13/19] Fixes GHSA-m435-9v6r-v5f6
---
mobsf/StaticAnalyzer/views/android/manifest_analysis.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/mobsf/StaticAnalyzer/views/android/manifest_analysis.py b/mobsf/StaticAnalyzer/views/android/manifest_analysis.py
index fd347c21..e8868ca0 100755
--- a/mobsf/StaticAnalyzer/views/android/manifest_analysis.py
+++ b/mobsf/StaticAnalyzer/views/android/manifest_analysis.py
@@ -84,12 +84,15 @@ def _check_url(host, w_url):
status_code = 0
r = requests.get(w_url,
- allow_redirects=True,
+ allow_redirects=False,
proxies=proxies,
verify=verify,
timeout=5)
status_code = r.status_code
+ if status_code == 302:
+ logger.warning('302 Redirect detected, skipping check')
+ status = False
if (str(status_code).startswith('2') and iden in str(r.json())):
status = True
From 3a7d12488cdb8e0f687c86e7bd3fec6d63446b6c Mon Sep 17 00:00:00 2001
From: Ajin Abraham
Date: Fri, 22 Mar 2024 17:03:04 -0700
Subject: [PATCH 14/19] Update SECURITY.md (#2364)
---
.github/SECURITY.md | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.github/SECURITY.md b/.github/SECURITY.md
index 952a7a16..d5e712d4 100644
--- a/.github/SECURITY.md
+++ b/.github/SECURITY.md
@@ -23,3 +23,6 @@ Please report all security issues [here](https://github.com/MobSF/Mobile-Securit
| [Arbitrary Local file read regression](https://github.com/MobSF/Mobile-Security-Framework-MobSF/issues/1197) | `<3.0.0` |
| [Upload a malicious zip file can overwrite arbitary files](https://github.com/MobSF/Mobile-Security-Framework-MobSF/issues/358) | `>=0.9.3.2, <=0.9.4.1` |
| [Arbitrary Local file read](https://github.com/MobSF/Mobile-Security-Framework-MobSF/pull/166) | `<=0.9.2` |
+| [SSRF in AppLink check via crafted android:host](https://github.com/MobSF/Mobile-Security-Framework-MobSF/security/advisories/GHSA-wfgj-wrgh-h3r3) | `<=3.9.5`|
+| [SSRF in AppLink check via abusing url redirect](https://github.com/MobSF/Mobile-Security-Framework-MobSF/security/advisories/GHSA-m435-9v6r-v5f6) | `<=3.9.6` |
+
From 356895c6119c7ceb3697472530aa2d3680225c95 Mon Sep 17 00:00:00 2001
From: Ajin Abraham
Date: Fri, 22 Mar 2024 17:10:34 -0700
Subject: [PATCH 15/19] Update SECURITY.md (#2365)
---
.github/SECURITY.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/SECURITY.md b/.github/SECURITY.md
index d5e712d4..d602e599 100644
--- a/.github/SECURITY.md
+++ b/.github/SECURITY.md
@@ -18,11 +18,11 @@ Please report all security issues [here](https://github.com/MobSF/Mobile-Securit
| Vulnerability | Affected Versions |
| ------- | ------------------ |
+| [SSRF in AppLink check via crafted android:host](https://github.com/MobSF/Mobile-Security-Framework-MobSF/security/advisories/GHSA-wfgj-wrgh-h3r3) | `<=3.9.5`|
+| [SSRF in AppLink check via abusing url redirect](https://github.com/MobSF/Mobile-Security-Framework-MobSF/security/advisories/GHSA-m435-9v6r-v5f6) | `<=3.9.6` |
| [Arbitrary Local file read in APK icon resource](https://github.com/MobSF/Mobile-Security-Framework-MobSF/commit/a58f8a8c0aa49e1581d97e19e8e2255ca96cd838) | `>=1.0.4, <=3.9.2` |
| [Remote Code Execution via arbitrary file overwrite vulnerability in apktool <2.9.2](https://github.com/MobSF/Mobile-Security-Framework-MobSF/commit/19c1b55c2c59596f2d43439926c9dc976cbeaec4), [[CVE-2024-21633]](https://github.com/0x33c0unt/CVE-2024-21633) | `<=3.9.1` |
| [Arbitrary Local file read regression](https://github.com/MobSF/Mobile-Security-Framework-MobSF/issues/1197) | `<3.0.0` |
| [Upload a malicious zip file can overwrite arbitary files](https://github.com/MobSF/Mobile-Security-Framework-MobSF/issues/358) | `>=0.9.3.2, <=0.9.4.1` |
| [Arbitrary Local file read](https://github.com/MobSF/Mobile-Security-Framework-MobSF/pull/166) | `<=0.9.2` |
-| [SSRF in AppLink check via crafted android:host](https://github.com/MobSF/Mobile-Security-Framework-MobSF/security/advisories/GHSA-wfgj-wrgh-h3r3) | `<=3.9.5`|
-| [SSRF in AppLink check via abusing url redirect](https://github.com/MobSF/Mobile-Security-Framework-MobSF/security/advisories/GHSA-m435-9v6r-v5f6) | `<=3.9.6` |
From 482bda913f347fae8f6bd1c4e4d8c31925ef9ada Mon Sep 17 00:00:00 2001
From: Ajin Abraham
Date: Fri, 22 Mar 2024 17:11:10 -0700
Subject: [PATCH 16/19] Update SECURITY.md
---
.github/SECURITY.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/SECURITY.md b/.github/SECURITY.md
index d602e599..d0d0869e 100644
--- a/.github/SECURITY.md
+++ b/.github/SECURITY.md
@@ -18,8 +18,8 @@ Please report all security issues [here](https://github.com/MobSF/Mobile-Securit
| Vulnerability | Affected Versions |
| ------- | ------------------ |
-| [SSRF in AppLink check via crafted android:host](https://github.com/MobSF/Mobile-Security-Framework-MobSF/security/advisories/GHSA-wfgj-wrgh-h3r3) | `<=3.9.5`|
| [SSRF in AppLink check via abusing url redirect](https://github.com/MobSF/Mobile-Security-Framework-MobSF/security/advisories/GHSA-m435-9v6r-v5f6) | `<=3.9.6` |
+| [SSRF in AppLink check via crafted android:host](https://github.com/MobSF/Mobile-Security-Framework-MobSF/security/advisories/GHSA-wfgj-wrgh-h3r3) | `<=3.9.5`|
| [Arbitrary Local file read in APK icon resource](https://github.com/MobSF/Mobile-Security-Framework-MobSF/commit/a58f8a8c0aa49e1581d97e19e8e2255ca96cd838) | `>=1.0.4, <=3.9.2` |
| [Remote Code Execution via arbitrary file overwrite vulnerability in apktool <2.9.2](https://github.com/MobSF/Mobile-Security-Framework-MobSF/commit/19c1b55c2c59596f2d43439926c9dc976cbeaec4), [[CVE-2024-21633]](https://github.com/0x33c0unt/CVE-2024-21633) | `<=3.9.1` |
| [Arbitrary Local file read regression](https://github.com/MobSF/Mobile-Security-Framework-MobSF/issues/1197) | `<3.0.0` |
From 8a59d6833df2aac7a1ac768a5032c73a09b65b09 Mon Sep 17 00:00:00 2001
From: "Ajin.Abraham"
Date: Fri, 22 Mar 2024 18:19:17 -0700
Subject: [PATCH 17/19] HOTFIX: Build and push docker arm64 and amd64 together
---
.github/workflows/docker-latest.yml | 15 +++------------
.github/workflows/docker-release.yml | 15 +++------------
.github/workflows/docker-test.yml | 4 ++--
3 files changed, 8 insertions(+), 26 deletions(-)
diff --git a/.github/workflows/docker-latest.yml b/.github/workflows/docker-latest.yml
index 930b1f5e..6a4de322 100644
--- a/.github/workflows/docker-latest.yml
+++ b/.github/workflows/docker-latest.yml
@@ -25,20 +25,11 @@ jobs:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
-
- name: Build and push AMD64 image
- id: docker_build_amd
+ name: Build and push AMD64, ARM64 images
+ id: docker_build
uses: docker/build-push-action@v5
with:
push: true
context: .
- platforms: linux/amd64
- tags: opensecurity/mobile-security-framework-mobsf:latest
- -
- name: Build and push ARM64 image
- id: docker_build_arm
- uses: docker/build-push-action@v5
- with:
- push: true
- context: .
- platforms: linux/arm64
+ platforms: linux/amd64,linux/arm64
tags: opensecurity/mobile-security-framework-mobsf:latest
diff --git a/.github/workflows/docker-release.yml b/.github/workflows/docker-release.yml
index a6de4b50..7e68bfd9 100644
--- a/.github/workflows/docker-release.yml
+++ b/.github/workflows/docker-release.yml
@@ -34,20 +34,11 @@ jobs:
fi
echo "tag=${TAG}" >> $GITHUB_OUTPUT
-
- name: Build and push AMD64 image release
- id: docker_build_amd
+ name: Build and push AMD64, ARM64 image release
+ id: docker_build
uses: docker/build-push-action@v5
with:
push: true
context: .
- platforms: linux/amd64
- tags: ${{ steps.release.outputs.tag }}
- -
- name: Build and push ARM64 image release
- id: docker_build_arm
- uses: docker/build-push-action@v5
- with:
- push: true
- context: .
- platforms: linux/arm64
+ platforms: linux/amd64,linux/arm64
tags: ${{ steps.release.outputs.tag }}
diff --git a/.github/workflows/docker-test.yml b/.github/workflows/docker-test.yml
index 4f3b42dd..fd63d2c0 100644
--- a/.github/workflows/docker-test.yml
+++ b/.github/workflows/docker-test.yml
@@ -18,7 +18,7 @@ jobs:
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
-
- name: Build and push AMD64 image
+ name: Build AMD64 image
id: docker_build_amd
uses: docker/build-push-action@v5
with:
@@ -27,7 +27,7 @@ jobs:
platforms: linux/amd64
tags: opensecurity/mobile-security-framework-mobsf:latest
-
- name: Build and push ARM64 image
+ name: Build ARM64 image
id: docker_build_arm
uses: docker/build-push-action@v5
with:
From ccfedc0e4534442a5da250b5ec782ab2560619a4 Mon Sep 17 00:00:00 2001
From: Ajin Abraham
Date: Sat, 23 Mar 2024 19:15:30 -0700
Subject: [PATCH 18/19] HOTFIX: Possible SSRF
---
mobsf/StaticAnalyzer/views/common/shared_func.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/mobsf/StaticAnalyzer/views/common/shared_func.py b/mobsf/StaticAnalyzer/views/common/shared_func.py
index fba6c7fe..a13b025e 100755
--- a/mobsf/StaticAnalyzer/views/common/shared_func.py
+++ b/mobsf/StaticAnalyzer/views/common/shared_func.py
@@ -32,6 +32,7 @@ from mobsf.MobSF.utils import (
is_safe_path,
print_n_send_error_response,
upstream_proxy,
+ valid_host,
)
from mobsf.MobSF.views.scanning import (
add_to_recent_scan,
@@ -254,6 +255,9 @@ def get_avg_cvss(findings):
def open_firebase(url):
# Detect Open Firebase Database
try:
+ if not valid_host(url):
+ logger.warning('Invalid Firebase URL')
+ return url, False
purl = urlparse(url)
base_url = '{}://{}/.json'.format(purl.scheme, purl.netloc)
proxies, verify = upstream_proxy('https')
From 6bce5a25b43d8297f24f761abd3b383a9301dcc5 Mon Sep 17 00:00:00 2001
From: ohyeah521
Date: Mon, 25 Mar 2024 05:56:26 +0800
Subject: [PATCH 19/19] Resolve the situation where the function name is bytes
(#2367)
fix error:
if function.name.endswith('_chk'):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: endswith first arg must be bytes or a tuple of bytes, not str
Co-authored-by: Ajin Abraham
---
mobsf/StaticAnalyzer/views/common/binary/elf.py | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/mobsf/StaticAnalyzer/views/common/binary/elf.py b/mobsf/StaticAnalyzer/views/common/binary/elf.py
index 61feb947..931d83f6 100644
--- a/mobsf/StaticAnalyzer/views/common/binary/elf.py
+++ b/mobsf/StaticAnalyzer/views/common/binary/elf.py
@@ -290,7 +290,14 @@ class ELFChecksec:
def fortify(self):
fortified_funcs = []
for function in self.elf.symbols:
- if function.name.endswith('_chk'):
+ if isinstance(function.name, bytes):
+ try:
+ function_name = function.name.decode('utf-8')
+ except UnicodeDecodeError:
+ function_name = function.name.decode('utf-8', 'replace')
+ else:
+ function_name = function.name
+ if function_name.endswith('_chk'):
fortified_funcs.append(function.name)
return fortified_funcs