The .NET API Lambda Function URL uses authType=AWS_IAM, but the four workload
Lambdas (suggestions, pdf-extract, pdf-generate, library-ingest) sent unsigned
requests with only X-Internal-Api-Key -> every internal call 403s. They also
used bare fromAsset() with no pip bundling -> ImportError at cold start. Both
made the SQS->Lambda->API pipeline non-functional when deployed (v1 pre-flight).
- Add _sign_request_headers (botocore SigV4Auth, service "lambda"); serialize the
JSON body once and send via httpx content= so the signed payload hash matches
the bytes sent; preserve X-Internal-Api-Key for the app-layer check. Sign per
retry attempt to avoid SigV4 timestamp expiry on slow retries.
- Add CDK pip bundling (--platform manylinux2014_aarch64 --only-binary=:all:) to
all four Lambdas so ARM64 wheels (reportlab, Pillow, pdfplumber) ship.
- Converge _retry_request across all four (fixes possibly-undefined return in
pdf-extract/pdf-generate).
- Add SigV4 signing regression tests.
Verified: ruff clean, infra tsc clean, aarch64 wheels resolve for all four,
23 pytest pass. GPT-4.1 cross-family review: no BLOCK (FIX + NIT applied).
Build artifacts (api/publish/, *.tsbuildinfo) were untracked-but-committable;
.NET publish output can include appsettings.*.json. Flagged by sh-build-review.
Re-theme the MUI app from teal (#0B5A73) to the Sea Haven Ops neutral-navy
structure (#111827) with action-blue (#2563EB) as the sole brand accent, driven
through the theme tokens so all screens update consistently. Cards become
border-driven (no shadow); table headers gray-50; status/priority chips aligned
to design-system token values.
Layout fixes:
- Topbar: square bottom corners (was inheriting MuiPaper radius)
- Sidebar: remove duplicate user tag (already shown in topbar)
- Main: drop redundant ml that double-counted the persistent drawer width
- New Proposal form: center the constrained container (mx: auto)
Restores the deliberate dev-pause from #86. The 2026-06-05 audit
remediation raised these limits to 5 without knowing the pause was
intentional; the resulting 14 version-update PRs were closed unmerged.
Security updates are unaffected by this setting. Re-raise at V1.
proposal-system's seven CI jobs have distinct names, so the org ruleset's
required 'ci / ci' status check never reported here. The aggregator needs
all real CI jobs and fails if any failed or was cancelled. NOTE: this
check will be red until the pre-existing Python Lint/Tests failures
(INFRA-55) are fixed — it reports CI state honestly.
Per handbook Pinning Principle: exact pins are kept current by Dependabot version updates gated by CI + dependency review. Blanket ignores let pins rot (see today's fast-uri incident).
Bump all actions/checkout references to @v6 (org target). v4 runs on a
node runtime version that is being deprecated; v6 is the verified org
standard alongside configure-aws-credentials@v6.
Ref: engineering-handbook cicd.md (workflow standardization).
Set open-pull-requests-limit to 0 on all 11 ecosystem entries so Dependabot
stops opening version-update PRs (which were being closed unactioned during
active development). Security updates are unaffected — they ignore this limit.
Revert the limits (or raise them) once the repo stabilizes.
- Add CloudFront origin to API Gateway CORS preflight and .NET CORS policy
- Replace HttpMethod.ANY with explicit methods so OPTIONS preflight doesn't
hit the JWT authorizer (was causing 403 on all API calls)
- Return Cognito ID token instead of access token from auth callback
(access tokens lack the aud claim required by API Gateway JWT authorizer)
- Add CloudFront callback URI to allowed redirect list
- Remove identity_provider=Google from login URL to show Cognito hosted UI
- Replace useBlocker (requires data router) with state-based navigation guard
to fix crash on AdminWorkspace with BrowserRouter
- Add auto-migration on Lambda cold start
- Enable Swagger in production
- AUDIT-REPORT.md: mark all Phase 6 findings fixed (API-M2/M5/M7/M9/M10/M12/M13,
WEB-M3/M4/M8/M9/M11, LAM-M2/M3/M6/M9, INF-M1/M2/M9), update test count to 149
- README.md: Function URL NONE→AWS_IAM, add Testing and Security sections,
expand CI table with test jobs, note SQS encryption/OpenSearch VPC/access logging
- Remove stale session docs (AUDIT-2026-05-20, HANDOFF, RETROSPECTIVE, CHATGPT prompt)
- Add .claude/agents/ to .gitignore
- Remove empty-state placeholder from SimilarProposalsPanel
- ProposalNumberGenerator tests (8 tests): format validation (SHI-YYYY-NNNN),
sequence incrementing, revision skipping, year boundary isolation, uniqueness,
zero-padding, high sequence rollover. Uses SQLite in-memory with Postgres
function stubs to support ExecuteSqlRawAsync.
- LineItemService state guard tests (18 tests): verifies line items cannot be
created/bulk-updated/deleted on Approved or Sent proposals (QA-C2), confirms
operations succeed on InReview and Revised statuses, validates
KeyNotFoundException on missing proposals, verifies audit logging.
- API client interceptor tests (14 tests): request interceptor attaches Bearer
token from sessionStorage (WEB-C1), handles missing/malformed token data,
response interceptor dispatches Redux logout on 401 (WEB-M2), returns friendly
messages for 403/404, extracts server error details, handles network errors.
- DbContextFactory updated to suppress InMemoryEventId.TransactionIgnoredWarning
so BulkUpdateAsync tests work with in-memory provider.
- Added SqliteDbContextFactory for tests requiring relational features.
- Added Microsoft.EntityFrameworkCore.Sqlite to test project dependencies.
Total: 104 .NET tests (was 77), 26 web tests (was 12). CI already wired.
- API-M2: Add comment for fail-loud auth config guard (already implemented)
- API-M5: Add FluentValidation validators for VendorProposal, GeneratedPdf,
and SimilarReference DTOs; move request records to Application DTOs
- API-M7: Add AsNoTracking() to all read-only queries in ProposalService,
LineItemService, AdminController, UsersController, FilesController
- API-M9: Log stderr from dev PDF generation instead of returning to client
- API-M10: Return generic "Authentication service unavailable" in auth
callbacks instead of leaking Cognito/DevMode configuration state
- API-M12: Enrich audit logging with before/after values for status changes,
proposal edits, and line item operations using structured JSON
- API-M13: Log previous role alongside new role on user role changes in
both UsersController and Cognito-synced role updates in AuthController
- WEB-M3: Add minimum length validation (10 chars) on scope of work field
with inline MUI error message
- WEB-M4: Add 'Other' to shared ServiceCategory contract to align with
API enum (already present in frontend and backend)
- WEB-M8: Show error alert with retry button instead of misleading zeros
when dashboard stats fetch fails (both dispatcher and admin dashboards)
- WEB-M9: Add MUI Skeleton loading state for line items in admin workspace
- WEB-M11: Wire 'Return to Review' button on approved proposals — backend
supports Approved->InReview transition, API client already had the method
LAM-M2: Add sanitize_user_text() to suggestions Lambda that strips common
prompt injection patterns (blocklist + delimiter neutralisation) before
including user-supplied text in Bedrock prompts.
LAM-M3: Add file size check in pdf-extract before downloading — rejects
PDFs over 50 MB with a logged warning and ValueError.
LAM-M6: Add validate_line_item_numerics() to suggestions Lambda that
rejects Bedrock-generated line items with negative values, NaN/Inf, or
amounts exceeding $10M ceiling.
LAM-M9: Replace indefinite API key cache with 5-minute TTL in all four
Lambdas (suggestions, pdf-extract, pdf-generate, library-ingest) so
rotated Secrets Manager values take effect promptly.
INF-M1: Replace wildcard anthropic.claude-* foundation-model ARN with the
specific cross-region inference profile ARN and its backing foundation model.
Both suggestions and pdf-extract Lambdas use us.anthropic.claude-sonnet-4-5-20250929-v1:0.
INF-M2: Replace aoss:* data access policy permissions with scoped actions.
KB role gets DescribeCollectionItems/CreateCollectionItems/UpdateCollectionItems
on collection and DescribeIndex/ReadDocument/WriteDocument on indexes.
Index creator gets CreateIndex/DescribeIndex/WriteDocument plus collection describe/create.
INF-M9: Change --require-approval never to --require-approval broadening in
infra/package.json deploy script so IAM/security changes require manual
confirmation during local development.
LAM-M1: Add event/record validation at handler entry for all 4 SQS-triggered
Lambdas. Validates Records key exists and is a non-empty list, checks each
record has a body key, and catches malformed JSON separately to add to
batchItemFailures.
LAM-M5: Change logger.error() to logger.exception() inside all except blocks
across pdf-extract, pdf-generate, suggestions, and library-ingest handlers
so stack traces are included in CloudWatch logs for debugging.
LAM-M8: Add _validate_s3_key() to pdf-extract, pdf-generate, and
library-ingest that strips path traversal sequences (../, ..\), collapses
double slashes, and rejects keys with disallowed characters via regex.
WEB-M2: 401 interceptor now dispatches Redux logout action to clear
auth state, not just localStorage.
WEB-M5: CreateProposalRequest uses typed ServiceCategory and Priority
unions aligned with shared/api-contracts contract.
WEB-M6: Vendor PDF upload validates MIME type (application/pdf),
file extension (.pdf), and max size (25 MB) before accepting.
WEB-M7: AdminWorkspace shows error Alert with retry button when
proposal fetch fails, instead of rendering empty workspace.
WEB-M10: State transition buttons (Approve, Send, Revise) are
disabled with explanatory tooltips when proposal is not in the
correct state for that transition.
WEB-M13: ToastContainer moved inside BrowserRouter so toasts
render in the correct React tree context.
- API-M3: Add dispatcher ownership check on line item reads
- API-M4: Add dispatcher ownership check on PDF endpoints
- API-M6: Add 25 MB file size validation on presigned upload URLs
- API-M8: Wrap BulkUpdate delete-all/insert-all in explicit transaction
- API-M11: Replace silent catch blocks with logged exceptions in
LineItemService and ProposalService
- API-M14: Validate dev signing key is present (from user-secrets or
env vars) instead of using null-forgiving operator
INF-M5: Add enforceSSL: true to all S3 buckets (uploads, generated,
library, web site) to require HTTPS-only access via bucket policy.
INF-M8: Pin all reusable GitHub Actions workflow references from @main
to commit SHA c040bfaa for supply chain security.
Fix CreateProposalRequest constructor calls (missing PoNumber param)
and ProposalService constructor (missing ILogger param) that diverged
when test-bootstrap and api-hardening worktrees merged.
Mark all Critical and High findings as fixed in AUDIT-REPORT.md with
remediation status for each phase.
API-H2: Validate redirectUri against an allowlist before exchanging the
authorization code with Cognito. Production URI is always allowed;
localhost is only allowed when Auth:DevMode is true.
API-H6: Inject ILogger<T> into ProposalService and LineItemService.
Log state transitions (approve, send, revise) at Information level,
invalid state transition attempts at Warning level, and caught
exceptions (audit/job publisher failures) at Error level.
LAM-C1/INF-H1: Change Function URL authType from NONE to AWS_IAM and
grant invokeUrl permission to all four caller Lambdas (suggestions,
pdf-extract, pdf-generate, library-ingest). Lambda HTTP clients will
need SigV4 signing as a follow-up.
INF-H3: Create OpenSearch Serverless VPC endpoint in private subnets
and update network policy from AllowFromPublic to SourceVPCEs, removing
public internet access to the vector search collection.
- WEB-C1 (Critical): Replace all localStorage token operations with
sessionStorage in authSlice.ts and client.ts. Tokens now clear when
the browser tab closes, reducing the XSS token-theft window.
httpOnly cookie migration documented as follow-up.
- WEB-M2: 401 interceptor now dispatches Redux logout() before
redirect so auth state stays consistent with cleared storage.
- WEB-H5/H6: Add onError toast handlers to sendMutation,
reviseMutation, and regenerateMutation in AdminWorkspace.
6-domain audit (API, web, mobile, lambdas, infra, QA) with fixes:
API security: scope internal API key middleware to allowed paths only,
return 401 on invalid key instead of falling through, remove unvalidated
JWT code path, sanitize error messages, add UpdateProposal validator,
remove status field from UpdateProposalRequest to prevent over-posting,
log swallowed exceptions in ProposalService.
Infrastructure: enforce SSL on all S3 buckets, encrypt SQS queues,
enable optional MFA on Cognito, add API Gateway access logging.
Lambdas: fix _retry_request undefined variable across all 4 Lambdas,
re-raise exceptions in pdf-extract/pdf-generate instead of swallowing,
add idempotency guard to suggestions Lambda.
Web: add ErrorBoundary, add auth loading state to ProtectedRoute,
add mutation error toasts in AdminWorkspace, fix dead Cognito link.
Mobile: add mutex to offline queue processing, distinguish permanent
vs retryable failures, register all screens for both roles, log sync
errors.
Swagger/OpenAPI: add Swashbuckle with JWT bearer security definition,
add ProducesResponseType attributes to key endpoints.
Includes AUDIT-REPORT.md with complete findings and CLAUDE.md project
instructions.