{"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-cea7560bf98ace36", "name": "`truncate` class without `title=` for hover reveal \u2014 src/app/dashboard/page.tsx:351", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/dashboard/page.tsx:351"}, "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-2d1bbd27667d1e1c", "name": "`truncate` class without `title=` for hover reveal \u2014 src/app/outreach/[id]/page.tsx:275", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/outreach/[id]/page.tsx:275"}, "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-6d165d5b69f7a055", "name": "`truncate` class without `title=` for hover reveal \u2014 src/app/customers/page.tsx:238", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/customers/page.tsx:238"}, "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-70f0cf9d54db65a3", "name": "`truncate` class without `title=` for hover reveal \u2014 src/app/quotes/page.tsx:248", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/quotes/page.tsx:248"}, "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-e964ac4308fa8a1d", "name": "`truncate` class without `title=` for hover reveal \u2014 src/components/CommandPalette.tsx:157", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/CommandPalette.tsx:157"}, "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-5f18c0ed612a9477", "name": "`truncate` class without `title=` for hover reveal \u2014 src/components/outreach/OutreachCard.tsx:40", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/outreach/OutreachCard.tsx:40"}, "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-0e8e65e78e7025ad", "name": "`truncate` class without `title=` for hover reveal \u2014 src/components/pipeline/PipelineCard.tsx:33", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/pipeline/PipelineCard.tsx:33"}, "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-461060ac3cac6a5f", "name": "`truncate` class without `title=` for hover reveal \u2014 src/components/schedule/ScheduleView.tsx:328", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/schedule/ScheduleView.tsx:328"}, "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-8b543bec0b6225bc", "name": "`truncate` class without `title=` for hover reveal \u2014 src/components/schedule/ScheduleCard.tsx:39", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/schedule/ScheduleCard.tsx:39"}, "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-4045a99f4e73b216", "name": "`truncate` class without `title=` for hover reveal \u2014 src/components/crew/CrewJobCard.tsx:84", "shortDescription": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/crew/CrewJobCard.tsx:84"}, "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-18727c5e4fe1c5c2", "name": "Insecure pattern 'cors_wildcard' in src/app/api/public/reviews/route.ts:4", "shortDescription": {"text": "Insecure pattern 'cors_wildcard' in src/app/api/public/reviews/route.ts:4"}, "fullDescription": {"text": "Found a known-risky pattern (cors_wildcard). Review and replace if possible."}, "properties": {"scanner": "scanner-primary", "layer": "security", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-6893a6c8b0861585", "name": "Very low test-to-source ratio", "shortDescription": {"text": "Very low test-to-source ratio"}, "fullDescription": {"text": "1 test file(s) for 221 source file(s) (ratio 0.00). Consider adding integration or unit tests for critical paths."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-141b30a41e03817b", "name": "No license file detected", "shortDescription": {"text": "No license file detected"}, "fullDescription": {"text": "No LICENSE/COPYING/NOTICE file was found. Generated repositories often omit licensing, which blocks reuse and automated intake."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "low", "confidence": 1.0}}, {"id": "scanner-72b2a6250083a784", "name": "Placeholder or mock-heavy implementation detected", "shortDescription": {"text": "Placeholder or mock-heavy implementation detected"}, "fullDescription": {"text": "Found 70 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-b9088664ace7f748", "name": "Composite production-readiness gap", "shortDescription": {"text": "Composite production-readiness gap"}, "fullDescription": {"text": "Multiple low-cost hardening controls are missing together: license, ci, tests. Opus verification showed these co-occurring gaps are a better readiness signal than reading each flag in isolation."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-749d4bc1bd66df5f", "name": "Agent instructions exist but release-hardening basics are missing", "shortDescription": {"text": "Agent instructions exist but release-hardening basics are missing"}, "fullDescription": {"text": "AI-coder instruction files were found, but the repo is missing license, ci, tests. Treat this as a contract gap: the agent is guided, but the generated output is not yet guarded by the controls that make it repeatable."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-4a81bfc5e17f134a", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/login/page.tsx:23", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/login/page.tsx:23"}, "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-4f14bb523897455f", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/jobs/JobForm.tsx:116", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/jobs/JobForm.tsx:116"}, "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-3a3595d27086e338", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/jobs/JobStatusSelect.tsx:31", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/jobs/JobStatusSelect.tsx:31"}, "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-8cafa17218d54968", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/jobs/MarkCompleteButton.tsx:15", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/jobs/MarkCompleteButton.tsx: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-463da77d2817a9fa", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/jobs/PhotoUpload.tsx:67", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/jobs/PhotoUpload.tsx:67"}, "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-a8eefcf40619aca4", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/jobs/DeleteJobButton.tsx:15", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/jobs/DeleteJobButton.tsx: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-c9f0d5627b6bee8b", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/expenses/ExpenseForm.tsx:74", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/expenses/ExpenseForm.tsx:74"}, "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-09f30e46c0190398", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/outreach/OutreachFollowUp.tsx:44", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/outreach/OutreachFollowUp.tsx:44"}, "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-79c0205a7725c20a", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/outreach/OutreachForm.tsx:90", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/outreach/OutreachForm.tsx:90"}, "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-d14ba09f414afc4a", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/customers/NewCustomerDialog.tsx:73", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/customers/NewCustomerDialog.tsx:73"}, "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-4f4b3cdb7faab6a4", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/customers/CustomerForm.tsx:54", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/customers/CustomerForm.tsx:54"}, "fullDescription": {"text": "Bare `fetch(...)` will throw an unhandled rejection on network failure. Wrap in try/catch, attach a `.catch(...)`, or pass an AbortSignal with a timeout."}, "properties": {"scanner": "scanner-primary", "layer": "quality", "severity": "medium", "confidence": 1.0}}, {"id": "scanner-c316a5322289f8ad", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/customers/DeleteCustomerButton.tsx:15", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/customers/DeleteCustomerButton.tsx: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-ecd639edaa6512db", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/quotes/ConvertToInvoiceButton.tsx:15", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/quotes/ConvertToInvoiceButton.tsx: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-16c514e0ef559c48", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/quotes/QuoteStatusSelect.tsx:25", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/quotes/QuoteStatusSelect.tsx: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-4918bed9d6bbd642", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/quotes/DeleteQuoteButton.tsx:15", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/quotes/DeleteQuoteButton.tsx: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-acae7e74b492c340", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/quotes/QuoteBuilder.tsx:88", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/quotes/QuoteBuilder.tsx:88"}, "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-eea10af46f3d9ff0", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/quotes/DuplicateQuoteButton.tsx:15", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/quotes/DuplicateQuoteButton.tsx: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-db277c8b8f06ca58", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/invoices/DeleteInvoiceButton.tsx:15", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/invoices/DeleteInvoiceButton.tsx: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-21b01611febb6987", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/invoices/DuplicateInvoiceButton.tsx:15", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/invoices/DuplicateInvoiceButton.tsx: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-5058a9abc0a2956a", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/invoices/MarkPaidButton.tsx:15", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/invoices/MarkPaidButton.tsx: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-3becb5d01953d456", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/invoices/InvoiceBuilder.tsx:60", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/invoices/InvoiceBuilder.tsx:60"}, "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-1a06f926c05161fa", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/invoices/InvoiceStatusSelect.tsx:25", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/invoices/InvoiceStatusSelect.tsx: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-c381eaf52bb3aee6", "name": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/crew/CrewPhotoUpload.tsx:58", "shortDescription": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/crew/CrewPhotoUpload.tsx:58"}, "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}}]}}, "automationDetails": {"id": "repobility/24236"}, "properties": {"repository": "anthr0pix/concrete-crm", "repoUrl": "https://github.com/anthr0pix/concrete-crm", "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-cea7560bf98ace36", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/dashboard/page.tsx:351"}, "properties": {"repobilityId": "4ba209c1bd3eacfd", "scanner": "scanner-primary", "fingerprint": "cea7560bf98ace36", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-2d1bbd27667d1e1c", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/outreach/[id]/page.tsx:275"}, "properties": {"repobilityId": "0d06a1206a3468dd", "scanner": "scanner-primary", "fingerprint": "2d1bbd27667d1e1c", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-6d165d5b69f7a055", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/customers/page.tsx:238"}, "properties": {"repobilityId": "2a86a086a5803557", "scanner": "scanner-primary", "fingerprint": "6d165d5b69f7a055", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-70f0cf9d54db65a3", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/app/quotes/page.tsx:248"}, "properties": {"repobilityId": "dd0a61c52b3c1421", "scanner": "scanner-primary", "fingerprint": "70f0cf9d54db65a3", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-e964ac4308fa8a1d", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/CommandPalette.tsx:157"}, "properties": {"repobilityId": "c2fdecf59e1ef076", "scanner": "scanner-primary", "fingerprint": "e964ac4308fa8a1d", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-5f18c0ed612a9477", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/outreach/OutreachCard.tsx:40"}, "properties": {"repobilityId": "a21bfa86d434886b", "scanner": "scanner-primary", "fingerprint": "5f18c0ed612a9477", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-0e8e65e78e7025ad", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/pipeline/PipelineCard.tsx:33"}, "properties": {"repobilityId": "db775166597e22cf", "scanner": "scanner-primary", "fingerprint": "0e8e65e78e7025ad", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-461060ac3cac6a5f", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/schedule/ScheduleView.tsx:328"}, "properties": {"repobilityId": "49ca5731678697ed", "scanner": "scanner-primary", "fingerprint": "461060ac3cac6a5f", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-8b543bec0b6225bc", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/schedule/ScheduleCard.tsx:39"}, "properties": {"repobilityId": "f90c6c4d4fdeec23", "scanner": "scanner-primary", "fingerprint": "8b543bec0b6225bc", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-4045a99f4e73b216", "level": "none", "message": {"text": "`truncate` class without `title=` for hover reveal \u2014 src/components/crew/CrewJobCard.tsx:84"}, "properties": {"repobilityId": "f4e18b8cf8633f8c", "scanner": "scanner-primary", "fingerprint": "4045a99f4e73b216", "layer": "frontend", "severity": "info", "confidence": 1.0, "tags": ["frontend-quality", "fq.truncate.no-title"]}}, {"ruleId": "scanner-18727c5e4fe1c5c2", "level": "warning", "message": {"text": "Insecure pattern 'cors_wildcard' in src/app/api/public/reviews/route.ts:4"}, "properties": {"repobilityId": "d405f6cf5f244d66", "scanner": "scanner-primary", "fingerprint": "18727c5e4fe1c5c2", "layer": "security", "severity": "medium", "confidence": 1.0, "tags": ["owasp", "cors_wildcard"]}, "locations": [{"physicalLocation": {"artifactLocation": {"uri": "src/app/api/public/reviews/route.ts"}, "region": {"startLine": 4}}}]}, {"ruleId": "scanner-6893a6c8b0861585", "level": "warning", "message": {"text": "Very low test-to-source ratio"}, "properties": {"repobilityId": "54a7de3f06314bf0", "scanner": "scanner-primary", "fingerprint": "6893a6c8b0861585", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["tests", "coverage"]}}, {"ruleId": "scanner-141b30a41e03817b", "level": "note", "message": {"text": "No license file detected"}, "properties": {"repobilityId": "2970a3966b13a409", "scanner": "scanner-primary", "fingerprint": "141b30a41e03817b", "layer": "quality", "severity": "low", "confidence": 1.0, "tags": ["license", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-72b2a6250083a784", "level": "warning", "message": {"text": "Placeholder or mock-heavy implementation detected"}, "properties": {"repobilityId": "fdef59ee384dffcb", "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": "e0f846e0cfabe045", "scanner": "scanner-primary", "fingerprint": "2d0c7b7ab8f8aacf", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["placeholder", "mock-data", "critical-flow", "generated-repo-pattern"]}}, {"ruleId": "scanner-b9088664ace7f748", "level": "warning", "message": {"text": "Composite production-readiness gap"}, "properties": {"repobilityId": "15ccea1ed94e8dda", "scanner": "scanner-primary", "fingerprint": "b9088664ace7f748", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["production-readiness", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-749d4bc1bd66df5f", "level": "warning", "message": {"text": "Agent instructions exist but release-hardening basics are missing"}, "properties": {"repobilityId": "8059eccefd0c6ff0", "scanner": "scanner-primary", "fingerprint": "749d4bc1bd66df5f", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["agent-instructions", "repo-hardening", "generated-repo-pattern"]}}, {"ruleId": "scanner-4a81bfc5e17f134a", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/app/login/page.tsx:23"}, "properties": {"repobilityId": "83b39116b3255ae2", "scanner": "scanner-primary", "fingerprint": "4a81bfc5e17f134a", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-4f14bb523897455f", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/jobs/JobForm.tsx:116"}, "properties": {"repobilityId": "6c58169539cc6e66", "scanner": "scanner-primary", "fingerprint": "4f14bb523897455f", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-3a3595d27086e338", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/jobs/JobStatusSelect.tsx:31"}, "properties": {"repobilityId": "77ef0674ac1a4737", "scanner": "scanner-primary", "fingerprint": "3a3595d27086e338", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-8cafa17218d54968", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/jobs/MarkCompleteButton.tsx:15"}, "properties": {"repobilityId": "a57ff1044681962d", "scanner": "scanner-primary", "fingerprint": "8cafa17218d54968", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-463da77d2817a9fa", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/jobs/PhotoUpload.tsx:67"}, "properties": {"repobilityId": "c475a2d4734549d1", "scanner": "scanner-primary", "fingerprint": "463da77d2817a9fa", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-a8eefcf40619aca4", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/jobs/DeleteJobButton.tsx:15"}, "properties": {"repobilityId": "ad1ffdbd8ba98d15", "scanner": "scanner-primary", "fingerprint": "a8eefcf40619aca4", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-c9f0d5627b6bee8b", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/expenses/ExpenseForm.tsx:74"}, "properties": {"repobilityId": "faa82f5bf5f71c11", "scanner": "scanner-primary", "fingerprint": "c9f0d5627b6bee8b", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-09f30e46c0190398", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/outreach/OutreachFollowUp.tsx:44"}, "properties": {"repobilityId": "3dc4c7d0342e545c", "scanner": "scanner-primary", "fingerprint": "09f30e46c0190398", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-79c0205a7725c20a", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/outreach/OutreachForm.tsx:90"}, "properties": {"repobilityId": "b200acc6f97a8d52", "scanner": "scanner-primary", "fingerprint": "79c0205a7725c20a", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-d14ba09f414afc4a", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/customers/NewCustomerDialog.tsx:73"}, "properties": {"repobilityId": "4c5448ef5292121e", "scanner": "scanner-primary", "fingerprint": "d14ba09f414afc4a", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-4f4b3cdb7faab6a4", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/customers/CustomerForm.tsx:54"}, "properties": {"repobilityId": "92952e2aebfd5d5e", "scanner": "scanner-primary", "fingerprint": "4f4b3cdb7faab6a4", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-c316a5322289f8ad", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/customers/DeleteCustomerButton.tsx:15"}, "properties": {"repobilityId": "87e1123c9a26e45e", "scanner": "scanner-primary", "fingerprint": "c316a5322289f8ad", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-ecd639edaa6512db", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/quotes/ConvertToInvoiceButton.tsx:15"}, "properties": {"repobilityId": "25061fe86446d91c", "scanner": "scanner-primary", "fingerprint": "ecd639edaa6512db", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-16c514e0ef559c48", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/quotes/QuoteStatusSelect.tsx:25"}, "properties": {"repobilityId": "95228c74d8df1854", "scanner": "scanner-primary", "fingerprint": "16c514e0ef559c48", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-4918bed9d6bbd642", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/quotes/DeleteQuoteButton.tsx:15"}, "properties": {"repobilityId": "ea8bc6584ba0edf1", "scanner": "scanner-primary", "fingerprint": "4918bed9d6bbd642", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-acae7e74b492c340", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/quotes/QuoteBuilder.tsx:88"}, "properties": {"repobilityId": "7630cdb23dc0fada", "scanner": "scanner-primary", "fingerprint": "acae7e74b492c340", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-eea10af46f3d9ff0", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/quotes/DuplicateQuoteButton.tsx:15"}, "properties": {"repobilityId": "deab7a34078af908", "scanner": "scanner-primary", "fingerprint": "eea10af46f3d9ff0", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-db277c8b8f06ca58", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/invoices/DeleteInvoiceButton.tsx:15"}, "properties": {"repobilityId": "af7e3ddee06d5436", "scanner": "scanner-primary", "fingerprint": "db277c8b8f06ca58", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-21b01611febb6987", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/invoices/DuplicateInvoiceButton.tsx:15"}, "properties": {"repobilityId": "68fdcc5a5c9c1073", "scanner": "scanner-primary", "fingerprint": "21b01611febb6987", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-5058a9abc0a2956a", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/invoices/MarkPaidButton.tsx:15"}, "properties": {"repobilityId": "2dee44ad01632795", "scanner": "scanner-primary", "fingerprint": "5058a9abc0a2956a", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-3becb5d01953d456", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/invoices/InvoiceBuilder.tsx:60"}, "properties": {"repobilityId": "35df02fc0f893264", "scanner": "scanner-primary", "fingerprint": "3becb5d01953d456", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-1a06f926c05161fa", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/invoices/InvoiceStatusSelect.tsx:25"}, "properties": {"repobilityId": "2f4fed7372c9100b", "scanner": "scanner-primary", "fingerprint": "1a06f926c05161fa", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}, {"ruleId": "scanner-c381eaf52bb3aee6", "level": "warning", "message": {"text": "`fetch()` without try/.catch or AbortSignal \u2014 src/components/crew/CrewPhotoUpload.tsx:58"}, "properties": {"repobilityId": "850c71ed81f49c80", "scanner": "scanner-primary", "fingerprint": "c381eaf52bb3aee6", "layer": "quality", "severity": "medium", "confidence": 1.0, "tags": ["integrity", "fragile-runtime", "robustness"]}}]}]}