{"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-b780ccbd956cdfb4", "name": "Stray `console.log` in TS/JS \u2014 scripts/clear-recipes.ts:6", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 scripts/clear-recipes.ts:6"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-b20d7dac7691c49f", "name": "Stray `console.log` in TS/JS \u2014 prisma/seed.ts:8", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 prisma/seed.ts:8"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-875d5dbe3724896d", "name": "`dangerouslySetInnerHTML` used in a React component \u2014 app/root.tsx:308", "shortDescription": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 app/root.tsx:308"}, "fullDescription": {"text": "Open XSS surface unless the input is provably trusted. Replace with explicit JSX or sanitize via a vetted library.\n\nWhy: OWASP basics. Already partially flagged by the security analyzer.\nRule id: fq.dangerous-html"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-5bbcebc2e88f97e7", "name": "`dangerouslySetInnerHTML` used in a React component \u2014 app/utils/client-hints.tsx:51", "shortDescription": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 app/utils/client-hints.tsx:51"}, "fullDescription": {"text": "Open XSS surface unless the input is provably trusted. Replace with explicit JSX or sanitize via a vetted library.\n\nWhy: OWASP basics. Already partially flagged by the security analyzer.\nRule id: fq.dangerous-html"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-19c37391ca682f26", "name": "Stray `console.log` in TS/JS \u2014 app/utils/providers/google.server.ts:28", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 app/utils/providers/google.server.ts:28"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-79f7010ea73962f8", "name": "`truncate` class without `title=` for hover reveal \u2014 app/components/recipe-selector.tsx:193", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 app/components/recipe-selector.tsx:193"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-3f7db99418d3a9d4", "name": "`truncate` class without `title=` for hover reveal \u2014 app/components/shopping-list-to-inventory.tsx:140", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 app/components/shopping-list-to-inventory.tsx:140"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-297a9524d86e9287", "name": "`truncate` class without `title=` for hover reveal \u2014 app/components/timer-widget.tsx:112", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 app/components/timer-widget.tsx:112"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-b34012dcac7e75dc", "name": "`truncate` class without `title=` for hover reveal \u2014 app/components/ingredient-fields.tsx:457", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 app/components/ingredient-fields.tsx:457"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-9fc9e0312596682d", "name": "`truncate` class without `title=` for hover reveal \u2014 app/components/suggest-meals-modal.tsx:389", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 app/components/suggest-meals-modal.tsx:389"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-411264ab78ae18c3", "name": "`dangerouslySetInnerHTML` used in a React component \u2014 app/routes/share.$recipeId.tsx:275", "shortDescription": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 app/routes/share.$recipeId.tsx:275"}, "fullDescription": {"text": "Open XSS surface unless the input is provably trusted. Replace with explicit JSX or sanitize via a vetted library.\n\nWhy: OWASP basics. Already partially flagged by the security analyzer.\nRule id: fq.dangerous-html"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-e968274402049e25", "name": "`truncate` class without `title=` for hover reveal \u2014 app/routes/settings/profile/index.tsx:113", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 app/routes/settings/profile/index.tsx:113"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-54ce3b0e96806627", "name": "`truncate` class without `title=` for hover reveal \u2014 app/routes/settings/profile/household.tsx:341", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 app/routes/settings/profile/household.tsx:341"}, "fullDescription": {"text": "A truncated value should reveal the full text on hover. Pass the full string via `title={...}` so the user can read it.\n\nWhy: P2 in CHECKLIST.md \u2014 truncate without hover-reveal.\nRule id: fq.truncate.no-title"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-5486a12dbafac14a", "name": "Stray `console.log` in TS/JS \u2014 app/routes/resources/healthcheck.tsx:23", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 app/routes/resources/healthcheck.tsx:23"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-de2b277611bc13b4", "name": "`dangerouslySetInnerHTML` used in a React component \u2014 app/routes/recipes/$recipeId.tsx:632", "shortDescription": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 app/routes/recipes/$recipeId.tsx:632"}, "fullDescription": {"text": "Open XSS surface unless the input is provably trusted. Replace with explicit JSX or sanitize via a vetted library.\n\nWhy: OWASP basics. Already partially flagged by the security analyzer.\nRule id: fq.dangerous-html"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-7dd0280a21ccf200", "name": "`dangerouslySetInnerHTML` used in a React component \u2014 app/routes/_marketing/index.tsx:57", "shortDescription": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 app/routes/_marketing/index.tsx:57"}, "fullDescription": {"text": "Open XSS surface unless the input is provably trusted. Replace with explicit JSX or sanitize via a vetted library.\n\nWhy: OWASP basics. Already partially flagged by the security analyzer.\nRule id: fq.dangerous-html"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-1c508b33b435d4f3", "name": "`dangerouslySetInnerHTML` used in a React component \u2014 app/routes/_marketing/support.tsx:84", "shortDescription": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 app/routes/_marketing/support.tsx:84"}, "fullDescription": {"text": "Open XSS surface unless the input is provably trusted. Replace with explicit JSX or sanitize via a vetted library.\n\nWhy: OWASP basics. Already partially flagged by the security analyzer.\nRule id: fq.dangerous-html"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-087b5b94b54deea0", "name": "Stray `console.log` in TS/JS \u2014 server/index.ts:148", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 server/index.ts:148"}, "fullDescription": {"text": "Replace with the toast helper, an error boundary, or remove. `console.warn` / `console.error` are acceptable.\n\nWhy: Hygiene \u2014 easy to leak debug output.\nRule id: fq.console-leak"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "low", "confidence": 1.0}}, {"id": "scanner-7560861370b89999", "name": "Dockerfile runs as root: other/Dockerfile", "shortDescription": {"text": "Dockerfile runs as root: other/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-111752ee584669c2", "name": "Docker base image is tag-pinned but not digest-pinned: oven/bun:1.3.13-alpine", "shortDescription": {"text": "Docker base image is tag-pinned but not digest-pinned: oven/bun:1.3.13-alpine"}, "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-f9c74af8cd18c0b7", "name": "Insecure pattern 'dangerous_innerhtml' in app/root.tsx:308", "shortDescription": {"text": "Insecure pattern 'dangerous_innerhtml' in app/root.tsx:308"}, "fullDescription": {"text": "Found a known-risky pattern (dangerous_innerhtml). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-706c12960611dc62", "name": "Insecure pattern 'dangerous_innerhtml' in app/utils/client-hints.tsx:51", "shortDescription": {"text": "Insecure pattern 'dangerous_innerhtml' in app/utils/client-hints.tsx:51"}, "fullDescription": {"text": "Found a known-risky pattern (dangerous_innerhtml). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-d92db9d62712308c", "name": "Insecure pattern 'exec_used' in app/utils/cache.server.ts:32", "shortDescription": {"text": "Insecure pattern 'exec_used' in app/utils/cache.server.ts:32"}, "fullDescription": {"text": "Found a known-risky pattern (exec_used). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "high", "confidence": 1.0}}, {"id": "scanner-991f333ee90f9443", "name": "Insecure pattern 'dangerous_innerhtml' in app/routes/share.$recipeId.tsx:275", "shortDescription": {"text": "Insecure pattern 'dangerous_innerhtml' in app/routes/share.$recipeId.tsx:275"}, "fullDescription": {"text": "Found a known-risky pattern (dangerous_innerhtml). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-d96c20599014a5a6", "name": "Insecure pattern 'dangerous_innerhtml' in app/routes/recipes/$recipeId.tsx:632", "shortDescription": {"text": "Insecure pattern 'dangerous_innerhtml' in app/routes/recipes/$recipeId.tsx:632"}, "fullDescription": {"text": "Found a known-risky pattern (dangerous_innerhtml). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-447bf9bbdcd5ebf9", "name": "Insecure pattern 'dangerous_innerhtml' in app/routes/_marketing/index.tsx:57", "shortDescription": {"text": "Insecure pattern 'dangerous_innerhtml' in app/routes/_marketing/index.tsx:57"}, "fullDescription": {"text": "Found a known-risky pattern (dangerous_innerhtml). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-f3bf701eb20f7fb8", "name": "Insecure pattern 'dangerous_innerhtml' in app/routes/_marketing/support.tsx:84", "shortDescription": {"text": "Insecure pattern 'dangerous_innerhtml' in app/routes/_marketing/support.tsx:84"}, "fullDescription": {"text": "Found a known-risky pattern (dangerous_innerhtml). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-0a468b5cb7e17347", "name": "Insecure pattern 'node_child_process' in other/generate-favicons.mjs:5", "shortDescription": {"text": "Insecure pattern 'node_child_process' in other/generate-favicons.mjs:5"}, "fullDescription": {"text": "Found a known-risky pattern (node_child_process). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-28c4a04bd807da0c", "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@v6 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-3ab5d313dda8e5f9", "name": "Debug logging residue appears in source files", "shortDescription": {"text": "Debug logging residue appears in source files"}, "fullDescription": {"text": "Found 24 console/debugger/print-style debug statements in non-test source. This is a common fast-generation residue before production cleanup."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-72b2a6250083a784", "name": "Placeholder or mock-heavy implementation detected", "shortDescription": {"text": "Placeholder or mock-heavy implementation detected"}, "fullDescription": {"text": "Found 72 placeholder/mock markers across 26 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-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-9098aa0124c36302", "name": "Commented-code block (7 lines) in vite.config.ts:76", "shortDescription": {"text": "Commented-code block (7 lines) in vite.config.ts:76"}, "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-3897a2c05d732ae0", "name": "`fetch()` without try/.catch or AbortSignal \u2014 app/utils/email.server.ts:55", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 app/utils/email.server.ts:55"}, "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-78ef4f360e4d1229", "name": "`fetch()` without try/.catch or AbortSignal \u2014 app/utils/storage.server.ts:14", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 app/utils/storage.server.ts:14"}, "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-40079607c4e1aca0", "name": "`fetch()` without try/.catch or AbortSignal \u2014 app/utils/recipe-extract-llm.server.ts:357", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 app/utils/recipe-extract-llm.server.ts:357"}, "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-1b0849c734799060", "name": "Commented-code block (5 lines) in app/utils/cache.server.ts:21", "shortDescription": {"text": "Commented-code block (5 lines) in app/utils/cache.server.ts:21"}, "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-9e0a7514e625b944", "name": "`fetch()` without try/.catch or AbortSignal \u2014 app/utils/providers/google.server.ts:45", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 app/utils/providers/google.server.ts:45"}, "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-d9895d8a7a7b60a3", "name": "Commented-code block (7 lines) in app/components/service-worker-data-sync.tsx:11", "shortDescription": {"text": "Commented-code block (7 lines) in app/components/service-worker-data-sync.tsx:11"}, "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-2a37e4fef7f6081b", "name": "`fetch()` without try/.catch or AbortSignal \u2014 app/components/nav-timing.tsx:49", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 app/components/nav-timing.tsx:49"}, "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-701d6a73cfcad04d", "name": "`fetch()` without try/.catch or AbortSignal \u2014 app/routes/settings/profile/passkeys.tsx:112", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 app/routes/settings/profile/passkeys.tsx:112"}, "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-01674ba41742e2a3", "name": "`fetch()` without try/.catch or AbortSignal \u2014 app/routes/admin/cache/sqlite.server.ts:25", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 app/routes/admin/cache/sqlite.server.ts: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-b93e9ddf5c9e6f81", "name": "Commented-code block (5 lines) in app/routes/plan/index.tsx:58", "shortDescription": {"text": "Commented-code block (5 lines) in app/routes/plan/index.tsx:58"}, "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-71aff0a0143aa735", "name": "Commented-code block (5 lines) in public/sw.js:12", "shortDescription": {"text": "Commented-code block (5 lines) in public/sw.js:12"}, "fullDescription": {"text": "A long run of `//` or `#` lines usually means abandoned code. Delete or move to git history. Keeps the canvas + dead-code detection honest."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "info", "confidence": 1.0}}, {"id": "scanner-618f287de21daaf0", "name": "`fetch()` without try/.catch or AbortSignal \u2014 public/sw.js:324", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 public/sw.js:324"}, "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-d0b17c8c07a7421d", "name": "8 env vars used in code but missing from .env.example", "shortDescription": {"text": "8 env vars used in code but missing from .env.example"}, "fullDescription": {"text": "Drift between code and config docs. The first few: `CI`, `FLY_APP_NAME`, `FLY_REGION`, `MOCKS`, `NODE_ENV`, `PLAYWRIGHT_TEST_BASE_URL`, `PORT`, `VITEST`. 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-f8fd5f38a29620d8", "name": "Dangling fetch: POST https://api.resend.com/emails (app/utils/email.server.ts:55)", "shortDescription": {"text": "Dangling fetch: POST https://api.resend.com/emails (app/utils/email.server.ts:55)"}, "fullDescription": {"text": "`app/utils/email.server.ts:55` calls `POST https://api.resend.com/emails` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/https:/api.resend.com/emails`\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-f8915d5f8152cab4", "name": "Dangling fetch: GET https://api.pwnedpasswords.com/range/${prefix} (app/utils/auth.server.ts:311)", "shortDescription": {"text": "Dangling fetch: GET https://api.pwnedpasswords.com/range/${prefix} (app/utils/auth.server.ts:311)"}, "fullDescription": {"text": "`app/utils/auth.server.ts:311` calls `GET https://api.pwnedpasswords.com/range/${prefix}` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/https:/api.pwnedpasswords.com/range/<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-f3d013dd54a832fa", "name": "Dangling fetch: GET https://www.googleapis.com/oauth2/v2/userinfo (app/utils/providers/google.server.ts:45)", "shortDescription": {"text": "Dangling fetch: GET https://www.googleapis.com/oauth2/v2/userinfo (app/utils/providers/google.server.ts:45)"}, "fullDescription": {"text": "`app/utils/providers/google.server.ts:45` calls `GET https://www.googleapis.com/oauth2/v2/userinfo` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/https:/www.googleapis.com/oauth2/v2/userinfo`\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-e7eba72bb21372ee", "name": "Dangling fetch: GET /webauthn/registration (app/routes/settings/profile/passkeys.tsx:104)", "shortDescription": {"text": "Dangling fetch: GET /webauthn/registration (app/routes/settings/profile/passkeys.tsx:104)"}, "fullDescription": {"text": "`app/routes/settings/profile/passkeys.tsx:104` calls `GET /webauthn/registration` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/webauthn/registration`\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-c1a0c847090a1b1b", "name": "Dangling fetch: POST /webauthn/registration (app/routes/settings/profile/passkeys.tsx:112)", "shortDescription": {"text": "Dangling fetch: POST /webauthn/registration (app/routes/settings/profile/passkeys.tsx:112)"}, "fullDescription": {"text": "`app/routes/settings/profile/passkeys.tsx:112` calls `POST /webauthn/registration` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/webauthn/registration`\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-de5453f222042a84", "name": "Dangling fetch: GET /webauthn/authentication (app/routes/_auth/login.tsx:236)", "shortDescription": {"text": "Dangling fetch: GET /webauthn/authentication (app/routes/_auth/login.tsx:236)"}, "fullDescription": {"text": "`app/routes/_auth/login.tsx:236` calls `GET /webauthn/authentication` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/webauthn/authentication`\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-cb754bd6a171c1dc", "name": "Dangling fetch: POST /webauthn/authentication (app/routes/_auth/login.tsx:245)", "shortDescription": {"text": "Dangling fetch: POST /webauthn/authentication (app/routes/_auth/login.tsx:245)"}, "fullDescription": {"text": "`app/routes/_auth/login.tsx:245` calls `POST /webauthn/authentication` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/webauthn/authentication`\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-a2deb7f42a7698bd", "name": "Dangling fetch: POST /resources/transcribe (app/hooks/use-speech-to-text.ts:124)", "shortDescription": {"text": "Dangling fetch: POST /resources/transcribe (app/hooks/use-speech-to-text.ts:124)"}, "fullDescription": {"text": "`app/hooks/use-speech-to-text.ts:124` calls `POST /resources/transcribe` but no backend route matches that path. This is a runtime 404 waiting to happen.\n\nTool: fetch\nNormalized path used for matching: `/resources/transcribe`\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-02d93607031ff690", "name": "Unused endpoint: USE /favicons", "shortDescription": {"text": "Unused endpoint: USE /favicons"}, "fullDescription": {"text": "`server/index.ts` declares `USE /favicons` 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-f22cf013c13a7f0c", "name": "Unused endpoint: GET /sw.js", "shortDescription": {"text": "Unused endpoint: GET /sw.js"}, "fullDescription": {"text": "`server/index.ts` declares `GET /sw.js` 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-3b59435b3211a9df", "name": "Unused endpoint: USE /assets", "shortDescription": {"text": "Unused endpoint: USE /assets"}, "fullDescription": {"text": "`server/index.ts` declares `USE /assets` 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/21053"}, "properties": {"repository": "the-artifabrian/quartermaster", "repoUrl": "https://github.com/the-artifabrian/quartermaster", "branch": "main"}, "results": [{"ruleId": "scanner-b780ccbd956cdfb4", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 scripts/clear-recipes.ts:6"}, "properties": {"repobilityId": "d51488d4fc454152", "scanner": "scanner-primary", "fingerprint": "b780ccbd956cdfb4", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-b20d7dac7691c49f", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 prisma/seed.ts:8"}, "properties": {"repobilityId": "f77d7e0fb34b08e2", "scanner": "scanner-primary", "fingerprint": "b20d7dac7691c49f", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-875d5dbe3724896d", "level": "warning", "message": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 app/root.tsx:308"}, "properties": {"repobilityId": "d3f28b65f714df42", "scanner": "scanner-primary", "fingerprint": "875d5dbe3724896d", "layer": "frontend", "severity": "medium", "confidence": 1.0, "tags": ["frontend-quality", "fq.dangerous-html"]}}, {"ruleId": "scanner-5bbcebc2e88f97e7", "level": "warning", "message": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 app/utils/client-hints.tsx:51"}, "properties": {"repobilityId": "f244d1d733ceaf9f", "scanner": "scanner-primary", "fingerprint": "5bbcebc2e88f97e7", "layer": "frontend", "severity": "medium", "confidence": 1.0, "tags": ["frontend-quality", "fq.dangerous-html"]}}, {"ruleId": "scanner-19c37391ca682f26", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 app/utils/providers/google.server.ts:28"}, "properties": {"repobilityId": "05b3a22205aff70a", "scanner": "scanner-primary", "fingerprint": "19c37391ca682f26", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-79f7010ea73962f8", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 app/components/recipe-selector.tsx:193"}, "properties": {"repobilityId": "204f41c0501cdae8", "scanner": "scanner-primary", "fingerprint": "79f7010ea73962f8", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-3f7db99418d3a9d4", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 app/components/shopping-list-to-inventory.tsx:140"}, "properties": {"repobilityId": "73c27ee0bdd7e87e", "scanner": "scanner-primary", "fingerprint": "3f7db99418d3a9d4", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-297a9524d86e9287", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 app/components/timer-widget.tsx:112"}, "properties": {"repobilityId": "58aa790da259e7ba", "scanner": "scanner-primary", "fingerprint": "297a9524d86e9287", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-b34012dcac7e75dc", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 app/components/ingredient-fields.tsx:457"}, "properties": {"repobilityId": "e39f4d4efe5ff92a", "scanner": "scanner-primary", "fingerprint": "b34012dcac7e75dc", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-9fc9e0312596682d", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 app/components/suggest-meals-modal.tsx:389"}, "properties": {"repobilityId": "31daecbf2938ac2f", "scanner": "scanner-primary", "fingerprint": "9fc9e0312596682d", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-411264ab78ae18c3", "level": "warning", "message": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 app/routes/share.$recipeId.tsx:275"}, "properties": {"repobilityId": "131ef08f6ee6a3a6", "scanner": "scanner-primary", "fingerprint": "411264ab78ae18c3", "layer": "frontend", "severity": "medium", "confidence": 1.0, "tags": ["frontend-quality", "fq.dangerous-html"]}}, {"ruleId": "scanner-e968274402049e25", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 app/routes/settings/profile/index.tsx:113"}, "properties": {"repobilityId": "e8e03addfda66c73", "scanner": "scanner-primary", "fingerprint": "e968274402049e25", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-54ce3b0e96806627", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 app/routes/settings/profile/household.tsx:341"}, "properties": {"repobilityId": "9ab77c2c86ae599b", "scanner": "scanner-primary", "fingerprint": "54ce3b0e96806627", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-5486a12dbafac14a", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 app/routes/resources/healthcheck.tsx:23"}, "properties": {"repobilityId": "e0ab8fcf7994f21a", "scanner": "scanner-primary", "fingerprint": "5486a12dbafac14a", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-de2b277611bc13b4", "level": "warning", "message": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 app/routes/recipes/$recipeId.tsx:632"}, "properties": {"repobilityId": "4b8e558988f4c64b", "scanner": "scanner-primary", "fingerprint": "de2b277611bc13b4", "layer": "frontend", "severity": "medium", "confidence": 1.0, "tags": ["frontend-quality", "fq.dangerous-html"]}}, {"ruleId": "scanner-7dd0280a21ccf200", "level": "warning", "message": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 app/routes/_marketing/index.tsx:57"}, "properties": {"repobilityId": "797ed28d6a274f65", "scanner": "scanner-primary", "fingerprint": "7dd0280a21ccf200", "layer": "frontend", "severity": "medium", "confidence": 1.0, "tags": ["frontend-quality", "fq.dangerous-html"]}}, {"ruleId": "scanner-1c508b33b435d4f3", "level": "warning", "message": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 app/routes/_marketing/support.tsx:84"}, "properties": {"repobilityId": "06962c30e2efe535", "scanner": "scanner-primary", "fingerprint": "1c508b33b435d4f3", "layer": "frontend", "severity": "medium", "confidence": 1.0, "tags": ["frontend-quality", "fq.dangerous-html"]}}, {"ruleId": "scanner-087b5b94b54deea0", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 server/index.ts:148"}, "properties": {"repobilityId": "b4961a877930fd2f", "scanner": "scanner-primary", "fingerprint": "087b5b94b54deea0", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-7560861370b89999", "level": "warning", "message": {"text": "Dockerfile runs as root: other/Dockerfile"}, "properties": {"repobilityId": "38b2ae3ab7d734ed", "scanner": "scanner-primary", "fingerprint": "7560861370b89999", "layer": "hardware", "severity": "medium", "confidence": 1.0, "tags": ["security", "container"]}}, {"ruleId": "scanner-111752ee584669c2", "level": "note", "message": {"text": "Docker base image is tag-pinned but not digest-pinned: oven/bun:1.3.13-alpine"}, "properties": {"repobilityId": "4f964c0177b041a1", "scanner": "scanner-primary", "fingerprint": "111752ee584669c2", "layer": "hardware", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "docker", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "other/Dockerfile"}, "region": {"startLine": 4}}}]}, {"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-f9c74af8cd18c0b7", "level": "warning", "message": {"text": "Insecure pattern 'dangerous_innerhtml' in app/root.tsx:308"}, "properties": {"repobilityId": "4e23d1962fdcfcfb", "scanner": "scanner-primary", "fingerprint": "f9c74af8cd18c0b7", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "dangerous_innerhtml"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "app/root.tsx"}, "region": {"startLine": 308}}}]}, {"ruleId": "scanner-706c12960611dc62", "level": "warning", "message": {"text": "Insecure pattern 'dangerous_innerhtml' in app/utils/client-hints.tsx:51"}, "properties": {"repobilityId": "8b32b93a7d4c315c", "scanner": "scanner-primary", "fingerprint": "706c12960611dc62", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "dangerous_innerhtml"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "app/utils/client-hints.tsx"}, "region": {"startLine": 51}}}]}, {"ruleId": "scanner-d92db9d62712308c", "level": "error", "message": {"text": "Insecure pattern 'exec_used' in app/utils/cache.server.ts:32"}, "properties": {"repobilityId": "41a88f4134c7b654", "scanner": "scanner-primary", "fingerprint": "d92db9d62712308c", "layer": "security", "severity": "high", "confidence": 1.0, "tags": ["owasp", "exec_used"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "app/utils/cache.server.ts"}, "region": {"startLine": 32}}}]}, {"ruleId": "scanner-991f333ee90f9443", "level": "warning", "message": {"text": "Insecure pattern 'dangerous_innerhtml' in app/routes/share.$recipeId.tsx:275"}, "properties": {"repobilityId": "0c169465dd610f07", "scanner": "scanner-primary", "fingerprint": "991f333ee90f9443", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "dangerous_innerhtml"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "app/routes/share.$recipeId.tsx"}, "region": {"startLine": 275}}}]}, {"ruleId": "scanner-d96c20599014a5a6", "level": "warning", "message": {"text": "Insecure pattern 'dangerous_innerhtml' in app/routes/recipes/$recipeId.tsx:632"}, "properties": {"repobilityId": "5ac3aa68516caa16", "scanner": "scanner-primary", "fingerprint": "d96c20599014a5a6", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "dangerous_innerhtml"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "app/routes/recipes/$recipeId.tsx"}, "region": {"startLine": 632}}}]}, {"ruleId": "scanner-447bf9bbdcd5ebf9", "level": "warning", "message": {"text": "Insecure pattern 'dangerous_innerhtml' in app/routes/_marketing/index.tsx:57"}, "properties": {"repobilityId": "03228e16d4a87252", "scanner": "scanner-primary", "fingerprint": "447bf9bbdcd5ebf9", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "dangerous_innerhtml"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "app/routes/_marketing/index.tsx"}, "region": {"startLine": 57}}}]}, {"ruleId": "scanner-f3bf701eb20f7fb8", "level": "warning", "message": {"text": "Insecure pattern 'dangerous_innerhtml' in app/routes/_marketing/support.tsx:84"}, "properties": {"repobilityId": "a80e6e9cd898a031", "scanner": "scanner-primary", "fingerprint": "f3bf701eb20f7fb8", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "dangerous_innerhtml"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "app/routes/_marketing/support.tsx"}, "region": {"startLine": 84}}}]}, {"ruleId": "scanner-0a468b5cb7e17347", "level": "warning", "message": {"text": "Insecure pattern 'node_child_process' in other/generate-favicons.mjs:5"}, "properties": {"repobilityId": "c6018e6311e93c4f", "scanner": "scanner-primary", "fingerprint": "0a468b5cb7e17347", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "node_child_process"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "other/generate-favicons.mjs"}, "region": {"startLine": 5}}}]}, {"ruleId": "scanner-28c4a04bd807da0c", "level": "note", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "722ab4643920a278", "scanner": "scanner-primary", "fingerprint": "28c4a04bd807da0c", "layer": "cicd", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/deploy.yml"}, "region": {"startLine": 23}}}]}, {"ruleId": "scanner-28c4a04bd807da0c", "level": "warning", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "a0a981ba790d4e7c", "scanner": "scanner-primary", "fingerprint": "28c4a04bd807da0c", "layer": "cicd", "severity": "medium", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/deploy.yml"}, "region": {"startLine": 26}}}]}, {"ruleId": "scanner-28c4a04bd807da0c", "level": "note", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "722ab4643920a278", "scanner": "scanner-primary", "fingerprint": "28c4a04bd807da0c", "layer": "cicd", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/deploy.yml"}, "region": {"startLine": 47}}}]}, {"ruleId": "scanner-28c4a04bd807da0c", "level": "warning", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "a0a981ba790d4e7c", "scanner": "scanner-primary", "fingerprint": "28c4a04bd807da0c", "layer": "cicd", "severity": "medium", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/deploy.yml"}, "region": {"startLine": 50}}}]}, {"ruleId": "scanner-28c4a04bd807da0c", "level": "note", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "722ab4643920a278", "scanner": "scanner-primary", "fingerprint": "28c4a04bd807da0c", "layer": "cicd", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/deploy.yml"}, "region": {"startLine": 74}}}]}, {"ruleId": "scanner-28c4a04bd807da0c", "level": "warning", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "a0a981ba790d4e7c", "scanner": "scanner-primary", "fingerprint": "28c4a04bd807da0c", "layer": "cicd", "severity": "medium", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/deploy.yml"}, "region": {"startLine": 77}}}]}, {"ruleId": "scanner-28c4a04bd807da0c", "level": "note", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "722ab4643920a278", "scanner": "scanner-primary", "fingerprint": "28c4a04bd807da0c", "layer": "cicd", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/deploy.yml"}, "region": {"startLine": 102}}}]}, {"ruleId": "scanner-28c4a04bd807da0c", "level": "warning", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "cdd488c07b90bf4b", "scanner": "scanner-primary", "fingerprint": "28c4a04bd807da0c", "layer": "cicd", "severity": "medium", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/deploy.yml"}, "region": {"startLine": 105}}}]}, {"ruleId": "scanner-28c4a04bd807da0c", "level": "warning", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "b5ff68ae3a5989fc", "scanner": "scanner-primary", "fingerprint": "28c4a04bd807da0c", "layer": "cicd", "severity": "medium", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/deploy.yml"}, "region": {"startLine": 111}}}]}, {"ruleId": "scanner-28c4a04bd807da0c", "level": "note", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "722ab4643920a278", "scanner": "scanner-primary", "fingerprint": "28c4a04bd807da0c", "layer": "cicd", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/deploy.yml"}, "region": {"startLine": 182}}}]}, {"ruleId": "scanner-28c4a04bd807da0c", "level": "warning", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "51525ad311ce9cb3", "scanner": "scanner-primary", "fingerprint": "28c4a04bd807da0c", "layer": "cicd", "severity": "medium", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/deploy.yml"}, "region": {"startLine": 191}}}]}, {"ruleId": "scanner-28c4a04bd807da0c", "level": "note", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "722ab4643920a278", "scanner": "scanner-primary", "fingerprint": "28c4a04bd807da0c", "layer": "cicd", "severity": "low", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/deploy.yml"}, "region": {"startLine": 225}}}]}, {"ruleId": "scanner-28c4a04bd807da0c", "level": "warning", "message": {"text": "GitHub Action is tag-pinned rather than SHA-pinned"}, "properties": {"repobilityId": "51525ad311ce9cb3", "scanner": "scanner-primary", "fingerprint": "28c4a04bd807da0c", "layer": "cicd", "severity": "medium", "confidence": 1.0, "tags": ["supply-chain", "github-actions", "pinned-dependencies"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/deploy.yml"}, "region": {"startLine": 234}}}]}, {"ruleId": "scanner-3ab5d313dda8e5f9", "level": "note", "message": {"text": "Debug logging residue appears in source files"}, "properties": {"repobilityId": "395ea4b56b4b0cf5", "scanner": "scanner-primary", "fingerprint": "3ab5d313dda8e5f9", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["debug", "cleanup", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-72b2a6250083a784", "level": "warning", "message": {"text": "Placeholder or mock-heavy implementation detected"}, "properties": {"repobilityId": "dd963ca8fbd69be7", "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": "ad122b15cab90c1b", "scanner": "scanner-primary", "fingerprint": "2d0c7b7ab8f8aacf", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["placeholder", "mock-data", "critical-flow", "generated-repo-pattern"]}}, {"ruleId": "scanner-ea8f3013f588db25", "level": "note", "message": {"text": "Shallow git history limits provenance confidence"}, "properties": {"repobilityId": "79b1ebc148b39cdd", "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": "1f4587a1862331ee", "scanner": "scanner-primary", "fingerprint": "8424db9c75e04ba4", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["provenance", "git-history", "generated-repo-pattern"]}}, {"ruleId": "scanner-9098aa0124c36302", "level": "none", "message": {"text": "Commented-code block (7 lines) in vite.config.ts:76"}, "properties": {"repobilityId": "45668d8706185061", "scanner": "scanner-primary", "fingerprint": "9098aa0124c36302", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-3897a2c05d732ae0", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 app/utils/email.server.ts:55"}, "properties": {"repobilityId": "2ada07e54aab0599", "scanner": "scanner-primary", "fingerprint": "3897a2c05d732ae0", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-78ef4f360e4d1229", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 app/utils/storage.server.ts:14"}, "properties": {"repobilityId": "8457879ccb70b388", "scanner": "scanner-primary", "fingerprint": "78ef4f360e4d1229", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-40079607c4e1aca0", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 app/utils/recipe-extract-llm.server.ts:357"}, "properties": {"repobilityId": "6d4fd7e8d1c08952", "scanner": "scanner-primary", "fingerprint": "40079607c4e1aca0", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-1b0849c734799060", "level": "none", "message": {"text": "Commented-code block (5 lines) in app/utils/cache.server.ts:21"}, "properties": {"repobilityId": "3bc1aecc2f59b09d", "scanner": "scanner-primary", "fingerprint": "1b0849c734799060", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-9e0a7514e625b944", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 app/utils/providers/google.server.ts:45"}, "properties": {"repobilityId": "2b2cab39db8021c4", "scanner": "scanner-primary", "fingerprint": "9e0a7514e625b944", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-d9895d8a7a7b60a3", "level": "none", "message": {"text": "Commented-code block (7 lines) in app/components/service-worker-data-sync.tsx:11"}, "properties": {"repobilityId": "19164175e262f36c", "scanner": "scanner-primary", "fingerprint": "d9895d8a7a7b60a3", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-2a37e4fef7f6081b", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 app/components/nav-timing.tsx:49"}, "properties": {"repobilityId": "3302cd2f6db66131", "scanner": "scanner-primary", "fingerprint": "2a37e4fef7f6081b", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-701d6a73cfcad04d", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 app/routes/settings/profile/passkeys.tsx:112"}, "properties": {"repobilityId": "b04d13402b516512", "scanner": "scanner-primary", "fingerprint": "701d6a73cfcad04d", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-01674ba41742e2a3", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 app/routes/admin/cache/sqlite.server.ts:25"}, "properties": {"repobilityId": "76f1c10c35c30bcb", "scanner": "scanner-primary", "fingerprint": "01674ba41742e2a3", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-b93e9ddf5c9e6f81", "level": "none", "message": {"text": "Commented-code block (5 lines) in app/routes/plan/index.tsx:58"}, "properties": {"repobilityId": "6e99c32d3c6bd90d", "scanner": "scanner-primary", "fingerprint": "b93e9ddf5c9e6f81", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-71aff0a0143aa735", "level": "none", "message": {"text": "Commented-code block (5 lines) in public/sw.js:12"}, "properties": {"repobilityId": "dfd1e8ab2717ff18", "scanner": "scanner-primary", "fingerprint": "71aff0a0143aa735", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-618f287de21daaf0", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 public/sw.js:324"}, "properties": {"repobilityId": "aa04f12c37e38eb4", "scanner": "scanner-primary", "fingerprint": "618f287de21daaf0", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-d0b17c8c07a7421d", "level": "note", "message": {"text": "8 env vars used in code but missing from .env.example"}, "properties": {"repobilityId": "17fe720e8247f9df", "scanner": "scanner-primary", "fingerprint": "d0b17c8c07a7421d", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "config-drift"]}}, {"ruleId": "scanner-f8fd5f38a29620d8", "level": "error", "message": {"text": "Dangling fetch: POST https://api.resend.com/emails (app/utils/email.server.ts:55)"}, "properties": {"repobilityId": "781373df879b3e25", "scanner": "scanner-primary", "fingerprint": "f8fd5f38a29620d8", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-f8915d5f8152cab4", "level": "error", "message": {"text": "Dangling fetch: GET https://api.pwnedpasswords.com/range/${prefix} (app/utils/auth.server.ts:311)"}, "properties": {"repobilityId": "0feee5de3329d175", "scanner": "scanner-primary", "fingerprint": "f8915d5f8152cab4", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-f3d013dd54a832fa", "level": "error", "message": {"text": "Dangling fetch: GET https://www.googleapis.com/oauth2/v2/userinfo (app/utils/providers/google.server.ts:45)"}, "properties": {"repobilityId": "8995f04f6bf17826", "scanner": "scanner-primary", "fingerprint": "f3d013dd54a832fa", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-e7eba72bb21372ee", "level": "error", "message": {"text": "Dangling fetch: GET /webauthn/registration (app/routes/settings/profile/passkeys.tsx:104)"}, "properties": {"repobilityId": "7939c6f9f8082507", "scanner": "scanner-primary", "fingerprint": "e7eba72bb21372ee", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-c1a0c847090a1b1b", "level": "error", "message": {"text": "Dangling fetch: POST /webauthn/registration (app/routes/settings/profile/passkeys.tsx:112)"}, "properties": {"repobilityId": "6f995ef62001624a", "scanner": "scanner-primary", "fingerprint": "c1a0c847090a1b1b", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-de5453f222042a84", "level": "error", "message": {"text": "Dangling fetch: GET /webauthn/authentication (app/routes/_auth/login.tsx:236)"}, "properties": {"repobilityId": "984fcd9193d38064", "scanner": "scanner-primary", "fingerprint": "de5453f222042a84", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-cb754bd6a171c1dc", "level": "error", "message": {"text": "Dangling fetch: POST /webauthn/authentication (app/routes/_auth/login.tsx:245)"}, "properties": {"repobilityId": "57b1fecd73c5526f", "scanner": "scanner-primary", "fingerprint": "cb754bd6a171c1dc", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-a2deb7f42a7698bd", "level": "error", "message": {"text": "Dangling fetch: POST /resources/transcribe (app/hooks/use-speech-to-text.ts:124)"}, "properties": {"repobilityId": "dbe6c7af7445cea6", "scanner": "scanner-primary", "fingerprint": "a2deb7f42a7698bd", "layer": "api", "severity": "high", "confidence": 1.0, "tags": ["wiring", "dangling-fetch", "fetch"]}}, {"ruleId": "scanner-02d93607031ff690", "level": "note", "message": {"text": "Unused endpoint: USE /favicons"}, "properties": {"repobilityId": "36c65068cfe0b694", "scanner": "scanner-primary", "fingerprint": "02d93607031ff690", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-f22cf013c13a7f0c", "level": "note", "message": {"text": "Unused endpoint: GET /sw.js"}, "properties": {"repobilityId": "0034abb587c4e9bd", "scanner": "scanner-primary", "fingerprint": "f22cf013c13a7f0c", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}, {"ruleId": "scanner-3b59435b3211a9df", "level": "note", "message": {"text": "Unused endpoint: USE /assets"}, "properties": {"repobilityId": "5272fdc04a51dd35", "scanner": "scanner-primary", "fingerprint": "3b59435b3211a9df", "layer": "api", "severity": "low", "confidence": 1.0, "tags": ["wiring", "unused-endpoint"]}}]}]}