{"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-bead75b0f9233e05", "name": "No API endpoints detected", "shortDescription": {"text": "No API endpoints detected"}, "fullDescription": {"text": "The scanner did not find FastAPI/Flask/Express/NestJS/GraphQL/gRPC routes. If this repo exposes APIs, the framework may be unsupported."}, "properties": {"scanner": "scanner-primary", "layer": "api", "severity": "info", "confidence": 1.0}}, {"id": "scanner-c2e6db3301bf1403", "name": "Stray `console.log` in TS/JS \u2014 backup-db.js:33", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 backup-db.js:33"}, "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-8556fc3881836664", "name": "`truncate` class without `title=` for hover reveal \u2014 src/app/about/_components/community-contributors.tsx:179", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/about/_components/community-contributors.tsx:179"}, "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-5cf043a47195fb18", "name": "TODO/FIXME marker in shipping code \u2014 src/app/degree-planner/page.tsx:5", "shortDescription": {"text": "TODO/FIXME marker in shipping code \u2014 src/app/degree-planner/page.tsx:5"}, "fullDescription": {"text": "Track in /reviews or /issues, not as a code comment that rots.\n\nWhy: Drift control \u2014 shouldn't be the same as Quality TODO scanner.\nRule id: fq.todo-marker"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-f13dc83c1002e43b", "name": "TODO/FIXME marker in shipping code \u2014 src/app/api/queens-answers/chat/route.ts:122", "shortDescription": {"text": "TODO/FIXME marker in shipping code \u2014 src/app/api/queens-answers/chat/route.ts:122"}, "fullDescription": {"text": "Track in /reviews or /issues, not as a code comment that rots.\n\nWhy: Drift control \u2014 shouldn't be the same as Quality TODO scanner.\nRule id: fq.todo-marker"}, "properties": {"scanner": "scanner-primary", "layer": "frontend", "severity": "info", "confidence": 1.0}}, {"id": "scanner-166e0d1d3d494cdd", "name": "`truncate` class without `title=` for hover reveal \u2014 src/app/plan-courses/components/SessionPicker.tsx:132", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/plan-courses/components/SessionPicker.tsx:132"}, "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-df800be4bd9a4bb8", "name": "`truncate` class without `title=` for hover reveal \u2014 src/app/plan-courses/components/StarredCoursesTab.tsx:550", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/plan-courses/components/StarredCoursesTab.tsx:550"}, "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-75d178ccfd58722d", "name": "`truncate` class without `title=` for hover reveal \u2014 src/app/plan-courses/components/WeeklyTimetable.tsx:73", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/plan-courses/components/WeeklyTimetable.tsx:73"}, "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-fe9d740ec7825aad", "name": "`truncate` class without `title=` for hover reveal \u2014 src/app/plan-courses/components/OfferingsList.tsx:131", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/plan-courses/components/OfferingsList.tsx:131"}, "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-bbfe79389d22bb04", "name": "`truncate` class without `title=` for hover reveal \u2014 src/app/plan-courses/components/MySchedulesPanel.tsx:124", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/plan-courses/components/MySchedulesPanel.tsx:124"}, "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-e2d5554a757cf5e5", "name": "`truncate` class without `title=` for hover reveal \u2014 src/app/plan-courses/components/FilterMultiSelect.tsx:71", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/plan-courses/components/FilterMultiSelect.tsx:71"}, "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-bffcb9e7f4380539", "name": "`truncate` class without `title=` for hover reveal \u2014 src/app/settings/_components/upload-history.tsx:69", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/settings/_components/upload-history.tsx:69"}, "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-dce36769a2a0ee23", "name": "`truncate` class without `title=` for hover reveal \u2014 src/app/add-courses/page.tsx:524", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/add-courses/page.tsx:524"}, "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-c3d61be48302ed8f", "name": "`truncate` class without `title=` for hover reveal \u2014 src/app/course-comments/page.tsx:466", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/course-comments/page.tsx:466"}, "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-9e7e6b3813c880c1", "name": "`dangerouslySetInnerHTML` used in a React component \u2014 src/app/course-comments/page.tsx:216", "shortDescription": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 src/app/course-comments/page.tsx:216"}, "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-772e3bc6c0543d1d", "name": "`truncate` class without `title=` for hover reveal \u2014 src/app/schools/queens/search/page.tsx:223", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/schools/queens/search/page.tsx:223"}, "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-d842668bf6b87ba9", "name": "`truncate` class without `title=` for hover reveal \u2014 src/app/schools/queens/[courseCode]/page.tsx:341", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/schools/queens/[courseCode]/page.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-7bba3a760e68c2a4", "name": "`truncate` class without `title=` for hover reveal \u2014 src/components/navigation.tsx:296", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/navigation.tsx:296"}, "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-4a689d7091a1edfc", "name": "`truncate` class without `title=` for hover reveal \u2014 src/components/seasonal-upload-banner.tsx:41", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/seasonal-upload-banner.tsx:41"}, "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-cb92e1cd4602a69b", "name": "`dangerouslySetInnerHTML` used in a React component \u2014 src/components/landing/faq.tsx:87", "shortDescription": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 src/components/landing/faq.tsx:87"}, "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-b209c77e83b119fd", "name": "`dangerouslySetInnerHTML` used in a React component \u2014 src/components/ui/chart.tsx:70", "shortDescription": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 src/components/ui/chart.tsx:70"}, "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-d1968ade4b91793c", "name": "Stray `console.log` in TS/JS \u2014 src/lib/db-debug.js:22", "shortDescription": {"text": "Stray `console.log` in TS/JS \u2014 src/lib/db-debug.js:22"}, "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-1d2e25e1ff60073c", "name": "Privileged port 12 in use", "shortDescription": {"text": "Privileged port 12 in use"}, "fullDescription": {"text": "Port 12 is privileged (<1024). Make sure the service runs with the right caps or front it with a non-privileged port via a load balancer."}, "properties": {"scanner": "scanner-primary", "layer": "network", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-883bb594247334c4", "name": "Privileged port 587 in use", "shortDescription": {"text": "Privileged port 587 in use"}, "fullDescription": {"text": "Port 587 is privileged (<1024). Make sure the service runs with the right caps or front it with a non-privileged port via a load balancer."}, "properties": {"scanner": "scanner-primary", "layer": "network", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-a0f14e63e31c601c", "name": "Insecure pattern 'dangerous_innerhtml' in src/app/course-comments/page.tsx:216", "shortDescription": {"text": "Insecure pattern 'dangerous_innerhtml' in src/app/course-comments/page.tsx:216"}, "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-5bf074fa5aa231ed", "name": "Insecure pattern 'dangerous_innerhtml' in src/components/landing/faq.tsx:87", "shortDescription": {"text": "Insecure pattern 'dangerous_innerhtml' in src/components/landing/faq.tsx:87"}, "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-ea3b5e389d8c9c0f", "name": "Low test-to-source ratio", "shortDescription": {"text": "Low test-to-source ratio"}, "fullDescription": {"text": "28 tests / 218 src (ratio 0.13)."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-3ab5d313dda8e5f9", "name": "Debug logging residue appears in source files", "shortDescription": {"text": "Debug logging residue appears in source files"}, "fullDescription": {"text": "Found 11 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 97 placeholder/mock markers across 23 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-9d79c4077342a7d0", "name": "Runtime service client appears to use placeholder configuration", "shortDescription": {"text": "Runtime service client appears to use placeholder configuration"}, "fullDescription": {"text": "A runtime source file appears to wire Supabase/Firebase/AI/payment-style clients to placeholder URLs, keys, or fallback values. In the Fable corpus this often means the UI/API shape is present while the backend service is not actually configured."}, "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-53372f73cc826d88", "name": "Legacy-named symbol `rag_chunks_rmp_backup` in backup-db.js:13", "shortDescription": {"text": "Legacy-named symbol `rag_chunks_rmp_backup` in backup-db.js:13"}, "fullDescription": {"text": "Names with suffixes like `_old`, `_v1`, `_deprecated` usually indicate replaced-but-not-removed code (typical AI-coder leftover). Confirm and delete, or rename if it's the active version."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-4e2108927c833fbc", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/about/_lib/community-contributors.ts:80", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/about/_lib/community-contributors.ts:80"}, "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-8e9c0c6de21402a4", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/api/issues/route.ts:91", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/api/issues/route.ts:91"}, "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-8c6545c78f605d44", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/plan-courses/hooks/authed-fetch.ts:24", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/plan-courses/hooks/authed-fetch.ts:24"}, "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-856fda9f8939efb5", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/settings/page.tsx:57", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/settings/page.tsx:57"}, "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-153064a32ed7b023", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/onboarding/page.tsx:62", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/onboarding/page.tsx:62"}, "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-4f14af6abe8e33dc", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/issues/page.tsx:123", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/issues/page.tsx:123"}, "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-2fe924e68393fab2", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/hooks/use-access-status.ts:30", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/hooks/use-access-status.ts:30"}, "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-10a046c9d802060f", "name": "Legacy-named symbol `rag_chunks_rmp_backup` in src/types/database.types.ts:475", "shortDescription": {"text": "Legacy-named symbol `rag_chunks_rmp_backup` in src/types/database.types.ts:475"}, "fullDescription": {"text": "Names with suffixes like `_old`, `_v1`, `_deprecated` usually indicate replaced-but-not-removed code (typical AI-coder leftover). Confirm and delete, or rename if it's the active version."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-e9b752208233bae1", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/onboarding-guard.tsx:79", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/onboarding-guard.tsx:79"}, "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-a046cb5dfdf9a033", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/lib/course-api.ts:35", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/lib/course-api.ts:35"}, "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-e47746c821b0eff9", "name": "Commented-code block (5 lines) in src/lib/pdf/parse-distribution.ts:180", "shortDescription": {"text": "Commented-code block (5 lines) in src/lib/pdf/parse-distribution.ts:180"}, "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-906343c177d5dc9f", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/lib/github/create-issue.ts:15", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/lib/github/create-issue.ts:15"}, "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-022e6399d1ef2beb", "name": "12 env vars used in code but missing from .env.example", "shortDescription": {"text": "12 env vars used in code but missing from .env.example"}, "fullDescription": {"text": "Drift between code and config docs. The first few: `BREVO_ADMIN_EMAIL`, `BREVO_SMTP_EMAIL`, `BREVO_SMTP_PASS`, `BREVO_SMTP_USER`, `CRON_SECRET`, `GITHUB_REPO_NAME`, `GITHUB_REPO_OWNER`, `GITHUB_TOKEN` + 4 more. 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}}]}}, "automationDetails": {"id": "repobility/24132"}, "properties": {"repository": "amaanjaved1/Coursify-WebApp", "repoUrl": "https://github.com/amaanjaved1/Coursify-WebApp", "branch": "main"}, "results": [{"ruleId": "scanner-bead75b0f9233e05", "level": "none", "message": {"text": "No API endpoints detected"}, "properties": {"repobilityId": "0f8bb852027c38f8", "scanner": "scanner-primary", "fingerprint": "bead75b0f9233e05", "layer": "api", "severity": "info", "confidence": 1.0, "tags": ["coverage"]}}, {"ruleId": "scanner-c2e6db3301bf1403", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 backup-db.js:33"}, "properties": {"repobilityId": "6940715719f0ab29", "scanner": "scanner-primary", "fingerprint": "c2e6db3301bf1403", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-8556fc3881836664", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/about/_components/community-contributors.tsx:179"}, "properties": {"repobilityId": "1ffafe9dc7d6a5ab", "scanner": "scanner-primary", "fingerprint": "8556fc3881836664", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-5cf043a47195fb18", "level": "none", "message": {"text": "TODO/FIXME marker in shipping code \u2014 src/app/degree-planner/page.tsx:5"}, "properties": {"repobilityId": "fe8ae4bbd451c360", "scanner": "scanner-primary", "fingerprint": "5cf043a47195fb18", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.todo-marker"]}}, {"ruleId": "scanner-f13dc83c1002e43b", "level": "none", "message": {"text": "TODO/FIXME marker in shipping code \u2014 src/app/api/queens-answers/chat/route.ts:122"}, "properties": {"repobilityId": "7584b1fadf3c9f1a", "scanner": "scanner-primary", "fingerprint": "f13dc83c1002e43b", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.todo-marker"]}}, {"ruleId": "scanner-166e0d1d3d494cdd", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/plan-courses/components/SessionPicker.tsx:132"}, "properties": {"repobilityId": "43e448121f3c921d", "scanner": "scanner-primary", "fingerprint": "166e0d1d3d494cdd", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-df800be4bd9a4bb8", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/plan-courses/components/StarredCoursesTab.tsx:550"}, "properties": {"repobilityId": "90eccb896f9f08bb", "scanner": "scanner-primary", "fingerprint": "df800be4bd9a4bb8", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-75d178ccfd58722d", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/plan-courses/components/WeeklyTimetable.tsx:73"}, "properties": {"repobilityId": "58efe92e8dfc1b7a", "scanner": "scanner-primary", "fingerprint": "75d178ccfd58722d", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-fe9d740ec7825aad", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/plan-courses/components/OfferingsList.tsx:131"}, "properties": {"repobilityId": "ced5974813db71d8", "scanner": "scanner-primary", "fingerprint": "fe9d740ec7825aad", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-bbfe79389d22bb04", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/plan-courses/components/MySchedulesPanel.tsx:124"}, "properties": {"repobilityId": "d80fbede9aa7c8af", "scanner": "scanner-primary", "fingerprint": "bbfe79389d22bb04", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-e2d5554a757cf5e5", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/plan-courses/components/FilterMultiSelect.tsx:71"}, "properties": {"repobilityId": "98f2748a3471354f", "scanner": "scanner-primary", "fingerprint": "e2d5554a757cf5e5", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-bffcb9e7f4380539", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/settings/_components/upload-history.tsx:69"}, "properties": {"repobilityId": "c85a86172221d485", "scanner": "scanner-primary", "fingerprint": "bffcb9e7f4380539", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-dce36769a2a0ee23", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/add-courses/page.tsx:524"}, "properties": {"repobilityId": "22999960ef5072fd", "scanner": "scanner-primary", "fingerprint": "dce36769a2a0ee23", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-c3d61be48302ed8f", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/course-comments/page.tsx:466"}, "properties": {"repobilityId": "d6d95cf779a5ec86", "scanner": "scanner-primary", "fingerprint": "c3d61be48302ed8f", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-9e7e6b3813c880c1", "level": "warning", "message": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 src/app/course-comments/page.tsx:216"}, "properties": {"repobilityId": "25898feae0ff579f", "scanner": "scanner-primary", "fingerprint": "9e7e6b3813c880c1", "layer": "frontend", "severity": "medium", "confidence": 1.0, "tags": ["frontend-quality", "fq.dangerous-html"]}}, {"ruleId": "scanner-772e3bc6c0543d1d", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/schools/queens/search/page.tsx:223"}, "properties": {"repobilityId": "fff92741fef58efa", "scanner": "scanner-primary", "fingerprint": "772e3bc6c0543d1d", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-d842668bf6b87ba9", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/schools/queens/[courseCode]/page.tsx:341"}, "properties": {"repobilityId": "69b5d403fe00d070", "scanner": "scanner-primary", "fingerprint": "d842668bf6b87ba9", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-7bba3a760e68c2a4", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/navigation.tsx:296"}, "properties": {"repobilityId": "6df047d9c8605beb", "scanner": "scanner-primary", "fingerprint": "7bba3a760e68c2a4", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-4a689d7091a1edfc", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/seasonal-upload-banner.tsx:41"}, "properties": {"repobilityId": "79958ad0c5641505", "scanner": "scanner-primary", "fingerprint": "4a689d7091a1edfc", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-cb92e1cd4602a69b", "level": "warning", "message": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 src/components/landing/faq.tsx:87"}, "properties": {"repobilityId": "927e46c8f3891337", "scanner": "scanner-primary", "fingerprint": "cb92e1cd4602a69b", "layer": "frontend", "severity": "medium", "confidence": 1.0, "tags": ["frontend-quality", "fq.dangerous-html"]}}, {"ruleId": "scanner-b209c77e83b119fd", "level": "warning", "message": {"text": "`dangerouslySetInnerHTML` used in a React component \u2014 src/components/ui/chart.tsx:70"}, "properties": {"repobilityId": "b8c68f17a4018107", "scanner": "scanner-primary", "fingerprint": "b209c77e83b119fd", "layer": "frontend", "severity": "medium", "confidence": 1.0, "tags": ["frontend-quality", "fq.dangerous-html"]}}, {"ruleId": "scanner-d1968ade4b91793c", "level": "note", "message": {"text": "Stray `console.log` in TS/JS \u2014 src/lib/db-debug.js:22"}, "properties": {"repobilityId": "f0a431553c4c2fe3", "scanner": "scanner-primary", "fingerprint": "d1968ade4b91793c", "layer": "frontend", "severity": "low", "confidence": 1.0, "tags": ["frontend-quality", "fq.console-leak"]}}, {"ruleId": "scanner-1d2e25e1ff60073c", "level": "warning", "message": {"text": "Privileged port 12 in use"}, "properties": {"repobilityId": "65e7eefa33f2def4", "scanner": "scanner-primary", "fingerprint": "1d2e25e1ff60073c", "layer": "network", "severity": "medium", "confidence": 1.0, "tags": ["security", "ports"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": ".github/workflows/onboarding-reminders.yml"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-883bb594247334c4", "level": "warning", "message": {"text": "Privileged port 587 in use"}, "properties": {"repobilityId": "a87a96c3b2e62c08", "scanner": "scanner-primary", "fingerprint": "883bb594247334c4", "layer": "network", "severity": "medium", "confidence": 1.0, "tags": ["security", "ports"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/lib/email.ts"}, "region": {"startLine": 1}}}]}, {"ruleId": "scanner-a0f14e63e31c601c", "level": "warning", "message": {"text": "Insecure pattern 'dangerous_innerhtml' in src/app/course-comments/page.tsx:216"}, "properties": {"repobilityId": "8bee62338929609e", "scanner": "scanner-primary", "fingerprint": "a0f14e63e31c601c", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "dangerous_innerhtml"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/app/course-comments/page.tsx"}, "region": {"startLine": 216}}}]}, {"ruleId": "scanner-5bf074fa5aa231ed", "level": "warning", "message": {"text": "Insecure pattern 'dangerous_innerhtml' in src/components/landing/faq.tsx:87"}, "properties": {"repobilityId": "a7b496f314b762ad", "scanner": "scanner-primary", "fingerprint": "5bf074fa5aa231ed", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "dangerous_innerhtml"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/components/landing/faq.tsx"}, "region": {"startLine": 87}}}]}, {"ruleId": "scanner-ea3b5e389d8c9c0f", "level": "note", "message": {"text": "Low test-to-source ratio"}, "properties": {"repobilityId": "ef7b2552cc00a375", "scanner": "scanner-primary", "fingerprint": "ea3b5e389d8c9c0f", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["tests"]}}, {"ruleId": "scanner-3ab5d313dda8e5f9", "level": "note", "message": {"text": "Debug logging residue appears in source files"}, "properties": {"repobilityId": "894b427ebb267e7f", "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": "4c9ee28fb8b11f22", "scanner": "scanner-primary", "fingerprint": "72b2a6250083a784", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["placeholder", "mock-data", "incomplete", "generated-repo-pattern"]}}, {"ruleId": "scanner-9d79c4077342a7d0", "level": "warning", "message": {"text": "Runtime service client appears to use placeholder configuration"}, "properties": {"repobilityId": "6c872d680cfea753", "scanner": "scanner-primary", "fingerprint": "9d79c4077342a7d0", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["placeholder", "runtime-config", "service-client", "generated-repo-pattern"]}}, {"ruleId": "scanner-2d0c7b7ab8f8aacf", "level": "warning", "message": {"text": "Critical user flow still appears backed by mock or placeholder data"}, "properties": {"repobilityId": "aabb30f0720e0707", "scanner": "scanner-primary", "fingerprint": "2d0c7b7ab8f8aacf", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["placeholder", "mock-data", "critical-flow", "generated-repo-pattern"]}}, {"ruleId": "scanner-53372f73cc826d88", "level": "note", "message": {"text": "Legacy-named symbol `rag_chunks_rmp_backup` in backup-db.js:13"}, "properties": {"repobilityId": "a2e4c62119e4ebf5", "scanner": "scanner-primary", "fingerprint": "53372f73cc826d88", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-4e2108927c833fbc", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/about/_lib/community-contributors.ts:80"}, "properties": {"repobilityId": "37bf1c261357b585", "scanner": "scanner-primary", "fingerprint": "4e2108927c833fbc", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-8e9c0c6de21402a4", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/api/issues/route.ts:91"}, "properties": {"repobilityId": "e8a01f098d1303b4", "scanner": "scanner-primary", "fingerprint": "8e9c0c6de21402a4", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-8c6545c78f605d44", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/plan-courses/hooks/authed-fetch.ts:24"}, "properties": {"repobilityId": "16a2a429b5fad898", "scanner": "scanner-primary", "fingerprint": "8c6545c78f605d44", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-856fda9f8939efb5", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/settings/page.tsx:57"}, "properties": {"repobilityId": "c12937cb88432e41", "scanner": "scanner-primary", "fingerprint": "856fda9f8939efb5", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-153064a32ed7b023", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/onboarding/page.tsx:62"}, "properties": {"repobilityId": "a94a528d7cfa4577", "scanner": "scanner-primary", "fingerprint": "153064a32ed7b023", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-4f14af6abe8e33dc", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/issues/page.tsx:123"}, "properties": {"repobilityId": "51b46bac0f7f4e14", "scanner": "scanner-primary", "fingerprint": "4f14af6abe8e33dc", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-2fe924e68393fab2", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/hooks/use-access-status.ts:30"}, "properties": {"repobilityId": "65a87cdae84cce3f", "scanner": "scanner-primary", "fingerprint": "2fe924e68393fab2", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-10a046c9d802060f", "level": "note", "message": {"text": "Legacy-named symbol `rag_chunks_rmp_backup` in src/types/database.types.ts:475"}, "properties": {"repobilityId": "abba0ccc2e76485e", "scanner": "scanner-primary", "fingerprint": "10a046c9d802060f", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "legacy-marker", "dead-code"]}}, {"ruleId": "scanner-e9b752208233bae1", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/onboarding-guard.tsx:79"}, "properties": {"repobilityId": "fb4095f05012db93", "scanner": "scanner-primary", "fingerprint": "e9b752208233bae1", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-a046cb5dfdf9a033", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/lib/course-api.ts:35"}, "properties": {"repobilityId": "3c3e36bef37a3608", "scanner": "scanner-primary", "fingerprint": "a046cb5dfdf9a033", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-e47746c821b0eff9", "level": "none", "message": {"text": "Commented-code block (5 lines) in src/lib/pdf/parse-distribution.ts:180"}, "properties": {"repobilityId": "f3322b1327fa4a52", "scanner": "scanner-primary", "fingerprint": "e47746c821b0eff9", "layer": "quality", "severity": "info", "confidence": 1.0, "tags": ["integrity", "commented-code", "dead-code"]}}, {"ruleId": "scanner-906343c177d5dc9f", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/lib/github/create-issue.ts:15"}, "properties": {"repobilityId": "6502425e684eea97", "scanner": "scanner-primary", "fingerprint": "906343c177d5dc9f", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-022e6399d1ef2beb", "level": "note", "message": {"text": "12 env vars used in code but missing from .env.example"}, "properties": {"repobilityId": "7bf4178327da75a2", "scanner": "scanner-primary", "fingerprint": "022e6399d1ef2beb", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["integrity", "config-drift"]}}]}]}