The lektor static file content management system
This report summarizes scanner evidence for lektor/lektor, a Python project with 3,950 GitHub stars. SynthScan examined 47,319 lines of code across 369 source files, recording 394 pattern matches distributed across 16 syntactic categories. The legacy adjusted pattern signal is 11.7 (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? 288 finding(s).
Are imports declared and package/API names verified? 58 finding(s).
Are wrappers, guards, and repeated structures necessary? 292 finding(s).
Which changes need focused human review? 394 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 394 distinct pattern matches across 16 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 | frontend/js/widgets.tsx | 119 | export function getWidgetComponentWithFallback( | CODE |
| LOW | frontend/js/shortcut-keys.ts | 132 | export function installShortcutKeyListener(keymap: ShortcutKeyMap) { | CODE |
| LOW | frontend/js/shortcut-keys.ts | 160 | export function deserializeShortcutKeyMap(obj: unknown): ShortcutKeyMap { | CODE |
| LOW | frontend/js/widgets/SingleLineTextInputWidget.tsx | 7 | export function SingleLineTextInputWidget( | CODE |
| LOW | tests/test_videotools.py | 51 | def test_get_ffmpeg_quality_bad_range(in_quality): | CODE |
| LOW | tests/test_videotools.py | 64 | def test_dimensions_aspect_ratio(dim, ar): | CODE |
| LOW | tests/test_videotools.py | 76 | def test_dimensions_fit_within(source, target, upscale, rescale): | CODE |
| LOW | tests/test_videotools.py | 127 | def test_dimensions_invalid_resize(): | CODE |
| LOW | tests/test_reporter.py | 24 | def test_describe_build_func_describes_partial(): | CODE |
| LOW | tests/test_reporter.py | 37 | def test_describe_build_func_deals_with_garbage(): | CODE |
| LOW | tests/test_utils.py | 221 | def test_url_builder_dot_handling(): | CODE |
| LOW | tests/test_utils.py | 268 | def test_make_relative_url_relative_source_absolute_target(): | CODE |
| LOW | tests/test_utils.py | 286 | def test_atomic_open_exception(tmp_path): | CODE |
| LOW | tests/test_utils.py | 309 | def test_atomic_open_respects_umask(tmp_path, umask): | CODE |
| LOW | tests/test_utils.py | 317 | def test_atomic_open_raises_on_bad_mode(tmp_path, mode): | CODE |
| LOW | tests/test_utils.py | 334 | def test_create_temp_respects_umask(tmp_path, mode, umask): | CODE |
| LOW | tests/test_utils.py | 407 | def test_deprecated_classmethod(): | CODE |
| LOW | tests/test_utils.py | 424 | def test_deprecated_raises_type_error(): | CODE |
| LOW | tests/test_utils.py | 429 | def test_deprecated_stacklevel(): | CODE |
| LOW | tests/test_utils.py | 457 | def test_untrusted_to_os_path(db_path, expected): | CODE |
| LOW | tests/test_utils.py | 462 | def test_format_lat_long_keeps_sign_below_one_degree(): | CODE |
| LOW | tests/test_issue_410.py | 41 | def test_build_childless_page(scratch_builder): | CODE |
| LOW | tests/test_pagination.py | 46 | def test_unpaginated_children(pad): | CODE |
| LOW | tests/test_pagination.py | 82 | def test_pagination_attributes(pad): | CODE |
| LOW | tests/test_pagination.py | 103 | def test_url_matching_for_pagination(pad): | CODE |
| LOW | tests/test_pagination.py | 125 | def test_pagination_url_paths(pad): | CODE |
| LOW | tests/test_pagination.py | 141 | def test_unpaginated_children_other_alt(pad): | CODE |
| LOW | tests/test_pagination.py | 177 | def test_url_matching_for_alt_pagination(pad): | CODE |
| LOW | tests/test_pagination.py | 190 | def test_pagination_items_filter(pad): | CODE |
| LOW | tests/test_pagination.py | 211 | def test_virtual_path_behavior(pad): | CODE |
| LOW | tests/conftest.py | 261 | def child_sources_test_project_builder(tmp_path, data_path, save_sys_path): | CODE |
| LOW | tests/test_unicode.py | 22 | def test_unicode_project_folder(pad, builder): | CODE |
| LOW | tests/test_unicode.py | 28 | def test_unicode_attachment_filename(pad, builder): | CODE |
| LOW | tests/test_packages.py | 153 | def test_update_cache_installs_requirements( | CODE |
| LOW | tests/test_packages.py | 165 | def test_update_cache_skips_install_if_up_to_date( | CODE |
| LOW | tests/test_packages.py | 178 | def test_update_cache_removes_package_cache_if_no_requirements(tmp_path: Path) -> None: | CODE |
| LOW | tests/test_packages.py | 186 | def test_load_packages_add_package_cache_to_sys_path(env: Environment) -> None: | CODE |
| LOW | tests/test_packages.py | 197 | def test_load_packages_reinstall_wipes_cache( | CODE |
| LOW | tests/test_assets.py | 60 | def test_asset_source_filename(asset, pad, asset_path): | CODE |
| LOW | tests/test_assets.py | 87 | def test_asset_url_content_path(asset, expected): | CODE |
| LOW | tests/test_assets.py | 117 | def test_asset_children_no_children_if_dir_unreadable(asset): | CODE |
| LOW | tests/test_assets.py | 150 | def test_asset_get_child_from_url_param_deprecated(asset): | CODE |
| LOW | tests/test_assets.py | 202 | def test_directory_merges_subdirectories(asset_root, asset_paths): | CODE |
| LOW | tests/test_assets.py | 214 | def test_directory_file_shadows_directory(asset_root, asset_paths): | CODE |
| LOW | tests/test_assets.py | 228 | def test_directory_directory_conflicts_with_file(asset_root, asset_paths): | CODE |
| LOW | tests/test_tree.py | 89 | def test_tree_item_can_be_deleted(tree, path, expect): | CODE |
| LOW | tests/test_tree.py | 103 | def test_tree_item_is_visible(tree, path, expect): | CODE |
| LOW | tests/test_tree.py | 116 | def test_tree_item_is_attachment(tree, path, is_attachment, attachment_type): | CODE |
| LOW | tests/test_tree.py | 130 | def test_tree_item_can_have_children(tree, path, expect): | CODE |
| LOW | tests/test_tree.py | 164 | def test_tree_implied_child_datamodel(tree, path, expect): | CODE |
| LOW | tests/test_tree.py | 215 | def test_tree_item_get_children(tree, path, offset, limit, order_by, expect): | CODE |
| LOW | tests/test_tree.py | 264 | def test_tree_item_iter_subpages(tree, path, order_by, expect): | CODE |
| LOW | tests/test_tree.py | 284 | def test_tree_item_iter_attachments(tree, path, expect): | CODE |
| LOW | tests/test_tree.py | 297 | def test_tree_item_get_order_by(tree, path, order_by, expect): | CODE |
| LOW | tests/test_tree.py | 359 | def test_alt_is_primary_overlay(tree, alt, expect): | CODE |
| LOW | tests/test_quickstart.py | 29 | def test_project_html_escaping(tmp_path, with_blog, project_name, escaped_name): | CODE |
| LOW⚡ | tests/test_quickstart.py | 82 | def test_default_author_from_pwd(mocker): | CODE |
| LOW⚡ | tests/test_quickstart.py | 90 | def test_default_author_from_username(mocker): | CODE |
| LOW⚡ | tests/test_quickstart.py | 98 | def test_default_author_email(git_config_file): | CODE |
| LOW | tests/test_quickstart.py | 111 | def test_default_author_email_no_default(monkeypatch): | CODE |
| 183 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | lektor/db.py | 2193 | except KeyError: | CODE |
| HIGH | lektor/db.py | 126 | except (ValueError, TypeError, OverflowError): | CODE |
| HIGH | lektor/db.py | 131 | except (ValueError, TypeError, OverflowError): | CODE |
| HIGH | lektor/db.py | 727 | except ValueError: | CODE |
| HIGH | lektor/databags.py | 42 | except OSError: | CODE |
| HIGH | lektor/devcli.py | 15 | except ImportError: | CODE |
| HIGH | lektor/packages.py | 223 | except OSError: | CODE |
| HIGH | lektor/builder.py | 728 | except OSError: | CODE |
| HIGH | lektor/builder.py | 1114 | except OSError: | CODE |
| HIGH | lektor/builder.py | 1152 | except OSError: | CODE |
| HIGH | lektor/builder.py | 995 | except OSError: | CODE |
| HIGH | lektor/utils.py | 62 | except LookupError: | CODE |
| HIGH | lektor/utils.py | 777 | except ValueError: | CODE |
| HIGH | lektor/buildfailures.py | 70 | except OSError: | CODE |
| HIGH | lektor/editor.py | 359 | except OSError: | CODE |
| HIGH | lektor/editor.py | 311 | except OSError: # noqa: PERF203 | CODE |
| HIGH | lektor/editor.py | 329 | except OSError: # noqa: PERF203 | CODE |
| HIGH | lektor/videotools.py | 262 | except (KeyError, TypeError, ValueError): | CODE |
| HIGH | lektor/quickstart.py | 136 | except KeyError: | CODE |
| HIGH | lektor/quickstart.py | 125 | except OSError: | CODE |
| HIGH | lektor/environment/__init__.py | 212 | except OSError: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | tests/conftest.py | 281 | reporter.push() | CODE |
| HIGH | tests/test_prev_next_sibling.py | 55 | reporter.push() | CODE |
| HIGH | lektor/reporter.py | 41 | _reporter_stack.push(self) | CODE |
| HIGH | lektor/reporter.py | 48 | self.push() | CODE |
| HIGH | lektor/builder.py | 960 | ctx.push() | CODE |
| HIGH | lektor/context.py | 136 | _ctx_stack.push(self) | CODE |
| HIGH | lektor/context.py | 143 | self.push() | CODE |
| HIGH⚡ | lektor/types/base.py | 10 | def get_undefined_info(undefined): | CODE |
| HIGH⚡ | lektor/types/base.py | 11 | if isinstance(undefined, Undefined): | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_pluginsystem.py | 3 | CODE | |
| LOW | tests/test_watcher.py | 1 | CODE | |
| LOW | tests/imagetools/test_exif.py | 1 | CODE | |
| LOW | tests/imagetools/test_thumbnail.py | 1 | CODE | |
| LOW | tests/imagetools/test_image_info.py | 1 | CODE | |
| LOW | tests/admin/test_serve.py | 1 | CODE | |
| LOW | lektor/devserver.py | 1 | CODE | |
| LOW | lektor/db.py | 2 | CODE | |
| LOW | lektor/cli_utils.py | 2 | CODE | |
| LOW | lektor/compat.py | 1 | CODE | |
| LOW | lektor/packages.py | 1 | CODE | |
| LOW | lektor/pluginsystem.py | 1 | CODE | |
| LOW | lektor/builder.py | 1 | CODE | |
| LOW | lektor/utils.py | 1 | CODE | |
| LOW | lektor/assets.py | 1 | CODE | |
| LOW | lektor/watcher.py | 1 | CODE | |
| LOW | lektor/publisher.py | 1 | CODE | |
| LOW | lektor/sourceobj.py | 1 | CODE | |
| LOW | lektor/project.py | 1 | CODE | |
| LOW | lektor/imagetools/image_info.py | 3 | CODE | |
| LOW | lektor/imagetools/thumbnail.py | 3 | CODE | |
| LOW | lektor/imagetools/__init__.py | 1 | CODE | |
| LOW | lektor/imagetools/__init__.py | 6 | CODE | |
| LOW | lektor/imagetools/__init__.py | 8 | CODE | |
| LOW | lektor/imagetools/__init__.py | 9 | CODE | |
| LOW | lektor/imagetools/__init__.py | 10 | CODE | |
| LOW | lektor/imagetools/__init__.py | 11 | CODE | |
| LOW | lektor/imagetools/exif.py | 3 | CODE | |
| LOW | lektor/imagetools/_compat.py | 3 | CODE | |
| LOW | lektor/types/__init__.py | 1 | CODE | |
| LOW | lektor/types/__init__.py | 2 | CODE | |
| LOW | lektor/types/__init__.py | 3 | CODE | |
| LOW | lektor/types/__init__.py | 4 | CODE | |
| LOW | lektor/markdown/controller.py | 23 | CODE | |
| LOW | lektor/markdown/mistune2.py | 3 | CODE | |
| LOW | lektor/markdown/__init__.py | 22 | CODE | |
| LOW | lektor/admin/webui.py | 1 | CODE | |
| LOW | lektor/admin/__init__.py | 1 | CODE | |
| LOW | lektor/admin/context.py | 1 | CODE | |
| LOW | lektor/admin/modules/serve.py | 1 | CODE | |
| LOW | lektor/admin/modules/livereload.py | 1 | CODE | |
| LOW | lektor/environment/__init__.py | 1 | CODE | |
| LOW | lektor/environment/__init__.py | 21 | CODE | |
| LOW | lektor/environment/__init__.py | 22 | CODE | |
| LOW | lektor/environment/__init__.py | 23 | CODE | |
| LOW | lektor/environment/__init__.py | 24 | CODE | |
| LOW | lektor/environment/__init__.py | 25 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_buildfailures.py | 33 | except Exception: | CODE |
| LOW⚡ | tests/test_buildfailures.py | 56 | except Exception: | CODE |
| LOW | tests/test_buildfailures.py | 88 | except Exception: | CODE |
| MEDIUM | tests/test_buildfailures.py | 53 | def test_failure_controller(failure_controller): | CODE |
| LOW | tests/admin/test_serve.py | 274 | except Exception: | CODE |
| LOW | lektor/devserver.py | 72 | except Exception: | CODE |
| LOW | lektor/datamodel.py | 329 | except Exception: | CODE |
| LOW | lektor/datamodel.py | 349 | except Exception as exc: | CODE |
| LOW | lektor/utils.py | 631 | except Exception: | CODE |
| LOW | lektor/publisher.py | 382 | except Exception as e: | CODE |
| LOW | lektor/publisher.py | 395 | except Exception as e: | CODE |
| LOW | lektor/publisher.py | 406 | except Exception as e: | CODE |
| LOW⚡ | lektor/publisher.py | 484 | except Exception as e: | CODE |
| LOW⚡ | lektor/publisher.py | 488 | except Exception as e: | CODE |
| LOW⚡ | lektor/publisher.py | 496 | except Exception as e: | CODE |
| LOW⚡ | lektor/publisher.py | 504 | except Exception as e: | CODE |
| MEDIUM | lektor/publisher.py | 477 | def rename_file(self, src, dst): | CODE |
| LOW | lektor/types/primitives.py | 110 | except Exception: | CODE |
| LOW | lektor/types/multi.py | 87 | except Exception: | CODE |
| LOW⚡ | lektor/types/base.py | 14 | except Exception as e: | CODE |
| LOW | lektor/environment/__init__.py | 114 | except Exception as exc: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_tree.py | 18 | CODE | |
| LOW | lektor/db.py | 598 | CODE | |
| LOW | lektor/db.py | 1134 | CODE | |
| LOW | lektor/db.py | 1302 | CODE | |
| LOW | lektor/db.py | 1358 | CODE | |
| LOW | lektor/db.py | 1455 | CODE | |
| LOW | lektor/db.py | 1493 | CODE | |
| LOW | lektor/reporter.py | 342 | CODE | |
| LOW | lektor/builder.py | 546 | CODE | |
| LOW | lektor/builder.py | 1177 | CODE | |
| LOW | lektor/builder.py | 1285 | CODE | |
| LOW | lektor/metaformat.py | 18 | CODE | |
| LOW | lektor/cli.py | 94 | CODE | |
| LOW | lektor/utils.py | 239 | CODE | |
| LOW | lektor/utils.py | 366 | CODE | |
| LOW | lektor/assets.py | 133 | CODE | |
| LOW | lektor/publisher.py | 549 | CODE | |
| LOW | lektor/sourceobj.py | 106 | CODE | |
| LOW | lektor/quickstart.py | 113 | CODE | |
| LOW | lektor/types/multi.py | 78 | CODE | |
| LOW | lektor/types/flow.py | 17 | CODE | |
| LOW | lektor/environment/__init__.py | 162 | CODE | |
| LOW | lektor/environment/__init__.py | 196 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | tests/test_pluginsystem.py | 195 | # Create a plugin with no associated distribution. | COMMENT |
| MEDIUM⚡ | tests/test_buildfailures.py | 76 | # Create a directory in the location that FailureController wants | COMMENT |
| MEDIUM | lektor/builder.py | 678 | """This class represents a build artifact.""" | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | frontend/js/widgets/MultiLineTextInputWidget.tsx | 51 | * @return True if we are on a touch device | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | lektor/db.py | 1076 | # child_config.order_by, but for now, just return None. | COMMENT |
| LOW | lektor/db.py | 1549 | # The alternative system is not configured, just return | COMMENT |
| LOW | lektor/db.py | 1743 | # not allowed but one was passed, we just return `None`. | COMMENT |
| MEDIUM | lektor/publisher.py | 102 | """A wrapper around subprocess.Popen to facilitate streaming output via generator. | STRING |
| LOW | lektor/imagetools/thumbnail.py | 450 | # resize anything, we just return it. This is not ideal but it's | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tox.ini | 41 | # WARNING: Skipping page https://pypi.org/simple/coverage/ because the GET request | COMMENT |
| LOW | tests/test_themes.py | 101 | # only exist in themes-project assets will be loaded from there | COMMENT |
| LOW | lektor/db.py | 2061 | """ | COMMENT |
| LOW | lektor/build_programs.py | 201 | child_sources = [] | COMMENT |
| LOW | lektor/imagetools/thumbnail.py | 281 | # XXX: The old imagemagick code (which ran `convert` with `-strip -colorspace sRGB`) | COMMENT |
| LOW | lektor/imagetools/thumbnail.py | 341 | # | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | lektor/compat.py | 16 | __all__ = ["werkzeug_urls_URL"] | CODE |
| LOW | lektor/publisher.py | 714 | def set_ssh_credentials(self, credentials: Mapping[str, str]) -> None: | CODE |
| LOW | lektor/publisher.py | 725 | def set_https_credentials(self, credentials: Mapping[str, str]) -> None: | CODE |
| LOW | lektor/imagetools/__init__.py | 14 | __all__ = [ | CODE |
| LOW | lektor/imagetools/_compat.py | 15 | __all__ = ["ExifTags", "Transpose", "UnidentifiedImageError"] | CODE |
| LOW | lektor/admin/__init__.py | 4 | __all__ = ["WebAdmin"] | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | lektor/datamodel.py | 99 | # Check if we have a cached version | COMMENT |
| LOW | lektor/videotools.py | 84 | # Check if we should rescale at all | COMMENT |
| LOW | lektor/videotools.py | 120 | # Check if we should rescale at all | COMMENT |
| LOW | lektor/videotools.py | 153 | # Check if we should rescale at all | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | tests/test_devcli.py | 116 | def test_function(): | STRING |
| LOW | lektor/db.py | 1516 | def process_data(self, data, datamodel, pad): | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | lektor/builder.py | 358 | # If we do have an already existing artifact, we need to check if | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | tests/test_devcli.py | 39 | email = "jrh@example.org" | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | lektor/datamodel.py | 232 | CODE |