diff --git a/.github/workflows/liccheck.yml b/.github/workflows/liccheck.yml new file mode 100644 index 0000000..88a6be2 --- /dev/null +++ b/.github/workflows/liccheck.yml @@ -0,0 +1,22 @@ +name: "license-check" +on: + push: + branches: [ '**' ] + +jobs: + license-check: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Check out repository + uses: actions/checkout@v2 + - name: Set up Python 3.11 + uses: actions/setup-python@v2 + with: + python-version: "3.11" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt liccheck==0.9.2 + - name: Run liccheck + run: liccheck diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..1ad063b --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,22 @@ +name: "lint" +on: + push: + branches: [ '**' ] + +jobs: + lint: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Check out repository + uses: actions/checkout@v2 + - name: Set up Python 3.11 + uses: actions/setup-python@v2 + with: + python-version: "3.11" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install pre-commit==3.5.0 + - name: Run pre-commit + run: pre-commit run --all-files diff --git a/.gitignore b/.gitignore index e982c61..faf7bc8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ __pycache__ *.swp venv/ -data/plugin_fuzz_results/scanned/ \ No newline at end of file +data/plugin_fuzz_results/scanned/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..068fd2d --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,20 @@ +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: end-of-file-fixer + - id: trailing-whitespace +- repo: https://github.com/psf/black + rev: 23.7.0 + hooks: + - id: black +- repo: https://github.com/pycqa/isort + rev: 5.12.0 + hooks: + - id: isort + name: isort (python) +- repo: https://github.com/PyCQA/flake8 + rev: 6.1.0 + hooks: + - id: flake8 + args: [.] diff --git a/README.md b/README.md index 7d3d323..865f59d 100644 --- a/README.md +++ b/README.md @@ -131,10 +131,19 @@ Warning: the tests take long (more than an hour) and because they check whether would find vulnerabilities, they fail with some probability. ### Reformatting code and running linters -To reformat and check the code, use: +wpgarlic uses `pre-commit` to run linters and format the code. +`pre-commit` is executed on CI to verify that the code is formatted properly. + +To run it locally, use: + +``` +pre-commit run --all-files +``` + +To setup `pre-commit` so that it runs before each commit, use: ``` -./bin/reformat +pre-commit install ``` ## Manual testing environment diff --git a/bin/reformat b/bin/reformat deleted file mode 100755 index 14df6c9..0000000 --- a/bin/reformat +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash - -set -e - -cd "$(dirname "$0")/.." - -. ./bin/create_and_enter_venv - -git ls-tree HEAD -r --name-only | grep .py$ | while read file; do - echo $file - isort "$file" - black "$file" - flake8 "$file" -done diff --git a/docker-compose.manual-testing.yml b/docker-compose.manual-testing.yml index cd166e7..54a22c5 100644 --- a/docker-compose.manual-testing.yml +++ b/docker-compose.manual-testing.yml @@ -1,5 +1,5 @@ version: "3.9" - + services: wordpress2: container_name: wordpress2 diff --git a/docker-compose.yml b/docker-compose.yml index b8579f7..ecdf98f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,5 @@ version: "3.9" - + services: db1: image: mysql:5.7 @@ -35,4 +35,3 @@ networks: internal: true network2: internal: false - diff --git a/docker_image/apache_config/000-default.conf b/docker_image/apache_config/000-default.conf index ed86e69..37d80dc 100644 --- a/docker_image/apache_config/000-default.conf +++ b/docker_image/apache_config/000-default.conf @@ -5,4 +5,3 @@ ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined - diff --git a/docker_image/download_admin.sh b/docker_image/download_admin.sh index d4d2c24..3077729 100755 --- a/docker_image/download_admin.sh +++ b/docker_image/download_admin.sh @@ -17,4 +17,3 @@ timeout 240 wget \ --domains=127.0.0.1 \ --reject-regex='.*(customize.php|theme-editor.php|plugins.php|update.php|/js/|plugin-install.php|plugin-editor.php|site-health.php).*' \ http://127.0.0.1:8001/wp-admin/ - diff --git a/docker_image/fuzz/__init__.py b/docker_image/fuzz/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/docker_image/fuzz/fuzz_actions.py b/docker_image/fuzz/fuzz_actions.py index e6a9fbd..17820c2 100644 --- a/docker_image/fuzz/fuzz_actions.py +++ b/docker_image/fuzz/fuzz_actions.py @@ -46,8 +46,6 @@ prefix = "" object_name = action + (" (admin)" if become_admin else "") - command_results += fuzz_command( - f"{prefix}php /fuzzer/execute/{cmd}.php '{action}'", payload_id, object_name - ) + command_results += fuzz_command(f"{prefix}php /fuzzer/execute/{cmd}.php '{action}'", payload_id, object_name) print(json.dumps(command_results)) diff --git a/docker_image/fuzz/utils.py b/docker_image/fuzz/utils.py index 8d94d11..c031340 100644 --- a/docker_image/fuzz/utils.py +++ b/docker_image/fuzz/utils.py @@ -33,10 +33,7 @@ def fuzz_command( ) -> typing.List[typing.Dict]: command_results = [] for intercept_prob in VARIABLE_INTERCEPT_PROBABILITIES: - cmd_prefixed = ( - f"export INTERCEPT_PROB={intercept_prob}; export PAYLOAD_ID={payload_id.strip()}; " - + cmd - ) + cmd_prefixed = f"export INTERCEPT_PROB={intercept_prob}; export PAYLOAD_ID={payload_id.strip()}; " + cmd cmd_wrapped = ["bash", "-c", cmd_prefixed] try: diff --git a/docker_image/get_fuzzable_entrypoints/get_menu_actions_to_fuzz.php b/docker_image/get_fuzzable_entrypoints/get_menu_actions_to_fuzz.php index e3f150f..46722bc 100644 --- a/docker_image/get_fuzzable_entrypoints/get_menu_actions_to_fuzz.php +++ b/docker_image/get_fuzzable_entrypoints/get_menu_actions_to_fuzz.php @@ -15,4 +15,3 @@ foreach($_registered_pages as $key => $value) { echo "MENU: " . $key . "\n"; } - diff --git a/docker_image/just_visit_admin_homepage.sh b/docker_image/just_visit_admin_homepage.sh index 6b347f1..0ef7709 100755 --- a/docker_image/just_visit_admin_homepage.sh +++ b/docker_image/just_visit_admin_homepage.sh @@ -8,4 +8,3 @@ curl 'http://127.0.0.1:8001/wp-login.php' \ sed -i -e "s/^#HttpOnly_//" /tmp/cookies.jar wget --load-cookies=/tmp/cookies.jar --keep-session-cookies http://127.0.0.1:8001/wp-admin/ - diff --git a/docker_image/magic_payloads.php b/docker_image/magic_payloads.php index 1fde36f..cb2814c 100644 --- a/docker_image/magic_payloads.php +++ b/docker_image/magic_payloads.php @@ -93,7 +93,7 @@ function getAndSaveForFurtherGets($key) { 'GET', 'GET', 'GET', 'POST', 'POST', 'POST', 'PUT', 'DELETE', 'OPTIONS']; - + $method = $methods[array_rand($methods)]; $this->parameters[$key] = $method; diff --git a/docker_image/php_source_patch.patch b/docker_image/php_source_patch.patch index ee0b021..45b0b1b 100644 --- a/docker_image/php_source_patch.patch +++ b/docker_image/php_source_patch.patch @@ -4,15 +4,15 @@ index 96169d9a27..20c05f455e 100644 +++ b/Zend/zend_string.h @@ -20,6 +20,8 @@ #define ZEND_STRING_H - + #include "zend.h" +#include +#include - + BEGIN_EXTERN_C() - + @@ -311,7 +313,43 @@ static zend_always_inline zend_bool zend_string_equal_val(zend_string *s1, zend_ - + static zend_always_inline zend_bool zend_string_equal_content(zend_string *s1, zend_string *s2) { - return ZSTR_LEN(s1) == ZSTR_LEN(s2) && zend_string_equal_val(s1, s2); @@ -54,21 +54,21 @@ index 96169d9a27..20c05f455e 100644 + + return 0; } - + static zend_always_inline zend_bool zend_string_equals(zend_string *s1, zend_string *s2) diff --git a/ext/json/json.c b/ext/json/json.c index 8474642266..b7fd3a13d5 100644 --- a/ext/json/json.c +++ b/ext/json/json.c @@ -33,7 +33,7 @@ - + static PHP_MINFO_FUNCTION(json); static PHP_FUNCTION(json_encode); -static PHP_FUNCTION(json_decode); +static PHP_FUNCTION(real_json_decode); static PHP_FUNCTION(json_last_error); static PHP_FUNCTION(json_last_error_msg); - + @@ -66,7 +66,7 @@ ZEND_END_ARG_INFO() /* {{{ json_functions[] */ static const zend_function_entry json_functions[] = { @@ -79,7 +79,7 @@ index 8474642266..b7fd3a13d5 100644 PHP_FE(json_last_error_msg, arginfo_json_last_error_msg) PHP_FE_END @@ -308,7 +308,7 @@ static PHP_FUNCTION(json_encode) - + /* {{{ proto mixed json_decode(string json [, bool assoc [, int depth]]) Decodes the JSON representation into a PHP value */ -static PHP_FUNCTION(json_decode) @@ -92,7 +92,7 @@ index 0dd4045887..e3820c2ec4 100644 --- a/ext/standard/base64.c +++ b/ext/standard/base64.c @@ -943,7 +943,7 @@ PHP_FUNCTION(base64_encode) - + /* {{{ proto string base64_decode(string str[, bool strict]) Decodes string using MIME base64 algorithm */ -PHP_FUNCTION(base64_decode) @@ -107,11 +107,11 @@ index faf245c5d9..f9ba87a077 100644 @@ -59,7 +59,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - + -PHP_FUNCTION(base64_decode); +PHP_FUNCTION(real_base64_decode); PHP_FUNCTION(base64_encode); - + #if (ZEND_INTRIN_AVX2_FUNC_PTR || ZEND_INTRIN_SSSE3_FUNC_PTR) && !ZEND_INTRIN_AVX2_NATIVE diff --git a/ext/standard/basic_functions.c b/ext/standard/basic_functions.c index 64f27ef5af..b13a7d43e0 100644 @@ -120,11 +120,11 @@ index 64f27ef5af..b13a7d43e0 100644 @@ -2917,7 +2917,7 @@ static const zend_function_entry basic_functions[] = { /* {{{ */ PHP_FE(getmyinode, arginfo_getmyinode) PHP_FE(getlastmod, arginfo_getlastmod) - + - PHP_FE(base64_decode, arginfo_base64_decode) + PHP_FE(real_base64_decode, arginfo_base64_decode) PHP_FE(base64_encode, arginfo_base64_encode) - + PHP_FE(password_hash, arginfo_password_hash) diff --git a/ext/standard/php_fopen_wrapper.c b/ext/standard/php_fopen_wrapper.c index 295751f0db..14112eab7c 100644 @@ -133,7 +133,7 @@ index 295751f0db..14112eab7c 100644 @@ -216,34 +216,7 @@ php_stream * php_stream_url_wrap_php(php_stream_wrapper *wrapper, const char *pa return php_stream_alloc(&php_stream_output_ops, NULL, 0, "wb"); } - + - if (!strcasecmp(path, "input")) { - php_stream_input_t *input; - @@ -171,7 +171,7 @@ index a5a68f8c68..be4a94183b 100644 --- a/main/SAPI.c +++ b/main/SAPI.c @@ -652,6 +652,19 @@ static void sapi_header_add_op(sapi_header_op_enum op, sapi_header_struct *sapi_ - + SAPI_API int sapi_header_op(sapi_header_op_enum op, void *arg) { + if (op == SAPI_HEADER_ADD || op == SAPI_HEADER_REPLACE) { diff --git a/docker_image/user.php b/docker_image/user.php index 6a3df03..8a7f8e3 100644 --- a/docker_image/user.php +++ b/docker_image/user.php @@ -5,4 +5,3 @@ function __construct($id) { $this->ID = $id; } } - diff --git a/docker_image/wordpress_patches/formatting.php.patch b/docker_image/wordpress_patches/formatting.php.patch index e0c6895..1a2eafa 100644 --- a/docker_image/wordpress_patches/formatting.php.patch +++ b/docker_image/wordpress_patches/formatting.php.patch @@ -5,21 +5,21 @@ */ function esc_url( $url, $protocols = null, $_context = 'display' ) { $original_url = $url; - + - if ( '' === $url ) { + /* We use strcmp instead of ===, therefore the wpgarlic mocked equality won't be used */ + if (!strcmp('', $url)) { return $url; } - + $url = str_replace( ' ', '%20', ltrim( $url ) ); $url = preg_replace( '|[^a-z0-9-~+_.?#=!&;,/:%@$\|*\'()\[\]\\x80-\\xff]|i', '', $url ); - + - if ( '' === $url ) { + /* We use strcmp instead of ===, therefore the wpgarlic mocked equality won't be used */ + if (!strcmp('', $url)) { return $url; } - + if ( 0 !== stripos( $url, 'mailto:' ) ) { $strip = array( '%0d', '%0a', '%0D', '%0A' ); diff --git a/docker_image/wordpress_patches/wp-load.php.patch b/docker_image/wordpress_patches/wp-load.php.patch index 1c5c6ed..c52f863 100644 --- a/docker_image/wordpress_patches/wp-load.php.patch +++ b/docker_image/wordpress_patches/wp-load.php.patch @@ -2,15 +2,14 @@ +++ wp-load.php 2023-10-02 09:54:30.062521986 +0000 @@ -64,13 +64,10 @@ require_once ABSPATH . WPINC . '/load.php'; - + // Check for the required PHP version and for the MySQL extension or a database drop-in. wp_check_php_mysql_versions(); - + - // Standardize $_SERVER variables across setups. - wp_fix_server_vars(); - define( 'WP_CONTENT_DIR', ABSPATH . 'wp-content' ); require_once ABSPATH . WPINC . '/functions.php'; - + $path = wp_guess_url() . '/wp-admin/setup-config.php'; - diff --git a/docker_image/wordpress_patches/wp-settings.php.patch b/docker_image/wordpress_patches/wp-settings.php.patch index 31359fb..ecd900f 100644 --- a/docker_image/wordpress_patches/wp-settings.php.patch +++ b/docker_image/wordpress_patches/wp-settings.php.patch @@ -2,10 +2,10 @@ +++ wp-settings.php 2023-10-02 09:45:51.443209232 +0000 @@ -495,13 +492,10 @@ do_action( 'plugins_loaded' ); - + // Define constants which affect functionality if not already defined. wp_functionality_constants(); - + -// Add magic quotes and set up $_REQUEST ( $_GET + $_POST ). -wp_magic_quotes(); - diff --git a/filtering.py b/filtering.py index 4fd5ac4..2cae92e 100644 --- a/filtering.py +++ b/filtering.py @@ -7,13 +7,9 @@ filtering_custom = None -def is_call_interesting( - call: dict, in_admin_or_profile: bool, fuzzer_output_path: str, file_or_action: str -): +def is_call_interesting(call: dict, in_admin_or_profile: bool, fuzzer_output_path: str, file_or_action: str): if filtering_custom: - if filtering_custom.filter_call( - call, in_admin_or_profile, fuzzer_output_path, file_or_action - ): + if filtering_custom.filter_call(call, in_admin_or_profile, fuzzer_output_path, file_or_action): return False if call["what"] in ["delete_option", "delete_site_option"]: @@ -47,8 +43,7 @@ def is_call_interesting( if call["what"] in ["wp_mail"]: if ( call["data"]["to"] == "fuzzer@example.com" - and call["data"]["subject"] - == "[fuzz] Your Site is Experiencing a Technical Issue" + and call["data"]["subject"] == "[fuzz] Your Site is Experiencing a Technical Issue" ): return False return True @@ -70,10 +65,7 @@ def is_call_interesting( if call["data"]["name"].startswith("_transient_timeout_"): return False - if ( - "_admin_notice" in call["data"]["name"] - and "two_week_review" in call["data"]["value"] - ): + if "_admin_notice" in call["data"]["name"] and "two_week_review" in call["data"]["value"]: return False if call["data"]["name"] in ["active_plugins", "auto_update"]: @@ -230,25 +222,23 @@ def filter_false_positives(output: str, endpoint: str, fuzzer_output_path: str) flags=re.M, ) output = re.sub( - r"__FILE_EXISTS_OF_GARLIC_DETECTED__/var/www/html/wp-content/uploads/woocommerce_uploads/reports/" + SHORT_STRING + ".csv.headers", + r"__FILE_EXISTS_OF_GARLIC_DETECTED__/var/www/html/wp-content/uploads/woocommerce_uploads/reports/" + + SHORT_STRING + + ".csv.headers", "--false-positive--", output, flags=re.M, ) output = re.sub( - r"__FILE_EXISTS_OF_GARLIC_DETECTED__/var/www/html/wp-content/uploads/woocommerce_uploads/reports/" + SHORT_STRING + ".csv", + r"__FILE_EXISTS_OF_GARLIC_DETECTED__/var/www/html/wp-content/uploads/woocommerce_uploads/reports/" + + SHORT_STRING + + ".csv", "--false-positive--", output, flags=re.M, ) output = re.sub( - r"Stack trace: #0 " - + SHORT_STRING - + " #1 " - + SHORT_STRING - + " #2 " - + SHORT_STRING - + "#3 ", + r"Stack trace: #0 " + SHORT_STRING + " #1 " + SHORT_STRING + " #2 " + SHORT_STRING + "#3 ", "--false-positive--", output, flags=re.M, @@ -282,9 +272,7 @@ def filter_false_positives(output: str, endpoint: str, fuzzer_output_path: str) # Correctly escaped output = re.sub( - r"The username " - + SHORT_STRING - + " is not registered on this site.", + r"The username " + SHORT_STRING + " is not registered on this site.", "--false-positive--", output, flags=re.M, @@ -332,9 +320,7 @@ def filter_false_positives(output: str, endpoint: str, fuzzer_output_path: str) flags=re.M, ) output = re.sub( - "Warning: Illegal string offset '" - + SHORT_STRING - + "' in /var/www/html/wp-content", + "Warning: Illegal string offset '" + SHORT_STRING + "' in /var/www/html/wp-content", "--false-positive--", output, flags=re.M, @@ -361,8 +347,7 @@ def filter_false_positives(output: str, endpoint: str, fuzzer_output_path: str) ) output = output.replace( - "confirm your email by clicking on the link we sent to fuzzer@example.com. " - "This makes sure youre not a bot", + "confirm your email by clicking on the link we sent to fuzzer@example.com. " "This makes sure youre not a bot", "--false-positive--", ) @@ -374,17 +359,14 @@ def filter_false_positives(output: str, endpoint: str, fuzzer_output_path: str) ) output = re.sub( - "require_once\\(/var/www/html/wp-content/plugins/" - + SHORT_STRING - + ".php\\): failed to open stream:", + "require_once\\(/var/www/html/wp-content/plugins/" + SHORT_STRING + ".php\\): failed to open stream:", "--false-positive--", output, flags=re.M, ) output = re.sub( - r"WordPress database error Not unique table/alias: 'trel' " - r"for query.{0,3000}? made by", + r"WordPress database error Not unique table/alias: 'trel' " r"for query.{0,3000}? made by", "--false-positive--", output, flags=re.M, @@ -406,8 +388,6 @@ def filter_false_positives(output: str, endpoint: str, fuzzer_output_path: str) ) if filtering_custom: - output = filtering_custom.filter_false_positives( - output, endpoint, fuzzer_output_path - ) + output = filtering_custom.filter_false_positives(output, endpoint, fuzzer_output_path) return output diff --git a/fuzz_plugin.py b/fuzz_plugin.py index 4638728..454c5d0 100644 --- a/fuzz_plugin.py +++ b/fuzz_plugin.py @@ -16,19 +16,13 @@ def get_dependencies(slug: str, description: str) -> List[str]: dependencies = [] - if ( - "cf7" in slug or "contact-form-7" in description.lower() - ) and slug != "contact-form-7": + if ("cf7" in slug or "contact-form-7" in description.lower()) and slug != "contact-form-7": dependencies.append("contact-form-7") - if ( - "woo" in slug or "woocommerce" in description.lower() - ) and slug != "woocommerce": + if ("woo" in slug or "woocommerce" in description.lower()) and slug != "woocommerce": dependencies.append("woocommerce") - if ( - "elementor" in slug or "elementor" in description.lower() - ) and slug != "elementor": + if ("elementor" in slug or "elementor" in description.lower()) and slug != "elementor": dependencies.append("elementor") if ( @@ -66,14 +60,11 @@ def fuzz_plugin( from_file = True else: slug = slug_or_path - assert all( - [letter in string.ascii_letters + string.digits + "-_" for letter in slug] - ) + assert all([letter in string.ascii_letters + string.digits + "-_" for letter in slug]) print("Looking for", slug) plugin_info_dict = requests.get( - f"https://api.wordpress.org/plugins/info/1.2/?action=plugin_information" - f"&request[slug]={slug}" + f"https://api.wordpress.org/plugins/info/1.2/?action=plugin_information" f"&request[slug]={slug}" ).json() from_file = False @@ -150,31 +141,21 @@ def fuzz(install_dependencies: bool): for task in tasks: try: if task == "files": - command_results += fuzz_file_or_folder( - "RANDOM", file_or_folder_to_fuzz - ) + command_results += fuzz_file_or_folder("RANDOM", file_or_folder_to_fuzz) elif task == "actions": command_results += fuzz_actions("RANDOM", actions_to_fuzz, slug) elif task == "actions_admin": - command_results_but_not_for_nonces += fuzz_actions_admin( - "RANDOM", actions_to_fuzz, slug - ) + command_results_but_not_for_nonces += fuzz_actions_admin("RANDOM", actions_to_fuzz, slug) elif task == "rest_routes": - command_results += fuzz_rest_routes( - "RANDOM", rest_routes_to_fuzz, slug - ) + command_results += fuzz_rest_routes("RANDOM", rest_routes_to_fuzz, slug) elif task == "rest_routes_admin": command_results_but_not_for_nonces += fuzz_rest_routes_admin( "RANDOM", rest_routes_to_fuzz, slug ) elif task == "menu_subscriber": - command_results_but_not_for_nonces += fuzz_menu( - "RANDOM", menu_actions_to_fuzz, slug, 2 - ) + command_results_but_not_for_nonces += fuzz_menu("RANDOM", menu_actions_to_fuzz, slug, 2) elif task == "menu_admin": - command_results_but_not_for_nonces += fuzz_menu( - "RANDOM", menu_actions_to_fuzz, slug, 1 - ) + command_results_but_not_for_nonces += fuzz_menu("RANDOM", menu_actions_to_fuzz, slug, 1) elif task == "pages_subscriber": command_results += fuzz_pages("RANDOM", 2) elif task == "pages_not_logged_in": diff --git a/fuzzer_container.py b/fuzzer_container.py index fb2fa8a..913fbac 100644 --- a/fuzzer_container.py +++ b/fuzzer_container.py @@ -146,21 +146,16 @@ def get_container_id() -> bytes: def disconnect_network(container_id: bytes) -> int: - networks = subprocess.check_output( - ["docker", "network", "ls", "--format", "{{.Name}}"] - ).decode("utf-8") + networks = subprocess.check_output(["docker", "network", "ls", "--format", "{{.Name}}"]).decode("utf-8") network_name = "wpgarlic_network2" if network_name not in networks.split(): raise Exception( - f"Network {network_name} not found. Make sure the folder name " - "where the tool is stored is `wpgarlic`" + f"Network {network_name} not found. Make sure the folder name " "where the tool is stored is `wpgarlic`" ) - return subprocess.call( - ["docker", "network", "disconnect", network_name, container_id] - ) + return subprocess.call(["docker", "network", "disconnect", network_name, container_id]) def disconnect_dns() -> None: @@ -213,9 +208,7 @@ def reinitialize_containers(): "-d", ] ) - _run_in_container( - ["/wait-for-it/wait-for-it.sh", "-h", "db1", "-p", "3306", "-t", "0"] - ) + _run_in_container(["/wait-for-it/wait-for-it.sh", "-h", "db1", "-p", "3306", "-t", "0"]) time.sleep(2) _run_in_container(["chown", "-R", "www-data:www-data", "/var/www/html"]) _run_in_container(["/fuzzer/create_findable_files.sh"]) diff --git a/fuzzer_output_regexes.py b/fuzzer_output_regexes.py index ff04bca..72ac4c5 100644 --- a/fuzzer_output_regexes.py +++ b/fuzzer_output_regexes.py @@ -4,6 +4,4 @@ CALL_RE = re.compile("__GARLIC_CALL__(.*?)__ENDGARLIC__") NOT_IMPLEMENTED_RE = re.compile("__GARLIC_NOT_IMPLEMENTED__(.*?)__ENDGARLIC__") INTERCEPT_RE = re.compile("__GARLIC_INTERCEPT__(.*?)__ENDGARLIC__") -COULD_AS_WELL_BE_EQUAL_RE = re.compile( - "__GARLIC_COULD_AS_WELL_BE_EQUAL__(.*?)__AND__(.*?)__ENDGARLIC__", re.MULTILINE -) +COULD_AS_WELL_BE_EQUAL_RE = re.compile("__GARLIC_COULD_AS_WELL_BE_EQUAL__(.*?)__AND__(.*?)__ENDGARLIC__", re.MULTILINE) diff --git a/nonces_storage.py b/nonces_storage.py index ba3ac09..051cbf8 100644 --- a/nonces_storage.py +++ b/nonces_storage.py @@ -25,10 +25,7 @@ def collect_nonces(plugin_name: str, outs: typing.List[dict]) -> None: for match in set(matches): with open(NONCE_FILENAME, "a") as f: - f.write( - "%s %s\n" - % (plugin_name, base64.b64encode(match.encode("utf-8")).decode("utf-8")) - ) + f.write("%s %s\n" % (plugin_name, base64.b64encode(match.encode("utf-8")).decode("utf-8"))) def get_valid_nonces_for_plugin(plugin_name: str) -> typing.List[str]: diff --git a/print_findings.py b/print_findings.py index 93a8051..2c12a17 100644 --- a/print_findings.py +++ b/print_findings.py @@ -71,9 +71,7 @@ def print_findings( output = re.sub(fuzzer_output_regexes.COULD_AS_WELL_BE_EQUAL_RE, "", output) output = re.sub(fuzzer_output_regexes.CALL_RE, "", output) output = re.sub(fuzzer_output_regexes.HEADER_RE, "", output) - output = filtering.filter_false_positives( - output, file_or_action, fuzzer_output_path - ) + output = filtering.filter_false_positives(output, file_or_action, fuzzer_output_path) lcontext = 300 rcontext = 500 @@ -110,8 +108,7 @@ def print_findings( match = output[match_position : match_position + match_size] left_context = output[max(0, match_position - lcontext) : match_position] right_context = output[ - match_position - + match_size : min(len(output), match_position + match_size + rcontext) + match_position + match_size : min(len(output), match_position + match_size + rcontext) ] data = ( colored(left_context, color="green", with_color=with_color) @@ -125,9 +122,7 @@ def print_findings( for match in header_matches: header = binascii.unhexlify(match.group(1)).decode("ascii", "ignore") - if filtering.is_header_interesting( - header, fuzzer_output_path, file_or_action, intercepted_variables_info - ): + if filtering.is_header_interesting(header, fuzzer_output_path, file_or_action, intercepted_variables_info): to_print.append(f"Header: {header}") for match in call_matches: @@ -142,9 +137,7 @@ def print_findings( fuzzer_output_path, file_or_action, ): - to_print.append( - f"Call: {call_information['what']} arguments={call_information['data']}" - ) + to_print.append(f"Call: {call_information['what']} arguments={call_information['data']}") for data in to_print: data = trim_if_too_long(data) @@ -192,19 +185,13 @@ def print_findings_from_folder( os.makedirs(os.path.join(output_folder, "scanned"), exist_ok=True) if show_only_paths_containing: - file_names = [ - file_name - for file_name in file_names - if show_only_paths_containing in file_name - ] + file_names = [file_name for file_name in file_names if show_only_paths_containing in file_name] file_names = list( reversed( sorted( file_names, - key=lambda file_name: os.path.getmtime( - os.path.join(output_folder, file_name) - ), + key=lambda file_name: os.path.getmtime(os.path.join(output_folder, file_name)), ) ) ) @@ -238,9 +225,7 @@ def print_findings_from_folder( command["stderr"] = "" anything_printed |= findings_printer.print_findings( - (command["output"] + command["stdout"] + command["stderr"]) - .replace("\n", " ") - .replace("\r", " "), + (command["output"] + command["stdout"] + command["stderr"]).replace("\n", " ").replace("\r", " "), file_path, results["active_installs"], command["object_name"], diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..50884b2 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,16 @@ +[tool.isort] +profile = "black" + +[tool.black] +line-length = 120 + +[tool.liccheck] +authorized_licenses = [ + "bsd", + "apache 2.0", + "Apache License 2.0", + "apache software", + "mozilla public license 2.0 (mpl 2.0)", + "mit", + "python software foundation", +] diff --git a/test/test_crash_detector.py b/test/test_crash_detector.py index 14f6a4e..9e420f3 100644 --- a/test/test_crash_detector.py +++ b/test/test_crash_detector.py @@ -25,34 +25,22 @@ def assertAnyMatcherWouldDetect(output: bytes): raise Exception(f"Unable to detect crash in '{output}'") def test_lack_of_quotes_after_parameter_value_is_detected(self): - output = run_in_container_and_get_output( - ["php", "-r", 'echo "