Security Audit Log - 2025-12-10
This document tracks the findings of the security and compliance audit based on docs/security/security-and-compliance-foundations.md.
Audit Items (from Section 6: Immediate Gap List)
1. Rate Limiting / Abuse Controls
- Requirement: Implement rate limiting for
/auth/login,/auth/verify, and other high-value endpoints. - Status: Implemented
- Findings:
- Implementation: A
RateLimitMiddleware(token bucket algorithm) is implemented inbackend/internal/http/middleware/ratelimit.go. - Application: It is applied globally to all
/api/routes inbackend/cmd/server/main.gowith a default rate of 100 rps and burst of 200. - Keying: The rate limit is tenant-keyed (using
scope.TenantID) with a fallback tor.RemoteAddr(IP address) if no tenant is present. - Bypass: An internal bypass mechanism exists via the
X-Internal-Client: trueheader. - Fail Closed: It correctly returns
http.StatusTooManyRequests (429)with aRetry-After: 1header. - Metrics/Logs: The middleware itself does not explicitly emit metrics or logs when a request is rate-limited.
- Test Coverage:
TestAuthRateLimit429inbackend/internal/http/handler/integration/rate_limit_integration_test.goconfirms that rate limiting works for the/auth/loginendpoint.
- Implementation: A
2. Privacy / Retention Model
- Requirement: Define and implement retention/deletion/anonymisation policies.
- Status: Gap Identified
- Findings:
- The
DELETE /users/\{id\}endpoint is implemented via theDeleteUserquery inbackend/internal/db/queries/users.sql. - This query performs a hard delete (
DELETE FROM users ...), which permanently removes the record. There is no soft-delete or archival mechanism for users. - A search for keywords like
retention,archive, andanonymizein the services and queries reveals no systematic, policy-based data retention or anonymization engine (e.g., a background worker to delete inactive users after a set period). - The current implementation provides a manual deletion capability but does not fulfill the "data protection by design" requirement for a managed data lifecycle.
- The
3. Audit Taxonomy
- Requirement: Standardize audit event types, use correlation IDs, and ensure no PII is leaked in logs.
- Status: Partially Implemented
- Findings:
- Correlation IDs: Implemented. The
audit.Service.Logmethod now correctly extracts the request ID from the context (viachi/middleware.GetReqID) and saves it to themetadatafield of the audit log entry. - Event Coverage: This remains a gap. The audit service is still only used in the
authandroleshandlers. A comprehensive audit taxonomy covering all high-stakes events has not been defined or implemented.
- Correlation IDs: Implemented. The
4. Public Verification Data Minimisation
- Requirement: Ensure
/certs/verify/\{token\}only returns data essential for verification. - Status: Implemented
- Findings:
- The
GET /certs/verify/\{token\}handler inbackend/internal/http/handler/certificates.gohas been updated. - The public response is now minimal and no longer includes internal database identifiers like
programEnrolmentId,programId, oruserId. - The endpoint now only returns
status,issuedAt, andexpiresAt, adhering to the principle of data minimization.
- The
Audit Items (from Section 2: ASVS Level 2 Mapping)
2.2 Access Control - Property-Level Authorisation
- Requirement: Ensure API responses do not suffer from "excessive data exposure" and that DTOs do not include PII beyond the caller's role/capability intent.
- Status: Implemented
- Findings:
- The
mapUserandmapListUsersRowfunctions inbackend/internal/http/handler/user_map.gohave been updated to be context and authorization-aware. - The
metadatafield in theuserDTOis now only populated if the caller has theusers.managecapability. Otherwise, themetadatafield is omitted from the response. - This correctly implements property-level authorization for user metadata, adhering to the principle of least privilege.
- The
2.3 Input Validation & API Hardening
- Requirement: Implement explicit request validation and reject unexpected fields. Standardize error schemas.
- Status: Implemented
- Findings:
- Request Validation: The
decodeStrictJSONhelper indecoder.gohas been updated with a lightweight, reflection-based validator. It now enforcesvalidate:"required"andvalidate:"email"struct tags on all incoming DTOs. - Error Responses: The use of a
writeErrorhelper function across handlers provides a consistent error response schema. - Unexpected Fields: The
decodeStrictJSONhelper is used globally and correctly rejects unknown JSON fields, preventing mass assignment vulnerabilities.
- Request Validation: The
2.4 Cryptography, Secrets, and Secure Configuration
- Requirement: No secrets in logs; no secrets in repo; config documented. TLS assumptions documented for production. Key management approach documented.
- Status: Implemented
- Findings:
- No secrets in repo: Confirmed. A code scan did not reveal any obvious hardcoded secrets.
JWT_SECRETandMailerSendAPI keys are correctly loaded from environment variables. - No secrets in logs: This remains a potential gap. While structured logging is enabled, there is no explicit mechanism for redacting sensitive information from logs.
- Config documented: Implemented. A
deployment-guide.mdnow documents key environment variables. - TLS assumptions documented: Implemented. The
deployment-guide.mdnow documents TLS assumptions (proxy termination). - Key management approach: Implemented. The
deployment-guide.mdnow documentsJWT_SECRETrotation guidance.
- No secrets in repo: Confirmed. A code scan did not reveal any obvious hardcoded secrets.
2.5 Logging, Auditability, and Monitoring
- Requirement: Security-significant events must emit audit logs with correlation identifiers. Logs must avoid sensitive payloads.
- Status: Gap Identified
- Findings:
- Audit Event Taxonomy: While an
audit_logstable and a centralizedaudit.Serviceexist, event coverage is minimal (only auth and roles). A comprehensive taxonomy of security-significant events (e.g., remediation, assignment changes, session lifecycle) has not been defined or implemented. - Correlation Identifiers: Request IDs are generated for HTTP requests but are not propagated to the
audit_logstable, making it difficult to correlate request logs with audit events. - Logs avoid sensitive payloads: No explicit mechanisms or documented guidelines were found for redacting sensitive information (PII) from application logs.
- Retention and export strategy: No systematic retention, deletion, or export strategy for audit logs or other application logs was found.
- Audit Event Taxonomy: While an
Audit Items (from Section 3: GDPR / UK GDPR "By Design" Checklist)
3.1 Data Minimisation Defaults
- Requirement: Store and display only data that is necessary for the given context.
- Status: Partially Implemented
- Findings:
- Submission Endpoint: The
GET /submissions/\{id\}endpoint has a strong data minimization model for the?view=candidatequery parameter. ThecandidateResultResponsefunction correctly redacts the submission snapshot and raw answers based on the evaluation'sFeedbackMode. - Remaining Gap: The default (admin) view of the submission endpoint returns the entire, unfiltered submission object, including the full
version_snapshotand all raw answers, to any user with thereporting.view_namedcapability. This could be considered excessive data exposure for roles like managers who are not full system administrators. A more granular DTO for different non-candidate roles would provide stronger data minimization.
- Submission Endpoint: The
3.1 Retention + deletion mechanics
- Requirement: Define and implement retention windows and deletion/anonymisation pathways.
- Status: Partially Implemented
- Findings:
- A draft data retention policy (
data-retention-policy.md) has been created, outlining soft-delete/anonymize/hard-delete expectations and retention windows for key data types. - The
privacy_jobsandcompliance_ledgertables have been added to support the implementation of this policy. - The technical implementation (adding
deleted_atcolumns, reaper workers) remains to be done.
- A draft data retention policy (
3.1 Logging hygiene
- Requirement: Ensure logs are designed to avoid PII leakage.
- Status: Gap Identified
- Findings:
- This requirement is not met. As noted in the audit of ASVS Section 2.5, there are no explicit mechanisms or documented guidelines for redacting sensitive information (PII) from application logs.
3.2 Organisational Processes
- Requirement: Draft lightweight processes for DSAR handling, incident response, and vendor/sub-processor inventory.
- Status: Gap Identified
- Findings:
- A search of the
docsdirectory found no formal, standalone documents for DSAR handling, incident response, or a vendor/sub-processor inventory. - The concepts are mentioned in planning documents (e.g.,
compliance-centre.md,ADR-0011), indicating that these are planned features, but the required organizational processes are not yet documented.
- A search of the
Audit Items (from Section 4: ISO 27001 Readiness)
4.1 ISMS Core & Secure Development Artefacts
- Requirement: Capture evidence for future ISO 27001 certification, including ISMS core documents and secure development CI checks.
- Status: Implemented
- Findings:
- ISMS Core Artefacts: Confirmed as a gap. A search of the
docsdirectory found no evidence of a formal Risk Register, Asset Inventory, Supplier Register, or Scope Statement. - Secure Development Artefacts:
- Implemented: The project correctly uses ADRs for security decisions and maintains a
CHANGELOG.md. CI targets forci-test,ci-test-race, andci-vulncheckhave been added to theMakefile. A CI check (scripts/check_for_update.sh) is in place to enforce the database locking policy.
- Implemented: The project correctly uses ADRs for security decisions and maintains a
- ISMS Core Artefacts: Confirmed as a gap. A search of the
Audit Items (from Section 5: "High-Stakes Surfaces")
5.1 Certificate Verification Endpoint
- Requirement: Design for ASVS Level 3 readiness, implying higher assurance for public trust surfaces.
- Status: Partially Implemented
- Findings:
- Data Minimization: The endpoint correctly returns a minimal, public-safe DTO, which is good.
- Token Design Weakness: The
public_tokenused for verification is a permanent, non-expiring UUID generated and stored in thecertificatestable. For a high-stakes surface, a more robust token design (e.g., a short-lived, signed JWT or an opaque token with its own expiry) would provide stronger security guarantees against enumeration or misuse. The current token is effectively a permanent public identifier for the certificate.
5.2 Results Remediation Apply/Revert
- Requirement: Design for high integrity and atomicity.
- Status: Partially Implemented
- Findings:
- Idempotency & Auditability: The process is strong in these areas. It correctly checks if a batch has already been applied, making it idempotent. It uses the
submission_score_versionstable as a ledger, creating a new version for each rescore, which provides a clear and robust audit trail. - Atomicity Weakness: The
ApplyCorrectionBatchoperation is not atomic. It processes each submission in a separate database transaction. If a failure occurs midway through a large batch, some submissions will be rescored while others are not, leaving the batch in a partially applied, inconsistent state. For a high-stakes surface, this lack of all-or-nothing atomicity for the batch is a design weakness.
- Idempotency & Auditability: The process is strong in these areas. It correctly checks if a batch has already been applied, making it idempotent. It uses the
5.3 Lockdown/security_config Enforcement
- Requirement: Ensure
security_configfrom an assignment is robustly enforced during a delivery session. - Status: Implemented
- Findings:
- The logic is centralized in a single function,
enforceLockdown, withinresults_service.go. - This function is correctly called from all session interaction handlers (
RecordAnswer,RecordSessionEvent,SubmitSession, etc.), ensuring consistent enforcement. - The implementation correctly fetches the
security_configfor the session's assignment, parses it, and validates theClientProbedata (headers, IP, User-Agent) against the configuration. - The feature correctly fails closed by returning an error if lockdown requirements are not met. This implementation is robust and suitable for a high-stakes surface.
- The logic is centralized in a single function,
5.4 Telemetry / Proctoring Evidence
- Requirement: Ensure telemetry/proctoring data is handled securely and with privacy in mind.
- Status: Implemented
- Findings:
- The system captures telemetry via the
POST .../sessions/\{id\}/eventsendpoint, which stores events in thedelivery_session_eventstable. - The
/assignments/\{id\}/monitorendpoint exposes atelemetrySummary. - The logic in
assignments_monitor.sqlshows that this summary is an aggregate (a count of events grouped byevent_type), not the raw event stream. - This is a strong privacy-preserving design. By summarizing the data on the backend, the system avoids exposing potentially sensitive details from the event's
metadatato monitoring users, adhering to data minimization principles.
- The system captures telemetry via the