Self-hosted bookmark manager that is designed be to be minimal, fast, and easy to set up using Docker.
This report summarizes scanner evidence for sissbruecker/linkding, a Python project with 11,290 GitHub stars. SynthScan examined 58,404 lines of code across 336 source files, recording 1254 pattern matches distributed across 12 syntactic categories. The legacy adjusted pattern signal is 19.8 (Elevated 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? 1191 finding(s).
Are imports declared and package/API names verified? 15 finding(s).
Are wrappers, guards, and repeated structures necessary? 1208 finding(s).
Which changes need focused human review? 1254 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 1254 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 | bookmarks/admin.py | 145 | def delete_selected_bookmarks(self, request, queryset: QuerySet): | CODE |
| LOW | bookmarks/admin.py | 160 | def archive_selected_bookmarks(self, request, queryset: QuerySet): | CODE |
| LOW | bookmarks/admin.py | 175 | def unarchive_selected_bookmarks(self, request, queryset: QuerySet): | CODE |
| LOW | bookmarks/queries.py | 139 | def _filter_search_query_legacy( | CODE |
| LOW | bookmarks/queries.py | 322 | def query_archived_bookmark_tags( | CODE |
| LOW | bookmarks/queries.py | 332 | def query_shared_bookmark_tags( | CODE |
| LOW | bookmarks/queries.py | 345 | def query_shared_bookmark_users( | CODE |
| LOW | bookmarks/queries.py | 372 | def get_shared_tags_for_query( | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_bundle_preview.py | 8 | def test_update_preview_on_filter_changes(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_dropdown.py | 20 | def test_click_toggle_opens_and_closes_dropdown(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_dropdown.py | 34 | def test_outside_click_closes_dropdown(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_dropdown.py | 48 | def test_escape_closes_dropdown_and_restores_focus(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_dropdown.py | 67 | def test_focus_out_closes_dropdown(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_dropdown.py | 83 | def test_aria_expanded_attribute(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_settings_general.py | 9 | def test_should_only_enable_public_sharing_if_sharing_is_enabled(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_settings_general.py | 56 | def test_should_not_show_bookmark_description_max_lines_when_display_inline(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_settings_general.py | 68 | def test_should_show_bookmark_description_max_lines_when_display_separate(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_settings_general.py | 80 | def test_should_update_bookmark_description_max_lines_when_changing_display(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_filter_drawer.py | 8 | def test_show_modal_close_modal(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_edit_bookmark_form.py | 29 | def test_should_not_check_for_existing_bookmark(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_edit_bookmark_form.py | 37 | def test_should_not_prefill_title_and_description(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_edit_bookmark_form.py | 50 | def test_enter_url_should_not_prefill_title_and_description(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_edit_bookmark_form.py | 63 | def test_refresh_button_should_be_visible_when_editing(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_collapse_side_panel.py | 23 | def test_side_panel_should_be_visible_by_default(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_collapse_side_panel.py | 33 | def test_side_panel_should_be_hidden_when_collapsed(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_bookmark_page_bulk_edit.py | 36 | def test_active_bookmarks_bulk_select_across(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_bookmark_page_bulk_edit.py | 73 | def test_archived_bookmarks_bulk_select_across(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_bookmark_page_bulk_edit.py | 110 | def test_active_bookmarks_bulk_select_across_respects_query(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_bookmark_page_bulk_edit.py | 147 | def test_archived_bookmarks_bulk_select_across_respects_query(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_bookmark_page_bulk_edit.py | 184 | def test_select_all_toggles_all_checkboxes(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_bookmark_page_bulk_edit.py | 207 | def test_select_all_shows_select_across(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_bookmark_page_bulk_edit.py | 223 | def test_select_across_is_unchecked_when_toggling_all(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_bookmark_page_bulk_edit.py | 244 | def test_select_across_is_unchecked_when_toggling_bookmark(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_bookmark_page_bulk_edit.py | 265 | def test_execute_resets_all_checkboxes(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_bookmark_page_bulk_edit.py | 296 | def test_update_select_across_bookmark_count(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_bookmark_page_bulk_edit.py | 323 | def test_execute_button_is_disabled_when_no_bookmarks_selected(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_bookmark_item.py | 11 | def test_toggle_notes_should_show_hide_notes(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_new_bookmark_form.py | 31 | def test_enter_url_prefills_title_and_description(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_new_bookmark_form.py | 43 | def test_enter_url_does_not_overwrite_modified_title_and_description(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_new_bookmark_form.py | 57 | def test_with_initial_url_prefills_title_and_description(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_new_bookmark_form.py | 74 | def test_with_initial_url_title_description_does_not_overwrite_title_and_description( | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_new_bookmark_form.py | 92 | def test_create_should_check_for_existing_bookmark(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_new_bookmark_form.py | 129 | def test_enter_url_of_existing_bookmark_should_show_notes(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_new_bookmark_form.py | 142 | def test_create_should_preview_auto_tags(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_new_bookmark_form.py | 163 | def test_clear_buttons_only_shown_when_fields_have_content(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_new_bookmark_form.py | 193 | def test_refresh_button_only_shown_for_existing_bookmarks(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_new_bookmark_form.py | 220 | def test_refresh_from_website_button_updates_title_and_description(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_new_bookmark_form.py | 261 | def test_refresh_from_website_button_does_not_modify_fields_if_metadata_is_same( | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_new_bookmark_form.py | 303 | def test_ctrl_enter_submits_form_from_description(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_a11y_navigation_focus.py | 8 | def test_initial_page_load_focus(self): | CODE |
| LOW | bookmarks/tests_e2e/e2e_test_a11y_navigation_focus.py | 30 | def test_page_navigation_focus(self): | CODE |
| LOW | …ks/tests_e2e/e2e_test_bookmark_page_partial_updates.py | 40 | def test_partial_update_respects_query(self): | CODE |
| LOW | …ks/tests_e2e/e2e_test_bookmark_page_partial_updates.py | 52 | def test_partial_update_respects_sort(self): | CODE |
| LOW | …ks/tests_e2e/e2e_test_bookmark_page_partial_updates.py | 66 | def test_partial_update_respects_page(self): | CODE |
| LOW | …ks/tests_e2e/e2e_test_bookmark_page_partial_updates.py | 82 | def test_multiple_partial_updates(self): | CODE |
| LOW | …ks/tests_e2e/e2e_test_bookmark_page_partial_updates.py | 101 | def test_active_bookmarks_partial_update_on_archive(self): | CODE |
| LOW | …ks/tests_e2e/e2e_test_bookmark_page_partial_updates.py | 112 | def test_active_bookmarks_partial_update_on_delete(self): | CODE |
| LOW | …ks/tests_e2e/e2e_test_bookmark_page_partial_updates.py | 124 | def test_active_bookmarks_partial_update_on_mark_as_read(self): | CODE |
| LOW | …ks/tests_e2e/e2e_test_bookmark_page_partial_updates.py | 139 | def test_active_bookmarks_partial_update_on_unshare(self): | CODE |
| LOW | …ks/tests_e2e/e2e_test_bookmark_page_partial_updates.py | 154 | def test_active_bookmarks_partial_update_on_bulk_archive(self): | CODE |
| 1090 more matches not shown… | ||||
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | bookmarks/models.py | 164 | except Exception: | CODE |
| HIGH | bookmarks/utils.py | 122 | except (OverflowError, ValueError, OSError): | CODE |
| HIGH | bookmarks/utils.py | 129 | except (OverflowError, ValueError, OSError): | CODE |
| HIGH | bookmarks/utils.py | 136 | except (OverflowError, ValueError, OSError): | CODE |
| HIGH | bookmarks/views/settings.py | 150 | except requests.exceptions.RequestException: | CODE |
| HIGH | bookmarks/services/website_loader.py | 85 | except Exception: | CODE |
| HIGH | bookmarks/services/website_loader.py | 165 | except http_client.RequestException: | CODE |
| HIGH | bookmarks/services/website_loader.py | 181 | except http_client.RequestException: | CODE |
| HIGH | bookmarks/services/http_client.py | 144 | except ValueError: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | bookmarks/settings/base.py | 286 | # Creating a connection loads the ICU extension into the SQLite | COMMENT |
| MEDIUM⚡ | bookmarks/tests/test_assets_service.py | 611 | # Create a bookmark with an existing latest_snapshot | COMMENT |
| MEDIUM⚡ | bookmarks/tests/test_assets_service.py | 617 | # Create a new snapshot asset that will fail | COMMENT |
| MEDIUM | bookmarks/tests/test_assets_service.py | 152 | # Create a bookmark with a very long URL | COMMENT |
| MEDIUM | bookmarks/tests/test_assets_service.py | 437 | # Create a bookmark with a very long URL | COMMENT |
| MEDIUM | bookmarks/tests/test_assets_service.py | 525 | # Create a bookmark with a very long URL | COMMENT |
| MEDIUM | bookmarks/tests/test_assets_service.py | 635 | # Create a bookmark with an existing latest_snapshot | COMMENT |
| MEDIUM | bookmarks/tests/test_assets_service.py | 654 | # Create a bookmark with multiple snapshots | COMMENT |
| MEDIUM | bookmarks/tests/test_assets_service.py | 714 | # Create a bookmark with multiple snapshots | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | bookmarks/models.py | 123 | except Exception as error: | CODE |
| LOW | bookmarks/models.py | 164 | except Exception: | CODE |
| LOW | bookmarks/models.py | 179 | except Exception as error: | CODE |
| LOW | bookmarks/middlewares.py | 33 | except Exception: | CODE |
| LOW | bookmarks/utils.py | 16 | except Exception as exc: | CODE |
| LOW | bookmarks/management/commands/migrate_tasks.py | 57 | except Exception: | CODE |
| LOW | bookmarks/management/commands/migrate_tasks.py | 71 | except Exception: | CODE |
| LOW | bookmarks/api/routes.py | 121 | except Exception as e: | CODE |
| LOW | bookmarks/api/routes.py | 200 | except Exception as e: | CODE |
| LOW | bookmarks/api/routes.py | 226 | except Exception as e: | CODE |
| LOW | bookmarks/views/health.py | 13 | except Exception: | CODE |
| LOW | bookmarks/views/settings.py | 266 | except Exception: | CODE |
| LOW | bookmarks/views/settings.py | 295 | except Exception: | CODE |
| LOW | bookmarks/services/website_loader.py | 85 | except Exception: | CODE |
| LOW⚡ | bookmarks/services/tasks.py | 34 | except Exception as exc: | CODE |
| LOW⚡ | bookmarks/services/tasks.py | 345 | except Exception as error: | CODE |
| LOW | bookmarks/services/tasks.py | 217 | except Exception as exc: | CODE |
| LOW | bookmarks/services/importer.py | 69 | except Exception: | CODE |
| LOW | bookmarks/services/importer.py | 195 | except Exception: | CODE |
| LOW | bookmarks/services/assets.py | 53 | except Exception as error: | CODE |
| LOW | bookmarks/services/assets.py | 219 | except Exception as e: | CODE |
| LOW | bookmarks/services/bookmarks.py | 237 | except Exception as e: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| CRITICAL | bookmarks/queries.py | 5 | from django.core.exceptions import ValidationError | CODE |
| CRITICAL | bookmarks/tests/test_bookmark_validation.py | 3 | from django.core.exceptions import ValidationError | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | bookmarks/utils.py | 83 | CODE | |
| LOW | bookmarks/queries.py | 60 | CODE | |
| LOW | bookmarks/queries.py | 227 | CODE | |
| LOW | bookmarks/tests_e2e/helpers.py | 32 | CODE | |
| LOW | bookmarks/tests/test_tag_cloud_template.py | 33 | CODE | |
| LOW | bookmarks/management/commands/full_backup.py | 15 | CODE | |
| LOW | bookmarks/views/tags.py | 113 | CODE | |
| LOW | bookmarks/services/assets.py | 92 | CODE | |
| LOW | bookmarks/services/assets.py | 176 | CODE | |
| LOW | bookmarks/services/search_query_parser.py | 353 | CODE | |
| LOW | bookmarks/services/search_query_parser.py | 419 | CODE | |
| LOW | bookmarks/services/search_query_parser.py | 511 | CODE | |
| LOW | bookmarks/services/search_query_parser.py | 59 | CODE | |
| LOW | bookmarks/services/search_query_parser.py | 126 | CODE | |
| LOW | bookmarks/services/search_query_parser.py | 303 | CODE | |
| LOW | bookmarks/services/favicon_loader.py | 60 | CODE | |
| LOW | bookmarks/services/bookmarks.py | 226 | CODE | |
| LOW | bookmarks/services/preview_image_loader.py | 26 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | bookmarks/models.py | 18 | logger = logging.getLogger(__name__) | CODE |
| LOW | bookmarks/middlewares.py | 12 | logger = logging.getLogger(__name__) | CODE |
| LOW | bookmarks/management/commands/enable_wal.py | 7 | logger = logging.getLogger(__name__) | CODE |
| LOW | …kmarks/management/commands/create_initial_superuser.py | 7 | logger = logging.getLogger(__name__) | CODE |
| LOW | bookmarks/management/commands/generate_secret_key.py | 7 | logger = logging.getLogger(__name__) | CODE |
| LOW | bookmarks/api/routes.py | 31 | logger = logging.getLogger(__name__) | CODE |
| LOW | bookmarks/views/settings.py | 28 | logger = logging.getLogger(__name__) | CODE |
| LOW | bookmarks/services/tags.py | 10 | logger = logging.getLogger(__name__) | CODE |
| LOW | bookmarks/services/singlefile.py | 14 | logger = logging.getLogger(__name__) | CODE |
| LOW | bookmarks/services/website_loader.py | 12 | logger = logging.getLogger(__name__) | CODE |
| LOW | bookmarks/services/tasks.py | 17 | logger = logging.getLogger(__name__) | CODE |
| LOW | bookmarks/services/importer.py | 12 | logger = logging.getLogger(__name__) | CODE |
| LOW | bookmarks/services/http_client.py | 54 | logger = logging.getLogger(__name__) | CODE |
| LOW | bookmarks/services/assets.py | 23 | logger = logging.getLogger(__name__) | CODE |
| LOW | bookmarks/services/favicon_loader.py | 14 | logger = logging.getLogger(__name__) | CODE |
| LOW | bookmarks/services/bookmarks.py | 9 | logger = logging.getLogger(__name__) | CODE |
| LOW | bookmarks/services/preview_image_loader.py | 11 | logger = logging.getLogger(__name__) | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | bookmarks/models.py | 262 | CODE | |
| LOW | bookmarks/middlewares.py | 49 | CODE | |
| LOW | bookmarks/templatetags/shared.py | 74 | CODE | |
| LOW | bookmarks/templatetags/shared.py | 93 | CODE | |
| LOW | bookmarks/tests/helpers.py | 54 | CODE | |
| LOW | bookmarks/tests/helpers.py | 105 | CODE | |
| LOW | bookmarks/tests/helpers.py | 174 | CODE | |
| LOW | bookmarks/tests/helpers.py | 205 | CODE | |
| LOW | bookmarks/tests/helpers.py | 414 | CODE | |
| LOW | bookmarks/views/turbo.py | 26 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | bookmarks/tasks.py | 3 | CODE | |
| LOW | bookmarks/apps.py | 10 | CODE | |
| LOW | bookmarks/settings/__init__.py | 4 | CODE | |
| LOW | bookmarks/settings/__init__.py | 6 | CODE | |
| LOW | bookmarks/settings/dev.py | 9 | CODE | |
| LOW | bookmarks/settings/dev.py | 66 | CODE | |
| LOW | bookmarks/settings/prod.py | 12 | CODE | |
| LOW | bookmarks/settings/prod.py | 63 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | bookmarks/tests/test_auth_proxy_support.py | 21 | "auth_proxy_user", "user@example.com", "password123" | CODE |
| LOW⚡ | bookmarks/tests/test_auth_proxy_support.py | 43 | "auth_proxy_user", "user@example.com", "password123" | CODE |
| LOW⚡ | bookmarks/tests/test_auth_proxy_support.py | 53 | "auth_proxy_user", "user@example.com", "password123" | CODE |
| LOW | bookmarks/tests/helpers.py | 281 | user = User.objects.create_user(name, "user@example.com", "password123") | CODE |
| LOW | …environments/authelia-oidc/authelia/users_database.yml | 14 | email: 'admin@example.com' | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | bookmarks/tests_e2e/helpers.py | 42 | # Check if THIS test is in failures/errors (not just any test) | COMMENT |
| LOW | bookmarks/management/commands/migrate_tasks.py | 15 | # Check if background_task table exists | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | install-linkding.sh | 1 | #!/usr/bin/env bash | COMMENT |
| LOW | docs/src/env.d.ts | 1 | /// <reference path="../.astro/types.d.ts" /> | COMMENT |