f0f178d77e
Deep, code-grounded audit of Job Tracker producing the mission deliverables under docs/remaster/: system audit, bug report, architecture/data-model/AI/UX reviews, remaster proposal, migration plan, competitor research, and the gated REBUILD_DECISION. Verdict: Incremental Refactor (no full rebuild). Evidence: no Critical defects; hardened cookie/CSRF auth (token never in JS storage), real SSRF defence, enforced multi-tenancy via global query filters, decoupled provider-swappable AI service, 135 backend tests. Debt is localised (god controllers/entity, missing hot-path indexes, prompt-injection hardening, CRA build debt) and reachable by in-place, test-guarded refactors. Also harden .gitignore: exclude agent tooling (.claude/, .bg-shell/, .agent.md) and restore/broaden the runtime-secrets block (**/keys/, **/backups/, exports, CV artifacts) so nested DataProtection keys can't be committed accidentally. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4.4 KiB
4.4 KiB
Bug Report — Job Tracker
Companion to: SYSTEM_AUDIT_REPORT.md
Severity: Critical / High / Medium / Low. Each item is code-grounded or explicitly [Speculative].
"Speculative" = a plausible defect I did not fully reproduce; verify before fixing.
Critical
None found. No auth bypass, no tenant-isolation break, no RCE/SSRF hole surfaced in the audited paths. (Auth uses HttpOnly cookie + CSRF; tenancy uses global query filters; ingestion has SSRF defence.) This is itself strong evidence against "rebuild".
High
| ID | Tag | Location | Description | Fix |
|---|---|---|---|---|
| H-1 | [Bug] | Models/JobApplication.cs:28-31 + attachment write paths |
Denormalised HasResume/HasCoverLetter/HasPortfolio/HasOtherAttachment can drift from the actual Attachments collection, so the checklist UI can show a resume attached when none is, or vice-versa. |
Make them computed projections, or maintain via one domain method; add a test. |
| H-2 | [Design flaw] | JobApplication.TailoredCvText vs TailoredCvDraft |
Two writable representations of the tailored CV with no precedence rule → stale-content reads. | Pick TailoredCvDraft, deprecate the inline string. |
| H-3 | [Perf/Bug] | Data/JobTrackerContext.cs (indexes) |
Missing indexes on IsDeleted, FollowUpAt, child FKs → full scans on every list/board/reminder/analytics query; degrades non-linearly with data. |
Add the 5 hot-path indexes. |
| H-4 | [Perf] | JobApplicationsController.GetStats (GetAnalyticsOverview ( |
Loads the whole table into memory then filters/GroupBy().Count() in .NET. |
Aggregate server-side (EF GroupBy/CountAsync). |
Medium
| ID | Tag | Location | Description | Fix |
|---|---|---|---|---|
| M-1 | [Perf/Bug] | GmailController :646-657, :701-711, :893-918 |
N+1 loops: per-message AnyAsync in CreateSuggestedJob; redundant re-loop after a HashSet is already built in RelinkThread; message-by-message import in RefreshLinkedThreads. |
Batch with a single set-based query. |
| M-2 | [Bug] | GmailController :659, :713 |
GmailReviewDecisions loaded with ToListAsync then scanned where FirstOrDefaultAsync suffices. |
Use FirstOrDefaultAsync. |
| M-3 | [Design flaw] | tools/summarizer/app.py:469,527,581 |
Prompt injection via raw interpolation of scraped JD + instruction (capped by human-review boundary). | Delimit untrusted inputs; add factuality check. |
| M-4 | [Design flaw] | JobCvMatchService |
"Match score" is keyword-literal; mislabels semantic matches as gaps. | Relabel + synonym map. |
| M-5 | [Design flaw] | job import UX | Scrape failure silently degrades to manual entry with no explanation/pre-fill. | Explicit partial-parse state. |
| M-6 | [Speculative] | repeated RulesEngine.GetSettings across list/detail/reminders |
Same per-user settings re-read many times per request cycle. | Cache in the already-registered IMemoryCache (short TTL). |
| M-7 | [Speculative] | JS-rendered boards | Static HttpClient fetch returns hydration-only HTML → empty parse. |
Document limitation; optional headless fetch. |
Low
| ID | Tag | Location | Description |
|---|---|---|---|
| L-1 | [Design flaw] | JobApplication.Salary (free-text) + structured salary |
Two salary representations; ensure writes keep them consistent or drop free-text after backfill. |
| L-2 | [Architectural weakness] | JobTrackerBackend link-compile glob |
Non-obvious build layout; onboarding hazard. |
| L-3 | [Design flaw] | Tags/*Json stored as JSON strings |
Unqueryable; fine for SQLite, revisit on MySQL/Postgres. |
| L-4 | [Speculative] | scraper plugins | Silent rot with no health telemetry. |
Cross-reference with .gsd
.gsdD007/D008 (Gmail full-thread continuity) is implemented — not a bug, a delivered decision..gsdD006 (notes-block workaround) is a known UX debt the register itself flags — Medium, schema fix..gsd/OVERRIDES.md"use next.js" is unimplemented — a plan/impl divergence, not a runtime bug, but it means the recorded frontend decision and the shipped stack disagree. Resolve deliberately.
Notes on what is NOT broken (verified, to prevent false alarms)
- Auth token is not in localStorage/sessionStorage (asserted by
login-page.test.tsx:70-71). - SSRF blocklist covers IPv4 private/CGNAT/link-local/benchmark + IPv6 ULA/link-local/Teredo.
- Match scoring is deterministic — no AI in the trust-bearing number.