thumbor is an open-source photo thumbnail service by globo.com
This report summarizes scanner evidence for thumbor/thumbor, a Python project with 10,518 GitHub stars. SynthScan examined 181,473 lines of code across 361 source files, recording 829 pattern matches distributed across 12 syntactic categories. The legacy adjusted pattern signal is 5.2 (Moderate pattern signal); it is not an authorship estimate.
Use the findings to ask whether maintainers can explain intent, verify dependencies, simplify unnecessary scaffolding, and focus review effort. Risk Payload v1 adds ownership, supply-chain, over-engineering, and review-effort vectors when available. These are prompts for human verification, not probabilities, security findings, or measured maintenance costs.
Use each vector to guide manual verification of the underlying findings.
Can maintainers explain intent, assumptions, and trade-offs? 797 finding(s).
Are imports declared and package/API names verified? 11 finding(s).
Are wrappers, guards, and repeated structures necessary? 771 finding(s).
Which changes need focused human review? 829 finding(s).
This chart tracks the legacy adjusted pattern score across scan runs. Changes can reflect source changes, scan coverage, or detector/scoring versions; they do not measure AI adoption or prove that a team refactored code. Compare runs only when their scoring versions are compatible. The dashed line tracks the raw pattern-hit count.
Groups findings by configured severity weight. Severity is not a confidence estimate, security rating, or proof that a package is missing. Verify imports against the project's manifests and inspect each finding in its local design context.
This horizontal bar chart shows where configured pattern scores accumulate across directories. Treat the bars as locations to inspect, not as evidence of authorship or code quality. Use the file-level findings to decide whether a directory needs a focused ownership, dependency, complexity, or review check.
The scanner identified 829 distinct pattern matches across 12 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a detector match to inspect in context. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the configured finding weight (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — legacy comment-context matches receive a ×1.5 score weight. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier under the legacy scoring model.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | integration_tests/pil_test.py | 28 | # def test_combined_params__with_pil(self): | COMMENT |
| LOW | tests/test_point.py | 17 | def test_default_alignment_percentages(self): | CODE |
| LOW | tests/test_point.py | 26 | def test_new_point_default_weight(self): | CODE |
| LOW⚡ | tests/test_point.py | 57 | def test_new_point_square_point(self): | CODE |
| LOW⚡ | tests/test_point.py | 63 | def test_aligned_point_center_middle(self): | CODE |
| LOW⚡ | tests/test_point.py | 69 | def test_aligned_point_top_left(self): | CODE |
| LOW⚡ | tests/test_point.py | 75 | def test_aligned_point_bottom_right(self): | CODE |
| LOW | tests/test_utils.py | 33 | def test_can_get_content_type(): | CODE |
| LOW⚡ | tests/test_utils.py | 94 | def test_logger_should_be_instance_of_python_logger(): | CODE |
| LOW⚡ | tests/test_utils.py | 98 | def test_logger_should_not_be_null(): | CODE |
| LOW⚡ | tests/test_utils.py | 102 | def test_logger_should_not_be_an_error(): | CODE |
| LOW⚡ | tests/test_utils.py | 106 | def test_get_color_space_handles_invalid_icc(): | CODE |
| LOW⚡ | tests/test_utils.py | 114 | def test_get_color_space_handles_null_xcolor_space(image_cms_profile_mock): | CODE |
| LOW⚡ | tests/test_utils.py | 122 | def test_get_color_space_missing_imagecms_returns_none(): | CODE |
| LOW⚡ | tests/test_utils.py | 128 | def test_ensure_srgb_handles_invalid_icc(): | CODE |
| LOW⚡ | tests/test_utils.py | 135 | def test_ensure_srgb_missing_imagecms_runtimeerror(): | CODE |
| LOW⚡ | tests/test_utils.py | 142 | def test_ensure_srgb_keeps_existing_srgb_profile(): | CODE |
| LOW⚡ | tests/test_utils.py | 151 | def test_ensure_srgb_returns_none_for_unsupported_color_space(color_space): | CODE |
| LOW⚡ | tests/test_utils.py | 159 | def test_ensure_srgb_returns_none_for_animated_cmyk(): | CODE |
| LOW | tests/test_console.py | 18 | def test_can_get_default_server_parameters(self): | CODE |
| LOW | tests/test_console.py | 29 | def test_can_get_custom_server_parameters(self): | CODE |
| LOW | tests/test_console.py | 51 | def test_keyfile_help_describes_the_security_key_file(self): | CODE |
| LOW | tests/test_url_composer.py | 45 | def test_fails_when_no_config_or_key(self, load_mock, mock_stdout): | CODE |
| LOW | tests/test_url_composer.py | 74 | def test_get_thumbor_params_with_crop(self): | CODE |
| LOW | tests/test_url_composer.py | 94 | def test_get_thumbor_params_full_adaptive_fitin_false(self): | CODE |
| LOW | tests/test_url_composer.py | 116 | def test_get_thumbor_params_full_adaptive_fitin(self): | CODE |
| LOW | tests/test_url_composer.py | 135 | def test_get_thumbor_params_adaptive_fitin(self): | CODE |
| LOW | tests/test_url_composer.py | 154 | def test_get_thumbor_params_full_fitin(self): | CODE |
| LOW | tests/test_url_composer.py | 173 | def test_get_thumbor_params_fitin(self): | CODE |
| LOW | tests/test_url_composer.py | 212 | def test_get_options_from_sys(self): | CODE |
| LOW | tests/test_url_composer.py | 233 | def test_get_options_fails_when_no_url(self): | CODE |
| LOW | tests/test_signal_handler.py | 18 | def test_setup_signal_handler_sets_handler(self, signal_mock): | CODE |
| LOW | tests/test_signal_handler.py | 31 | def test_signal_handler_calls_add_callback_from_signal(self, ioloop_mock): | CODE |
| LOW | tests/test_doctor.py | 20 | def test_reports_module_load_failure(capsys, module_name, error_type): | CODE |
| LOW | tests/test_doctor.py | 44 | def test_format_error_indents_multiline_text(): | CODE |
| LOW | tests/test_doctor.py | 60 | def test_doctor_finishes_when_cairo_library_is_missing(capsys): | CODE |
| LOW | tests/test_doctor.py | 96 | def test_get_doctor_output_no_config(capsys, doctor_output_no_config): | CODE |
| LOW | tests/test_importer.py | 38 | def test_import_item_should_be_proper_item(self): | CODE |
| LOW | tests/test_importer.py | 67 | def test_single_item_should_equal_file_storage(): | CODE |
| LOW | tests/test_importer.py | 77 | def test_multiple_items_can_be_imported(): | CODE |
| LOW | tests/test_threadpool.py | 21 | async def test_can_get_threadpool_instance(): | CODE |
| LOW | tests/test_threadpool.py | 36 | async def test_can_run_task_in_foreground(): | CODE |
| LOW | tests/test_threadpool.py | 48 | async def test_can_run_task_in_foreground_and_exception_happens(): | CODE |
| LOW | tests/test_threadpool.py | 61 | async def test_queueing_task_when_no_pool_runs_sync(): | CODE |
| LOW | tests/test_threadpool.py | 73 | async def test_queueing_task_when_no_pool_runs_sync_and_exception_happens(): | CODE |
| LOW | tests/test_threadpool.py | 99 | async def test_can_run_async_with_queue(): | CODE |
| LOW⚡ | tests/test_server.py | 103 | def test_can_get_value_as_integer(self): | CODE |
| LOW⚡ | tests/test_server.py | 109 | def test_can_get_config_from_path(self): | CODE |
| LOW⚡ | tests/test_server.py | 117 | def test_can_get_config_with_env_enabled(self): | CODE |
| LOW⚡ | tests/test_server.py | 141 | def test_can_configure_log_from_dict_config(self, dict_config_mock): | CODE |
| LOW⚡ | tests/test_server.py | 151 | def test_can_import_default_modules(self): | CODE |
| LOW⚡ | tests/test_server.py | 158 | def test_can_import_with_custom_error_handler_class(self): | CODE |
| LOW⚡ | tests/test_server.py | 202 | def test_validate_config_validates_allowed_sizes(self, validation_mock): | CODE |
| LOW⚡ | tests/test_server.py | 211 | def test_validate_gifsicle_path(self, which_mock): | CODE |
| LOW⚡ | tests/test_server.py | 221 | def test_validate_null_gifsicle_path(self, which_mock): | CODE |
| LOW | tests/test_server.py | 36 | def fixture_server_without_unix_sockets(monkeypatch): | CODE |
| LOW | tests/test_server.py | 46 | def test_can_start_tcp_without_unix_sockets(server_without_unix_sockets): | CODE |
| LOW | tests/test_server.py | 59 | def test_can_use_fd_without_unix_sockets( | CODE |
| LOW | tests/test_server.py | 82 | def test_rejects_unavailable_unix_socket(server_without_unix_sockets): | CODE |
| LOW | tests/test_server.py | 128 | def test_can_configure_log_from_config(self, basic_config_mock): | CODE |
| 682 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| CRITICAL | thumbor/transformer.py | 188 | self.context.modules.importer.error_handler.handle_error( | CODE |
| CRITICAL | thumbor/handlers/__init__.py | 856 | self.context.modules.importer.error_handler.handle_error( | CODE |
| CRITICAL | thumbor/handlers/__init__.py | 1231 | self.context.modules.importer.error_handler.handle_error( | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | integration_tests/urls_helpers.py | 149 | except Exception as err: # pylint: disable=broad-except | CODE |
| LOW | thumbor/url_composer.py | 232 | except Exception: # pylint: disable=broad-except | CODE |
| LOW | thumbor/utils.py | 34 | except Exception: | CODE |
| MEDIUM | thumbor/utils.py | 31 | def _get_flag(flags, name): | CODE |
| LOW | thumbor/transformer.py | 182 | except Exception: # pylint: disable=broad-except | CODE |
| LOW | thumbor/filters/watermark.py | 219 | except Exception as error: | CODE |
| LOW | thumbor/filters/__init__.py | 233 | except Exception: | CODE |
| LOW | thumbor/engines/gif.py | 157 | except Exception: | CODE |
| LOW | thumbor/engines/__init__.py | 208 | except Exception as error: # pylint: disable=broad-except | CODE |
| LOW | thumbor/engines/__init__.py | 283 | except Exception as error: | CODE |
| LOW | thumbor/engines/__init__.py | 340 | except Exception as error: | CODE |
| LOW | thumbor/engines/pil.py | 228 | except Exception: | CODE |
| MEDIUM | thumbor/engines/pil.py | 147 | def create_image(self, buffer): | CODE |
| LOW | thumbor/detectors/feature_detector/__init__.py | 33 | except Exception as error: | CODE |
| LOW | thumbor/handlers/__init__.py | 207 | except Exception as error: | CODE |
| LOW | thumbor/handlers/__init__.py | 314 | except Exception as error: | CODE |
| LOW | thumbor/handlers/__init__.py | 344 | except Exception as error: | CODE |
| LOW | thumbor/handlers/__init__.py | 851 | except Exception as error: # pylint: disable=broad-except | CODE |
| LOW | thumbor/handlers/__init__.py | 1170 | except Exception as error: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/handlers/test_animated_webp.py | 82 | CODE | |
| LOW | tests/handlers/test_gif_post_transform_filters.py | 76 | CODE | |
| LOW | tests/handlers/test_gif_post_transform_filters.py | 104 | CODE | |
| LOW | tests/handlers/test_gif_post_transform_filters.py | 148 | CODE | |
| LOW | thumbor/importer.py | 148 | CODE | |
| LOW | thumbor/url_composer.py | 169 | CODE | |
| LOW | thumbor/transformer.py | 131 | CODE | |
| LOW | thumbor/filters/watermark.py | 59 | CODE | |
| LOW | thumbor/filters/redeye.py | 35 | CODE | |
| LOW | thumbor/engines/__init__.py | 110 | CODE | |
| LOW | thumbor/engines/__init__.py | 300 | CODE | |
| LOW | thumbor/engines/pil.py | 354 | CODE | |
| LOW | thumbor/loaders/http_loader.py | 130 | CODE | |
| LOW | thumbor/loaders/http_loader.py | 185 | CODE | |
| LOW | thumbor/handlers/__init__.py | 368 | CODE | |
| LOW | thumbor/handlers/__init__.py | 521 | CODE | |
| LOW | thumbor/handlers/__init__.py | 744 | CODE | |
| LOW | thumbor/handlers/__init__.py | 785 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | thumbor/handlers/image_resource.py | 22 | # Check if image exists | COMMENT |
| LOW⚡ | thumbor/handlers/image_resource.py | 50 | # Check if image overwriting is allowed | COMMENT |
| LOW⚡ | thumbor/handlers/image_resource.py | 55 | # Check if the image uploaded is valid | COMMENT |
| LOW⚡ | thumbor/handlers/image_resource.py | 62 | # Check if image deleting is allowed | COMMENT |
| LOW⚡ | thumbor/handlers/image_resource.py | 67 | # Check if image exists | COMMENT |
| LOW | thumbor/handlers/upload.py | 26 | # Check if the image uploaded is a multipart/form-data | COMMENT |
| LOW | thumbor/handlers/upload.py | 39 | # Check if the image uploaded is valid | COMMENT |
| LOW | thumbor/handlers/__init__.py | 1252 | # Check if image is valid | COMMENT |
| LOW | thumbor/handlers/imaging.py | 42 | # Check if an image with an uuid exists in storage | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/loaders/test_http_loader.py | 241 | class DummyAsyncHttpClientTestCase(TestCase): | COMMENT |
| LOW | docs/conf.py | 1 | # -*- coding: utf-8 -*- | COMMENT |
| LOW | docs/conf.py | 81 | # language = None | COMMENT |
| LOW | docs/conf.py | 101 | # unit titles (such as .. function::). | COMMENT |
| LOW | docs/conf.py | 121 | # a list of builtin themes. | COMMENT |
| LOW | docs/conf.py | 141 | # The name of an image file (within the static path) to use as favicon of the | COMMENT |
| LOW | docs/conf.py | 161 | # typographically correct entities. | COMMENT |
| LOW | docs/conf.py | 181 | # html_show_sourcelink = True | COMMENT |
| LOW | docs/conf.py | 221 | "manual", | COMMENT |
| LOW | docs/conf.py | 281 | # How to display URL addresses: 'footnote', 'no', or 'inline'. | COMMENT |
| LOW | thumbor/url.py | 1 | # -*- coding: utf-8 -*- | COMMENT |
| LOW | thumbor/lib/__init__.py | 1 | # -*- coding: utf-8 -*- | COMMENT |
| LOW | .github/workflows/codeql-analysis.yml | 1 | # For most projects, this workflow file will not need changing; you simply need | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | thumbor/detectors/feature_detector/__init__.py | 13 | except ImportError: | CODE |
| HIGH | thumbor/handlers/__init__.py | 1203 | except KeyError: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/conftest.py | 7 | CODE | |
| LOW | thumbor/url.py | 10 | CODE | |
| LOW | thumbor/doctor.py | 184 | CODE | |
| LOW | thumbor/doctor.py | 185 | CODE | |
| LOW | thumbor/compatibility/__init__.py | 10 | CODE | |
| LOW | thumbor/url_signers/__init__.py | 14 | CODE | |
| LOW | thumbor/detectors/__init__.py | 13 | CODE | |
| LOW | thumbor/detectors/__init__.py | 14 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/loaders/test_file_loader_http_fallback.py | 33 | CODE | |
| LOW | tests/loaders/test_file_loader_http_fallback.py | 44 | CODE | |
| LOW | thumbor/context.py | 255 | CODE | |
| LOW | thumbor/storages/mixed_storage.py | 77 | CODE | |
| LOW | thumbor/storages/mixed_storage.py | 82 | CODE | |
| LOW | thumbor/storages/mixed_storage.py | 87 | CODE | |
| LOW | thumbor/storages/mixed_storage.py | 92 | CODE | |
| LOW | thumbor/result_storages/file_storage.py | 120 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | tests/compatibility/legacy_file_result_storage.py | 10 | # This file is copied from legacy way of using thumbor result storages, | COMMENT |
| MEDIUM | docs/conf.py | 6 | # This file is execfile()d with the current directory set to its | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/conftest.py | 185 | Don't forget to copy this log and add it to the description. | CODE |
| LOW | thumbor/doctor.py | 467 | print_info("Don't forget to copy this log and add it to the description.") | STRING |
| LOW | .github/ISSUE_TEMPLATE/bug_report.yml | 12 | ⚠️ Don't forget to **replace your `SECURITY_KEY`** in the config with a | CODE |
| LOW | .github/ISSUE_TEMPLATE/bug_report.yml | 61 | Paste your `thumbor.conf` contents here. **Make sure to replace your | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | thumbor/engines/pil.py | 427 | # Can't simply set options['progressive'] to the value | COMMENT |