docs: consolidate repository guides and preserve audit history

This commit is contained in:
cesnimda
2026-09-11 14:22:41 +02:00
parent 115441a409
commit 682a3028bb
404 changed files with 3402 additions and 2621 deletions
@@ -1,26 +0,0 @@
# Accessibility code-review evidence
Updated: 2026-08-15
Classification: code inspection, component tests and local authenticated Chromium. Native assistive-technology and production checks remain external.
## Corrected issues
- Every frontend `IconButton` now has an explicit programmatic name. This includes shell navigation/user/settings/search, company edit, correspondence delete, table columns and row actions, saved views, attachments, and CV Builder navigation. File-specific actions include the affected record name.
- CV cards expose link semantics, keyboard focus, a visible focus indicator and Enter/Space activation. Their nested action menu remains independent of card navigation.
- The anonymous public CV no longer puts a fixed 210mm element directly into a narrow page. It retains a full A4 iframe viewport, measures multi-page document height and scales the complete frame to the available width. Local Chromium proves no outer or inner horizontal overflow at 375px.
- Semantic alert surfaces remain theme-owned. Authenticated Chromium measures the dark-mode missing-job Alert foreground against its composited background at or above WCAG AA 4.5:1.
- The public loading state is announced with `role=status` and uses a stronger readable foreground.
## Verification
- Focused accessibility-related UI: 4 suites, 9/9 tests.
- Full frontend: 54/54 suites, 228/228 tests.
- Optimized production frontend build/TypeScript: pass.
- Full Playwright: 7/7; final targeted workspace/public-CV rerun: 2/2.
- Static `IconButton` audit: no control without an explicit `aria-label`.
## Remaining external and future gates
- Native screen-reader/switch-control and operating-system high-contrast checks are not available in the local automated environment and remain a production-readiness spot check.
- No axe, pa11y or Lighthouse dependency/configuration is present, so CI does not yet run a general automated accessibility crawler. Adding one requires an approved dependency change and should complement, not replace, the focused semantic and contrast assertions above.
-10
View File
@@ -1,10 +0,0 @@
# AI-001 evidence index
- `../../../verification/ai-001-durable-ai-queue.md`
- `../../../architecture/durable-operations.md`
- `../../verification-log.md` entries V-096 and V-097
- `JobTrackerApi.Tests/AiOperationQueueTests.cs`
- Existing OPS evidence under sibling `ops-001a`, `ops-001b` and `ops-001c` directories
All execution used synthetic local SQLite data and fake handlers. No model, provider, browser or production service was invoked.
-8
View File
@@ -1,8 +0,0 @@
# AI-002 evidence
- `../../../verification/ai-002-provider-routing.md` — route matrix, implementation and remaining gates.
- `../../verification-log.md` entries V-098 through V-100 — exact commands and results.
- `../../../../tools/summarizer/tests/test_app.py` — fake-transport local-first, consent, circuit, schema, cost-cap and failure tests.
- `../../../../JobTrackerApi.Tests/AiOperationQueueTests.cs`, `AiPrivacyPolicyTests.cs`, `SummarizerServiceTests.cs` and `AiWorkspaceTests.cs` — policy propagation and provenance tests.
No screenshot was captured because localhost browser access remains denied by administrator policy. No model/provider call, paid service, real private data or production system was used.
-9
View File
@@ -1,9 +0,0 @@
# AI-003 evidence index
Synthetic/local evidence only; no secrets, private CVs, provider payloads or screenshots are stored here.
- Implementation and limitations: `docs/verification/ai-003-strategy-snapshot-queue.md`
- Commands/results: `docs/audits/verification-log.md` V-101–V-103
- Backend automated coverage: `JobTrackerApi.Tests/StrategySnapshotOperationTests.cs`
- Frontend automated coverage: `job-tracker-ui/src/job-details-generated-drafts.test.tsx`
- Implementation commit: `a621226`
-9
View File
@@ -1,9 +0,0 @@
# AI-004 evidence index
Synthetic/local evidence only; no secrets, private CVs, provider payloads or screenshots are stored here.
- Implementation and limitations: `docs/verification/ai-004-cv-processing-queue.md`
- Commands/results: `docs/audits/verification-log.md` V-104–V-107
- Backend automated coverage: `JobTrackerApi.Tests/CvProcessingOperationTests.cs` and `JobTrackerApi.Tests/ProfileCvControllerTests.cs`
- Frontend automated coverage: `job-tracker-ui/src/profile-page.test.tsx`
- Implementation commit: `c3c5af8`
-29
View File
@@ -1,29 +0,0 @@
# Browser evidence and blocker
Captured: 2026-08-02
## Genuine running-browser coverage
The repository's own Playwright configuration created an isolated API, Next application, Chromium browser, and temporary database. `npm run test:e2e` passed 4/4:
1. local login establishes an authenticated session and reaches Dashboard;
2. a saved job is created through the reviewed multi-step UI;
3. the Career Workspace shell renders its heading and explanatory copy;
4. an explicitly published synthetic CV renders anonymously and its PDF response is a real `%PDF-` document.
The Career Workspace assertion proves only the shell rendered; it does not prove every downstream panel loaded. Later direct API checks confirmed that CV-list and application-workspace endpoints fail under default SQLite.
## Interactive browser blocker
The required `browser:control-in-app-browser` skill was selected for the broader manual journey, console, keyboard, responsive, and screenshot review. Its mandatory client module was absent from the installed plugin bundle:
`C:/Users/Cesnimda/.codex/plugins/cache/openai-bundled/browser/26.721.81911/scripts/browser-client.mjs`
Import through the required browser runtime failed with `Module not found`. The skill explicitly forbids substituting standalone Playwright or another browser-control implementation when its client is missing, so the interactive review stopped at that boundary.
## Consequences
- No audit screenshots were captured.
- Browser console/network monitoring beyond the passing repository tests was not performed.
- 375px, 768px, and 1440px viewport checks, keyboard-only navigation, modal focus, reduced motion, dark-theme comparison, back/forward, refresh, multi-tab, and throttled-network checks are blocked.
- Code inspection and component tests are labelled as such; they are not represented as manual browser testing.
-13
View File
@@ -1,13 +0,0 @@
# CAREER-001 evidence index
Updated: 2026-08-09
No screenshot is claimed for this package because the browser session had already been finalized.
- Execution-path and design evidence: `docs/verification/career-001-career-workspace.md`
- Command log: `docs/audits/verification-log.md` V-117–V-119
- State/navigation tests: `job-tracker-ui/src/career-workspace-page.test.tsx`
- Approval-gate and profile regression tests: `job-tracker-ui/src/profile-page.test.tsx`
- Implementation: `job-tracker-ui/src/views/career/CareerWorkspaceOverview.tsx`, `CareerProfilePage.tsx`, `CareerWorkspacePage.tsx`, `ProfileCompleteness.tsx`
- Implementation commit: `268b3a0`
- Synthetic data only; no private CV, provider, model, production service or external request was used.
-12
View File
@@ -1,12 +0,0 @@
# CAREER-002 evidence index
Updated: 2026-08-09
No screenshot or fresh FlowCV inspection is claimed because browser tooling had already been finalized.
- Execution path, capability matrix and limitations: `docs/verification/career-002-cv-builder.md`
- Commands/results: `docs/audits/verification-log.md` V-120–V-125
- Interaction/save/navigation/preview tests: `job-tracker-ui/src/cv-builder-deep-link.test.tsx`
- List/helper regressions: `job-tracker-ui/src/cv-builder-page.test.tsx`, `job-tracker-ui/src/cvBuilder.test.ts`
- Implementation commits: `b58cc19`, `a5b74e0`, `2043349`
- Synthetic data only; no private CV, FlowCV account, provider, model, production service or external request was used.
@@ -1 +0,0 @@
Synthetic attachment for local SEC-008 verification only.
@@ -1,30 +0,0 @@
# Dependency and supply-chain evidence
Captured: 2026-08-02
## Advisory checks
- NuGet: no known vulnerable direct or transitive package was reported.
- npm: two affected package entries (`react-router` and `react-router-dom`) cover four moderate advisories. One narrow redirect issue is fixed in 6.30.4; the remaining audit-suggested resolution is React Router 7.18.2, a breaking major upgrade. No upgrade was attempted.
- Python: `pip-audit` returned 119 records across six installed packages; after deduplicating repeated aliases, the affected counts were `transformers` 21, `torch` 22, `pillow` 17, `pypdf` 35, `python-multipart` 6, and transitive `starlette` 7.
Reachability matters:
- `pypdf` directly parses authenticated user PDF uploads and multiple advisories describe infinite loops, excessive CPU, or memory exhaustion from crafted PDFs.
- Pillow directly opens authenticated image uploads; advisories include decompression bombs and memory-corruption cases. Extension routing is not a content-signature check.
- `python-multipart`/Starlette parse the sidecar upload before the endpoint's eight-megabyte post-read check; several advisories are request-parsing denial of service.
- Many `torch`/`transformers` advisories concern model or checkpoint loading. The application loads a fixed configured model, not a user-supplied model, so those records are not all treated as directly exploitable.
Existing mitigations: authenticated backend upload path, private backend-only AI network, required production service token, eight-megabyte application limit, accepted-extension list, and no host-published sidecar port. Residual risk: containers have no resource limits and the parser handles untrusted bytes in-process.
## Reproducibility and provenance
- npm has `package-lock.json` and uses `npm ci`.
- NuGet has no lockfile; the repository has no `global.json`, so local builds selected SDK 10 while the project targets .NET 9.
- Python top-level requirements are exact pins, but transitive dependencies are not hash-locked.
- Docker base images use mutable tags rather than digests.
- Gitea Actions use mutable major tags for checkout/setup-node, an unpinned remote `dotnet-install.sh`, and a tagged SSH action rather than immutable commit SHAs.
- The AI image upgrades pip/setuptools/wheel during build and downloads the configured Hugging Face model at runtime unless already cached.
- No SBOM generation, package licence gate, container CVE scan, or signed-provenance check is configured.
`dotnet list ... --deprecated` marked xUnit 2.9.2 and its transitive xUnit 2 packages as legacy. This is maintenance information, not a current security defect.
-21
View File
@@ -1,21 +0,0 @@
# MAIL-001 evidence index
Updated: 2026-08-10
- Progress report: `docs/verification/mail-001-job-email-hub.md`
- Commands/results: `docs/audits/verification-log.md` V-126–V-140
- Hub/legacy-route tests: `job-tracker-ui/src/correspondence-inbox-page.test.tsx`
- Reused review decision tests: `job-tracker-ui/src/gmail-review-page.test.tsx`
- Provider-neutral API tests: `JobTrackerApi.Tests/EmailControllerTests.cs`
- Saved-copy/tenant tests: `JobTrackerApi.Tests/CorrespondenceControllerTests.cs`
- Send-ledger tests: `JobTrackerApi.Tests/EmailSendAttemptStoreTests.cs`
- Delivery adapter tests: `JobTrackerApi.Tests/EmailProviderDeliveryTests.cs`
- Explicit-send/tenant/idempotency tests: `JobTrackerApi.Tests/EmailSendControllerTests.cs`
- Confirmed composer tests: `job-tracker-ui/src/correspondence-inbox-page.test.tsx`
- Restart-recovery/two-owner tests: `JobTrackerApi.Tests/EmailSendAttemptStoreTests.cs`
- Export/cascade tests: `JobTrackerApi.Tests/BackupControllerTests.cs`, `BackgroundWorkerTenantTests.cs`, `EmailSendAttemptStoreTests.cs`
- `job-email-empty-connected-status-20260810.png`: authenticated disposable local user, empty linked-message view, explicit disconnected/read-only Gmail/Outlook/IMAP status at 1280×720.
- `job-email-review-disconnected-20260810.png`: canonical review view after compatibility redirect, disconnected-provider handling at 1280×720.
- Implementation commits: `6008b4a`, `536d403`, `a20775c`, `653f011`, `e9937ac`, `123fc55`, `449faeb`, `ee5ef7e`, `8fe3903`, `aff34cc`
- Mocked provider data only; no real email, provider connection, private content, production service or external request was used.
- Local browser used disposable `@example.test` data. Provider actions, mobile/tablet/1440 viewport controls and keyboard focus traversal remain unverified; the in-app browser could not resize or dispatch native Tab traversal in this run.
-10
View File
@@ -1,10 +0,0 @@
# POL-001 evidence index
- Focused backend policy/worker/CV/controller slice: 74/74 passed.
- Full backend regression: 568/568 passed.
- Focused frontend AI/usage/job/CV slice: 22/22 passed.
- Full frontend regression: 47 suites and 157 tests passed.
- Frontend production build: passed.
- Patch hygiene: passed, with repository line-ending notices only.
No browser, provider, paid AI, email, production service or production data was used.
-10
View File
@@ -1,10 +0,0 @@
# POL-002 evidence index
- `../../../verification/pol-002-ai-privacy.md` — implementation and verification record.
- `../../../architecture/ai-privacy.md` — enforced configuration and trust-boundary contract.
- `../../verification-log.md` entries V-089 through V-095 — exact commands and outcomes.
- Synthetic-only provider routing tests: `tools/summarizer/tests/test_app.py`.
- Server policy tests: `JobTrackerApi.Tests/AiPrivacyPolicyTests.cs` and `ProEntitlementAuthorizationTests.cs`.
No screenshot was captured because localhost browser access is denied by administrator policy. No real CV, email, credential, provider call or production data was used.
-10
View File
@@ -1,10 +0,0 @@
# PROD-002 evidence index
- Synthetic fixture validation: 1/1 passed.
- Full backend: 569/569 passed.
- Full frontend: 47 suites, 157 tests passed.
- Frontend production build: passed.
- Model/provider calls: none.
- Real user data: none.
Primary evidence: `docs/verification/prod-002-ai-evaluation.md` and `docs/ai/workload-inventory.md`.
-9
View File
@@ -1,9 +0,0 @@
# QA-001 evidence index
Synthetic/local evidence only. No provider calls, private job descriptions, credentials or personal data were used.
- Implementation and limits: `docs/verification/qa-001-job-term-quality.md`
- Commands/results: `docs/audits/verification-log.md` V-114–V-116
- Seven deterministic fixtures: `JobTrackerApi.Tests/JobCvMatchServiceTests.cs`
- UI label regression: `job-tracker-ui/src/match-score-panel.test.tsx`
- Browser screenshots: not captured; required presentation checks remain explicit in the master plan.
@@ -1,60 +0,0 @@
# Repository inventory evidence
Captured: 2026-08-02
## Scope and worktree
- Branch: `release-readiness`, tracking `origin/release-readiness`.
- Pre-existing user changes preserved: deleted tracked `.agent.md`; untracked `AGENTS.md`.
- Audit-created paths: `docs/audits/` only.
- Tracked-file distribution: 301 documentation files, 194 API files, 171 frontend files, 67 API-test files, 13 AI/tool files, 12 scripts, and 6 deployment files.
## Executable components
| Component | Implementation | Responsibility |
|---|---|---|
| Browser client | `job-tracker-ui/` — React 19, TypeScript, MUI, React Router inside a Next.js static-export shell | Public landing/auth pages and authenticated job, career, CV, email, settings, and administration workflows |
| API host | `JobTrackerApi/` — ASP.NET Core / .NET 9 | Authentication, authorization, REST endpoints, application workflows, data/file access, integrations, and hosted services |
| Data layer | EF Core 9; SQLite default or Pomelo MariaDB/MySQL | Identity and tenant-owned job, profile, CV, correspondence, attachment, AI, and workflow state |
| AI sidecar | `tools/summarizer/` — FastAPI, Transformers, OCR/document parsers | Local summaries and extraction; routes selected generation calls to Ollama, Gemini, or Groq |
| Background processing | Seven hosted services in the API process | Backups, rules, reminders, daily export, enrichment, AI readiness probing, and queued CV processing |
| Security fixture tool | `tools/hostile-fixture-db/` | Generates synthetic hostile database fixtures for local authorization testing |
| Delivery | Dockerfiles, Docker Compose, nginx, `deploy/deploy.sh`, Gitea Actions | Builds, health checks, backup-gated deployment, and direct-to-production replacement after CI |
## External boundaries
- Authentication: local ASP.NET Identity/JWT/cookie sessions; Google and Microsoft ID-token exchange/linking; TOTP 2FA.
- Mail: Gmail OAuth/API, Microsoft Graph, IMAP, and SMTP. Audit tests must mock these boundaries.
- Billing and abuse prevention: Stripe hosted flows/webhook and Cloudflare Turnstile.
- Job discovery/import: NAV feed and site-specific URL parsers for Finn, LinkedIn, and Jobbnorge; optional LibreTranslate.
- AI: private sidecar; Ollama locally or Gemini/Groq when configured.
- File/PDF: local data-root storage and headless Chromium PDF export.
## Data ownership and trust boundaries
- `ApplicationUser` is the identity root.
- Tenant entities use `OwnerUserId`; EF global query filters deny access when the current user is absent and scope reads to that owner.
- Several child entities rely on filtered parent navigation or explicit owner predicates rather than their own owner column.
- Public CV is the main anonymous data-release boundary and requires an explicit `IsPublic` flag plus a random slug.
- nginx is intended as the only production ingress to the API; the AI service is on a private backend-only network.
- The API process owns database migrations/reconciliation and all seven workers; the current deployment assumes one API replica.
## Documentation-to-code differences observed during discovery
- `job-tracker-ui/README.md` is obsolete Create React App boilerplate; the frontend now uses Next.js/Jest directly.
- `docs/architecture/current.md` reports a smaller/older controller surface and stale file sizes; the current controller directory contains 29 controller classes plus partials/DTO files.
- Root README API documentation omits substantial implemented surfaces including CV variants, billing, career profiles, AI workspace/history, job discovery, sessions, and several application-workspace APIs.
- `deploy/README.md` recommends PostgreSQL, but the application implements SQLite and MariaDB/MySQL providers only.
- Ignored local `vendor/`, `JobTrackerBackend/`, `.claude/worktrees/`, build outputs, databases, virtual environments, and frontend dependencies remain on disk but are not current tracked application source.
## Manual-audit exclusions
- Generated/build/runtime: `.next/`, `out/`, `build/`, `node_modules/`, `bin/`, `obj/`, local databases, backups, keys, CV artifacts, test results, caches, and virtual environments.
- Ignored historical/local copies: `.claude/worktrees/`, `JobTrackerBackend/`, `vendor/`, `tmp/`.
- Archived documentation under `docs/_archive/` is historical evidence, not the current implementation contract.
- Package lockfiles and EF generated migrations/model snapshot are reviewed for supply-chain and schema implications, not line-by-line as handwritten application logic.
## Unfinished-marker search
No production-code `TODO`, `FIXME`, `HACK`, stub, or `NotImplementedException` was found outside deliberate test doubles, normal placeholder UI text, and a guided-acceptance script template. This does not prove feature completeness; incomplete behaviour is assessed through routes, tests, and browser journeys.
-96
View File
@@ -1,96 +0,0 @@
# Disposable runtime evidence
Captured: 2026-08-02
Environment: local Development configuration, compiled Release API, Next development server, disposable SQLite data root under the current user's temporary directory. No production service, real mailbox, paid AI provider, or personal data was used.
## Default-provider endpoint results
Two synthetic local users were registered. User A owned one company, one job (`Syntetisk utvikler æøå`), one correspondence record, two CV variants, one career profile, and one text attachment. User B had no data.
| Request | User | Result | Evidence |
|---|---|---:|---|
| `GET /api/jobapplications?page=1&pageSize=20` | B | 200, 0 items | Empty-account isolation works. |
| `GET /api/companies` | B | 200, 0 items | Empty-account isolation works. |
| `GET /api/correspondence?jobApplicationId=1` | B | 200, 0 items | User A's message was not disclosed. |
| `GET /api/careerprofile` | B | 404 | User A's profile was not disclosed. |
| `GET /api/jobapplications/1` | A / B | 200 / 404 | Job ownership enforced. |
| `GET /api/attachments/1` | A / B | 200 / 404 | Attachment row and file ownership enforced. |
| `GET /api/cv/variants` | A and empty B | 500 | EF SQLite cannot translate the `DateTimeOffset` ordering. |
| `GET /api/jobapplications/1/workspace` | A | 500 | Same provider-translation family, reached on the core workspace. |
| `GET /api/jobapplications/1/ai/history` | A | 500 | Same provider-translation family. |
| `GET /api/ai/usage` | A | 500 | Same provider-translation family. |
| `GET /api/jobapplications/1/timeline` | A and B | 500 | Ambiguous endpoint selection occurs before ownership evaluation. |
| `GET /api/jobapplications/1/interview-prep` | A and B | 500 | Ambiguous endpoint selection occurs before ownership evaluation. |
| `GET /api/jobapplications/1/checklist` | A | 200 | Sibling workspace endpoint works. |
| `GET /api/jobapplications/1/analysis` | A | 200 | Sibling intelligence endpoint works. |
| `GET /api/jobapplications/1/match` | A | 200 | Sibling intelligence endpoint works. |
The application returned generic 500 problem responses; no secret-bearing exception details were exposed to the client.
## Background-service reproduction
The synthetic job was set to `Applied`, dated 40 days earlier than the audit, and had `Tags` and `ShortSummary` set to null. After a controlled API restart and more than the documented initial delays:
- status remained `Applied`, although the default rules threshold should have marked it ghosted;
- `Tags` remained null despite deterministic `SkillTagger` input containing `C#`;
- `ShortSummary` remained null.
This reproduces the deny-on-null tenant filter in a background scope with no HTTP user. The same query structure exists in rules, follow-up reminders, enrichment, and daily export. The queued CV processor is the counterexample: it deliberately uses `IgnoreQueryFilters()` for cross-tenant worker reads.
## Authentication lifecycle reproductions
### Logout
1. Logged in as User A and copied the issued synthetic cookie jar.
2. Posted logout with the CSRF cookie/header pair.
3. The browser jar received cookie deletion and subsequently got 401.
4. The copied pre-logout cookie still received 200 from `/api/auth/me`.
Result: logout clears client cookies but does not revoke the server-side `UserSession`.
### Email verification
A separate disposable API started with `Auth:RequireEmailVerification=true`, registration enabled, and email delivery disabled.
| Check | Result |
|---|---|
| Register synthetic account | 200 |
| Stored `EmailConfirmed` | `0` |
| Immediate `/api/auth/me` with registration-issued session | 200 |
The same synthetic row was then marked confirmed solely to isolate the email-change path. A normal login followed by `PUT /api/auth/profile` changed the email, returned 204, and left `EmailConfirmed=1`. No verification challenge was required for the new address.
## Backup and restoration rehearsal
The built-in SQLite backup runner created `jobtracker_backup_20260802_184234.db` from the disposable database. A read-only copied snapshot passed `PRAGMA integrity_check` and matched the current database:
| Dataset | Current | Restored copy |
|---|---:|---:|
| Users | 2 | 2 |
| Jobs | 1 | 1 |
| Companies | 1 | 1 |
| Attachment rows | 1 | 1 |
| CV variants | 2 | 2 |
A second isolated API was started from the restored database plus separately copied `Attachments/` and `keys/`. `/health` returned 200; synthetic login, job retrieval, and attachment download all returned 200.
Conclusion: SQLite database snapshotting works. Complete recovery additionally requires file storage, the Data Protection key ring, deployment secrets/configuration, and a documented restore procedure. MariaDB/MySQL backup is intentionally unsupported by the in-process runner and was not available for rehearsal.
## Safe local performance samples
Release API, disposable SQLite database with one job, ten requests after warm-up:
| Endpoint | Mean | p95 | Response bytes |
|---|---:|---:|---:|
| `/health` | 11.3 ms | 43.9 ms | 35 |
| `/api/jobapplications?page=1&pageSize=20` | 17.8 ms | 111.3 ms | 1,547 |
| `/api/companies` | 6.5 ms | 33.3 ms | 255 |
The static export contained 49 JavaScript chunks totalling 2,762,618 uncompressed bytes and one 293-byte CSS file. This aggregate is not an initial-page transfer measurement because Next.js loads route chunks selectively.
No load test was performed. Results do not establish production capacity.
## Cleanup
Listeners on audit ports 3001, 5402, 5403, and 5404 were resolved to their exact audit-owned command lines and stopped. Temporary evidence remains under the disposable local data root; no repository source/configuration was changed.
@@ -1,40 +0,0 @@
# Two-user isolation evidence
Captured: 2026-08-02
Scope: disposable local SQLite environment only. User A and User B used synthetic `@audit.invalid` identities. No production data or credentials were used.
## Results
| Resource or operation | UI level | API direct-ID test | Service/query protection | Result |
|---|---|---|---|---|
| Jobs | Browser blocked | A 200; B 404 for A's job; B list 0 | Explicit owner predicates and global `JobApplication` filter | Pass at API/data-query levels |
| Companies | Browser blocked | B 404 for A's company; B list 0 | Explicit owner predicates and global `Company` filter | Pass at API/data-query levels |
| Correspondence | Browser blocked | B list for A's job returned 0; copied ID returned 404 | Filter through owned `JobApplication` navigation | Pass at API/data-query levels |
| Attachments | Browser blocked | A upload/list/download 200; B job access and copied attachment download 404 | Owned-job query before file operation | Pass at API/data-query levels |
| Career Profile | Browser blocked | B's current-profile request 404 | Current-user lookup plus owner query filter | Pass at API/data-query levels |
| CV variants | Browser blocked | Copied A variant ID returned 404, but B's own list returned 500 | Owner predicate/filter exists; list blocked by SQLite translation | Partial: protection inspected and direct ID passed; list broken |
| Application workspace | Browser blocked | B copied A ID returned 404; A request returned 500 | Owner predicate exists; owner path blocked by SQLite translation | Partial |
| Checklist | Browser blocked | B copied A job returned 404; A 200 | Owner predicate on job/items | Pass at API/data-query levels |
| Timeline | Browser blocked | Both users received 500 | Ambiguous route selection occurs before authorization logic | Blocked by endpoint defect; no exposure observed |
| Interview preparation | Browser blocked | Both users received 500 | Ambiguous route selection occurs before authorization logic | Blocked by endpoint defect; no exposure observed |
| AI results | Browser blocked | History endpoint returned 500 | Owner predicates and query filter exist | Blocked by SQLite translation |
| Settings/session | Browser blocked | B `/auth/me` returned only B | Identity/session-bound current user | Pass at API level |
| Administrative operation | Browser blocked | B request returned 403 | `[Authorize(Roles = "Admin")]` | Pass at API level |
| Email threads/provider data | External providers not connected | Local correspondence passed; provider-specific IDs not live-tested | Owner-scoped connection and correspondence queries inspected | Code-inspected/partially tested |
## Protection layers observed
- UI: protected routes require an authenticated shell, but hiding was not counted as authorization.
- API: controllers use explicit local-auth or admin authorization attributes.
- Service: important reads carry `OwnerUserId` or owned-parent predicates.
- EF: global filters deny on null current user and match owner IDs.
- Database: tenant ownership is primarily enforced in application queries; many owner columns are not foreign keys to `AspNetUsers`, so database constraints alone do not provide tenant isolation.
## Limitations
- The in-app browser control client was missing, so UI navigation as A/B was not performed.
- Timeline/interview and several CV/AI paths failed before an ownership result could be observed.
- No Gmail, Microsoft Graph, IMAP, Stripe, cloud AI, or remote object storage was contacted.
No cross-user disclosure was confirmed in the paths that returned a meaningful result.
-9
View File
@@ -1,9 +0,0 @@
# UX-001 browser evidence
Synthetic/local evidence only. No credentials, tokens or personal data are present.
- `login-dark-375.png` — local sign-in form at 375 × 812.
- `login-dark-768.png` — local sign-in form at 768 × 900.
- `login-dark-1440.png` — local sign-in form at 1440 × 1000.
The isolated browser had no API configuration service, so configured Google/Microsoft alternatives were covered by mocked component tests rather than these captures. The browser used its dark system theme. A full-page capture mode produced an invalid rendering artifact and was discarded; the retained viewport captures match computed DOM bounds and visible browser state.
-9
View File
@@ -1,9 +0,0 @@
# UX-002 browser evidence
Synthetic/local evidence only. No credentials, tokens or personal data are present.
- `settings-light-375.png` — explicit Light at 375 × 812 after mobile tab overflow correction.
- `settings-light-768.png` — explicit Light at 768 × 900.
- `settings-dark-1440.png` — explicit Dark at 1440 × 1000.
The browser also verified System mode, refresh, navigation and two-tab Dark → Light synchronization. Those state transitions are recorded in V-113; no screenshots were needed for every duplicate state.