thumbor is an open-source photo thumbnail service by globo.com
This report presents the forensic synthetic code analysis of thumbor/thumbor, a Python project with 10,510 GitHub stars. SynthScan v2.0 examined 170,924 lines of code across 279 source files, recording 626 pattern matches distributed across 11 syntactic categories. The overall adjusted score of 4.3 places this repository in the Likely human-written band.
The scanner applied 160+ deterministic lexical heuristics, multi-line block detectors, abstract syntax tree depth profilers, and a cross-file Jaccard similarity matrix to construct a statistically normalised synthetic code estimate. All matches are individually weighted by severity coefficient and contextual multiplier before summation, and the resulting headline score is temporally discounted to account for the repository's development history relative to the commercial emergence of large language model coding tooling (November 2022 onward).
Longitudinal tracking requires multiple scan runs. Once this repository is re-scanned after new commits land, this chart will visualise how the synthetic code signal evolves over time — enabling you to detect whether AI authorship is growing, stabilising, or being actively corrected by human engineers.
Classifies detected patterns by their diagnostic confidence and structural impact. CRITICAL patterns (coefficient 10) represent definitive synthetic signatures — hallucinated imports, explicit LLM attribution metadata — virtually never produced by human authors. HIGH (5) indicates strong structural tells such as cross-file repetition or cross-linguistic idioms. MEDIUM (2) covers recognisable conversational padding and AI-specific vocabulary. LOW (1) captures subtle indicators like tautological comments and generic boilerplate that require density to carry independent signal.
This horizontal bar chart decomposes the repository's raw synthetic code score by top-level directory, allowing you to pinpoint precisely which modules or components carry the highest AI authorship density. Directories with disproportionately high scores relative to their size warrant targeted manual review: concentrated AI signatures often trace back to mass-generated configuration layers, auto-ported test suites, LLM-scaffolded boilerplate classes, or entire subsystems authored under heavy copilot assistance. Use this view to prioritise your human code-review effort.
The scanner identified 626 distinct pattern matches across 11 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a statistically significant AI authorship indicator. 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 diagnostic confidence level (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — comment-context matches receive a ×1.5 weight because LLMs systematically over-annotate. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier as dense clusters constitute far stronger evidence of synthetic authorship than isolated hits.
| 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 | 59 | def test_new_point_square_point(self): | CODE |
| LOW⚡ | tests/test_point.py | 65 | def test_aligned_point_center_middle(self): | CODE |
| LOW⚡ | tests/test_point.py | 71 | def test_aligned_point_top_left(self): | CODE |
| LOW⚡ | tests/test_point.py | 77 | def test_aligned_point_bottom_right(self): | CODE |
| LOW | tests/test_utils.py | 34 | def test_can_get_content_type(): | CODE |
| LOW⚡ | tests/test_utils.py | 95 | def test_logger_should_be_instance_of_python_logger(): | CODE |
| LOW⚡ | tests/test_utils.py | 99 | def test_logger_should_not_be_null(): | CODE |
| LOW⚡ | tests/test_utils.py | 103 | def test_logger_should_not_be_an_error(): | CODE |
| LOW⚡ | tests/test_utils.py | 107 | def test_get_color_space_handles_invalid_icc(): | CODE |
| LOW⚡ | tests/test_utils.py | 115 | def test_get_color_space_handles_null_xcolor_space(image_cms_profile_mock): | CODE |
| LOW⚡ | tests/test_utils.py | 123 | def test_get_color_space_missing_imagecms_returns_none(): | CODE |
| LOW⚡ | tests/test_utils.py | 129 | def test_ensure_srgb_handles_invalid_icc(): | CODE |
| LOW⚡ | tests/test_utils.py | 136 | def test_ensure_srgb_missing_imagecms_runtimeerror(): | CODE |
| LOW⚡ | tests/test_utils.py | 143 | def test_ensure_srgb_keeps_existing_srgb_profile(): | CODE |
| LOW⚡ | tests/test_utils.py | 152 | def test_ensure_srgb_returns_none_for_unsupported_color_space(color_space): | CODE |
| LOW⚡ | tests/test_utils.py | 160 | 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_url_composer.py | 47 | def test_fails_when_no_config_or_key(self, load_mock, mock_stdout): | CODE |
| LOW | tests/test_url_composer.py | 76 | def test_get_thumbor_params_with_crop(self): | CODE |
| LOW | tests/test_url_composer.py | 96 | def test_get_thumbor_params_full_adaptive_fitin_false(self): | CODE |
| LOW | tests/test_url_composer.py | 118 | def test_get_thumbor_params_full_adaptive_fitin(self): | CODE |
| LOW | tests/test_url_composer.py | 137 | def test_get_thumbor_params_adaptive_fitin(self): | CODE |
| LOW | tests/test_url_composer.py | 156 | def test_get_thumbor_params_full_fitin(self): | CODE |
| LOW | tests/test_url_composer.py | 175 | def test_get_thumbor_params_fitin(self): | CODE |
| LOW | tests/test_url_composer.py | 214 | def test_get_options_from_sys(self): | CODE |
| LOW | tests/test_url_composer.py | 235 | 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 | 26 | def test_get_doctor_output_no_config(capsys, doctor_output_no_config): | CODE |
| LOW | tests/test_importer.py | 40 | def test_import_item_should_be_proper_item(self): | CODE |
| LOW | tests/test_importer.py | 69 | def test_single_item_should_equal_file_storage(): | CODE |
| LOW | tests/test_importer.py | 79 | def test_multiple_items_can_be_imported(): | CODE |
| LOW | tests/test_threadpool.py | 22 | async def test_can_get_threadpool_instance(): | CODE |
| LOW | tests/test_threadpool.py | 37 | async def test_can_run_task_in_foreground(): | CODE |
| LOW | tests/test_threadpool.py | 49 | async def test_can_run_task_in_foreground_and_exception_happens(): | CODE |
| LOW | tests/test_threadpool.py | 62 | async def test_queueing_task_when_no_pool_runs_sync(): | CODE |
| LOW | tests/test_threadpool.py | 74 | async def test_queueing_task_when_no_pool_runs_sync_and_exception_happens(): | CODE |
| LOW | tests/test_threadpool.py | 100 | async def test_can_run_async_with_queue(): | CODE |
| LOW⚡ | tests/test_server.py | 33 | def test_can_get_value_as_integer(self): | CODE |
| LOW⚡ | tests/test_server.py | 39 | def test_can_get_config_from_path(self): | CODE |
| LOW⚡ | tests/test_server.py | 47 | def test_can_get_config_with_env_enabled(self): | CODE |
| LOW | tests/test_server.py | 58 | def test_can_configure_log_from_config(self, basic_config_mock): | CODE |
| LOW⚡ | tests/test_server.py | 71 | def test_can_configure_log_from_dict_config(self, dict_config_mock): | CODE |
| LOW⚡ | tests/test_server.py | 81 | def test_can_import_default_modules(self): | CODE |
| LOW⚡ | tests/test_server.py | 88 | def test_can_import_with_custom_error_handler_class(self): | CODE |
| LOW | tests/test_server.py | 101 | def test_validate_config_security_key(self): | CODE |
| LOW | tests/test_server.py | 112 | def test_validate_config_security_key_from_config(self): | CODE |
| LOW | tests/test_server.py | 120 | def test_validate_config_warns_about_legacy_allowed_sources( | CODE |
| LOW | tests/test_server.py | 134 | def test_validate_gifsicle_path(self, which_mock): | CODE |
| LOW | tests/test_server.py | 144 | def test_validate_null_gifsicle_path(self, which_mock): | CODE |
| LOW | tests/test_server.py | 180 | def test_can_run_server_with_default_params(self, server_mock): | CODE |
| LOW | tests/test_server.py | 196 | def test_can_run_server_with_multiple_processes(self, server_mock): | CODE |
| LOW | tests/test_server.py | 212 | def test_can_run_server_with_fd(self, socket_mock, server_mock): | CODE |
| LOW | tests/test_server.py | 229 | def test_can_run_server_with_fd_non_blocking( | CODE |
| LOW | tests/test_server.py | 253 | def test_can_run_server_with_unix_socket( | CODE |
| LOW | tests/test_server.py | 274 | def test_run_server_returns_server(self, server_mock): | CODE |
| 483 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| CRITICAL | thumbor/transformer.py | 171 | self.context.modules.importer.error_handler.handle_error( | CODE |
| CRITICAL | thumbor/handlers/__init__.py | 684 | self.context.modules.importer.error_handler.handle_error( | CODE |
| CRITICAL | thumbor/handlers/__init__.py | 1060 | self.context.modules.importer.error_handler.handle_error( | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | integration_tests/urls_helpers.py | 147 | except Exception as err: # pylint: disable=broad-except | CODE |
| LOW | tests/filters/test_convolution.py | 66 | except Exception as exc: # 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 | 165 | except Exception: # pylint: disable=broad-except | CODE |
| LOW | thumbor/filters/watermark.py | 213 | 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 | 203 | except Exception as error: # pylint: disable=broad-except | STRING |
| LOW | thumbor/engines/__init__.py | 275 | except Exception as error: | STRING |
| LOW | thumbor/engines/__init__.py | 332 | except Exception as error: | STRING |
| LOW | thumbor/engines/pil.py | 144 | except Exception: | CODE |
| MEDIUM | thumbor/engines/pil.py | 103 | def create_image(self, buffer): | CODE |
| LOW | thumbor/detectors/feature_detector/__init__.py | 33 | except Exception as error: | CODE |
| LOW | thumbor/handlers/__init__.py | 146 | except Exception as error: | CODE |
| LOW | thumbor/handlers/__init__.py | 253 | except Exception as error: | CODE |
| LOW | thumbor/handlers/__init__.py | 283 | except Exception as error: | CODE |
| LOW | thumbor/handlers/__init__.py | 679 | except Exception as error: # pylint: disable=broad-except | CODE |
| LOW | thumbor/handlers/__init__.py | 999 | except Exception as error: | 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 | 25 | # Check if the image uploaded is a multipart/form-data | COMMENT |
| LOW | thumbor/handlers/upload.py | 38 | # Check if the image uploaded is valid | COMMENT |
| LOW | thumbor/handlers/__init__.py | 1081 | # Check if image is valid | COMMENT |
| LOW | thumbor/handlers/imaging.py | 34 | # Check if an image with an uuid exists in storage | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | docs/conf.py | 1 | # -*- coding: utf-8 -*- | COMMENT |
| LOW | docs/conf.py | 61 | COMMENT | |
| LOW | docs/conf.py | 81 | # non-false value, then it is used: | COMMENT |
| LOW | docs/conf.py | 101 | # If true, sectionauthor and moduleauthor directives will be shown in the | COMMENT |
| LOW | docs/conf.py | 121 | 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 |
| LOW | .github/workflows/codeql-analysis.yml | 41 | uses: actions/checkout@v7 | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | thumbor/importer.py | 148 | CODE | |
| LOW | thumbor/url_composer.py | 169 | CODE | |
| LOW | thumbor/transformer.py | 114 | CODE | |
| LOW | thumbor/filters/watermark.py | 57 | CODE | |
| LOW | thumbor/filters/redeye.py | 35 | CODE | |
| LOW | thumbor/engines/__init__.py | 107 | CODE | |
| LOW | thumbor/engines/__init__.py | 292 | CODE | |
| LOW | thumbor/engines/pil.py | 245 | CODE | |
| LOW | thumbor/loaders/http_loader.py | 82 | CODE | |
| LOW | thumbor/loaders/http_loader.py | 137 | CODE | |
| LOW | thumbor/handlers/__init__.py | 307 | CODE | |
| LOW | thumbor/handlers/__init__.py | 496 | CODE | |
| LOW | thumbor/handlers/__init__.py | 573 | CODE | |
| LOW | thumbor/handlers/__init__.py | 614 | 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 | 34 | CODE | |
| LOW | tests/loaders/test_file_loader_http_fallback.py | 45 | CODE | |
| LOW | thumbor/context.py | 228 | 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 | 109 | 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 | 172 | Don't forget to copy this log and add it to the description. | CODE |
| LOW | thumbor/doctor.py | 466 | 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 | 296 | # Can't simply set options['progressive'] to the value | COMMENT |