{"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-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-de3c1f217d72a063", "name": "Docker base image is tag-pinned but not digest-pinned: python:3.12-slim", "shortDescription": {"text": "Docker base image is tag-pinned but not digest-pinned: python:3.12-slim"}, "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-e5cc447ba39f22b1", "name": "Insecure pattern 'insert_adjacent_html' in static/js/bill.js:165", "shortDescription": {"text": "Insecure pattern 'insert_adjacent_html' in static/js/bill.js:165"}, "fullDescription": {"text": "Found a known-risky pattern (insert_adjacent_html). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-6dbb07bfd9e65435", "name": "Insecure pattern 'domparser_html_parse' in static/js/bill.js:9", "shortDescription": {"text": "Insecure pattern 'domparser_html_parse' in static/js/bill.js:9"}, "fullDescription": {"text": "Found a known-risky pattern (domparser_html_parse). 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-131a25bfb6a45dc6", "name": "GitHub Action is tag-pinned rather than SHA-pinned", "shortDescription": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "fullDescription": {"text": "actions/checkout@v4 can move without a code change in this repo. Pin third-party actions to a reviewed 40-character commit SHA."}, "properties": {"scanner": "scanner-primary", "layer": "cicd", "severity": "low", "confidence": 1.0}}, {"id": "scanner-96bc5cba2304a1b9", "name": "GitHub Actions workflow grants broad write permissions", "shortDescription": {"text": "GitHub Actions workflow grants broad write permissions"}, "fullDescription": {"text": "CI tokens with write permissions increase blast radius when an action, dependency, or PR workflow is compromised. Prefer job-level least-privilege permissions."}, "properties": {"scanner": "scanner-primary", "layer": "cicd", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-1a50467f36b413ec", "name": "GitHub Action is tag-pinned rather than SHA-pinned", "shortDescription": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "fullDescription": {"text": "actions/checkout@v4 can move without a code change in this repo. Pin third-party actions to a reviewed 40-character commit SHA."}, "properties": {"scanner": "scanner-primary", "layer": "cicd", "severity": "low", "confidence": 1.0}}, {"id": "scanner-90e6b78b112cfdc5", "name": "GitHub Action is tag-pinned rather than SHA-pinned", "shortDescription": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "fullDescription": {"text": "actions/checkout@v4 can move without a code change in this repo. Pin third-party actions to a reviewed 40-character commit SHA."}, "properties": {"scanner": "scanner-primary", "layer": "cicd", "severity": "low", "confidence": 1.0}}, {"id": "scanner-6003026d5dd9be5a", "name": "GitHub Actions workflow grants broad write permissions", "shortDescription": {"text": "GitHub Actions workflow grants broad write permissions"}, "fullDescription": {"text": "CI tokens with write permissions increase blast radius when an action, dependency, or PR workflow is compromised. Prefer job-level least-privilege permissions."}, "properties": {"scanner": "scanner-primary", "layer": "cicd", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-fadd4dd09694e339", "name": "Very large file: sync.py (2162 lines)", "shortDescription": {"text": "Very large file: sync.py (2162 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-faccb9061e9b52a0", "name": "No README detected", "shortDescription": {"text": "No README detected"}, "fullDescription": {"text": "No README file was found. Generated repos without README context are hard to operate, validate, or safely hand off."}, "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 269 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-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, 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": "low", "confidence": 1.0}}, {"id": "scanner-281b7f1c901f1e86", "name": "`fetch()` without try/.catch or AbortSignal \u2014 static/js/feedback.js:144", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 static/js/feedback.js:144"}, "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-b5da946bb82a5711", "name": "`fetch()` without try/.catch or AbortSignal \u2014 static/js/bill.js:395", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 static/js/bill.js:395"}, "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-319e66a630e14977", "name": "2 env vars used in code but missing from .env.example", "shortDescription": {"text": "2 env vars used in code but missing from .env.example"}, "fullDescription": {"text": "Drift between code and config docs. The first few: `GOOGLE_SHEETS_CREDENTIALS_JSON`, `GOOGLE_SHEETS_SPREADSHEET_ID`. Add them (with a placeholder/comment) to .env.example so onboarding doesn't break."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "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:\nsync.py:sync_bill_summaries, sync.py:sync_bill_arguments\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:\nsync.py:writer_fn, sync.py:writer_fn, sync.py:writer_fn, sync.py:writer_fn\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-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:\napp/services/summary_grader.py:load_learnings, app/services/arguments_grader.py:load_learnings, app/services/member_narrative_grader.py:load_learnings\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-1138191ff4654aa5", "name": "FastAPI POST `submit_signup` without auth dependency \u2014 app/routers/notify.py:36", "shortDescription": {"text": "FastAPI POST `submit_signup` without auth dependency \u2014 app/routers/notify.py:36"}, "fullDescription": {"text": "`@router.post` decorator with no `Depends(get_current_user)` or auth-shaped dependency in its signature. Mutating endpoints should require authentication unless explicitly public."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "high", "confidence": 1.0}}, {"id": "scanner-cbb81f7dfde6dd22", "name": "FastAPI POST `submit_feedback` without auth dependency \u2014 app/routers/feedback.py:29", "shortDescription": {"text": "FastAPI POST `submit_feedback` without auth dependency \u2014 app/routers/feedback.py:29"}, "fullDescription": {"text": "`@router.post` decorator with no `Depends(get_current_user)` or auth-shaped dependency in its signature. Mutating endpoints should require authentication unless explicitly public."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "high", "confidence": 1.0}}, {"id": "scanner-a1e1e29b8a0f31c6", "name": "Dangling fetch: POST /api/feedback (static/js/feedback.js:144)", "shortDescription": {"text": "Dangling fetch: POST /api/feedback (static/js/feedback.js:144)"}, "fullDescription": {"text": "`static/js/feedback.js:144` calls `POST /api/feedback` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/feedback`\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-d581dfb6c5f304ca", "name": "Dangling fetch: GET /api/members/counts (static/js/app.js:107)", "shortDescription": {"text": "Dangling fetch: GET /api/members/counts (static/js/app.js:107)"}, "fullDescription": {"text": "`static/js/app.js:107` calls `GET /api/members/counts` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/members/counts`\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-bdbe8d3882a9ae7c", "name": "Dangling fetch: GET /api/votes/latest (static/js/app.js:121)", "shortDescription": {"text": "Dangling fetch: GET /api/votes/latest (static/js/app.js:121)"}, "fullDescription": {"text": "`static/js/app.js:121` calls `GET /api/votes/latest` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/votes/latest`\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-5d681681e733f1a8", "name": "Dangling fetch: POST /api/notify (static/js/app.js:391)", "shortDescription": {"text": "Dangling fetch: POST /api/notify (static/js/app.js:391)"}, "fullDescription": {"text": "`static/js/app.js:391` calls `POST /api/notify` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/notify`\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-113e320a959130f2", "name": "Dangling fetch: GET /api/search/bills?q=${encodeURIComponent(query)}&limit=50 (static/js/app.js:499)", "shortDescription": {"text": "Dangling fetch: GET /api/search/bills?q=${encodeURIComponent(query)}&limit=50 (static/js/app.js:499)"}, "fullDescription": {"text": "`static/js/app.js:499` calls `GET /api/search/bills?q=${encodeURIComponent(query)}&limit=50` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/search/bills`\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-622bf96aed1d9c07", "name": "Dangling fetch: GET /api/bills/${congress}/${type}/${number} (static/js/bill.js:67)", "shortDescription": {"text": "Dangling fetch: GET /api/bills/${congress}/${type}/${number} (static/js/bill.js:67)"}, "fullDescription": {"text": "`static/js/bill.js:67` calls `GET /api/bills/${congress}/${type}/${number}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/bills/<p>/<p>/<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-b1c4c171a5286dbc", "name": "Dangling fetch: GET /api/bills/${congress}/${type}/${number}/ai-summary (static/js/bill.js:232)", "shortDescription": {"text": "Dangling fetch: GET /api/bills/${congress}/${type}/${number}/ai-summary (static/js/bill.js:232)"}, "fullDescription": {"text": "`static/js/bill.js:232` calls `GET /api/bills/${congress}/${type}/${number}/ai-summary` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/bills/<p>/<p>/<p>/ai-summary`\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-8b97954f71558522", "name": "Dangling fetch: GET /api/bills/${congress}/${type}/${number}/votes (static/js/bill.js:302)", "shortDescription": {"text": "Dangling fetch: GET /api/bills/${congress}/${type}/${number}/votes (static/js/bill.js:302)"}, "fullDescription": {"text": "`static/js/bill.js:302` calls `GET /api/bills/${congress}/${type}/${number}/votes` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/bills/<p>/<p>/<p>/votes`\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-9c163b2a8d0eb93d", "name": "Dangling fetch: GET /api/members/detail/${bioguideId} (static/js/member.js:52)", "shortDescription": {"text": "Dangling fetch: GET /api/members/detail/${bioguideId} (static/js/member.js:52)"}, "fullDescription": {"text": "`static/js/member.js:52` calls `GET /api/members/detail/${bioguideId}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/members/detail/<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-ad32dedcbe4f015f", "name": "Dangling fetch: GET /api/members/detail/${bioguideId}?show_party=true (static/js/member.js:97)", "shortDescription": {"text": "Dangling fetch: GET /api/members/detail/${bioguideId}?show_party=true (static/js/member.js:97)"}, "fullDescription": {"text": "`static/js/member.js:97` calls `GET /api/members/detail/${bioguideId}?show_party=true` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/members/detail/<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-425d988d788ef9b0", "name": "Dangling fetch: GET /api/members/detail/${bioguideId}?show_party=true (static/js/member.js:149)", "shortDescription": {"text": "Dangling fetch: GET /api/members/detail/${bioguideId}?show_party=true (static/js/member.js:149)"}, "fullDescription": {"text": "`static/js/member.js:149` calls `GET /api/members/detail/${bioguideId}?show_party=true` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/members/detail/<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-866954014d43d498", "name": "Dangling fetch: GET /api/members/${bioguideId}/sponsored (static/js/member.js:370)", "shortDescription": {"text": "Dangling fetch: GET /api/members/${bioguideId}/sponsored (static/js/member.js:370)"}, "fullDescription": {"text": "`static/js/member.js:370` calls `GET /api/members/${bioguideId}/sponsored` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/members/<p>/sponsored`\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-f4669dc29aa2ffcb", "name": "Dangling fetch: GET /api/members/${bioguideId}/votes?limit=2000 (static/js/member.js:459)", "shortDescription": {"text": "Dangling fetch: GET /api/members/${bioguideId}/votes?limit=2000 (static/js/member.js:459)"}, "fullDescription": {"text": "`static/js/member.js:459` calls `GET /api/members/${bioguideId}/votes?limit=2000` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/members/<p>/votes`\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-c19497e9ca5c3efa", "name": "Dangling fetch: GET /api/members/${bioguideId}/summary (static/js/member.js:460)", "shortDescription": {"text": "Dangling fetch: GET /api/members/${bioguideId}/summary (static/js/member.js:460)"}, "fullDescription": {"text": "`static/js/member.js:460` calls `GET /api/members/${bioguideId}/summary` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/members/<p>/summary`\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-7a82eed9534a0755", "name": "Dangling fetch: GET /api/members/${bioguideId}/donations (static/js/member.js:921)", "shortDescription": {"text": "Dangling fetch: GET /api/members/${bioguideId}/donations (static/js/member.js:921)"}, "fullDescription": {"text": "`static/js/member.js:921` calls `GET /api/members/${bioguideId}/donations` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/members/<p>/donations`\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-b542ec8b6118065c", "name": "Dangling fetch: GET /api/members/${stateCode}/overview (static/js/state.js:77)", "shortDescription": {"text": "Dangling fetch: GET /api/members/${stateCode}/overview (static/js/state.js:77)"}, "fullDescription": {"text": "`static/js/state.js:77` calls `GET /api/members/${stateCode}/overview` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/members/<p>/overview`\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-5baa8971ebe192a1", "name": "Unused endpoint: GET /", "shortDescription": {"text": "Unused endpoint: GET /"}, "fullDescription": {"text": "`app/main.py` declares `GET /` 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-5ce04017f50a9349", "name": "Unused endpoint: GET /member", "shortDescription": {"text": "Unused endpoint: GET /member"}, "fullDescription": {"text": "`app/main.py` declares `GET /member` 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-b7e950ec470b782d", "name": "Unused endpoint: GET /bill", "shortDescription": {"text": "Unused endpoint: GET /bill"}, "fullDescription": {"text": "`app/main.py` declares `GET /bill` 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-5fff7d7c74ad81e4", "name": "Unused endpoint: GET /about", "shortDescription": {"text": "Unused endpoint: GET /about"}, "fullDescription": {"text": "`app/main.py` declares `GET /about` 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-24127e99f5c184fb", "name": "Unused endpoint: GET /state", "shortDescription": {"text": "Unused endpoint: GET /state"}, "fullDescription": {"text": "`app/main.py` declares `GET /state` 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-7a009b1a56794f45", "name": "Unused endpoint: POST /", "shortDescription": {"text": "Unused endpoint: POST /"}, "fullDescription": {"text": "`app/routers/notify.py` declares `POST /` 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-0f663fbd44c1d8e7", "name": "Unused endpoint: GET /{congress}/{bill_type}/{bill_number}", "shortDescription": {"text": "Unused endpoint: GET /{congress}/{bill_type}/{bill_number}"}, "fullDescription": {"text": "`app/routers/bills.py` declares `GET /{congress}/{bill_type}/{bill_number}` 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-6912008048b73964", "name": "Unused endpoint: GET /{congress}/{bill_type}/{bill_number}/ai-summary", "shortDescription": {"text": "Unused endpoint: GET /{congress}/{bill_type}/{bill_number}/ai-summary"}, "fullDescription": {"text": "`app/routers/bills.py` declares `GET /{congress}/{bill_type}/{bill_number}/ai-summary` 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-35aab798c25a1569", "name": "Unused endpoint: GET /{congress}/{bill_type}/{bill_number}/votes", "shortDescription": {"text": "Unused endpoint: GET /{congress}/{bill_type}/{bill_number}/votes"}, "fullDescription": {"text": "`app/routers/bills.py` declares `GET /{congress}/{bill_type}/{bill_number}/votes` 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-5ceac7791188c3d1", "name": "Unused endpoint: GET /{bioguide_id}/donations", "shortDescription": {"text": "Unused endpoint: GET /{bioguide_id}/donations"}, "fullDescription": {"text": "`app/routers/members.py` declares `GET /{bioguide_id}/donations` 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-4917400abe62316e", "name": "Unused endpoint: GET /{bioguide_id}/sponsored", "shortDescription": {"text": "Unused endpoint: GET /{bioguide_id}/sponsored"}, "fullDescription": {"text": "`app/routers/members.py` declares `GET /{bioguide_id}/sponsored` 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-39bf98a0d91fe84d", "name": "Unused endpoint: GET /{bioguide_id}/votes", "shortDescription": {"text": "Unused endpoint: GET /{bioguide_id}/votes"}, "fullDescription": {"text": "`app/routers/members.py` declares `GET /{bioguide_id}/votes` 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-570514defa75c007", "name": "Unused endpoint: GET /{bioguide_id}/summary", "shortDescription": {"text": "Unused endpoint: GET /{bioguide_id}/summary"}, "fullDescription": {"text": "`app/routers/members.py` declares `GET /{bioguide_id}/summary` 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-2f102c8ae6c444d2", "name": "Unused endpoint: GET /counts", "shortDescription": {"text": "Unused endpoint: GET /counts"}, "fullDescription": {"text": "`app/routers/members.py` declares `GET /counts` 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-208b430af298c9b0", "name": "Unused endpoint: GET /{state_code}/overview", "shortDescription": {"text": "Unused endpoint: GET /{state_code}/overview"}, "fullDescription": {"text": "`app/routers/members.py` declares `GET /{state_code}/overview` 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-28a56c2bd1acd303", "name": "Unused endpoint: GET /{state_code}", "shortDescription": {"text": "Unused endpoint: GET /{state_code}"}, "fullDescription": {"text": "`app/routers/members.py` declares `GET /{state_code}` 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-9d095b258bfe3997", "name": "Unused endpoint: GET /detail/{bioguide_id}", "shortDescription": {"text": "Unused endpoint: GET /detail/{bioguide_id}"}, "fullDescription": {"text": "`app/routers/members.py` declares `GET /detail/{bioguide_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-81090702e5b5a42a", "name": "Unused endpoint: GET /{state_code}/{district}", "shortDescription": {"text": "Unused endpoint: GET /{state_code}/{district}"}, "fullDescription": {"text": "`app/routers/members.py` declares `GET /{state_code}/{district}` 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-37157900d71c21d3", "name": "Unused endpoint: GET /latest", "shortDescription": {"text": "Unused endpoint: GET /latest"}, "fullDescription": {"text": "`app/routers/votes.py` declares `GET /latest` 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-95dbcd4df22ec825", "name": "Unused endpoint: GET /senate/{congress}/{session}/{vote_number}", "shortDescription": {"text": "Unused endpoint: GET /senate/{congress}/{session}/{vote_number}"}, "fullDescription": {"text": "`app/routers/votes.py` declares `GET /senate/{congress}/{session}/{vote_number}` 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-dfb016f2889e6a7d", "name": "Unused endpoint: GET /house/{congress}/{session}/{vote_number}", "shortDescription": {"text": "Unused endpoint: GET /house/{congress}/{session}/{vote_number}"}, "fullDescription": {"text": "`app/routers/votes.py` declares `GET /house/{congress}/{session}/{vote_number}` 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/25006"}, "properties": {"repository": "Clearvoting/clearvoting", "repoUrl": "https://github.com/Clearvoting/clearvoting", "branch": "main"}, "results": [{"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-de3c1f217d72a063", "level": "note", "message": {"text": "Docker base image is tag-pinned but not digest-pinned: python:3.12-slim"}, "properties": {"repobilityId": "1de0ecd007803dbd", "scanner": "scanner-primary", "fingerprint": "de3c1f217d72a063", "layer": "hardware", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "docker", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "Dockerfile"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-e5cc447ba39f22b1", "level": "warning", "message": {"text": "Insecure pattern 'insert_adjacent_html' in static/js/bill.js:165"}, "properties": {"repobilityId": "213a1782ed4ff0f9", "scanner": "scanner-primary", "fingerprint": "e5cc447ba39f22b1", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "insert_adjacent_html"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "static/js/bill.js"}, "region": {"startLine": 165}}}]}, {"ruleId": "scanner-6dbb07bfd9e65435", "level": "warning", "message": {"text": "Insecure pattern 'domparser_html_parse' in static/js/bill.js:9"}, "properties": {"repobilityId": "934c966d93ad486a", "scanner": "scanner-primary", "fingerprint": "6dbb07bfd9e65435", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "domparser_html_parse"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "static/js/bill.js"}, "region": {"startLine": 9}}}]}, {"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-131a25bfb6a45dc6", "level": "note", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "9d81ab6ebfb39f4c", "scanner": "scanner-primary", "fingerprint": "131a25bfb6a45dc6", "layer": "cicd", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/weekly-sync.yml"}, "region": {"startLine": 31}}}]}, {"ruleId": "scanner-131a25bfb6a45dc6", "level": "note", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "f7367ca49de4de58", "scanner": "scanner-primary", "fingerprint": "131a25bfb6a45dc6", "layer": "cicd", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/weekly-sync.yml"}, "region": {"startLine": 34}}}]}, {"ruleId": "scanner-96bc5cba2304a1b9", "level": "warning", "message": {"text": "GitHub Actions workflow grants broad write permissions"}, "properties": {"repobilityId": "7fbcb5c705e859fa", "scanner": "scanner-primary", "fingerprint": "96bc5cba2304a1b9", "layer": "cicd", "severity": "medium", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "least-privilege"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/weekly-sync.yml"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-1a50467f36b413ec", "level": "note", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "aecc3f31ca3f42da", "scanner": "scanner-primary", "fingerprint": "1a50467f36b413ec", "layer": "cicd", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/tests.yml"}, "region": {"startLine": 19}}}]}, {"ruleId": "scanner-1a50467f36b413ec", "level": "note", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "926b5aa2fc342af4", "scanner": "scanner-primary", "fingerprint": "1a50467f36b413ec", "layer": "cicd", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/tests.yml"}, "region": {"startLine": 22}}}]}, {"ruleId": "scanner-90e6b78b112cfdc5", "level": "note", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "0c41fdf96719844b", "scanner": "scanner-primary", "fingerprint": "90e6b78b112cfdc5", "layer": "cicd", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/ai-sync.yml"}, "region": {"startLine": 42}}}]}, {"ruleId": "scanner-90e6b78b112cfdc5", "level": "note", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "c2639a3b4cca26ab", "scanner": "scanner-primary", "fingerprint": "90e6b78b112cfdc5", "layer": "cicd", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/ai-sync.yml"}, "region": {"startLine": 46}}}]}, {"ruleId": "scanner-6003026d5dd9be5a", "level": "warning", "message": {"text": "GitHub Actions workflow grants broad write permissions"}, "properties": {"repobilityId": "69af4d90dfc067e1", "scanner": "scanner-primary", "fingerprint": "6003026d5dd9be5a", "layer": "cicd", "severity": "medium", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "least-privilege"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/ai-sync.yml"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-fadd4dd09694e339", "level": "note", "message": {"text": "Very large file: sync.py (2162 lines)"}, "properties": {"repobilityId": "6c7ebde6343a16ad", "scanner": "scanner-primary", "fingerprint": "fadd4dd09694e339", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["complexity"]}}, {"ruleId": "scanner-141b30a41e03817b", "level": "note", "message": {"text": "No license file detected"}, "properties": {"repobilityId": "3a8777aadecb83a8", "scanner": "scanner-primary", "fingerprint": "141b30a41e03817b", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["license", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-faccb9061e9b52a0", "level": "note", "message": {"text": "No README detected"}, "properties": {"repobilityId": "012567bbc90e9834", "scanner": "scanner-primary", "fingerprint": "faccb9061e9b52a0", "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": "bc21745296e3964d", "scanner": "scanner-primary", "fingerprint": "3ab5d313dda8e5f9", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["debug", "cleanup", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-2d0c7b7ab8f8aacf", "level": "warning", "message": {"text": "Critical user flow still appears backed by mock or placeholder data"}, "properties": {"repobilityId": "58d6c70911e301ba", "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": "note", "message": {"text": "Composite production-readiness gap"}, "properties": {"repobilityId": "382347c7801c519f", "scanner": "scanner-primary", "fingerprint": "b9088664ace7f748", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["production-readiness", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-281b7f1c901f1e86", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 static/js/feedback.js:144"}, "properties": {"repobilityId": "9dfacce889a221a1", "scanner": "scanner-primary", "fingerprint": "281b7f1c901f1e86", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-b5da946bb82a5711", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 static/js/bill.js:395"}, "properties": {"repobilityId": "d4222439ee7de3a0", "scanner": "scanner-primary", "fingerprint": "b5da946bb82a5711", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-319e66a630e14977", "level": "none", "message": {"text": "2 env vars used in code but missing from .env.example"}, "properties": {"repobilityId": "d838d1fc5d8231c9", "scanner": "scanner-primary", "fingerprint": "319e66a630e14977", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "config-drift"]}}, {"ruleId": "scanner-2c04133e54348533", "level": "note", "message": {"text": "Near-duplicate function bodies in 2 places"}, "properties": {"repobilityId": "48377dfa0e55724e", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "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": "d798e25985a61511", "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": "88aa15dad105394b", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "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": "940ef02bde19260a", "scanner": "scanner-primary", "fingerprint": "2c04133e54348533", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-be46ea126aa5d8dc", "level": "note", "message": {"text": "Near-duplicate function bodies in 3 places"}, "properties": {"repobilityId": "b4135e3308361cd3", "scanner": "scanner-primary", "fingerprint": "be46ea126aa5d8dc", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-be46ea126aa5d8dc", "level": "note", "message": {"text": "Near-duplicate function bodies in 3 places"}, "properties": {"repobilityId": "addda88cf9f950e6", "scanner": "scanner-primary", "fingerprint": "be46ea126aa5d8dc", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "duplicate", "dry"]}}, {"ruleId": "scanner-1138191ff4654aa5", "level": "error", "message": {"text": "FastAPI POST `submit_signup` without auth dependency \u2014 app/routers/notify.py:36"}, "properties": {"repobilityId": "fcb7a0471eafcaf1", "scanner": "scanner-primary", "fingerprint": "1138191ff4654aa5", "layer": "security", "severity": "high", "confidence": 1.0, "tags": ["auth", "owasp", "auth.fastapi.unauth_mutation"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "app/routers/notify.py"}, "region": {"startLine": 36}}}]}, {"ruleId": "scanner-cbb81f7dfde6dd22", "level": "error", "message": {"text": "FastAPI POST `submit_feedback` without auth dependency \u2014 app/routers/feedback.py:29"}, "properties": {"repobilityId": "994ad47830913063", "scanner": "scanner-primary", "fingerprint": "cbb81f7dfde6dd22", "layer": "security", "severity": "high", "confidence": 1.0, "tags": ["auth", "owasp", "auth.fastapi.unauth_mutation"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "app/routers/feedback.py"}, "region": {"startLine": 29}}}]}, {"ruleId": "scanner-a1e1e29b8a0f31c6", "level": "error", "message": {"text": "Dangling fetch: POST /api/feedback (static/js/feedback.js:144)"}, "properties": {"repobilityId": "4841fb5e81adffbe", "scanner": "scanner-primary", "fingerprint": "a1e1e29b8a0f31c6", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-d581dfb6c5f304ca", "level": "error", "message": {"text": "Dangling fetch: GET /api/members/counts (static/js/app.js:107)"}, "properties": {"repobilityId": "b1e1e2ed792c8b14", "scanner": "scanner-primary", "fingerprint": "d581dfb6c5f304ca", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-bdbe8d3882a9ae7c", "level": "error", "message": {"text": "Dangling fetch: GET /api/votes/latest (static/js/app.js:121)"}, "properties": {"repobilityId": "f4e026ade69d3306", "scanner": "scanner-primary", "fingerprint": "bdbe8d3882a9ae7c", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-5d681681e733f1a8", "level": "error", "message": {"text": "Dangling fetch: POST /api/notify (static/js/app.js:391)"}, "properties": {"repobilityId": "df0343da89d4e203", "scanner": "scanner-primary", "fingerprint": "5d681681e733f1a8", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-113e320a959130f2", "level": "error", "message": {"text": "Dangling fetch: GET /api/search/bills?q=${encodeURIComponent(query)}&limit=50 (static/js/app.js:499)"}, "properties": {"repobilityId": "af34187155963b56", "scanner": "scanner-primary", "fingerprint": "113e320a959130f2", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-622bf96aed1d9c07", "level": "error", "message": {"text": "Dangling fetch: GET /api/bills/${congress}/${type}/${number} (static/js/bill.js:67)"}, "properties": {"repobilityId": "a8b99c8023b45b7b", "scanner": "scanner-primary", "fingerprint": "622bf96aed1d9c07", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-b1c4c171a5286dbc", "level": "error", "message": {"text": "Dangling fetch: GET /api/bills/${congress}/${type}/${number}/ai-summary (static/js/bill.js:232)"}, "properties": {"repobilityId": "8cd5ba4b68bf4e85", "scanner": "scanner-primary", "fingerprint": "b1c4c171a5286dbc", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-8b97954f71558522", "level": "error", "message": {"text": "Dangling fetch: GET /api/bills/${congress}/${type}/${number}/votes (static/js/bill.js:302)"}, "properties": {"repobilityId": "7c91eb99fb499e44", "scanner": "scanner-primary", "fingerprint": "8b97954f71558522", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-9c163b2a8d0eb93d", "level": "error", "message": {"text": "Dangling fetch: GET /api/members/detail/${bioguideId} (static/js/member.js:52)"}, "properties": {"repobilityId": "06335c269a88725d", "scanner": "scanner-primary", "fingerprint": "9c163b2a8d0eb93d", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-ad32dedcbe4f015f", "level": "error", "message": {"text": "Dangling fetch: GET /api/members/detail/${bioguideId}?show_party=true (static/js/member.js:97)"}, "properties": {"repobilityId": "38eaeed4c37c2cc2", "scanner": "scanner-primary", "fingerprint": "ad32dedcbe4f015f", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-425d988d788ef9b0", "level": "error", "message": {"text": "Dangling fetch: GET /api/members/detail/${bioguideId}?show_party=true (static/js/member.js:149)"}, "properties": {"repobilityId": "61db0cec83b52027", "scanner": "scanner-primary", "fingerprint": "425d988d788ef9b0", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-866954014d43d498", "level": "error", "message": {"text": "Dangling fetch: GET /api/members/${bioguideId}/sponsored (static/js/member.js:370)"}, "properties": {"repobilityId": "4c0fdf112c67ef12", "scanner": "scanner-primary", "fingerprint": "866954014d43d498", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-f4669dc29aa2ffcb", "level": "error", "message": {"text": "Dangling fetch: GET /api/members/${bioguideId}/votes?limit=2000 (static/js/member.js:459)"}, "properties": {"repobilityId": "00ae506e199d00ec", "scanner": "scanner-primary", "fingerprint": "f4669dc29aa2ffcb", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-c19497e9ca5c3efa", "level": "error", "message": {"text": "Dangling fetch: GET /api/members/${bioguideId}/summary (static/js/member.js:460)"}, "properties": {"repobilityId": "7b18a3b77e49c3f4", "scanner": "scanner-primary", "fingerprint": "c19497e9ca5c3efa", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-7a82eed9534a0755", "level": "error", "message": {"text": "Dangling fetch: GET /api/members/${bioguideId}/donations (static/js/member.js:921)"}, "properties": {"repobilityId": "fa7be3a2100a82b6", "scanner": "scanner-primary", "fingerprint": "7a82eed9534a0755", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-b542ec8b6118065c", "level": "error", "message": {"text": "Dangling fetch: GET /api/members/${stateCode}/overview (static/js/state.js:77)"}, "properties": {"repobilityId": "048a5128accd4dc4", "scanner": "scanner-primary", "fingerprint": "b542ec8b6118065c", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-5baa8971ebe192a1", "level": "note", "message": {"text": "Unused endpoint: GET /"}, "properties": {"repobilityId": "d8721f71fae1e2af", "scanner": "scanner-primary", "fingerprint": "5baa8971ebe192a1", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-5ce04017f50a9349", "level": "note", "message": {"text": "Unused endpoint: GET /member"}, "properties": {"repobilityId": "ff032d38bac174b9", "scanner": "scanner-primary", "fingerprint": "5ce04017f50a9349", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-b7e950ec470b782d", "level": "note", "message": {"text": "Unused endpoint: GET /bill"}, "properties": {"repobilityId": "d733595b9aa8d0c1", "scanner": "scanner-primary", "fingerprint": "b7e950ec470b782d", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-5fff7d7c74ad81e4", "level": "note", "message": {"text": "Unused endpoint: GET /about"}, "properties": {"repobilityId": "7e8226706b81b447", "scanner": "scanner-primary", "fingerprint": "5fff7d7c74ad81e4", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-24127e99f5c184fb", "level": "note", "message": {"text": "Unused endpoint: GET /state"}, "properties": {"repobilityId": "90cd48184785dd26", "scanner": "scanner-primary", "fingerprint": "24127e99f5c184fb", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-7a009b1a56794f45", "level": "note", "message": {"text": "Unused endpoint: POST /"}, "properties": {"repobilityId": "3913b006b6ab0e97", "scanner": "scanner-primary", "fingerprint": "7a009b1a56794f45", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-0f663fbd44c1d8e7", "level": "note", "message": {"text": "Unused endpoint: GET /{congress}/{bill_type}/{bill_number}"}, "properties": {"repobilityId": "98ca27536b9ab0d9", "scanner": "scanner-primary", "fingerprint": "0f663fbd44c1d8e7", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-6912008048b73964", "level": "note", "message": {"text": "Unused endpoint: GET /{congress}/{bill_type}/{bill_number}/ai-summary"}, "properties": {"repobilityId": "8994aa70143077f7", "scanner": "scanner-primary", "fingerprint": "6912008048b73964", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-35aab798c25a1569", "level": "note", "message": {"text": "Unused endpoint: GET /{congress}/{bill_type}/{bill_number}/votes"}, "properties": {"repobilityId": "937b6be2ead31506", "scanner": "scanner-primary", "fingerprint": "35aab798c25a1569", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-5ceac7791188c3d1", "level": "note", "message": {"text": "Unused endpoint: GET /{bioguide_id}/donations"}, "properties": {"repobilityId": "c30dff8e36397fb7", "scanner": "scanner-primary", "fingerprint": "5ceac7791188c3d1", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-4917400abe62316e", "level": "note", "message": {"text": "Unused endpoint: GET /{bioguide_id}/sponsored"}, "properties": {"repobilityId": "feba8cac3d7db0d6", "scanner": "scanner-primary", "fingerprint": "4917400abe62316e", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-39bf98a0d91fe84d", "level": "note", "message": {"text": "Unused endpoint: GET /{bioguide_id}/votes"}, "properties": {"repobilityId": "07e029e56d2eb098", "scanner": "scanner-primary", "fingerprint": "39bf98a0d91fe84d", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-570514defa75c007", "level": "note", "message": {"text": "Unused endpoint: GET /{bioguide_id}/summary"}, "properties": {"repobilityId": "1b4c51fdac820964", "scanner": "scanner-primary", "fingerprint": "570514defa75c007", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-2f102c8ae6c444d2", "level": "note", "message": {"text": "Unused endpoint: GET /counts"}, "properties": {"repobilityId": "67d0810ed29228e4", "scanner": "scanner-primary", "fingerprint": "2f102c8ae6c444d2", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-208b430af298c9b0", "level": "note", "message": {"text": "Unused endpoint: GET /{state_code}/overview"}, "properties": {"repobilityId": "d13950b8161f0dc6", "scanner": "scanner-primary", "fingerprint": "208b430af298c9b0", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-28a56c2bd1acd303", "level": "note", "message": {"text": "Unused endpoint: GET /{state_code}"}, "properties": {"repobilityId": "273fbf5764745554", "scanner": "scanner-primary", "fingerprint": "28a56c2bd1acd303", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-9d095b258bfe3997", "level": "note", "message": {"text": "Unused endpoint: GET /detail/{bioguide_id}"}, "properties": {"repobilityId": "16d0cdd938572793", "scanner": "scanner-primary", "fingerprint": "9d095b258bfe3997", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-81090702e5b5a42a", "level": "note", "message": {"text": "Unused endpoint: GET /{state_code}/{district}"}, "properties": {"repobilityId": "59827543639181e3", "scanner": "scanner-primary", "fingerprint": "81090702e5b5a42a", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-37157900d71c21d3", "level": "note", "message": {"text": "Unused endpoint: GET /latest"}, "properties": {"repobilityId": "eedb3f32c7a354f8", "scanner": "scanner-primary", "fingerprint": "37157900d71c21d3", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-95dbcd4df22ec825", "level": "note", "message": {"text": "Unused endpoint: GET /senate/{congress}/{session}/{vote_number}"}, "properties": {"repobilityId": "bd557b7fa23ddffb", "scanner": "scanner-primary", "fingerprint": "95dbcd4df22ec825", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-dfb016f2889e6a7d", "level": "note", "message": {"text": "Unused endpoint: GET /house/{congress}/{session}/{vote_number}"}, "properties": {"repobilityId": "1e469bd9463c9b86", "scanner": "scanner-primary", "fingerprint": "dfb016f2889e6a7d", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}]}]}