- Cognito User Pool with strong password policy (12+ chars, upper, lower, number, symbol)
- JWT authorizer on all sensitive API routes
- In-app sign-in/sign-up (no Cognito Hosted UI redirect)
All interview management endpoints require JWT:
- Create, edit, delete interviews
- Transcript editing, finalization
- Coding review, privacy review
- Participant management
| Endpoint | Risk | Justification |
|---|---|---|
| GET /analytics/stats | MEDIUM | Returns aggregate data + Bedrock-redacted quotes. Intentionally public for dashboard consumption. Quotes are PII-redacted. |
| POST /copilot | MEDIUM | AI assistant. Token consumption risk. Rate limiting recommended. |
| GET /analytics/themes | LOW | Aggregate thematic data only |
| GET /config/framework | LOW | Static coding framework definition |
- Both buckets have
PublicAccessBlockenabled (all 4 flags true) - Server-side encryption: AES-256 (SSE-S3)
- Bucket policies: only CDK auto-delete role + Transcribe service principal (scoped)
- No public objects
- Each Lambda has its own least-privilege role
- Comprehend Lambda:
comprehend:DetectPiiEntities,comprehend:DetectEntities,comprehend:DetectKeyPhraseson Resource*- Justification: Synchronous Comprehend Detect* APIs do not support resource-level ARN restrictions. The wildcard is required by the AWS IAM model for these actions. Only the ComprehendProcess Lambda role has this policy.
- Lambda invoke permissions are scoped to specific function ARNs
- Raw transcripts: private to uploading user
- Finalized transcripts: accessible to authenticated reviewers
- Analysis (redacted) transcripts: used for analytics
- Exported quotes: Bedrock-redacted before storage in analytics path
- Legacy data quarantined at
legacy/prefix
All state transitions are enforced server-side:
submit-review.js: Rejects approval ifcodingStateis DRAFT or STALEedit-codes.js: Rejects edits on DRAFT/STALE codingedit-transcript.js: Rejects edits after finalizationexport-analytics.js: UsesisAnalyticsEligible()dual gate (5 conditions)privacy-review.js: Rejects review if privacy processing incomplete
- No Lambda logs full transcript content or raw PII
- Logs contain: action type, interview IDs, counts, status, error messages (truncated)
- Allowed origins:
https://heart.dqwgm9x6cucpa.amplifyapp.com,http://localhost:5173 - Production note: Remove localhost origin before stakeholder release
- Add rate limiting to public endpoints (API Gateway throttling)
- Remove localhost from CORS for production
- Consider adding JWT auth to /analytics/stats if quote exposure is a concern
- Enable CloudTrail for DynamoDB/S3 audit logging