{"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-25de52372c63f233", "name": "Docker base image is tag-pinned but not digest-pinned: node:24-slim", "shortDescription": {"text": "Docker base image is tag-pinned but not digest-pinned: node:24-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-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-16b3fb7a6233b3f4", "name": "Insecure pattern 'direct_innerhtml_assignment' in public/js/ui/shell.js:154", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in public/js/ui/shell.js:154"}, "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-005365387b042244", "name": "Insecure pattern 'direct_innerhtml_assignment' in public/js/ui/password-strength.js:54", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in public/js/ui/password-strength.js:54"}, "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-c8b9f5bf46c1a9f6", "name": "Insecure pattern 'direct_innerhtml_assignment' in public/js/core/router.js:71", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in public/js/core/router.js:71"}, "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-4779d37bf7a15691", "name": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/settings/settings.js:138", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/settings/settings.js:138"}, "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-95a1bfaca838e0c9", "name": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/collection/collection.js:106", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/collection/collection.js:106"}, "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-97037b1dd0008248", "name": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/admin/deck-sync.js:167", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/admin/deck-sync.js:167"}, "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-c4de14bb67b955f2", "name": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/admin/cards.js:50", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/admin/cards.js:50"}, "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-48f33615519549f1", "name": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/admin/users.js:26", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/admin/users.js:26"}, "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-f64a4ec112e7a432", "name": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/deck/draw.js:36", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/deck/draw.js:36"}, "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-908eea70d00a3b2c", "name": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/history/history.js:48", "shortDescription": {"text": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/history/history.js:48"}, "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-46c644c6227e4d4a", "name": "GitHub Action is tag-pinned rather than SHA-pinned", "shortDescription": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "fullDescription": {"text": "docker/build-push-action@v7 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": "medium", "confidence": 1.0}}, {"id": "scanner-1838a141491ce38c", "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-6893a6c8b0861585", "name": "Very low test-to-source ratio", "shortDescription": {"text": "Very low test-to-source ratio"}, "fullDescription": {"text": "0 test file(s) for 54 source file(s) (ratio 0.00). Consider adding integration or unit tests for critical paths."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-72b2a6250083a784", "name": "Placeholder or mock-heavy implementation detected", "shortDescription": {"text": "Placeholder or mock-heavy implementation detected"}, "fullDescription": {"text": "Found 12 placeholder/mock markers across 4 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-11825279136b53a3", "name": "CI is configured but no tests are detected", "shortDescription": {"text": "CI is configured but no tests are detected"}, "fullDescription": {"text": "A CI pipeline exists, but the scan found no test files to gate. Opus labeled this generated-code pattern as config theater: release machinery exists, but it has little behavioral signal."}, "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-cccb60660c32c216", "name": "Commented-code block (5 lines) in scripts/vendor-entry-zxcvbn.js:1", "shortDescription": {"text": "Commented-code block (5 lines) in scripts/vendor-entry-zxcvbn.js:1"}, "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-1de7b181e6c6363b", "name": "Commented-code block (5 lines) in server/src/lib/deckSync.js:1", "shortDescription": {"text": "Commented-code block (5 lines) in server/src/lib/deckSync.js:1"}, "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-ae1b3536c08d0e69", "name": "Commented-code block (5 lines) in server/src/routes/auth.js:20", "shortDescription": {"text": "Commented-code block (5 lines) in server/src/routes/auth.js:20"}, "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-cf027d40c19bd9bf", "name": "Commented-code block (5 lines) in server/src/routes/cards.js:39", "shortDescription": {"text": "Commented-code block (5 lines) in server/src/routes/cards.js:39"}, "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-888dcd1565f9c2f6", "name": "Commented-code block (5 lines) in server/src/routes/admin-cards.js:1", "shortDescription": {"text": "Commented-code block (5 lines) in server/src/routes/admin-cards.js:1"}, "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-727cd1002ec7cf19", "name": "Commented-code block (5 lines) in server/src/plugins/csrf.js:1", "shortDescription": {"text": "Commented-code block (5 lines) in server/src/plugins/csrf.js:1"}, "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-07e4a6bcef783b6c", "name": "Commented-code block (5 lines) in public/sw.js:1", "shortDescription": {"text": "Commented-code block (5 lines) in public/sw.js:1"}, "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-a6e26648a4e0f204", "name": "Commented-code block (7 lines) in public/js/ui/shell.js:135", "shortDescription": {"text": "Commented-code block (7 lines) in public/js/ui/shell.js:135"}, "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-bf091b72ff730378", "name": "Commented-code block (5 lines) in public/js/core/idb.js:1", "shortDescription": {"text": "Commented-code block (5 lines) in public/js/core/idb.js:1"}, "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-42a13077c6a2c593", "name": "Commented-code block (5 lines) in public/js/core/sync.js:120", "shortDescription": {"text": "Commented-code block (5 lines) in public/js/core/sync.js:120"}, "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-b14c7f3c84061b53", "name": "`fetch()` without try/.catch or AbortSignal \u2014 public/js/core/i18n.js:25", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 public/js/core/i18n.js:25"}, "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-e15afe590af81d41", "name": "`fetch()` without try/.catch or AbortSignal \u2014 public/js/core/router.js:28", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 public/js/core/router.js:28"}, "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-ba9cab9d58863dfc", "name": "Commented-code block (5 lines) in public/js/features/admin/emoji-slugs.js:1", "shortDescription": {"text": "Commented-code block (5 lines) in public/js/features/admin/emoji-slugs.js:1"}, "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-9f49e65fcaba6bd3", "name": "`fetch()` without try/.catch or AbortSignal \u2014 public/js/features/admin/deck-sync.js:22", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 public/js/features/admin/deck-sync.js:22"}, "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-aba4b81badbe0f0a", "name": "Commented-code block (5 lines) in public/js/features/deck/draw.js:501", "shortDescription": {"text": "Commented-code block (5 lines) in public/js/features/deck/draw.js:501"}, "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-8e047ab7af03962f", "name": "7 env vars used in code but missing from .env.example", "shortDescription": {"text": "7 env vars used in code but missing from .env.example"}, "fullDescription": {"text": "Drift between code and config docs. The first few: `DATA_DIR`, `DATA_SEED_DIR`, `DB_PATH`, `HOST`, `LANG`, `NODE_ENV`, `PUBLIC_DIR`. 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-ad52cc37b223396c", "name": "Dangling fetch: GET /locales/${locale}.json (public/js/core/i18n.js:25)", "shortDescription": {"text": "Dangling fetch: GET /locales/${locale}.json (public/js/core/i18n.js:25)"}, "fullDescription": {"text": "`public/js/core/i18n.js:25` calls `GET /locales/${locale}.json` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/locales/<p>.json`\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-4441b404de4c5295", "name": "Dangling fetch: GET /views/${name}.html (public/js/core/router.js:28)", "shortDescription": {"text": "Dangling fetch: GET /views/${name}.html (public/js/core/router.js:28)"}, "fullDescription": {"text": "`public/js/core/router.js:28` calls `GET /views/${name}.html` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/views/<p>.html`\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-fe8a139f7e8bce17", "name": "Dangling fetch: GET /api/admin/cards/export (public/js/features/admin/deck-sync.js:22)", "shortDescription": {"text": "Dangling fetch: GET /api/admin/cards/export (public/js/features/admin/deck-sync.js:22)"}, "fullDescription": {"text": "`public/js/features/admin/deck-sync.js:22` calls `GET /api/admin/cards/export` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/admin/cards/export`\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-a481afcdbc8e74db", "name": "Dangling fetch: POST /api/admin/cards/sync (public/js/features/admin/deck-sync.js:121)", "shortDescription": {"text": "Dangling fetch: POST /api/admin/cards/sync (public/js/features/admin/deck-sync.js:121)"}, "fullDescription": {"text": "`public/js/features/admin/deck-sync.js:121` calls `POST /api/admin/cards/sync` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/admin/cards/sync`\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-e9673975871718b5", "name": "Dangling fetch: POST /api/admin/cards/sync (public/js/features/admin/deck-sync.js:162)", "shortDescription": {"text": "Dangling fetch: POST /api/admin/cards/sync (public/js/features/admin/deck-sync.js:162)"}, "fullDescription": {"text": "`public/js/features/admin/deck-sync.js:162` calls `POST /api/admin/cards/sync` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/admin/cards/sync`\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-5af05a2d1a367875", "name": "Dangling fetch: POST /api/admin/cards/import (public/js/features/admin/deck-sync.js:210)", "shortDescription": {"text": "Dangling fetch: POST /api/admin/cards/import (public/js/features/admin/deck-sync.js:210)"}, "fullDescription": {"text": "`public/js/features/admin/deck-sync.js:210` calls `POST /api/admin/cards/import` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/admin/cards/import`\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-627bed1ac4473e48", "name": "Dangling fetch: POST /api/admin/cards/import (public/js/features/admin/deck-sync.js:243)", "shortDescription": {"text": "Dangling fetch: POST /api/admin/cards/import (public/js/features/admin/deck-sync.js:243)"}, "fullDescription": {"text": "`public/js/features/admin/deck-sync.js:243` calls `POST /api/admin/cards/import` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/admin/cards/import`\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-bac3d6ecceb89e20", "name": "Dangling fetch: GET /api/admin/users (public/js/features/admin/users.js:14)", "shortDescription": {"text": "Dangling fetch: GET /api/admin/users (public/js/features/admin/users.js:14)"}, "fullDescription": {"text": "`public/js/features/admin/users.js:14` calls `GET /api/admin/users` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/admin/users`\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-8b2631e1820677b1", "name": "Dangling fetch: POST /api/admin/users (public/js/features/admin/users.js:102)", "shortDescription": {"text": "Dangling fetch: POST /api/admin/users (public/js/features/admin/users.js:102)"}, "fullDescription": {"text": "`public/js/features/admin/users.js:102` calls `POST /api/admin/users` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/admin/users`\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-e638bb494ae91dd3", "name": "Dangling fetch: POST /api/admin/users/${id}/reset-password (public/js/features/admin/users.js:115)", "shortDescription": {"text": "Dangling fetch: POST /api/admin/users/${id}/reset-password (public/js/features/admin/users.js:115)"}, "fullDescription": {"text": "`public/js/features/admin/users.js:115` calls `POST /api/admin/users/${id}/reset-password` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/admin/users/<p>/reset-password`\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-9fa1e0a1670d12de", "name": "Dangling fetch: DELETE /api/admin/users/${id} (public/js/features/admin/users.js:129)", "shortDescription": {"text": "Dangling fetch: DELETE /api/admin/users/${id} (public/js/features/admin/users.js:129)"}, "fullDescription": {"text": "`public/js/features/admin/users.js:129` calls `DELETE /api/admin/users/${id}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/admin/users/<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-6308a194b4639dfe", "name": "Dangling fetch: POST /api/admin/users/${id}/unlock (public/js/features/admin/users.js:135)", "shortDescription": {"text": "Dangling fetch: POST /api/admin/users/${id}/unlock (public/js/features/admin/users.js:135)"}, "fullDescription": {"text": "`public/js/features/admin/users.js:135` calls `POST /api/admin/users/${id}/unlock` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: helper:request\nNormalized path used for matching: `/admin/users/<p>/unlock`\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-9cd3c933d741fc34", "name": "Unused endpoint: GET /manifest.webmanifest", "shortDescription": {"text": "Unused endpoint: GET /manifest.webmanifest"}, "fullDescription": {"text": "`server/src/routes/manifest.js` declares `GET /manifest.webmanifest` 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-841e50bf821e4158", "name": "Unused endpoint: GET /users", "shortDescription": {"text": "Unused endpoint: GET /users"}, "fullDescription": {"text": "`server/src/routes/users.js` declares `GET /users` 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-1bca8100a44f7f25", "name": "Unused endpoint: POST /users", "shortDescription": {"text": "Unused endpoint: POST /users"}, "fullDescription": {"text": "`server/src/routes/users.js` declares `POST /users` 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-a2da8d08e12b122c", "name": "Unused endpoint: PATCH /users/:id", "shortDescription": {"text": "Unused endpoint: PATCH /users/:id"}, "fullDescription": {"text": "`server/src/routes/users.js` declares `PATCH /users/: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-79406b9fd85263bf", "name": "Unused endpoint: DELETE /users/:id", "shortDescription": {"text": "Unused endpoint: DELETE /users/:id"}, "fullDescription": {"text": "`server/src/routes/users.js` declares `DELETE /users/: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-fa4e1071fc9bdadb", "name": "Unused endpoint: POST /users/:id/reset-password", "shortDescription": {"text": "Unused endpoint: POST /users/:id/reset-password"}, "fullDescription": {"text": "`server/src/routes/users.js` declares `POST /users/:id/reset-password` 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-e17fa09cc2f42715", "name": "Unused endpoint: POST /users/:id/unlock", "shortDescription": {"text": "Unused endpoint: POST /users/:id/unlock"}, "fullDescription": {"text": "`server/src/routes/users.js` declares `POST /users/:id/unlock` 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-fddfc63401d8df03", "name": "Unused endpoint: GET /cards/export", "shortDescription": {"text": "Unused endpoint: GET /cards/export"}, "fullDescription": {"text": "`server/src/routes/admin-cards.js` declares `GET /cards/export` 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-df94c0d8f0102475", "name": "Unused endpoint: POST /cards/sync", "shortDescription": {"text": "Unused endpoint: POST /cards/sync"}, "fullDescription": {"text": "`server/src/routes/admin-cards.js` declares `POST /cards/sync` 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-8b15cae2e663534e", "name": "Unused endpoint: POST /cards/import", "shortDescription": {"text": "Unused endpoint: POST /cards/import"}, "fullDescription": {"text": "`server/src/routes/admin-cards.js` declares `POST /cards/import` 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/17574"}, "properties": {"repository": "qiaeru/couplecards", "repoUrl": "https://github.com/qiaeru/couplecards", "branch": "main"}, "results": [{"ruleId": "scanner-25de52372c63f233", "level": "note", "message": {"text": "Docker base image is tag-pinned but not digest-pinned: node:24-slim"}, "properties": {"repobilityId": "ba835541596e8da4", "scanner": "scanner-primary", "fingerprint": "25de52372c63f233", "layer": "hardware", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "docker", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "Dockerfile"}, "region": {"startLine": 10}}}]}, {"ruleId": "scanner-25de52372c63f233", "level": "note", "message": {"text": "Docker base image is tag-pinned but not digest-pinned: node:24-slim"}, "properties": {"repobilityId": "fe0b196e5151b947", "scanner": "scanner-primary", "fingerprint": "25de52372c63f233", "layer": "hardware", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "docker", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "Dockerfile"}, "region": {"startLine": 16}}}]}, {"ruleId": "scanner-25de52372c63f233", "level": "note", "message": {"text": "Docker base image is tag-pinned but not digest-pinned: node:24-slim"}, "properties": {"repobilityId": "d967e0ad7535b832", "scanner": "scanner-primary", "fingerprint": "25de52372c63f233", "layer": "hardware", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "docker", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "Dockerfile"}, "region": {"startLine": 25}}}]}, {"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-16b3fb7a6233b3f4", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in public/js/ui/shell.js:154"}, "properties": {"repobilityId": "6f65bda95302ce32", "scanner": "scanner-primary", "fingerprint": "16b3fb7a6233b3f4", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "public/js/ui/shell.js"}, "region": {"startLine": 154}}}]}, {"ruleId": "scanner-005365387b042244", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in public/js/ui/password-strength.js:54"}, "properties": {"repobilityId": "1a512a5b5794788d", "scanner": "scanner-primary", "fingerprint": "005365387b042244", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "public/js/ui/password-strength.js"}, "region": {"startLine": 54}}}]}, {"ruleId": "scanner-c8b9f5bf46c1a9f6", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in public/js/core/router.js:71"}, "properties": {"repobilityId": "35e0aa1f9cb6e532", "scanner": "scanner-primary", "fingerprint": "c8b9f5bf46c1a9f6", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "public/js/core/router.js"}, "region": {"startLine": 71}}}]}, {"ruleId": "scanner-4779d37bf7a15691", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/settings/settings.js:138"}, "properties": {"repobilityId": "f4294c12fdaf5996", "scanner": "scanner-primary", "fingerprint": "4779d37bf7a15691", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "public/js/features/settings/settings.js"}, "region": {"startLine": 138}}}]}, {"ruleId": "scanner-95a1bfaca838e0c9", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/collection/collection.js:106"}, "properties": {"repobilityId": "5ba74d84a475b6fc", "scanner": "scanner-primary", "fingerprint": "95a1bfaca838e0c9", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "public/js/features/collection/collection.js"}, "region": {"startLine": 106}}}]}, {"ruleId": "scanner-97037b1dd0008248", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/admin/deck-sync.js:167"}, "properties": {"repobilityId": "c356560dfc801c09", "scanner": "scanner-primary", "fingerprint": "97037b1dd0008248", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "public/js/features/admin/deck-sync.js"}, "region": {"startLine": 167}}}]}, {"ruleId": "scanner-c4de14bb67b955f2", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/admin/cards.js:50"}, "properties": {"repobilityId": "5b09a65879ced5c6", "scanner": "scanner-primary", "fingerprint": "c4de14bb67b955f2", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "public/js/features/admin/cards.js"}, "region": {"startLine": 50}}}]}, {"ruleId": "scanner-48f33615519549f1", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/admin/users.js:26"}, "properties": {"repobilityId": "bd13742141ddeeaf", "scanner": "scanner-primary", "fingerprint": "48f33615519549f1", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "public/js/features/admin/users.js"}, "region": {"startLine": 26}}}]}, {"ruleId": "scanner-f64a4ec112e7a432", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/deck/draw.js:36"}, "properties": {"repobilityId": "63d7505d562a0692", "scanner": "scanner-primary", "fingerprint": "f64a4ec112e7a432", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "public/js/features/deck/draw.js"}, "region": {"startLine": 36}}}]}, {"ruleId": "scanner-908eea70d00a3b2c", "level": "warning", "message": {"text": "Insecure pattern 'direct_innerhtml_assignment' in public/js/features/history/history.js:48"}, "properties": {"repobilityId": "adf72ec61fe7954d", "scanner": "scanner-primary", "fingerprint": "908eea70d00a3b2c", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "direct_innerhtml_assignment"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "public/js/features/history/history.js"}, "region": {"startLine": 48}}}]}, {"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-46c644c6227e4d4a", "level": "warning", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "9a38b98594265645", "scanner": "scanner-primary", "fingerprint": "46c644c6227e4d4a", "layer": "cicd", "severity": "medium", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/release.yml"}, "region": {"startLine": 34}}}]}, {"ruleId": "scanner-1838a141491ce38c", "level": "warning", "message": {"text": "GitHub Actions workflow grants broad write permissions"}, "properties": {"repobilityId": "b8fd4f5048f96576", "scanner": "scanner-primary", "fingerprint": "1838a141491ce38c", "layer": "cicd", "severity": "medium", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "least-privilege"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/release.yml"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-6893a6c8b0861585", "level": "warning", "message": {"text": "Very low test-to-source ratio"}, "properties": {"repobilityId": "54a7de3f06314bf0", "scanner": "scanner-primary", "fingerprint": "6893a6c8b0861585", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["tests", "coverage"]}}, {"ruleId": "scanner-72b2a6250083a784", "level": "warning", "message": {"text": "Placeholder or mock-heavy implementation detected"}, "properties": {"repobilityId": "d26bc27a3a661e01", "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": "a6a66f74ccb1db6b", "scanner": "scanner-primary", "fingerprint": "2d0c7b7ab8f8aacf", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["placeholder", "mock-data", "critical-flow", "generated-repo-pattern"]}}, {"ruleId": "scanner-11825279136b53a3", "level": "warning", "message": {"text": "CI is configured but no tests are detected"}, "properties": {"repobilityId": "925f10effe645ad1", "scanner": "scanner-primary", "fingerprint": "11825279136b53a3", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["tests", "ci", "config-theater", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-ea8f3013f588db25", "level": "note", "message": {"text": "Shallow git history limits provenance confidence"}, "properties": {"repobilityId": "9e5aba3835051a2c", "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": "710ee2f43c71c3bd", "scanner": "scanner-primary", "fingerprint": "8424db9c75e04ba4", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["provenance", "git-history", "generated-repo-pattern"]}}, {"ruleId": "scanner-cccb60660c32c216", "level": "none", "message": {"text": "Commented-code block (5 lines) in scripts/vendor-entry-zxcvbn.js:1"}, "properties": {"repobilityId": "ff6c8f5f7e65f40b", "scanner": "scanner-primary", "fingerprint": "cccb60660c32c216", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-1de7b181e6c6363b", "level": "none", "message": {"text": "Commented-code block (5 lines) in server/src/lib/deckSync.js:1"}, "properties": {"repobilityId": "2895ad9d114756d3", "scanner": "scanner-primary", "fingerprint": "1de7b181e6c6363b", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-ae1b3536c08d0e69", "level": "none", "message": {"text": "Commented-code block (5 lines) in server/src/routes/auth.js:20"}, "properties": {"repobilityId": "5ea6a0b423866fde", "scanner": "scanner-primary", "fingerprint": "ae1b3536c08d0e69", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-cf027d40c19bd9bf", "level": "none", "message": {"text": "Commented-code block (5 lines) in server/src/routes/cards.js:39"}, "properties": {"repobilityId": "0658572016ec2b70", "scanner": "scanner-primary", "fingerprint": "cf027d40c19bd9bf", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-888dcd1565f9c2f6", "level": "none", "message": {"text": "Commented-code block (5 lines) in server/src/routes/admin-cards.js:1"}, "properties": {"repobilityId": "d703e64378f78322", "scanner": "scanner-primary", "fingerprint": "888dcd1565f9c2f6", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-727cd1002ec7cf19", "level": "none", "message": {"text": "Commented-code block (5 lines) in server/src/plugins/csrf.js:1"}, "properties": {"repobilityId": "3fac46ed4dc5123d", "scanner": "scanner-primary", "fingerprint": "727cd1002ec7cf19", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-07e4a6bcef783b6c", "level": "none", "message": {"text": "Commented-code block (5 lines) in public/sw.js:1"}, "properties": {"repobilityId": "cdf86ddfbbf4ba1a", "scanner": "scanner-primary", "fingerprint": "07e4a6bcef783b6c", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-a6e26648a4e0f204", "level": "none", "message": {"text": "Commented-code block (7 lines) in public/js/ui/shell.js:135"}, "properties": {"repobilityId": "4baf79aff02f9938", "scanner": "scanner-primary", "fingerprint": "a6e26648a4e0f204", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-bf091b72ff730378", "level": "none", "message": {"text": "Commented-code block (5 lines) in public/js/core/idb.js:1"}, "properties": {"repobilityId": "a1f8401f69060a91", "scanner": "scanner-primary", "fingerprint": "bf091b72ff730378", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-42a13077c6a2c593", "level": "none", "message": {"text": "Commented-code block (5 lines) in public/js/core/sync.js:120"}, "properties": {"repobilityId": "44fb8f5c8143c483", "scanner": "scanner-primary", "fingerprint": "42a13077c6a2c593", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-b14c7f3c84061b53", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 public/js/core/i18n.js:25"}, "properties": {"repobilityId": "534fa8f53715cc70", "scanner": "scanner-primary", "fingerprint": "b14c7f3c84061b53", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-e15afe590af81d41", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 public/js/core/router.js:28"}, "properties": {"repobilityId": "82d2c960563c6b0f", "scanner": "scanner-primary", "fingerprint": "e15afe590af81d41", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-ba9cab9d58863dfc", "level": "none", "message": {"text": "Commented-code block (5 lines) in public/js/features/admin/emoji-slugs.js:1"}, "properties": {"repobilityId": "1ecd1a63ec8df668", "scanner": "scanner-primary", "fingerprint": "ba9cab9d58863dfc", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-9f49e65fcaba6bd3", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 public/js/features/admin/deck-sync.js:22"}, "properties": {"repobilityId": "ca0527db9d0bf23f", "scanner": "scanner-primary", "fingerprint": "9f49e65fcaba6bd3", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-aba4b81badbe0f0a", "level": "none", "message": {"text": "Commented-code block (5 lines) in public/js/features/deck/draw.js:501"}, "properties": {"repobilityId": "8f5098663f976bff", "scanner": "scanner-primary", "fingerprint": "aba4b81badbe0f0a", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-8e047ab7af03962f", "level": "note", "message": {"text": "7 env vars used in code but missing from .env.example"}, "properties": {"repobilityId": "5a0455f6498c27ad", "scanner": "scanner-primary", "fingerprint": "8e047ab7af03962f", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "config-drift"]}}, {"ruleId": "scanner-ad52cc37b223396c", "level": "error", "message": {"text": "Dangling fetch: GET /locales/${locale}.json (public/js/core/i18n.js:25)"}, "properties": {"repobilityId": "de7322a91d4fa4f9", "scanner": "scanner-primary", "fingerprint": "ad52cc37b223396c", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-4441b404de4c5295", "level": "error", "message": {"text": "Dangling fetch: GET /views/${name}.html (public/js/core/router.js:28)"}, "properties": {"repobilityId": "e1c12c85eb6f1c95", "scanner": "scanner-primary", "fingerprint": "4441b404de4c5295", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-fe8a139f7e8bce17", "level": "error", "message": {"text": "Dangling fetch: GET /api/admin/cards/export (public/js/features/admin/deck-sync.js:22)"}, "properties": {"repobilityId": "5429842e34c59427", "scanner": "scanner-primary", "fingerprint": "fe8a139f7e8bce17", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-a481afcdbc8e74db", "level": "error", "message": {"text": "Dangling fetch: POST /api/admin/cards/sync (public/js/features/admin/deck-sync.js:121)"}, "properties": {"repobilityId": "d2611d8243047505", "scanner": "scanner-primary", "fingerprint": "a481afcdbc8e74db", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-e9673975871718b5", "level": "error", "message": {"text": "Dangling fetch: POST /api/admin/cards/sync (public/js/features/admin/deck-sync.js:162)"}, "properties": {"repobilityId": "be6e561acc475552", "scanner": "scanner-primary", "fingerprint": "e9673975871718b5", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-5af05a2d1a367875", "level": "error", "message": {"text": "Dangling fetch: POST /api/admin/cards/import (public/js/features/admin/deck-sync.js:210)"}, "properties": {"repobilityId": "8ef26af848fa8d41", "scanner": "scanner-primary", "fingerprint": "5af05a2d1a367875", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-627bed1ac4473e48", "level": "error", "message": {"text": "Dangling fetch: POST /api/admin/cards/import (public/js/features/admin/deck-sync.js:243)"}, "properties": {"repobilityId": "c1ed8f9c3c203c78", "scanner": "scanner-primary", "fingerprint": "627bed1ac4473e48", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-bac3d6ecceb89e20", "level": "error", "message": {"text": "Dangling fetch: GET /api/admin/users (public/js/features/admin/users.js:14)"}, "properties": {"repobilityId": "a51fe3385fcb0f9b", "scanner": "scanner-primary", "fingerprint": "bac3d6ecceb89e20", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-8b2631e1820677b1", "level": "error", "message": {"text": "Dangling fetch: POST /api/admin/users (public/js/features/admin/users.js:102)"}, "properties": {"repobilityId": "d7e373d37bd02cde", "scanner": "scanner-primary", "fingerprint": "8b2631e1820677b1", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-e638bb494ae91dd3", "level": "error", "message": {"text": "Dangling fetch: POST /api/admin/users/${id}/reset-password (public/js/features/admin/users.js:115)"}, "properties": {"repobilityId": "0c0257936ab81381", "scanner": "scanner-primary", "fingerprint": "e638bb494ae91dd3", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-9fa1e0a1670d12de", "level": "error", "message": {"text": "Dangling fetch: DELETE /api/admin/users/${id} (public/js/features/admin/users.js:129)"}, "properties": {"repobilityId": "2c052cec8d090d6b", "scanner": "scanner-primary", "fingerprint": "9fa1e0a1670d12de", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-6308a194b4639dfe", "level": "error", "message": {"text": "Dangling fetch: POST /api/admin/users/${id}/unlock (public/js/features/admin/users.js:135)"}, "properties": {"repobilityId": "bb72370abfac464f", "scanner": "scanner-primary", "fingerprint": "6308a194b4639dfe", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "helper:request"]}}, {"ruleId": "scanner-9cd3c933d741fc34", "level": "note", "message": {"text": "Unused endpoint: GET /manifest.webmanifest"}, "properties": {"repobilityId": "cb40318b295360df", "scanner": "scanner-primary", "fingerprint": "9cd3c933d741fc34", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-841e50bf821e4158", "level": "note", "message": {"text": "Unused endpoint: GET /users"}, "properties": {"repobilityId": "478f2dc9a024b6a6", "scanner": "scanner-primary", "fingerprint": "841e50bf821e4158", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-1bca8100a44f7f25", "level": "note", "message": {"text": "Unused endpoint: POST /users"}, "properties": {"repobilityId": "d10a71b4e174262b", "scanner": "scanner-primary", "fingerprint": "1bca8100a44f7f25", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-a2da8d08e12b122c", "level": "note", "message": {"text": "Unused endpoint: PATCH /users/:id"}, "properties": {"repobilityId": "c0037e67cd0e89cd", "scanner": "scanner-primary", "fingerprint": "a2da8d08e12b122c", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-79406b9fd85263bf", "level": "note", "message": {"text": "Unused endpoint: DELETE /users/:id"}, "properties": {"repobilityId": "b0f1501f632d8256", "scanner": "scanner-primary", "fingerprint": "79406b9fd85263bf", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-fa4e1071fc9bdadb", "level": "note", "message": {"text": "Unused endpoint: POST /users/:id/reset-password"}, "properties": {"repobilityId": "c8332ff7711c6771", "scanner": "scanner-primary", "fingerprint": "fa4e1071fc9bdadb", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-e17fa09cc2f42715", "level": "note", "message": {"text": "Unused endpoint: POST /users/:id/unlock"}, "properties": {"repobilityId": "36233fbce7274e04", "scanner": "scanner-primary", "fingerprint": "e17fa09cc2f42715", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-fddfc63401d8df03", "level": "note", "message": {"text": "Unused endpoint: GET /cards/export"}, "properties": {"repobilityId": "575db0bff22a5593", "scanner": "scanner-primary", "fingerprint": "fddfc63401d8df03", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-df94c0d8f0102475", "level": "note", "message": {"text": "Unused endpoint: POST /cards/sync"}, "properties": {"repobilityId": "5db9b07c95fa7ccc", "scanner": "scanner-primary", "fingerprint": "df94c0d8f0102475", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-8b15cae2e663534e", "level": "note", "message": {"text": "Unused endpoint: POST /cards/import"}, "properties": {"repobilityId": "bc5245c20e26f3be", "scanner": "scanner-primary", "fingerprint": "8b15cae2e663534e", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}]}]}