Take potentially dangerous PDFs, office documents, or images and convert them to safe PDFs
This report presents the forensic synthetic code analysis of freedomofpress/dangerzone, a Python project with 5,649 GitHub stars. SynthScan v2.0 examined 26,759 lines of code across 153 source files, recording 348 pattern matches distributed across 16 syntactic categories. The overall adjusted score of 16.5 places this repository in the Moderate AI signal 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 348 distinct pattern matches across 16 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 | dodo.py | 167 | def task_clean_container_runtime(): | CODE |
| LOW | dodo.py | 177 | def task_check_container_runtime(): | CODE |
| LOW | install/linux/build-rpm.py | 39 | def build_insecure_converter_rpm(dist_path): | CODE |
| LOW | tests/test_registry.py | 16 | def test_parse_image_location_no_tag() -> None: | CODE |
| LOW | tests/test_registry.py | 29 | def test_parse_image_location_with_tag() -> None: | CODE |
| LOW | tests/test_registry.py | 41 | def test_parse_image_location_tag_plus_digest() -> None: | CODE |
| LOW | tests/test_registry.py | 62 | def test_parse_invalid_image_location() -> None: | CODE |
| LOW | tests/test_registry.py | 77 | def test_replace_image_digest() -> None: | CODE |
| LOW | tests/test_registry.py | 159 | def test_get_manifest_digest_from_registry(mocker: MockerFixture) -> None: | CODE |
| LOW | tests/conftest.py | 193 | def pytest_collection_modifyitems( | CODE |
| LOW | tests/test_startup.py | 51 | def test_startup_fail_allowed(mock_startup_spy: StartupSpy) -> None: | CODE |
| LOW | tests/test_startup.py | 67 | def test_startup_fail_not_allowed(mock_startup_spy: StartupSpy) -> None: | CODE |
| LOW | tests/test_startup.py | 99 | def test_machine_stop_others_task( | CODE |
| LOW | tests/test_startup.py | 163 | def test_startup_skips_podman_tasks_if_custom_runtime_is_specified( | CODE |
| LOW | tests/test_util.py | 16 | def test_replace_control_chars(uncommon_text: str, sanitized_text: str) -> None: | CODE |
| LOW⚡ | tests/test_signatures.py | 77 | def test_load_valid_signatures(file: Path) -> None: | CODE |
| LOW⚡ | tests/test_signatures.py | 84 | def test_load_invalid_signatures(file: Path) -> None: | CODE |
| LOW⚡ | tests/test_signatures.py | 90 | def test_load_tampered_signatures(file: Path) -> None: | CODE |
| LOW⚡ | tests/test_signatures.py | 95 | def test_get_log_index_from_signatures() -> None: | CODE |
| LOW⚡ | tests/test_signatures.py | 100 | def test_get_log_index_from_signatures_empty() -> None: | CODE |
| LOW⚡ | tests/test_signatures.py | 105 | def test_get_log_index_from_malformed_signatures() -> None: | CODE |
| LOW⚡ | tests/test_signatures.py | 110 | def test_get_log_index_from_missing_log_index() -> None: | CODE |
| LOW⚡ | tests/test_signatures.py | 115 | def test_upgrade_container_without_signatures(mocker: Any) -> None: | CODE |
| LOW | tests/test_signatures.py | 127 | def test_upgrade_container_lower_log_index(mocker: Any) -> None: | CODE |
| LOW | tests/test_signatures.py | 153 | def test_get_remote_signatures_error(fp: FakeProcess, mocker: Any) -> None: | CODE |
| LOW | tests/test_signatures.py | 164 | def test_get_remote_signatures_empty(fp: FakeProcess, mocker: Any) -> None: | CODE |
| LOW | tests/test_signatures.py | 175 | def test_get_remote_signatures_cosign_error(mocker: Any, fp: FakeProcess) -> None: | CODE |
| LOW | tests/test_signatures.py | 188 | def test_store_signatures_with_different_digests( | CODE |
| LOW | tests/test_signatures.py | 226 | def test_stores_signatures_updates_last_log_index( | CODE |
| LOW | tests/test_signatures.py | 258 | def test_get_remote_digest_and_logindex_when_remote_image_available( | CODE |
| LOW | tests/test_signatures.py | 296 | def test_verify_signature_tampered(tampered_signature: dict[str, Any]) -> None: | CODE |
| LOW | tests/test_signatures.py | 307 | def test_verify_signatures_empty_list() -> None: | CODE |
| LOW | tests/test_container_utils.py | 30 | def test_create_containers_conf(mocker: MockerFixture, tmp_path: pathlib.Path) -> None: | CODE |
| LOW | tests/test_container_utils.py | 72 | def test_init_podman_command_custom_runtime(mocker: MockerFixture) -> None: | CODE |
| LOW | tests/test_container_utils.py | 113 | def test_list_containers_empty(mocker: MockerFixture) -> None: | CODE |
| LOW | tests/test_container_utils.py | 140 | def test_kill_container_timeout(mocker: MockerFixture, caplog: Any) -> None: | CODE |
| LOW | tests/test_container_utils.py | 155 | def test_kill_container_exception(mocker: MockerFixture, caplog: Any) -> None: | CODE |
| LOW | tests/test_container_utils.py | 179 | def test_clear_old_images_deletes_digests(mocker: MockerFixture) -> None: | CODE |
| LOW | tests/test_releases.py | 35 | def test_new_dz_versions_are_skipped_on_linux( | CODE |
| LOW | tests/test_releases.py | 86 | def test_new_sandbox_is_checked_on_all_platforms( | CODE |
| LOW | tests/test_releases.py | 142 | def test_updates_are_postponed_if_needed( | CODE |
| LOW | tests/test_releases.py | 178 | def test_updates_proceed_after_cooldown_expires( | CODE |
| LOW | tests/test_releases.py | 230 | def test_cached_github_release_is_returned_on_subsequent_checks( | CODE |
| LOW | tests/test_releases.py | 264 | def test_error_report_is_returned_on_exception( | CODE |
| LOW | tests/test_updater_installer.py | 14 | def test_install_raise_if_local_image_cant_be_installed( | CODE |
| LOW | tests/test_updater_installer.py | 28 | def test_user_installs_dangerzone_for_the_first_time(mocker: MockerFixture) -> None: | CODE |
| LOW | tests/test_updater_installer.py | 46 | def test_upgrades_disabled_detect_wrong_container_upgrade( | CODE |
| LOW | tests/test_updater_installer.py | 89 | def test_building_dangerzone_from_source_first_time(mocker: MockerFixture) -> None: | CODE |
| LOW | tests/test_updater_installer.py | 107 | def test_building_dangerzone_from_source_nth_time(mocker: MockerFixture) -> None: | CODE |
| LOW | tests/test_updater_installer.py | 129 | def test_enable_updates_after_some_time(mocker: MockerFixture) -> None: | CODE |
| LOW | tests/test_updater_installer.py | 176 | def test_enable_updates_no_new_image_available(mocker: MockerFixture) -> None: | CODE |
| LOW | tests/test_updater_installer.py | 199 | def test_downgrade_dangerzone_application(mocker: MockerFixture) -> None: | CODE |
| LOW | tests/test_updater_installer.py | 243 | def test_podman_state_reset_updates_enabled(mocker: MockerFixture) -> None: | CODE |
| LOW | tests/test_updater_installer.py | 262 | def test_podman_state_reset_updates_disabled(mocker: MockerFixture) -> None: | CODE |
| LOW | tests/test_updater_installer.py | 280 | def test_upgrade_to_latest_container_via_cli(mocker: MockerFixture) -> None: | CODE |
| LOW | tests/test_updater_installer.py | 303 | def test_install_new_dangerzone_version_updates_enabled(mocker: MockerFixture) -> None: | CODE |
| LOW | tests/test_updater_installer.py | 326 | def test_airgapped_installation_container_tarball(mocker: MockerFixture) -> None: | CODE |
| LOW | tests/test_updater_installer.py | 348 | def test_no_bundled_container_tar_first_install(mocker: MockerFixture) -> None: | CODE |
| LOW | tests/test_updater_installer.py | 372 | def test_no_bundled_container_tar_updates_disabled(mocker: MockerFixture) -> None: | CODE |
| LOW | tests/test_cli.py | 196 | def test_other_machine_running_error( | CODE |
| 158 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | dev_scripts/env.py | 68 | RUN touch /var/mail/ubuntu && chown ubuntu /var/mail/ubuntu && userdel -r ubuntu | CODE |
| HIGH | dev_scripts/env.py | 95 | && apt-get install -y passt || echo "Skipping installation of passt package" \ | CODE |
| HIGH | dev_scripts/env.py | 123 | RUN dnf install -y mupdf thunar && dnf clean all | CODE |
| HIGH | dev_scripts/env.py | 163 | RUN cd /home/user/dangerzone && poetry --no-ansi install | CODE |
| HIGH | dev_scripts/env.py | 174 | RUN dnf install -y mupdf thunar && dnf clean all | CODE |
| HIGH | dev_scripts/env.py | 194 | RUN apt-get update && apt-get install -y gpg ca-certificates | CODE |
| HIGH | dev_scripts/env.py | 201 | RUN . /etc/os-release && echo "deb [signed-by=/etc/apt/keyrings/fpf-apt-tools-archive-keyring.gpg] \ | CODE |
| HIGH | dev_scripts/env.py | 670 | install_cmd = "apt-get update && apt-get install -y" | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | install/windows/build-wxs.py | 260 | # Create the directory structure for the installed product | COMMENT |
| MEDIUM | install/windows/build-wxs.py | 263 | # Create a component group for application components | COMMENT |
| MEDIUM | tests/test_registry.py | 144 | # Create a sample manifest content | COMMENT |
| MEDIUM | tests/test_signatures.py | 239 | # Create an existing last_log_index file with a lower value | COMMENT |
| MEDIUM | dev_scripts/env.py | 133 | # Create a non-root user to run Dangerzone | COMMENT |
| MEDIUM | dev_scripts/env.py | 221 | # Create a non-root user to run Dangerzone | COMMENT |
| MEDIUM | dev_scripts/sign-assets.py | 111 | # Create a file that holds the SHA-256 hashes of the assets. | COMMENT |
| MEDIUM | dev_scripts/qa.py | 540 | # The following class method is available since Python 3.6. For more details, see: | COMMENT |
| MEDIUM | dangerzone/capture_output.py | 7 | # This module provides patching utilities to redirect the standard output and | COMMENT |
| MEDIUM | dangerzone/gui/main_window.py | 331 | # Create the hamburger button, whose main purpose is to inform the user about | COMMENT |
| MEDIUM | dangerzone/gui/main_window.py | 347 | # Create the menu for the hamburger button | COMMENT |
| MEDIUM | dangerzone/gui/run.py | 48 | # Create the Qt app | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | dangerzone/podman/__init__.py | 1 | CODE | |
| LOW | dangerzone/podman/__init__.py | 2 | CODE | |
| LOW | dangerzone/podman/command/__init__.py | 6 | CODE | |
| LOW | dangerzone/podman/command/__init__.py | 7 | CODE | |
| LOW | dangerzone/podman/errors/__init__.py | 32 | CODE | |
| LOW | dangerzone/podman/errors/__init__.py | 32 | CODE | |
| LOW | dangerzone/podman/errors/__init__.py | 32 | CODE | |
| LOW | dangerzone/podman/errors/__init__.py | 32 | CODE | |
| LOW | dangerzone/podman/errors/__init__.py | 32 | CODE | |
| LOW | dangerzone/podman/errors/__init__.py | 32 | CODE | |
| LOW | dangerzone/podman/errors/__init__.py | 32 | CODE | |
| LOW | dangerzone/podman/errors/__init__.py | 32 | CODE | |
| LOW | dangerzone/podman/errors/__init__.py | 32 | CODE | |
| LOW | dangerzone/podman/errors/__init__.py | 32 | CODE | |
| LOW | dangerzone/podman/errors/__init__.py | 32 | CODE | |
| LOW | dangerzone/podman/errors/__init__.py | 32 | CODE | |
| LOW | dangerzone/podman/errors/__init__.py | 68 | CODE | |
| LOW | dangerzone/podman/errors/exceptions.py | 7 | CODE | |
| LOW | dangerzone/podman/errors/exceptions.py | 12 | CODE | |
| LOW | dangerzone/podman/errors/exceptions.py | 13 | CODE | |
| LOW | dangerzone/updater/__init__.py | 5 | CODE | |
| LOW | dangerzone/updater/__init__.py | 5 | CODE | |
| LOW | dangerzone/updater/__init__.py | 6 | CODE | |
| LOW | dangerzone/updater/__init__.py | 9 | CODE | |
| LOW | dangerzone/updater/__init__.py | 9 | CODE | |
| LOW | dangerzone/updater/__init__.py | 9 | CODE | |
| LOW | dangerzone/updater/__init__.py | 14 | CODE | |
| LOW | dangerzone/updater/__init__.py | 15 | CODE | |
| LOW | dangerzone/updater/__init__.py | 15 | CODE | |
| LOW | dangerzone/updater/__init__.py | 15 | CODE | |
| LOW | dangerzone/updater/__init__.py | 16 | CODE | |
| LOW | dangerzone/updater/__init__.py | 16 | CODE | |
| LOW | dangerzone/updater/__init__.py | 16 | CODE | |
| LOW | dangerzone/updater/__init__.py | 16 | CODE | |
| LOW | dangerzone/updater/__init__.py | 16 | CODE | |
| LOW | dangerzone/updater/__init__.py | 16 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | dangerzone/podman/command/cli_runner.py | 198 | Run the specified Podman command. Args: cmd (list[str]): The command to run, as a list of strings. | STRING |
| HIGH | dangerzone/podman/command/cli_runner.py | 229 | Run the command without additional construction. Mostly for internal use. Args: cmd (list[str]): Th | STRING |
| HIGH | dangerzone/podman/command/command.py | 62 | Run the specified Podman command. Args: cmd (list[str]): The command to run, as a list of strings. | STRING |
| HIGH | dangerzone/windows/shellexec.py | 44 | Executes a command using the Windows ShellExecuteEx API. This allows for features like running executables with pri | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | install/linux/debian-vendor-pymupdf.py | 10 | logger = logging.getLogger(__name__) | CODE |
| LOW | dev_scripts/qa.py | 15 | logger = logging.getLogger(__name__) | CODE |
| LOW | dangerzone/startup.py | 21 | logger = logging.getLogger(__name__) | CODE |
| LOW | dangerzone/shutdown.py | 7 | logger = logging.getLogger(__name__) | CODE |
| LOW | dangerzone/__init__.py | 5 | logger = logging.getLogger(__name__) | CODE |
| LOW | dangerzone/document.py | 179 | def set_output_dir(self, path: str) -> None: | CODE |
| LOW | dangerzone/podman/machine.py | 14 | logger = logging.getLogger(__name__) | CODE |
| LOW | dangerzone/podman/cli.py | 14 | logger = logging.getLogger(__name__) | CODE |
| LOW | dangerzone/podman/command/__init__.py | 9 | __all__ = ["GlobalOptions", "PodmanCommand"] | CODE |
| LOW | dangerzone/podman/errors/__init__.py | 14 | __all__ = [ | CODE |
| LOW⚡ | dangerzone/gui/main_window.py | 241 | def set_status_ok(self, message: str) -> None: | CODE |
| LOW⚡ | dangerzone/gui/main_window.py | 248 | def set_status_working(self, message: str) -> None: | CODE |
| LOW⚡ | dangerzone/gui/main_window.py | 255 | def set_status_error(self, message: str) -> None: | CODE |
| LOW⚡ | dangerzone/gui/main_window.py | 1468 | def set_safe_extension_invalid_label(self, string: str) -> None: | CODE |
| LOW | dangerzone/gui/main_window.py | 1492 | def update_ui(self) -> None: | CODE |
| LOW | dangerzone/gui/main_window.py | 1511 | def update_doc_n_labels(self) -> None: | CODE |
| LOW | dangerzone/gui/main_window.py | 1734 | def update_progress(self, error: bool, text: str, percentage: int) -> None: | CODE |
| LOW | dangerzone/gui/main_window.py | 1752 | def update_status_image(self) -> None: | CODE |
| LOW | dangerzone/gui/main_window.py | 223 | def _update_style(self) -> None: | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | dev_scripts/qa.py | 799 | except Exception: # noqa: BLE001 | CODE |
| LOW | dangerzone/container_utils.py | 52 | except Exception as e: | CODE |
| LOW | dangerzone/container_utils.py | 285 | except Exception: | CODE |
| LOW | dangerzone/startup.py | 325 | except Exception as e: # NOQA -- Actually catch all exceptions here | CODE |
| LOW | dangerzone/logic.py | 65 | except Exception: | CODE |
| LOW | dangerzone/settings.py | 124 | except Exception as e: # noqa: BLE001 | CODE |
| LOW | dangerzone/updater/releases.py | 110 | except Exception as e: # noqa: BLE001 | CODE |
| LOW | dangerzone/updater/releases.py | 246 | except Exception as e: # noqa: BLE001 | CODE |
| LOW | dangerzone/updater/cli.py | 72 | except Exception as e: # noqa: BLE001 | CODE |
| LOW | dangerzone/updater/signatures.py | 59 | except Exception: # noqa: BLE001 | CODE |
| LOW | dangerzone/updater/signatures.py | 140 | except Exception as e: # noqa: BLE001 | CODE |
| LOW | dangerzone/gui/logic.py | 138 | except Exception: | CODE |
| LOW | dangerzone/isolation_provider/base.py | 41 | except Exception: | CODE |
| LOW | dangerzone/isolation_provider/base.py | 116 | except Exception as e: # NOQA -- Catch all exceptions here. | CODE |
| LOW | dangerzone/isolation_provider/base.py | 154 | except Exception as e: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | install/windows/build-wxs.py | 7 | CODE | |
| LOW | tests/gui/test_startup.py | 97 | CODE | |
| LOW | tests/gui/test_startup.py | 118 | CODE | |
| LOW | tests/gui/test_startup.py | 139 | CODE | |
| LOW | dangerzone/startup.py | 103 | CODE | |
| LOW | dangerzone/util.py | 45 | CODE | |
| LOW | dangerzone/settings.py | 106 | CODE | |
| LOW | dangerzone/podman/command/cli_runner.py | 130 | CODE | |
| LOW | dangerzone/gui/main_window.py | 1752 | CODE | |
| LOW | dangerzone/gui/startup.py | 121 | CODE | |
| LOW | dangerzone/gui/logic.py | 66 | CODE | |
| LOW | dangerzone/gui/logic.py | 97 | CODE | |
| LOW | dangerzone/isolation_provider/qubes.py | 87 | CODE | |
| LOW | dangerzone/isolation_provider/base.py | 183 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/isolation_provider/test_qubes.py | 21 | COMMENT | |
| LOW | dangerzone/container_utils.py | 101 | # | COMMENT |
| LOW | dangerzone/container_utils.py | 141 | COMMENT | |
| LOW | dangerzone/podman/command/command.py | 141 | ret = self.proc_service.wait(timeout=timeout) | COMMENT |
| LOW | dangerzone/podman/command/command.py | 161 | # Args: | COMMENT |
| LOW | dangerzone/updater/installer.py | 61 | and the released container images if updates are enabled. | COMMENT |
| LOW | dangerzone/updater/installer.py | 81 | # This log index and the corresponding signatures have been verified | COMMENT |
| LOW | dangerzone/updater/installer.py | 141 | elif bundled_log_index == max_log_index: | COMMENT |
| LOW | dangerzone/isolation_provider/container.py | 141 | # | COMMENT |
| LOW | dangerzone/windows/wsl.py | 81 | log.info(f"Attempting to install WSL via '{cmd}'") | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_settings.py | 67 | # Check if settings file was reset to the default | COMMENT |
| LOW⚡ | tests/test_settings.py | 105 | # Check if new setting persisted | COMMENT |
| LOW | dev_scripts/qa.py | 466 | # Check if there have been any changes from the cached section stored in this | COMMENT |
| LOW | dangerzone/isolation_provider/container.py | 158 | # Check if the container no longer exists, either because we successfully killed | COMMENT |
| LOW | dangerzone/isolation_provider/base.py | 393 | # Check if the process completed. | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | .github/workflows/check_repos.yml | 5 | # NOTE: When you change either place, please make sure to keep the two files in | COMMENT |
| MEDIUM | dangerzone/container_utils.py | 354 | # There is a "digest" filter that you can use with | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | .github/workflows/check_repos.yml | 2 | # ==================================================== | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | dangerzone/container_utils.py | 145 | # We cannot simply use the vendored Podman binary in order to start a Podman | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_registry.py | 117 | mock_response_auth.json.return_value = {"token": "dummy_token"} | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | dangerzone/startup.py | 304 | def run_task(self, task: Task) -> None: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | dangerzone/podman/command/machine_manager.py | 39 | CODE |