Commit graph

3 commits

Author SHA1 Message Date
9632c1048c
fix: resolve Phase 6c gate findings (2 high, 5 low) across api, web, lambda, mobile
Gemini scanner sweep (token-bypass), GPT-4.1 cross-review, and the
6-detector /sh-security-review fan-out ran against 6acfdab..HEAD; every
confirmed finding fixed:

HIGH (deployment blockers, logic detector):
- CONC-L1: suggestions lambda's bulk line-item PUT sent no
  proposalVersion — every AI suggestion job would 422 and be silently
  swallowed. Now fetches the proposal's rowVersion, echoes it, and
  retries once with a fresh token on 409. Pytest updated (38 green).
- CONC-L2: mobile admin surface (update/approve/send/revise, bulk line
  items) sent no tokens — the entire mobile admin workflow would 422.
  Tokens threaded through mobile api layer + workspace/line-item
  screens with 409 refetch handling. tsc clean.

MEDIUM-adjacent (scanner):
- VendorProposalsController: the VendorTotalCost write on Proposal now
  bumps Version (was a silent lost-update path bypassing the guard).
- FilesController: GeneratePDF audit staged into the same SaveChanges.

LOW (detectors):
- 409 envelope is schema-validated client-side
  (proposalConcurrencyConflictSchema.safeParse) and id-checked before
  seeding the react-query cache; malformed state degrades to
  invalidation (INJ-409-01/WEB-CONC-L1).
- ProposalConcurrencyException.CurrentState typed ProposalResponse?
  so an EF entity can never serialize into the 409 body (SC-1).
- Guard caller contract documented + GuardedEndpointAuthorizationTests
  reflection tripwire: guard-reaching endpoints must stay admin-gated
  (AUTHZ-CG-01).
- Pre-check currentState now loads display navigations so both 409
  paths return the same shape (CONC-L3).
- Save chain's trailing getById failure no longer misreports a
  committed save; falls back to invalidation (CONC-L4).

Also caught during fix verification: the handler's manual currentState
serialization lacked JsonStringEnumConverter — enums would serialize
as numbers, client schema validation would reject every guarded 409,
and the state would always be discarded. Now matches the MVC pipeline
and is pinned by a wire test.

193 xUnit / 70 vitest / 38 pytest green; mobile + shared tsc clean;
Playwright smoke 2/2.
2026-07-13 21:20:12 -04:00
Adam Moussa
5d84399a0d
feat: pricing library — curated priced items feed the RAG corpus (#127)
Some checks are pending
Deploy / Deploy to AWS (push) Waiting to run
Adds a managed pricing library so admins can seed/curate reference priced items
directly, instead of the corpus being populated only by ingesting Sent proposals.
v1 PR5.

API:
- PricingLibraryItem entity + migration; /api/pricing-library CRUD (admin), with
  GET {id} reachable by internal Lambda callers (admins role via internal key).
- Create/update publish an ADDITIVE library-ingest SQS job {pricingLibraryItemId},
  wrapped so a publish failure never rolls back the save.

Lambda (library-ingest):
- Additive event-shape branch: pricingLibraryItemId -> fetch item, format markdown,
  upload to pricing-library/{category}/{id}.md, trigger KB sync. The existing
  proposalId path is byte-for-byte unchanged. Explicit error when neither id present;
  warns when both present.

Web:
- Pricing Library management page (/admin/pricing-library): list / create / edit / delete.

GPT-4.1 cross-review on the event-shape change: no BLOCK (neither/both-id handling
applied). Verified: api 159 tests; web tsc + 26 tests; lambdas ruff + 37 pytest.
2026-06-18 12:49:47 -04:00
Adam Moussa
d21b1c5edb test: bootstrap test infrastructure with critical coverage (QA-C1 through QA-C6)
QA-C1: Create xUnit test project, add to solution, wire dependencies
- api/tests/ProposalSystem.Tests with xUnit + FluentAssertions + NSubstitute
- InMemory EF Core provider for isolated DB tests

QA-C2: Proposal state machine transition tests (16 tests)
- Valid: InReview->Approved, Approved->Sent, Sent->Revised
- Invalid: InReview->Sent, Approved->Revised, Draft->Approved, etc.
- Edge cases: idempotency, missing line items, revision line item copying
- Audit and job publisher verification

QA-C3: Authorization attribute tests (16 tests)
- Controller-level [Authorize] on all controllers except AuthController
- Role requirements: admins/sysadmins on admin actions
- Dispatcher exclusion from admin/sysadmin routes
- SysAdmin-only user management enforcement

QA-C4: InternalApiKeyMiddleware tests (8 tests)
- Valid key sets claims and calls next
- Invalid key passes through to JWT (no 401/403)
- Missing key/empty config disables middleware
- Documents API-C1 vulnerability (key works on any path)

QA-C5: ProtectedRoute and RoleGuard tests (12 tests, vitest)
- ProtectedRoute: renders children when authenticated, redirects when not
- RoleGuard: role-based access, dispatcher/admin/sysadmin enforcement
- authSlice: setUser, logout, expired token handling

QA-C6: Lambda SQS handler tests (19 tests, pytest)
- pdf-generate: batch processing, failure reporting, malformed body
- suggestions: batch processing, proposal-not-found skip, AI item preservation
- API key caching, retry helpers

Total: 107 tests (76 .NET + 12 web + 19 Python), all passing.
2026-05-27 18:18:44 -04:00