{"version": "2.1.0", "$schema": "https://json.schemastore.org/sarif-2.1.0.json", "runs": [{"tool": {"driver": {"name": "Repobility", "informationUri": "https://repobility.com", "rules": [{"id": "scanner-21cfbf5e432159a9", "name": "Possibly dead Python function: extract_pymupdf", "shortDescription": {"text": "Possibly dead Python function: extract_pymupdf"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-2437acc5c3ab7e86", "name": "Possibly dead Python function: extract_pdfplumber", "shortDescription": {"text": "Possibly dead Python function: extract_pdfplumber"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-9b1a8cd4ae544235", "name": "Possibly dead Python function: extract_camelot", "shortDescription": {"text": "Possibly dead Python function: extract_camelot"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-622406a3eb890e20", "name": "Possibly dead Python function: extract_img2table", "shortDescription": {"text": "Possibly dead Python function: extract_img2table"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-4fee7a0f2467dbd4", "name": "Possibly dead Python function: bbox_overlap_ratio", "shortDescription": {"text": "Possibly dead Python function: bbox_overlap_ratio"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-d5180407481ff16f", "name": "Possibly dead Python function: pagination_args", "shortDescription": {"text": "Possibly dead Python function: pagination_args"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-37ac8f1242467f5f", "name": "Possibly dead Python function: highlight_code", "shortDescription": {"text": "Possibly dead Python function: highlight_code"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-437c578b741c50b1", "name": "Possibly dead Python function: decorator", "shortDescription": {"text": "Possibly dead Python function: decorator"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-f27d244d829beefe", "name": "Possibly dead Python function: handle_starttag", "shortDescription": {"text": "Possibly dead Python function: handle_starttag"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-4982acfef99843ba", "name": "Possibly dead Python function: handle_endtag", "shortDescription": {"text": "Possibly dead Python function: handle_endtag"}, "fullDescription": {"text": "No callers detected by AST scan in this repo. Could be exported for external callers or a framework handler."}, "properties": {"scanner": "scanner-primary", "layer": "software", "severity": "low", "confidence": 1.0}}, {"id": "scanner-d63da3583b14afc0", "name": "Dockerfile runs as root: Dockerfile", "shortDescription": {"text": "Dockerfile runs as root: Dockerfile"}, "fullDescription": {"text": "No non-root USER set. Containers running as root expand the blast radius of any vulnerability inside the image."}, "properties": {"scanner": "scanner-primary", "layer": "hardware", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-6d66309abd3f1100", "name": "Docker base image is tag-pinned but not digest-pinned: mcr.microsoft.com/playwright/python:v1.44.0-jammy", "shortDescription": {"text": "Docker base image is tag-pinned but not digest-pinned: mcr.microsoft.com/playwright/python:v1.44.0-jammy"}, "fullDescription": {"text": "Container tags can be retagged upstream. Pin production base images to a reviewed digest (`image@sha256:...`) when reproducibility and supply-chain integrity matter."}, "properties": {"scanner": "scanner-primary", "layer": "hardware", "severity": "low", "confidence": 1.0}}, {"id": "scanner-aa5acaa49eb8315b", "name": "Containers defined but no K8s/orchestration manifest found", "shortDescription": {"text": "Containers defined but no K8s/orchestration manifest found"}, "fullDescription": {"text": "Repo has Dockerfiles/compose but no Kubernetes/Nomad manifests. If the target deployment is K8s, the manifests may live in a separate ops repo."}, "properties": {"scanner": "scanner-primary", "layer": "hardware", "severity": "low", "confidence": 1.0}}, {"id": "scanner-9710c8d059e53154", "name": "No frontend routes/components detected", "shortDescription": {"text": "No frontend routes/components detected"}, "fullDescription": {"text": "No React/Vue/Next routes were found. This is fine for backend-only repos."}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-620ad2c6168830bd", "name": "Insecure pattern 'debug_true' in app.py:78", "shortDescription": {"text": "Insecure pattern 'debug_true' in app.py:78"}, "fullDescription": {"text": "Found a known-risky pattern (debug_true). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "low", "confidence": 1.0}}, {"id": "scanner-200dcd9280503fba", "name": "Insecure pattern 'direct_innerhtml_assignment' in static/js/library.js:34", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in static/js/library.js:34"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-55a857cb62116edd", "name": "Insecure pattern 'direct_innerhtml_assignment' in static/js/_utils.js:47", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in static/js/_utils.js:47"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-ee9adc33aee9608b", "name": "Insecure pattern 'direct_innerhtml_assignment' in static/js/audio_converter.js:399", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in static/js/audio_converter.js:399"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-a38a35fd49998fbd", "name": "Insecure pattern 'direct_innerhtml_assignment' in static/js/library_detail.js:34", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in static/js/library_detail.js:34"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-aa3b58fa08bf6e4c", "name": "Insecure pattern 'direct_innerhtml_assignment' in static/js/document_converter.js:28", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in static/js/document_converter.js:28"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-884c1bc1fb4117f7", "name": "Insecure pattern 'direct_innerhtml_assignment' in static/js/markdown_converter.js:425", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in static/js/markdown_converter.js:425"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-90779f0cfd7345db", "name": "Insecure pattern 'direct_innerhtml_assignment' in static/js/tags.js:21", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in static/js/tags.js:21"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-eb6c041184101d3a", "name": "Insecure pattern 'direct_innerhtml_assignment' in static/js/mermaid_converter.js:55", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in static/js/mermaid_converter.js:55"}, "fullDescription": {"text": "Found a known-risky pattern (direct_innerhtml_assignment). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-6372cebde0220094", "name": "No auth library detected", "shortDescription": {"text": "No auth library detected"}, "fullDescription": {"text": "The scanner did not find any standard auth library (JWT, OAuth, NextAuth, Auth0, etc.). The repo has auth/admin/session surface indicators, so auth may live in custom code, in a separate service, or be missing."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-4601e3ad3bb28677", "name": "No CI/CD pipelines detected", "shortDescription": {"text": "No CI/CD pipelines detected"}, "fullDescription": {"text": "No GitHub Actions, GitLab CI, or CircleCI configs found. Without CI you can't gate deploys on tests/lints."}, "properties": {"scanner": "scanner-primary", "layer": "cicd", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-7ffe6673e43d6d89", "name": "Very large file: static/js/audio_converter.js (1467 lines)", "shortDescription": {"text": "Very large file: static/js/audio_converter.js (1467 lines)"}, "fullDescription": {"text": "Files with >800 lines often hide complexity hotspots and discourage tests."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-13910abc121f93e3", "name": "Very large file: static/js/library_detail.js (1599 lines)", "shortDescription": {"text": "Very large file: static/js/library_detail.js (1599 lines)"}, "fullDescription": {"text": "Files with >800 lines often hide complexity hotspots and discourage tests."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-141b30a41e03817b", "name": "No license file detected", "shortDescription": {"text": "No license file detected"}, "fullDescription": {"text": "No LICENSE/COPYING/NOTICE file was found. Generated repositories often omit licensing, which blocks reuse and automated intake."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-01f96f71d9907a8c", "name": "README lacks setup or run instructions", "shortDescription": {"text": "README lacks setup or run instructions"}, "fullDescription": {"text": "A README exists, but it does not contain common install/setup/run markers. This matches a frequent generated-code pattern: UI is present, operational handoff is thin."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-3ab5d313dda8e5f9", "name": "Debug logging residue appears in source files", "shortDescription": {"text": "Debug logging residue appears in source files"}, "fullDescription": {"text": "Found 15 console/debugger/print-style debug statements in non-test source. This is a common fast-generation residue before production cleanup."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-72b2a6250083a784", "name": "Placeholder or mock-heavy implementation detected", "shortDescription": {"text": "Placeholder or mock-heavy implementation detected"}, "fullDescription": {"text": "Found 27 placeholder/mock markers across 3 source files. This often means the repo looks complete while core flows still use generated scaffolding or fake data."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-2d0c7b7ab8f8aacf", "name": "Critical user flow still appears backed by mock or placeholder data", "shortDescription": {"text": "Critical user flow still appears backed by mock or placeholder data"}, "fullDescription": {"text": "A payment/auth/admin/order/billing-style flow contains mock, fake, TODO, dummy, or placeholder markers in runtime source. In the Fable corpus this is a high-leverage completeness smell: the app can look finished while the money, identity, or tenant flow is still scaffolded."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-b9088664ace7f748", "name": "Composite production-readiness gap", "shortDescription": {"text": "Composite production-readiness gap"}, "fullDescription": {"text": "Multiple low-cost hardening controls are missing together: license, ci, operator-readme. Opus verification showed these co-occurring gaps are a better readiness signal than reading each flag in isolation."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-749d4bc1bd66df5f", "name": "Agent instructions exist but release-hardening basics are missing", "shortDescription": {"text": "Agent instructions exist but release-hardening basics are missing"}, "fullDescription": {"text": "AI-coder instruction files were found, but the repo is missing license, ci, operator-readme. Treat this as a contract gap: the agent is guided, but the generated output is not yet guarded by the controls that make it repeatable."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-ea8f3013f588db25", "name": "Shallow git history limits provenance confidence", "shortDescription": {"text": "Shallow git history limits provenance confidence"}, "fullDescription": {"text": "The repository is a shallow clone. Origin/evolution analysis cannot distinguish fresh generation, imported legacy code, or long-lived human code with high confidence."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-8424db9c75e04ba4", "name": "Very short observed git history", "shortDescription": {"text": "Very short observed git history"}, "fullDescription": {"text": "The repo has multiple source files but two or fewer visible commits. This is not a failure by itself, but it lowers confidence in evolution-based diagnosis."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-aa60f2fc029528bc", "name": "Network/subprocess call without timeout or try/except \u2014 test_full_flow.py:47", "shortDescription": {"text": "Network/subprocess call without timeout or try/except \u2014 test_full_flow.py:47"}, "fullDescription": {"text": "`requests.get(...)` here lacks both a `timeout=` arg and an enclosing try/except. This is exactly the class of bug that took down our git-clone earlier (HTTP/2 stream cancel surfaced as a fatal). Add a `timeout=` and wrap in try/except, or use a wrapper that retries."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-6ca1092db422e025", "name": "Commented-code block (5 lines) in tests/test_reading_list.py:141", "shortDescription": {"text": "Commented-code block (5 lines) in tests/test_reading_list.py:141"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-8de7bf2d70d812ab", "name": "Commented-code block (5 lines) in static/js/library.js:59", "shortDescription": {"text": "Commented-code block (5 lines) in static/js/library.js:59"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-ea1fdf3688f9c157", "name": "`fetch()` without try/.catch or AbortSignal \u2014 static/js/library.js:27", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 static/js/library.js:27"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-2d020fc04caf82af", "name": "Commented-code block (5 lines) in static/js/audio_converter.js:675", "shortDescription": {"text": "Commented-code block (5 lines) in static/js/audio_converter.js:675"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-4a60ea76726fbe9d", "name": "`fetch()` without try/.catch or AbortSignal \u2014 static/js/audio_converter.js:172", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 static/js/audio_converter.js:172"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-e54bd5dca159f37c", "name": "Commented-code block (5 lines) in static/js/library_detail.js:167", "shortDescription": {"text": "Commented-code block (5 lines) in static/js/library_detail.js:167"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-40a32ce91d2b454f", "name": "`fetch()` without try/.catch or AbortSignal \u2014 static/js/library_detail.js:54", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 static/js/library_detail.js:54"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-faa1813072f7b35c", "name": "`fetch()` without try/.catch or AbortSignal \u2014 static/js/markdown_converter.js:128", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 static/js/markdown_converter.js:128"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-f18b2b98f5803e2e", "name": "Network/subprocess call without timeout or try/except \u2014 services/audio_chunker.py:51", "shortDescription": {"text": "Network/subprocess call without timeout or try/except \u2014 services/audio_chunker.py:51"}, "fullDescription": {"text": "`subprocess.run(...)` here lacks both a `timeout=` arg and an enclosing try/except. This is exactly the class of bug that took down our git-clone earlier (HTTP/2 stream cancel surfaced as a fatal). Add a `timeout=` and wrap in try/except, or use a wrapper that retries."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-a6e39640fca7d085", "name": "Commented-code block (6 lines) in app_pkg/__init__.py:118", "shortDescription": {"text": "Commented-code block (6 lines) in app_pkg/__init__.py:118"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-f5274ef5aff23c2d", "name": "Commented-code block (6 lines) in app_pkg/library.py:28", "shortDescription": {"text": "Commented-code block (6 lines) in app_pkg/library.py:28"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-0272a44ffd3dc412", "name": "Commented-code block (6 lines) in app_pkg/config.py:12", "shortDescription": {"text": "Commented-code block (6 lines) in app_pkg/config.py:12"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-b334d875f2339fa1", "name": "5 env vars used in code but missing from .env.example", "shortDescription": {"text": "5 env vars used in code but missing from .env.example"}, "fullDescription": {"text": "Drift between code and config docs. The first few: `DATABASE_URL`, `MCP_AUTH_TOKEN`, `NOTION_MCP_URL`, `NOTION_TOKEN`, `REDIS_URL`. Add them (with a placeholder/comment) to .env.example so onboarding doesn't break."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-be46ea126aa5d8dc", "name": "Near-duplicate function bodies in 3 places", "shortDescription": {"text": "Near-duplicate function bodies in 3 places"}, "fullDescription": {"text": "Functions with the same first-5-line body hash:\nmodels.py:to_dict, models.py:to_dict, models.py:to_dict\n\nThis is *the* AI-coder failure mode (4\u00d7 more duplication in vibe-coded repos \u2014 see https://jw.hn/ai-code-hygiene). Consolidate or document why they're separate."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-02525d39071dd2c7", "name": "Near-duplicate function bodies in 5 places", "shortDescription": {"text": "Near-duplicate function bodies in 5 places"}, "fullDescription": {"text": "Functions with the same first-5-line body hash:\nservices/pdf_extraction/detectors.py:detect, services/pdf_extraction/detectors.py:detect, services/pdf_extraction/detectors.py:detect, services/pdf_extraction/detectors.py:detect\n\nThis is *the* AI-coder failure mode (4\u00d7 more duplication in vibe-coded repos \u2014 see https://jw.hn/ai-code-hygiene). Consolidate or document why they're separate."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-49c98f7cedd9c977", "name": "Near-duplicate function bodies in 4 places", "shortDescription": {"text": "Near-duplicate function bodies in 4 places"}, "fullDescription": {"text": "Functions with the same first-5-line body hash:\napp_pkg/library.py:api_update_conversion, app_pkg/library.py:api_update_conversion_progress, app_pkg/library.py:api_attach_conversion_tag, app_pkg/highlights.py:api_create_highlight\n\nThis is *the* AI-coder failure mode (4\u00d7 more duplication in vibe-coded repos \u2014 see https://jw.hn/ai-code-hygiene). Consolidate or document why they're separate."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-2c04133e54348533", "name": "Near-duplicate function bodies in 2 places", "shortDescription": {"text": "Near-duplicate function bodies in 2 places"}, "fullDescription": {"text": "Functions with the same first-5-line body hash:\napp_pkg/integrations/notion.py:fetch, app_pkg/integrations/notion.py:fetch\n\nThis is *the* AI-coder failure mode (4\u00d7 more duplication in vibe-coded repos \u2014 see https://jw.hn/ai-code-hygiene). Consolidate or document why they're separate."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-0b46e7df1704e2c4", "name": "Flask mutation route `login` without `@login_required` \u2014 app_pkg/auth.py:17", "shortDescription": {"text": "Flask mutation route `login` without `@login_required` \u2014 app_pkg/auth.py:17"}, "fullDescription": {"text": "Flask route declares POST/PUT/DELETE/PATCH methods without an auth decorator. Add `@login_required` (Flask-Login) or equivalent."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "high", "confidence": 1.0}}, {"id": "scanner-7a312d7396425bc3", "name": "Flask mutation route `api_ingest_conversion` without `@login_required` \u2014 app_pkg/ingest.py:117", "shortDescription": {"text": "Flask mutation route `api_ingest_conversion` without `@login_required` \u2014 app_pkg/ingest.py:117"}, "fullDescription": {"text": "Flask route declares POST/PUT/DELETE/PATCH methods without an auth decorator. Add `@login_required` (Flask-Login) or equivalent."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "high", "confidence": 1.0}}, {"id": "scanner-3c0028e3fe1f947c", "name": "Dangling fetch: PUT /api/conversions/${id} (static/js/library.js:27)", "shortDescription": {"text": "Dangling fetch: PUT /api/conversions/${id} (static/js/library.js:27)"}, "fullDescription": {"text": "`static/js/library.js:27` calls `PUT /api/conversions/${id}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/conversions/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-2fbea5eef0108815", "name": "Dangling fetch: PUT /api/conversions/${id} (static/js/library.js:51)", "shortDescription": {"text": "Dangling fetch: PUT /api/conversions/${id} (static/js/library.js:51)"}, "fullDescription": {"text": "`static/js/library.js:51` calls `PUT /api/conversions/${id}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/conversions/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-17b851144e28ead1", "name": "Dangling fetch: DELETE /api/conversions/${id} (static/js/library.js:125)", "shortDescription": {"text": "Dangling fetch: DELETE /api/conversions/${id} (static/js/library.js:125)"}, "fullDescription": {"text": "`static/js/library.js:125` calls `DELETE /api/conversions/${id}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/conversions/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-9144c531ed1d69ba", "name": "Dangling fetch: POST /api/conversions/${id}/queue (static/js/library.js:154)", "shortDescription": {"text": "Dangling fetch: POST /api/conversions/${id}/queue (static/js/library.js:154)"}, "fullDescription": {"text": "`static/js/library.js:154` calls `POST /api/conversions/${id}/queue` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/conversions/<p>/queue`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-42cf49a9865655f5", "name": "Dangling fetch: POST /api/conversions/${id}/queue (static/js/library.js:195)", "shortDescription": {"text": "Dangling fetch: POST /api/conversions/${id}/queue (static/js/library.js:195)"}, "fullDescription": {"text": "`static/js/library.js:195` calls `POST /api/conversions/${id}/queue` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/conversions/<p>/queue`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-1bb153f7a523f9c6", "name": "Dangling fetch: POST /podcast-cancel/${podcastJobId} (static/js/audio_converter.js:910)", "shortDescription": {"text": "Dangling fetch: POST /podcast-cancel/${podcastJobId} (static/js/audio_converter.js:910)"}, "fullDescription": {"text": "`static/js/audio_converter.js:910` calls `POST /podcast-cancel/${podcastJobId}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/podcast-cancel/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-0affa058ef3e588c", "name": "Dangling fetch: GET /podcast-status/${jobId} (static/js/audio_converter.js:1080)", "shortDescription": {"text": "Dangling fetch: GET /podcast-status/${jobId} (static/js/audio_converter.js:1080)"}, "fullDescription": {"text": "`static/js/audio_converter.js:1080` calls `GET /podcast-status/${jobId}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/podcast-status/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-59001600afe916a2", "name": "Dangling fetch: GET /podcast-download/${jobId} (static/js/audio_converter.js:1186)", "shortDescription": {"text": "Dangling fetch: GET /podcast-download/${jobId} (static/js/audio_converter.js:1186)"}, "fullDescription": {"text": "`static/js/audio_converter.js:1186` calls `GET /podcast-download/${jobId}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/podcast-download/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-cf84f010f815735a", "name": "Dangling fetch: GET /podcast-status/${jobId} (static/js/audio_converter.js:1307)", "shortDescription": {"text": "Dangling fetch: GET /podcast-status/${jobId} (static/js/audio_converter.js:1307)"}, "fullDescription": {"text": "`static/js/audio_converter.js:1307` calls `GET /podcast-status/${jobId}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/podcast-status/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-f87fc60e6375b20a", "name": "Dangling fetch: PUT /api/conversions/${CONVERSION_ID} (static/js/library_detail.js:54)", "shortDescription": {"text": "Dangling fetch: PUT /api/conversions/${CONVERSION_ID} (static/js/library_detail.js:54)"}, "fullDescription": {"text": "`static/js/library_detail.js:54` calls `PUT /api/conversions/${CONVERSION_ID}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/conversions/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-91ece29ac844ae16", "name": "Dangling fetch: PUT /api/conversions/${CONVERSION_ID} (static/js/library_detail.js:92)", "shortDescription": {"text": "Dangling fetch: PUT /api/conversions/${CONVERSION_ID} (static/js/library_detail.js:92)"}, "fullDescription": {"text": "`static/js/library_detail.js:92` calls `PUT /api/conversions/${CONVERSION_ID}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/conversions/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-9973937b50b178ef", "name": "Dangling fetch: POST /api/conversions/${CONVERSION_ID}/queue (static/js/library_detail.js:117)", "shortDescription": {"text": "Dangling fetch: POST /api/conversions/${CONVERSION_ID}/queue (static/js/library_detail.js:117)"}, "fullDescription": {"text": "`static/js/library_detail.js:117` calls `POST /api/conversions/${CONVERSION_ID}/queue` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/conversions/<p>/queue`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-fa4d54c7ebdce843", "name": "Dangling fetch: PUT /api/conversions/${CONVERSION_ID} (static/js/library_detail.js:143)", "shortDescription": {"text": "Dangling fetch: PUT /api/conversions/${CONVERSION_ID} (static/js/library_detail.js:143)"}, "fullDescription": {"text": "`static/js/library_detail.js:143` calls `PUT /api/conversions/${CONVERSION_ID}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/conversions/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-8222515c24b96789", "name": "Dangling fetch: PUT /api/conversions/${CONVERSION_ID} (static/js/library_detail.js:173)", "shortDescription": {"text": "Dangling fetch: PUT /api/conversions/${CONVERSION_ID} (static/js/library_detail.js:173)"}, "fullDescription": {"text": "`static/js/library_detail.js:173` calls `PUT /api/conversions/${CONVERSION_ID}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/conversions/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-112b2a33cf4d339c", "name": "Dangling fetch: DELETE /api/conversions/${CONVERSION_ID} (static/js/library_detail.js:247)", "shortDescription": {"text": "Dangling fetch: DELETE /api/conversions/${CONVERSION_ID} (static/js/library_detail.js:247)"}, "fullDescription": {"text": "`static/js/library_detail.js:247` calls `DELETE /api/conversions/${CONVERSION_ID}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/conversions/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-62af52355b17c37d", "name": "Dangling fetch: POST /api/conversions/${CONVERSION_ID}/send-to-notion (static/js/library_detail.js:463)", "shortDescription": {"text": "Dangling fetch: POST /api/conversions/${CONVERSION_ID}/send-to-notion (static/js/library_detail.js:463)"}, "fullDescription": {"text": "`static/js/library_detail.js:463` calls `POST /api/conversions/${CONVERSION_ID}/send-to-notion` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/conversions/<p>/send-to-notion`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-ce6bc345a89e85dc", "name": "Dangling fetch: POST /api/conversions/${CONVERSION_ID}/tags (static/js/library_detail.js:544)", "shortDescription": {"text": "Dangling fetch: POST /api/conversions/${CONVERSION_ID}/tags (static/js/library_detail.js:544)"}, "fullDescription": {"text": "`static/js/library_detail.js:544` calls `POST /api/conversions/${CONVERSION_ID}/tags` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/conversions/<p>/tags`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-745092806a67f4bb", "name": "Dangling fetch: DELETE /api/conversions/${CONVERSION_ID}/tags/${tagId} (static/js/library_detail.js:576)", "shortDescription": {"text": "Dangling fetch: DELETE /api/conversions/${CONVERSION_ID}/tags/${tagId} (static/js/library_detail.js:576)"}, "fullDescription": {"text": "`static/js/library_detail.js:576` calls `DELETE /api/conversions/${CONVERSION_ID}/tags/${tagId}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/conversions/<p>/tags/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-e8023bb6fa8f989c", "name": "Dangling fetch: POST /api/conversions/${CONVERSION_ID}/highlights (static/js/library_detail.js:746)", "shortDescription": {"text": "Dangling fetch: POST /api/conversions/${CONVERSION_ID}/highlights (static/js/library_detail.js:746)"}, "fullDescription": {"text": "`static/js/library_detail.js:746` calls `POST /api/conversions/${CONVERSION_ID}/highlights` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/conversions/<p>/highlights`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-50b73870e6b10787", "name": "Dangling fetch: GET /api/conversions/${CONVERSION_ID}/highlights (static/js/library_detail.js:775)", "shortDescription": {"text": "Dangling fetch: GET /api/conversions/${CONVERSION_ID}/highlights (static/js/library_detail.js:775)"}, "fullDescription": {"text": "`static/js/library_detail.js:775` calls `GET /api/conversions/${CONVERSION_ID}/highlights` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/conversions/<p>/highlights`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-ecd2403f029736f5", "name": "Dangling fetch: POST /api/highlights/${id}/tags (static/js/library_detail.js:1179)", "shortDescription": {"text": "Dangling fetch: POST /api/highlights/${id}/tags (static/js/library_detail.js:1179)"}, "fullDescription": {"text": "`static/js/library_detail.js:1179` calls `POST /api/highlights/${id}/tags` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/highlights/<p>/tags`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-6808f54f7acfc4f9", "name": "Dangling fetch: DELETE /api/highlights/${id}/tags/${tagId} (static/js/library_detail.js:1224)", "shortDescription": {"text": "Dangling fetch: DELETE /api/highlights/${id}/tags/${tagId} (static/js/library_detail.js:1224)"}, "fullDescription": {"text": "`static/js/library_detail.js:1224` calls `DELETE /api/highlights/${id}/tags/${tagId}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/highlights/<p>/tags/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-f8de5ccd9e761fb0", "name": "Dangling fetch: PATCH /api/highlights/${id} (static/js/library_detail.js:1251)", "shortDescription": {"text": "Dangling fetch: PATCH /api/highlights/${id} (static/js/library_detail.js:1251)"}, "fullDescription": {"text": "`static/js/library_detail.js:1251` calls `PATCH /api/highlights/${id}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/highlights/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-77df924271df6e95", "name": "Dangling fetch: DELETE /api/highlights/${id} (static/js/library_detail.js:1277)", "shortDescription": {"text": "Dangling fetch: DELETE /api/highlights/${id} (static/js/library_detail.js:1277)"}, "fullDescription": {"text": "`static/js/library_detail.js:1277` calls `DELETE /api/highlights/${id}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/highlights/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-e29edcca147f885a", "name": "Dangling fetch: PATCH /api/conversions/${CONVERSION_ID}/progress (static/js/library_detail.js:1437)", "shortDescription": {"text": "Dangling fetch: PATCH /api/conversions/${CONVERSION_ID}/progress (static/js/library_detail.js:1437)"}, "fullDescription": {"text": "`static/js/library_detail.js:1437` calls `PATCH /api/conversions/${CONVERSION_ID}/progress` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/conversions/<p>/progress`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-7285aebc00879b31", "name": "Dangling fetch: PATCH /api/conversions/${CONVERSION_ID}/progress (static/js/library_detail.js:1519)", "shortDescription": {"text": "Dangling fetch: PATCH /api/conversions/${CONVERSION_ID}/progress (static/js/library_detail.js:1519)"}, "fullDescription": {"text": "`static/js/library_detail.js:1519` calls `PATCH /api/conversions/${CONVERSION_ID}/progress` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/conversions/<p>/progress`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-861020437407a1fa", "name": "Dangling fetch: GET /static/css/pdf_styles/${theme}.css (static/js/markdown_converter.js:300)", "shortDescription": {"text": "Dangling fetch: GET /static/css/pdf_styles/${theme}.css (static/js/markdown_converter.js:300)"}, "fullDescription": {"text": "`static/js/markdown_converter.js:300` calls `GET /static/css/pdf_styles/${theme}.css` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/static/css/pdf_styles/<p>.css`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-91a93cb8c1486052", "name": "Dangling fetch: DELETE /api/tags/${tagId} (static/js/tags.js:61)", "shortDescription": {"text": "Dangling fetch: DELETE /api/tags/${tagId} (static/js/tags.js:61)"}, "fullDescription": {"text": "`static/js/tags.js:61` calls `DELETE /api/tags/${tagId}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/tags/<p>`\nIf this points at an external API, prefix it with `https://` so the matcher skips it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "high", "confidence": 1.0}}, {"id": "scanner-3d47f5777840c8d6", "name": "Unused endpoint: ANY /library", "shortDescription": {"text": "Unused endpoint: ANY /library"}, "fullDescription": {"text": "`app_pkg/library.py` declares `ANY /library` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-1749db7e736b0619", "name": "Unused endpoint: ANY /library/<int:conversion_id>", "shortDescription": {"text": "Unused endpoint: ANY /library/<int:conversion_id>"}, "fullDescription": {"text": "`app_pkg/library.py` declares `ANY /library/<int:conversion_id>` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-b953d30771cff125", "name": "Unused endpoint: ANY /api/conversions/<int:conversion_id>", "shortDescription": {"text": "Unused endpoint: ANY /api/conversions/<int:conversion_id>"}, "fullDescription": {"text": "`app_pkg/library.py` declares `ANY /api/conversions/<int:conversion_id>` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-211af594c5bcaab3", "name": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/progress", "shortDescription": {"text": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/progress"}, "fullDescription": {"text": "`app_pkg/library.py` declares `ANY /api/conversions/<int:conversion_id>/progress` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-97db0c407728f6df", "name": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/place", "shortDescription": {"text": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/place"}, "fullDescription": {"text": "`app_pkg/library.py` declares `ANY /api/conversions/<int:conversion_id>/place` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-ba227e3488ef4f8a", "name": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/queue", "shortDescription": {"text": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/queue"}, "fullDescription": {"text": "`app_pkg/library.py` declares `ANY /api/conversions/<int:conversion_id>/queue` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-1b9a5b7c9489378c", "name": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/tags", "shortDescription": {"text": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/tags"}, "fullDescription": {"text": "`app_pkg/library.py` declares `ANY /api/conversions/<int:conversion_id>/tags` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-469484c2f382b511", "name": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/tags/<int:tag_id>", "shortDescription": {"text": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/tags/<int:tag_id>"}, "fullDescription": {"text": "`app_pkg/library.py` declares `ANY /api/conversions/<int:conversion_id>/tags/<int:tag_id>` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-4c7ad61ba1b23915", "name": "Unused endpoint: ANY /login", "shortDescription": {"text": "Unused endpoint: ANY /login"}, "fullDescription": {"text": "`app_pkg/auth.py` declares `ANY /login` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-c9e449c3b9e73b73", "name": "Unused endpoint: ANY /logout", "shortDescription": {"text": "Unused endpoint: ANY /logout"}, "fullDescription": {"text": "`app_pkg/auth.py` declares `ANY /logout` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-b97afe2fdf389a9a", "name": "Unused endpoint: ANY /generate-podcast", "shortDescription": {"text": "Unused endpoint: ANY /generate-podcast"}, "fullDescription": {"text": "`app_pkg/podcasts.py` declares `ANY /generate-podcast` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-eeecf7eb13ecd50d", "name": "Unused endpoint: ANY /api/get-google-voices", "shortDescription": {"text": "Unused endpoint: ANY /api/get-google-voices"}, "fullDescription": {"text": "`app_pkg/podcasts.py` declares `ANY /api/get-google-voices` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-26c76d3972599c54", "name": "Unused endpoint: ANY /podcast-status/<job_id>", "shortDescription": {"text": "Unused endpoint: ANY /podcast-status/<job_id>"}, "fullDescription": {"text": "`app_pkg/podcasts.py` declares `ANY /podcast-status/<job_id>` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-fdaa9fcc6c99ccae", "name": "Unused endpoint: ANY /podcast-cancel/<job_id>", "shortDescription": {"text": "Unused endpoint: ANY /podcast-cancel/<job_id>"}, "fullDescription": {"text": "`app_pkg/podcasts.py` declares `ANY /podcast-cancel/<job_id>` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-bed17f7f2d330b30", "name": "Unused endpoint: ANY /podcast-download/<job_id>", "shortDescription": {"text": "Unused endpoint: ANY /podcast-download/<job_id>"}, "fullDescription": {"text": "`app_pkg/podcasts.py` declares `ANY /podcast-download/<job_id>` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-1907fac19d43a278", "name": "Unused endpoint: ANY /api/get-gemini-voices", "shortDescription": {"text": "Unused endpoint: ANY /api/get-gemini-voices"}, "fullDescription": {"text": "`app_pkg/podcasts.py` declares `ANY /api/get-gemini-voices` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-34278c8f06851986", "name": "Unused endpoint: ANY /audio-converter", "shortDescription": {"text": "Unused endpoint: ANY /audio-converter"}, "fullDescription": {"text": "`app_pkg/audio.py` declares `ANY /audio-converter` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-fff17b0708d45152", "name": "Unused endpoint: ANY /mermaid-converter", "shortDescription": {"text": "Unused endpoint: ANY /mermaid-converter"}, "fullDescription": {"text": "`app_pkg/mermaid.py` declares `ANY /mermaid-converter` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-9dd5db53dcf95a2b", "name": "Unused endpoint: ANY /api/ingest/conversion", "shortDescription": {"text": "Unused endpoint: ANY /api/ingest/conversion"}, "fullDescription": {"text": "`app_pkg/ingest.py` declares `ANY /api/ingest/conversion` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-e8b3d1fa5e2f4b0f", "name": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/highlights", "shortDescription": {"text": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/highlights"}, "fullDescription": {"text": "`app_pkg/highlights.py` declares `ANY /api/conversions/<int:conversion_id>/highlights` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-a2aebe5ab6fefd02", "name": "Unused endpoint: ANY /api/highlights/<int:highlight_id>", "shortDescription": {"text": "Unused endpoint: ANY /api/highlights/<int:highlight_id>"}, "fullDescription": {"text": "`app_pkg/highlights.py` declares `ANY /api/highlights/<int:highlight_id>` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-bf876c650798eb55", "name": "Unused endpoint: ANY /document-converter", "shortDescription": {"text": "Unused endpoint: ANY /document-converter"}, "fullDescription": {"text": "`app_pkg/documents.py` declares `ANY /document-converter` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-11388f23814d03bd", "name": "Unused endpoint: ANY /transform-document", "shortDescription": {"text": "Unused endpoint: ANY /transform-document"}, "fullDescription": {"text": "`app_pkg/documents.py` declares `ANY /transform-document` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-1a6d91f2236825bf", "name": "Unused endpoint: ANY /", "shortDescription": {"text": "Unused endpoint: ANY /"}, "fullDescription": {"text": "`app_pkg/markdown.py` declares `ANY /` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-10632aefbdfb84e1", "name": "Unused endpoint: ANY /convert-markdown", "shortDescription": {"text": "Unused endpoint: ANY /convert-markdown"}, "fullDescription": {"text": "`app_pkg/markdown.py` declares `ANY /convert-markdown` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-7d5d8aabe3340ac6", "name": "Unused endpoint: ANY /api/tags", "shortDescription": {"text": "Unused endpoint: ANY /api/tags"}, "fullDescription": {"text": "`app_pkg/tags.py` declares `ANY /api/tags` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-9fdb2106da0264c4", "name": "Unused endpoint: ANY /api/highlights/<int:highlight_id>/tags", "shortDescription": {"text": "Unused endpoint: ANY /api/highlights/<int:highlight_id>/tags"}, "fullDescription": {"text": "`app_pkg/tags.py` declares `ANY /api/highlights/<int:highlight_id>/tags` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-619cfad737b6028f", "name": "Unused endpoint: ANY /api/highlights/<int:highlight_id>/tags/<int:tag_id>", "shortDescription": {"text": "Unused endpoint: ANY /api/highlights/<int:highlight_id>/tags/<int:tag_id>"}, "fullDescription": {"text": "`app_pkg/tags.py` declares `ANY /api/highlights/<int:highlight_id>/tags/<int:tag_id>` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-3e35565e57e41812", "name": "Unused endpoint: ANY /api/tags/<int:tag_id>", "shortDescription": {"text": "Unused endpoint: ANY /api/tags/<int:tag_id>"}, "fullDescription": {"text": "`app_pkg/tags.py` declares `ANY /api/tags/<int:tag_id>` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}, {"id": "scanner-1c38dcd1fcf37f56", "name": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/send-to-notion", "shortDescription": {"text": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/send-to-notion"}, "fullDescription": {"text": "`app_pkg/integrations/notion.py` declares `ANY /api/conversions/<int:conversion_id>/send-to-notion` but no frontend code we scanned calls it. This is fine if the endpoint serves external clients (mobile app, third-party, server-side webhooks). Otherwise it's dead code \u2014 consider removing or documenting who consumes it."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "low", "confidence": 1.0}}]}}, "automationDetails": {"id": "repobility/21149"}, "properties": {"repository": "TheReallyRealComedian/CONVERTER", "repoUrl": "https://github.com/TheReallyRealComedian/CONVERTER", "branch": "main"}, "results": [{"ruleId": "scanner-21cfbf5e432159a9", "level": "note", "message": {"text": "Possibly dead Python function: extract_pymupdf"}, "properties": {"repobilityId": "abbbb23051f7e557", "scanner": "scanner-primary", "fingerprint": "21cfbf5e432159a9", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "services/pdf_extraction/extractors.py:13"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-2437acc5c3ab7e86", "level": "note", "message": {"text": "Possibly dead Python function: extract_pdfplumber"}, "properties": {"repobilityId": "5e305b137e515e4d", "scanner": "scanner-primary", "fingerprint": "2437acc5c3ab7e86", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "services/pdf_extraction/extractors.py:27"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-9b1a8cd4ae544235", "level": "note", "message": {"text": "Possibly dead Python function: extract_camelot"}, "properties": {"repobilityId": "e570f4eb5c17f453", "scanner": "scanner-primary", "fingerprint": "9b1a8cd4ae544235", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "services/pdf_extraction/extractors.py:38"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-622406a3eb890e20", "level": "note", "message": {"text": "Possibly dead Python function: extract_img2table"}, "properties": {"repobilityId": "4aea9e849e997bd6", "scanner": "scanner-primary", "fingerprint": "622406a3eb890e20", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "services/pdf_extraction/extractors.py:52"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-4fee7a0f2467dbd4", "level": "note", "message": {"text": "Possibly dead Python function: bbox_overlap_ratio"}, "properties": {"repobilityId": "48fa1f2aec1fe8e2", "scanner": "scanner-primary", "fingerprint": "4fee7a0f2467dbd4", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "services/pdf_extraction/utils.py:26"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-d5180407481ff16f", "level": "note", "message": {"text": "Possibly dead Python function: pagination_args"}, "properties": {"repobilityId": "b2d9414fd737eb44", "scanner": "scanner-primary", "fingerprint": "d5180407481ff16f", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "app_pkg/library.py:41"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-37ac8f1242467f5f", "level": "note", "message": {"text": "Possibly dead Python function: highlight_code"}, "properties": {"repobilityId": "8b606fcbed3c3752", "scanner": "scanner-primary", "fingerprint": "37ac8f1242467f5f", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "app_pkg/markdown_render.py:16"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-437c578b741c50b1", "level": "note", "message": {"text": "Possibly dead Python function: decorator"}, "properties": {"repobilityId": "ffbcbc9bc189af1c", "scanner": "scanner-primary", "fingerprint": "437c578b741c50b1", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "app_pkg/decorators.py:41"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-f27d244d829beefe", "level": "note", "message": {"text": "Possibly dead Python function: handle_starttag"}, "properties": {"repobilityId": "3bf216d149959ebf", "scanner": "scanner-primary", "fingerprint": "f27d244d829beefe", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "app_pkg/markdown.py:35"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-4982acfef99843ba", "level": "note", "message": {"text": "Possibly dead Python function: handle_endtag"}, "properties": {"repobilityId": "7eb6f3decafc1766", "scanner": "scanner-primary", "fingerprint": "4982acfef99843ba", "layer": "software", "severity": "low", "confidence": 1.0, "tags": ["dead-code"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "app_pkg/markdown.py:47"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-d63da3583b14afc0", "level": "warning", "message": {"text": "Dockerfile runs as root: Dockerfile"}, "properties": {"repobilityId": "a2ed1bd120e507db", "scanner": "scanner-primary", "fingerprint": "d63da3583b14afc0", "layer": "hardware", "severity": "medium", "confidence": 1.0, "tags": ["security", "container"]}}, {"ruleId": "scanner-6d66309abd3f1100", "level": "note", "message": {"text": "Docker base image is tag-pinned but not digest-pinned: mcr.microsoft.com/playwright/python:v1.44.0-jammy"}, "properties": {"repobilityId": "2815ca3cd28cf886", "scanner": "scanner-primary", "fingerprint": "6d66309abd3f1100", "layer": "hardware", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "docker", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "Dockerfile"}, "region": {"startLine": 2}}}]}, {"ruleId": "scanner-aa5acaa49eb8315b", "level": "note", "message": {"text": "Containers defined but no K8s/orchestration manifest found"}, "properties": {"repobilityId": "b230ea9b68736081", "scanner": "scanner-primary", "fingerprint": "aa5acaa49eb8315b", "layer": "hardware", "severity": "low", "confidence": 1.0, "tags": ["coverage", "deployment"]}}, {"ruleId": "scanner-9710c8d059e53154", "level": "none", "message": {"text": "No frontend routes/components detected"}, "properties": {"repobilityId": "44ca61485762e494", "scanner": "scanner-primary", "fingerprint": "9710c8d059e53154", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["coverage"]}}, {"ruleId": "scanner-620ad2c6168830bd", "level": "note", "message": {"text": "Insecure pattern 'debug_true' in app.py:78"}, "properties": {"repobilityId": "820a29d1c28c9121", "scanner": "scanner-primary", "fingerprint": "620ad2c6168830bd", "layer": "security", "severity": "low", "confidence": 1.0, "tags": ["owasp", "debug_true"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "app.py"}, "region": {"startLine": 78}}}]}, {"ruleId": "scanner-200dcd9280503fba", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in static/js/library.js:34"}, "properties": {"repobilityId": "2c31bdebf79c2b52", "scanner": "scanner-primary", "fingerprint": "200dcd9280503fba", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "static/js/library.js"}, "region": {"startLine": 34}}}]}, {"ruleId": "scanner-55a857cb62116edd", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in static/js/_utils.js:47"}, "properties": {"repobilityId": "56c2caefe142ecf2", "scanner": "scanner-primary", "fingerprint": "55a857cb62116edd", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "static/js/_utils.js"}, "region": {"startLine": 47}}}]}, {"ruleId": "scanner-ee9adc33aee9608b", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in static/js/audio_converter.js:399"}, "properties": {"repobilityId": "56fc0b417f5fc67e", "scanner": "scanner-primary", "fingerprint": "ee9adc33aee9608b", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "static/js/audio_converter.js"}, "region": {"startLine": 399}}}]}, {"ruleId": "scanner-a38a35fd49998fbd", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in static/js/library_detail.js:34"}, "properties": {"repobilityId": "a89b571204bca923", "scanner": "scanner-primary", "fingerprint": "a38a35fd49998fbd", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "static/js/library_detail.js"}, "region": {"startLine": 34}}}]}, {"ruleId": "scanner-aa3b58fa08bf6e4c", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in static/js/document_converter.js:28"}, "properties": {"repobilityId": "5c5d39bfb52bd25e", "scanner": "scanner-primary", "fingerprint": "aa3b58fa08bf6e4c", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "static/js/document_converter.js"}, "region": {"startLine": 28}}}]}, {"ruleId": "scanner-884c1bc1fb4117f7", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in static/js/markdown_converter.js:425"}, "properties": {"repobilityId": "ff13e03b43c1cca5", "scanner": "scanner-primary", "fingerprint": "884c1bc1fb4117f7", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "static/js/markdown_converter.js"}, "region": {"startLine": 425}}}]}, {"ruleId": "scanner-90779f0cfd7345db", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in static/js/tags.js:21"}, "properties": {"repobilityId": "00bd8a4b7047e804", "scanner": "scanner-primary", "fingerprint": "90779f0cfd7345db", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "static/js/tags.js"}, "region": {"startLine": 21}}}]}, {"ruleId": "scanner-eb6c041184101d3a", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in static/js/mermaid_converter.js:55"}, "properties": {"repobilityId": "d7c1fc98bf2848a4", "scanner": "scanner-primary", "fingerprint": "eb6c041184101d3a", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "static/js/mermaid_converter.js"}, "region": {"startLine": 55}}}]}, {"ruleId": "scanner-6372cebde0220094", "level": "warning", "message": {"text": "No auth library detected"}, "properties": {"repobilityId": "a5b6035a5bbf8054", "scanner": "scanner-primary", "fingerprint": "6372cebde0220094", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["coverage", "auth"]}}, {"ruleId": "scanner-4601e3ad3bb28677", "level": "warning", "message": {"text": "No CI/CD pipelines detected"}, "properties": {"repobilityId": "c3ee439bce2bc51e", "scanner": "scanner-primary", "fingerprint": "4601e3ad3bb28677", "layer": "cicd", "severity": "medium", "confidence": 1.0, "tags": ["coverage"]}}, {"ruleId": "scanner-7ffe6673e43d6d89", "level": "note", "message": {"text": "Very large file: static/js/audio_converter.js (1467 lines)"}, "properties": {"repobilityId": "6c4e32a60958cf13", "scanner": "scanner-primary", "fingerprint": "7ffe6673e43d6d89", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-13910abc121f93e3", "level": "note", "message": {"text": "Very large file: static/js/library_detail.js (1599 lines)"}, "properties": {"repobilityId": "07823428aee08db1", "scanner": "scanner-primary", "fingerprint": "13910abc121f93e3", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-141b30a41e03817b", "level": "note", "message": {"text": "No license file detected"}, "properties": {"repobilityId": "bd598fe5d43a6a8c", "scanner": "scanner-primary", "fingerprint": "141b30a41e03817b", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["license", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-01f96f71d9907a8c", "level": "note", "message": {"text": "README lacks setup or run instructions"}, "properties": {"repobilityId": "9a2b37d2381b48e6", "scanner": "scanner-primary", "fingerprint": "01f96f71d9907a8c", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["docs", "readme", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-3ab5d313dda8e5f9", "level": "note", "message": {"text": "Debug logging residue appears in source files"}, "properties": {"repobilityId": "65f7b9df06207fab", "scanner": "scanner-primary", "fingerprint": "3ab5d313dda8e5f9", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["debug", "cleanup", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-72b2a6250083a784", "level": "warning", "message": {"text": "Placeholder or mock-heavy implementation detected"}, "properties": {"repobilityId": "6ac8cc50d9d8c394", "scanner": "scanner-primary", "fingerprint": "72b2a6250083a784", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["placeholder", "mock-data", "incomplete", "generated-repo-pattern"]}}, {"ruleId": "scanner-2d0c7b7ab8f8aacf", "level": "warning", "message": {"text": "Critical user flow still appears backed by mock or placeholder data"}, "properties": {"repobilityId": "335e4fedcd7ee87f", "scanner": "scanner-primary", "fingerprint": "2d0c7b7ab8f8aacf", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["placeholder", "mock-data", "critical-flow", "generated-repo-pattern"]}}, {"ruleId": "scanner-b9088664ace7f748", "level": "warning", "message": {"text": "Composite production-readiness gap"}, "properties": {"repobilityId": "3182b8c3619fba20", "scanner": "scanner-primary", "fingerprint": "b9088664ace7f748", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["production-readiness", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-749d4bc1bd66df5f", "level": "warning", "message": {"text": "Agent instructions exist but release-hardening basics are missing"}, "properties": {"repobilityId": "f25f471fbe3e5014", "scanner": "scanner-primary", "fingerprint": "749d4bc1bd66df5f", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["agent-instructions", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-ea8f3013f588db25", "level": "note", "message": {"text": "Shallow git history limits provenance confidence"}, "properties": {"repobilityId": "5b6c21c9b712007d", "scanner": "scanner-primary", "fingerprint": "ea8f3013f588db25", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["provenance", "git-history", "generated-repo-pattern"]}}, {"ruleId": "scanner-8424db9c75e04ba4", "level": "none", "message": {"text": "Very short observed git history"}, "properties": {"repobilityId": "e1a4b00436a52995", "scanner": "scanner-primary", "fingerprint": "8424db9c75e04ba4", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["provenance", "git-history", "generated-repo-pattern"]}}, {"ruleId": "scanner-aa60f2fc029528bc", "level": "warning", "message": {"text": "Network/subprocess call without timeout or try/except \u2014 test_full_flow.py:47"}, "properties": {"repobilityId": "dd2e323ed3499323", "scanner": "scanner-primary", "fingerprint": "aa60f2fc029528bc", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-6ca1092db422e025", "level": "none", "message": {"text": "Commented-code block (5 lines) in tests/test_reading_list.py:141"}, "properties": {"repobilityId": "b0931cac58d0f95f", "scanner": "scanner-primary", "fingerprint": "6ca1092db422e025", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-8de7bf2d70d812ab", "level": "none", "message": {"text": "Commented-code block (5 lines) in static/js/library.js:59"}, "properties": {"repobilityId": "ae55f27de392516d", "scanner": "scanner-primary", "fingerprint": "8de7bf2d70d812ab", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-ea1fdf3688f9c157", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 static/js/library.js:27"}, "properties": {"repobilityId": "b54822c9da1635f6", "scanner": "scanner-primary", "fingerprint": "ea1fdf3688f9c157", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-2d020fc04caf82af", "level": "none", "message": {"text": "Commented-code block (5 lines) in static/js/audio_converter.js:675"}, "properties": {"repobilityId": "7b83a5637102ad46", "scanner": "scanner-primary", "fingerprint": "2d020fc04caf82af", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-4a60ea76726fbe9d", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 static/js/audio_converter.js:172"}, "properties": {"repobilityId": "60353d05d94d6208", "scanner": "scanner-primary", "fingerprint": "4a60ea76726fbe9d", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-e54bd5dca159f37c", "level": "none", "message": {"text": "Commented-code block (5 lines) in static/js/library_detail.js:167"}, "properties": {"repobilityId": "f6271dfba6799ae1", "scanner": "scanner-primary", "fingerprint": "e54bd5dca159f37c", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-40a32ce91d2b454f", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 static/js/library_detail.js:54"}, "properties": {"repobilityId": "fd7d87f24c75efd1", "scanner": "scanner-primary", "fingerprint": "40a32ce91d2b454f", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-faa1813072f7b35c", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 static/js/markdown_converter.js:128"}, "properties": {"repobilityId": "8a51af196835c643", "scanner": "scanner-primary", "fingerprint": "faa1813072f7b35c", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-f18b2b98f5803e2e", "level": "warning", "message": {"text": "Network/subprocess call without timeout or try/except \u2014 services/audio_chunker.py:51"}, "properties": {"repobilityId": "573695a9b4e5e735", "scanner": "scanner-primary", "fingerprint": "f18b2b98f5803e2e", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-a6e39640fca7d085", "level": "none", "message": {"text": "Commented-code block (6 lines) in app_pkg/__init__.py:118"}, "properties": {"repobilityId": "96cdef8e5cc2f499", "scanner": "scanner-primary", "fingerprint": "a6e39640fca7d085", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-f5274ef5aff23c2d", "level": "none", "message": {"text": "Commented-code block (6 lines) in app_pkg/library.py:28"}, "properties": {"repobilityId": "dc424e1f9c004025", "scanner": "scanner-primary", "fingerprint": "f5274ef5aff23c2d", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-0272a44ffd3dc412", "level": "none", "message": {"text": "Commented-code block (6 lines) in app_pkg/config.py:12"}, "properties": {"repobilityId": "c6fe920e38f926e0", "scanner": "scanner-primary", "fingerprint": "0272a44ffd3dc412", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-b334d875f2339fa1", "level": "note", "message": {"text": "5 env vars used in code but missing from .env.example"}, "properties": {"repobilityId": "faa38682016f5d53", "scanner": "scanner-primary", "fingerprint": "b334d875f2339fa1", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "config-drift"]}}, {"ruleId": "scanner-be46ea126aa5d8dc", "level": "note", "message": {"text": "Near-duplicate function bodies in 3 places"}, "properties": {"repobilityId": "c4167d38bd9d0d4e", "scanner": "scanner-primary", "fingerprint": "be46ea126aa5d8dc", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-02525d39071dd2c7", "level": "note", "message": {"text": "Near-duplicate function bodies in 5 places"}, "properties": {"repobilityId": "6c1d2a2ff16d361d", "scanner": "scanner-primary", "fingerprint": "02525d39071dd2c7", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-49c98f7cedd9c977", "level": "note", "message": {"text": "Near-duplicate function bodies in 4 places"}, "properties": {"repobilityId": "7dfb652180bb696c", "scanner": "scanner-primary", "fingerprint": "49c98f7cedd9c977", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "487ff810fa5f34e1", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-0b46e7df1704e2c4", "level": "error", "message": {"text": "Flask mutation route `login` without `@login_required` \u2014 app_pkg/auth.py:17"}, "properties": {"repobilityId": "c947a3e1f9acecf5", "scanner": "scanner-primary", "fingerprint": "0b46e7df1704e2c4", "layer": "security", "severity": "high", "confidence": 1.0, "tags": ["auth", "owasp", "auth.flask.unauth_route"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "app_pkg/auth.py"}, "region": {"startLine": 17}}}]}, {"ruleId": "scanner-7a312d7396425bc3", "level": "error", "message": {"text": "Flask mutation route `api_ingest_conversion` without `@login_required` \u2014 app_pkg/ingest.py:117"}, "properties": {"repobilityId": "d5bd5d67c43c455d", "scanner": "scanner-primary", "fingerprint": "7a312d7396425bc3", "layer": "security", "severity": "high", "confidence": 1.0, "tags": ["auth", "owasp", "auth.flask.unauth_route"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "app_pkg/ingest.py"}, "region": {"startLine": 117}}}]}, {"ruleId": "scanner-3c0028e3fe1f947c", "level": "error", "message": {"text": "Dangling fetch: PUT /api/conversions/${id} (static/js/library.js:27)"}, "properties": {"repobilityId": "8ac9bd753765adb5", "scanner": "scanner-primary", "fingerprint": "3c0028e3fe1f947c", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-2fbea5eef0108815", "level": "error", "message": {"text": "Dangling fetch: PUT /api/conversions/${id} (static/js/library.js:51)"}, "properties": {"repobilityId": "15ca13a8ae32d519", "scanner": "scanner-primary", "fingerprint": "2fbea5eef0108815", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-17b851144e28ead1", "level": "error", "message": {"text": "Dangling fetch: DELETE /api/conversions/${id} (static/js/library.js:125)"}, "properties": {"repobilityId": "f071a70612ad874d", "scanner": "scanner-primary", "fingerprint": "17b851144e28ead1", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-9144c531ed1d69ba", "level": "error", "message": {"text": "Dangling fetch: POST /api/conversions/${id}/queue (static/js/library.js:154)"}, "properties": {"repobilityId": "62088e293769f528", "scanner": "scanner-primary", "fingerprint": "9144c531ed1d69ba", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-42cf49a9865655f5", "level": "error", "message": {"text": "Dangling fetch: POST /api/conversions/${id}/queue (static/js/library.js:195)"}, "properties": {"repobilityId": "4dd8bf05c596a4fe", "scanner": "scanner-primary", "fingerprint": "42cf49a9865655f5", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-1bb153f7a523f9c6", "level": "error", "message": {"text": "Dangling fetch: POST /podcast-cancel/${podcastJobId} (static/js/audio_converter.js:910)"}, "properties": {"repobilityId": "72e6f1ff4d71872e", "scanner": "scanner-primary", "fingerprint": "1bb153f7a523f9c6", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-0affa058ef3e588c", "level": "error", "message": {"text": "Dangling fetch: GET /podcast-status/${jobId} (static/js/audio_converter.js:1080)"}, "properties": {"repobilityId": "b77ab5514a3cec2e", "scanner": "scanner-primary", "fingerprint": "0affa058ef3e588c", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-59001600afe916a2", "level": "error", "message": {"text": "Dangling fetch: GET /podcast-download/${jobId} (static/js/audio_converter.js:1186)"}, "properties": {"repobilityId": "98e5c69a1ff934ea", "scanner": "scanner-primary", "fingerprint": "59001600afe916a2", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-cf84f010f815735a", "level": "error", "message": {"text": "Dangling fetch: GET /podcast-status/${jobId} (static/js/audio_converter.js:1307)"}, "properties": {"repobilityId": "3d277a0daf429ad5", "scanner": "scanner-primary", "fingerprint": "cf84f010f815735a", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-f87fc60e6375b20a", "level": "error", "message": {"text": "Dangling fetch: PUT /api/conversions/${CONVERSION_ID} (static/js/library_detail.js:54)"}, "properties": {"repobilityId": "054b942506d7fab6", "scanner": "scanner-primary", "fingerprint": "f87fc60e6375b20a", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-91ece29ac844ae16", "level": "error", "message": {"text": "Dangling fetch: PUT /api/conversions/${CONVERSION_ID} (static/js/library_detail.js:92)"}, "properties": {"repobilityId": "f1979a1611e9702c", "scanner": "scanner-primary", "fingerprint": "91ece29ac844ae16", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-9973937b50b178ef", "level": "error", "message": {"text": "Dangling fetch: POST /api/conversions/${CONVERSION_ID}/queue (static/js/library_detail.js:117)"}, "properties": {"repobilityId": "8484d12317d8d9e5", "scanner": "scanner-primary", "fingerprint": "9973937b50b178ef", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-fa4d54c7ebdce843", "level": "error", "message": {"text": "Dangling fetch: PUT /api/conversions/${CONVERSION_ID} (static/js/library_detail.js:143)"}, "properties": {"repobilityId": "cca7ebffc0b9fc46", "scanner": "scanner-primary", "fingerprint": "fa4d54c7ebdce843", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-8222515c24b96789", "level": "error", "message": {"text": "Dangling fetch: PUT /api/conversions/${CONVERSION_ID} (static/js/library_detail.js:173)"}, "properties": {"repobilityId": "c132ccf283e2440d", "scanner": "scanner-primary", "fingerprint": "8222515c24b96789", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-112b2a33cf4d339c", "level": "error", "message": {"text": "Dangling fetch: DELETE /api/conversions/${CONVERSION_ID} (static/js/library_detail.js:247)"}, "properties": {"repobilityId": "d54b875975023ce6", "scanner": "scanner-primary", "fingerprint": "112b2a33cf4d339c", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-62af52355b17c37d", "level": "error", "message": {"text": "Dangling fetch: POST /api/conversions/${CONVERSION_ID}/send-to-notion (static/js/library_detail.js:463)"}, "properties": {"repobilityId": "a0854675be18c813", "scanner": "scanner-primary", "fingerprint": "62af52355b17c37d", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-ce6bc345a89e85dc", "level": "error", "message": {"text": "Dangling fetch: POST /api/conversions/${CONVERSION_ID}/tags (static/js/library_detail.js:544)"}, "properties": {"repobilityId": "81581c150bf6ad47", "scanner": "scanner-primary", "fingerprint": "ce6bc345a89e85dc", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-745092806a67f4bb", "level": "error", "message": {"text": "Dangling fetch: DELETE /api/conversions/${CONVERSION_ID}/tags/${tagId} (static/js/library_detail.js:576)"}, "properties": {"repobilityId": "17b67f9a70ca7785", "scanner": "scanner-primary", "fingerprint": "745092806a67f4bb", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-e8023bb6fa8f989c", "level": "error", "message": {"text": "Dangling fetch: POST /api/conversions/${CONVERSION_ID}/highlights (static/js/library_detail.js:746)"}, "properties": {"repobilityId": "20a6bbfb8b241711", "scanner": "scanner-primary", "fingerprint": "e8023bb6fa8f989c", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-50b73870e6b10787", "level": "error", "message": {"text": "Dangling fetch: GET /api/conversions/${CONVERSION_ID}/highlights (static/js/library_detail.js:775)"}, "properties": {"repobilityId": "ae078d8621df9ed2", "scanner": "scanner-primary", "fingerprint": "50b73870e6b10787", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-ecd2403f029736f5", "level": "error", "message": {"text": "Dangling fetch: POST /api/highlights/${id}/tags (static/js/library_detail.js:1179)"}, "properties": {"repobilityId": "e9098b89210f90a0", "scanner": "scanner-primary", "fingerprint": "ecd2403f029736f5", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-6808f54f7acfc4f9", "level": "error", "message": {"text": "Dangling fetch: DELETE /api/highlights/${id}/tags/${tagId} (static/js/library_detail.js:1224)"}, "properties": {"repobilityId": "5e5c4c1de7b1af4c", "scanner": "scanner-primary", "fingerprint": "6808f54f7acfc4f9", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-f8de5ccd9e761fb0", "level": "error", "message": {"text": "Dangling fetch: PATCH /api/highlights/${id} (static/js/library_detail.js:1251)"}, "properties": {"repobilityId": "f02bd4213c5a2492", "scanner": "scanner-primary", "fingerprint": "f8de5ccd9e761fb0", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-77df924271df6e95", "level": "error", "message": {"text": "Dangling fetch: DELETE /api/highlights/${id} (static/js/library_detail.js:1277)"}, "properties": {"repobilityId": "98ee87b140c7a096", "scanner": "scanner-primary", "fingerprint": "77df924271df6e95", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-e29edcca147f885a", "level": "error", "message": {"text": "Dangling fetch: PATCH /api/conversions/${CONVERSION_ID}/progress (static/js/library_detail.js:1437)"}, "properties": {"repobilityId": "f5f1d12f53291f18", "scanner": "scanner-primary", "fingerprint": "e29edcca147f885a", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-7285aebc00879b31", "level": "error", "message": {"text": "Dangling fetch: PATCH /api/conversions/${CONVERSION_ID}/progress (static/js/library_detail.js:1519)"}, "properties": {"repobilityId": "7ae8eba7db00f4f6", "scanner": "scanner-primary", "fingerprint": "7285aebc00879b31", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-861020437407a1fa", "level": "error", "message": {"text": "Dangling fetch: GET /static/css/pdf_styles/${theme}.css (static/js/markdown_converter.js:300)"}, "properties": {"repobilityId": "6f2c0d651d882d97", "scanner": "scanner-primary", "fingerprint": "861020437407a1fa", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-91a93cb8c1486052", "level": "error", "message": {"text": "Dangling fetch: DELETE /api/tags/${tagId} (static/js/tags.js:61)"}, "properties": {"repobilityId": "00eb4d17245a6a0f", "scanner": "scanner-primary", "fingerprint": "91a93cb8c1486052", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-3d47f5777840c8d6", "level": "note", "message": {"text": "Unused endpoint: ANY /library"}, "properties": {"repobilityId": "c814c74e9a5e5687", "scanner": "scanner-primary", "fingerprint": "3d47f5777840c8d6", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-1749db7e736b0619", "level": "note", "message": {"text": "Unused endpoint: ANY /library/<int:conversion_id>"}, "properties": {"repobilityId": "6fad1056f1804c4e", "scanner": "scanner-primary", "fingerprint": "1749db7e736b0619", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-b953d30771cff125", "level": "note", "message": {"text": "Unused endpoint: ANY /api/conversions/<int:conversion_id>"}, "properties": {"repobilityId": "7ae0727556dc33db", "scanner": "scanner-primary", "fingerprint": "b953d30771cff125", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-211af594c5bcaab3", "level": "note", "message": {"text": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/progress"}, "properties": {"repobilityId": "242829ffb8b0d583", "scanner": "scanner-primary", "fingerprint": "211af594c5bcaab3", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-97db0c407728f6df", "level": "note", "message": {"text": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/place"}, "properties": {"repobilityId": "f217cb8fcc4667d9", "scanner": "scanner-primary", "fingerprint": "97db0c407728f6df", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-ba227e3488ef4f8a", "level": "note", "message": {"text": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/queue"}, "properties": {"repobilityId": "32d2e3f8cf1f75b9", "scanner": "scanner-primary", "fingerprint": "ba227e3488ef4f8a", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-1b9a5b7c9489378c", "level": "note", "message": {"text": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/tags"}, "properties": {"repobilityId": "b0f0d1571872076b", "scanner": "scanner-primary", "fingerprint": "1b9a5b7c9489378c", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-469484c2f382b511", "level": "note", "message": {"text": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/tags/<int:tag_id>"}, "properties": {"repobilityId": "39226d431be6452b", "scanner": "scanner-primary", "fingerprint": "469484c2f382b511", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-4c7ad61ba1b23915", "level": "note", "message": {"text": "Unused endpoint: ANY /login"}, "properties": {"repobilityId": "7e64a2fdb22fc5ad", "scanner": "scanner-primary", "fingerprint": "4c7ad61ba1b23915", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-c9e449c3b9e73b73", "level": "note", "message": {"text": "Unused endpoint: ANY /logout"}, "properties": {"repobilityId": "c1d56806d8cd5213", "scanner": "scanner-primary", "fingerprint": "c9e449c3b9e73b73", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-b97afe2fdf389a9a", "level": "note", "message": {"text": "Unused endpoint: ANY /generate-podcast"}, "properties": {"repobilityId": "1145fe4d36426cec", "scanner": "scanner-primary", "fingerprint": "b97afe2fdf389a9a", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-eeecf7eb13ecd50d", "level": "note", "message": {"text": "Unused endpoint: ANY /api/get-google-voices"}, "properties": {"repobilityId": "b4761ecf19117921", "scanner": "scanner-primary", "fingerprint": "eeecf7eb13ecd50d", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-26c76d3972599c54", "level": "note", "message": {"text": "Unused endpoint: ANY /podcast-status/<job_id>"}, "properties": {"repobilityId": "f3d9b4b36d93e861", "scanner": "scanner-primary", "fingerprint": "26c76d3972599c54", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-fdaa9fcc6c99ccae", "level": "note", "message": {"text": "Unused endpoint: ANY /podcast-cancel/<job_id>"}, "properties": {"repobilityId": "1738b0934378ce41", "scanner": "scanner-primary", "fingerprint": "fdaa9fcc6c99ccae", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-bed17f7f2d330b30", "level": "note", "message": {"text": "Unused endpoint: ANY /podcast-download/<job_id>"}, "properties": {"repobilityId": "05470dfefdea7a5e", "scanner": "scanner-primary", "fingerprint": "bed17f7f2d330b30", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-1907fac19d43a278", "level": "note", "message": {"text": "Unused endpoint: ANY /api/get-gemini-voices"}, "properties": {"repobilityId": "03a052e28663bb91", "scanner": "scanner-primary", "fingerprint": "1907fac19d43a278", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-34278c8f06851986", "level": "note", "message": {"text": "Unused endpoint: ANY /audio-converter"}, "properties": {"repobilityId": "7fd2f8630310dd64", "scanner": "scanner-primary", "fingerprint": "34278c8f06851986", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-fff17b0708d45152", "level": "note", "message": {"text": "Unused endpoint: ANY /mermaid-converter"}, "properties": {"repobilityId": "4105a52c0ab7e6a1", "scanner": "scanner-primary", "fingerprint": "fff17b0708d45152", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-9dd5db53dcf95a2b", "level": "note", "message": {"text": "Unused endpoint: ANY /api/ingest/conversion"}, "properties": {"repobilityId": "d822829f07474674", "scanner": "scanner-primary", "fingerprint": "9dd5db53dcf95a2b", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-e8b3d1fa5e2f4b0f", "level": "note", "message": {"text": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/highlights"}, "properties": {"repobilityId": "73eea599d4387530", "scanner": "scanner-primary", "fingerprint": "e8b3d1fa5e2f4b0f", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-a2aebe5ab6fefd02", "level": "note", "message": {"text": "Unused endpoint: ANY /api/highlights/<int:highlight_id>"}, "properties": {"repobilityId": "29236c6276edea1d", "scanner": "scanner-primary", "fingerprint": "a2aebe5ab6fefd02", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-bf876c650798eb55", "level": "note", "message": {"text": "Unused endpoint: ANY /document-converter"}, "properties": {"repobilityId": "13187ef2305ad065", "scanner": "scanner-primary", "fingerprint": "bf876c650798eb55", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-11388f23814d03bd", "level": "note", "message": {"text": "Unused endpoint: ANY /transform-document"}, "properties": {"repobilityId": "7f45c5183c787db3", "scanner": "scanner-primary", "fingerprint": "11388f23814d03bd", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-1a6d91f2236825bf", "level": "note", "message": {"text": "Unused endpoint: ANY /"}, "properties": {"repobilityId": "8841caa90429d52c", "scanner": "scanner-primary", "fingerprint": "1a6d91f2236825bf", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-10632aefbdfb84e1", "level": "note", "message": {"text": "Unused endpoint: ANY /convert-markdown"}, "properties": {"repobilityId": "e4ecae78ccc0489e", "scanner": "scanner-primary", "fingerprint": "10632aefbdfb84e1", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-7d5d8aabe3340ac6", "level": "note", "message": {"text": "Unused endpoint: ANY /api/tags"}, "properties": {"repobilityId": "a4045bfb77cad5a8", "scanner": "scanner-primary", "fingerprint": "7d5d8aabe3340ac6", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-9fdb2106da0264c4", "level": "note", "message": {"text": "Unused endpoint: ANY /api/highlights/<int:highlight_id>/tags"}, "properties": {"repobilityId": "9fff7b3cfca2ea9d", "scanner": "scanner-primary", "fingerprint": "9fdb2106da0264c4", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-619cfad737b6028f", "level": "note", "message": {"text": "Unused endpoint: ANY /api/highlights/<int:highlight_id>/tags/<int:tag_id>"}, "properties": {"repobilityId": "c41409e8397999f6", "scanner": "scanner-primary", "fingerprint": "619cfad737b6028f", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-3e35565e57e41812", "level": "note", "message": {"text": "Unused endpoint: ANY /api/tags/<int:tag_id>"}, "properties": {"repobilityId": "85137d567f45f843", "scanner": "scanner-primary", "fingerprint": "3e35565e57e41812", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-1c38dcd1fcf37f56", "level": "note", "message": {"text": "Unused endpoint: ANY /api/conversions/<int:conversion_id>/send-to-notion"}, "properties": {"repobilityId": "521861f00c1a6bf4", "scanner": "scanner-primary", "fingerprint": "1c38dcd1fcf37f56", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}]}]}