Files
cleveragents-core/robot/audit_service_wiring.robot
HAL9000 8ed8750160
CI / push-validation (pull_request) Successful in 23s
CI / helm (pull_request) Successful in 30s
CI / build (pull_request) Successful in 43s
CI / lint (pull_request) Successful in 55s
CI / quality (pull_request) Successful in 54s
CI / typecheck (pull_request) Successful in 57s
CI / security (pull_request) Successful in 1m28s
CI / integration_tests (pull_request) Successful in 3m4s
CI / unit_tests (pull_request) Successful in 7m30s
CI / docker (pull_request) Failing after 14m0s
CI / coverage (pull_request) Failing after 21m7s
CI / status-check (pull_request) Has been cancelled
fix(audit): forward DomainEvent.timestamp to AuditService.record()
AuditService.record() was generating its own timestamp internally,
discarding the original DomainEvent.timestamp. This means audit entries
recorded when an event was audited, not when the domain event actually
occurred, breaking forensic accuracy per §Audit Logging (SEC7).

Changes:
- Add `timestamp: datetime | None = None` keyword parameter to
  AuditService.record(). When provided, uses it as created_at;
  falls back to datetime.now(tz=UTC) for backward compatibility.
  Applied to both the async queue path and the synchronous DB path.
- AuditEventSubscriber._handle_event() now passes timestamp=event.timestamp
  so the original event creation time is preserved in audit entries.
- Add 3 Behave BDD scenarios covering: full pipeline timestamp
  preservation, direct record() with explicit timestamp, and backward
  compatibility (record() without timestamp auto-generates created_at).
- Add preserve_event_timestamp Robot integration test and helper subcommand.
- Add static source check in security_audit.robot verifying the
  timestamp parameter signature exists.

ISSUES CLOSED: #719
2026-05-29 16:36:39 -04:00

109 lines
4.7 KiB
Plaintext

*** Settings ***
Documentation Integration test for AuditService EventBus wiring
Resource ${CURDIR}/common.resource
Suite Setup Setup Test Environment With Database Isolation
Suite Teardown Cleanup Test Environment
*** Variables ***
${HELPER} ${CURDIR}/helper_audit_wiring.py
*** Test Cases ***
Audit Subscriber Records Events
[Documentation] Verify AuditEventSubscriber records events from EventBus
[Tags] observability audit
${result}= Run Process ${PYTHON} ${HELPER} record_events
... cwd=${WORKSPACE}
Log ${result.stdout}
Log ${result.stderr}
Should Be Equal As Integers ${result.rc} 0
Should Contain ${result.stdout} AUDIT_OK
Audit Subscriber Redacts Sensitive Data
[Documentation] Verify sensitive data is redacted in audit entries
[Tags] observability audit security
${result}= Run Process ${PYTHON} ${HELPER} redact_secrets
... cwd=${WORKSPACE}
Log ${result.stdout}
Log ${result.stderr}
Should Be Equal As Integers ${result.rc} 0
Should Contain ${result.stdout} REDACT_OK
Audit Subscriber Ignores Non-Security Events
[Documentation] Verify non-security events are not recorded
[Tags] observability audit
${result}= Run Process ${PYTHON} ${HELPER} ignore_non_security
... cwd=${WORKSPACE}
Log ${result.stdout}
Log ${result.stderr}
Should Be Equal As Integers ${result.rc} 0
Should Contain ${result.stdout} FILTER_OK
Container Wires Audit Subscriber
[Documentation] Verify DI container registers AuditEventSubscriber
[Tags] observability audit di
${result}= Run Process ${PYTHON} ${HELPER} container_wiring
... cwd=${WORKSPACE}
Log ${result.stdout}
Log ${result.stderr}
Should Be Equal As Integers ${result.rc} 0
Should Contain ${result.stdout} CONTAINER_OK
E2E Plan Lifecycle Through Audit Pipeline
[Documentation] End-to-end: PlanLifecycleService cancel_plan -> EventBus -> AuditEventSubscriber -> AuditService DB
[Tags] observability audit e2e
${result}= Run Process ${PYTHON} ${HELPER} e2e_plan_lifecycle
... cwd=${WORKSPACE}
Log ${result.stdout}
Log ${result.stderr}
Should Be Equal As Integers ${result.rc} 0
Should Contain ${result.stdout} E2E_OK
User Identity Field Propagates Through Audit Pipeline
[Documentation] DomainEvent.user_identity propagates via AuditEventSubscriber to AuditService DB entry
[Tags] observability audit identity
${result}= Run Process ${PYTHON} ${HELPER} user_identity_field
... cwd=${WORKSPACE}
Log ${result.stdout}
Log ${result.stderr}
Should Be Equal As Integers ${result.rc} 0
Should Contain ${result.stdout} IDENTITY_FIELD_OK
User Identity Fallback From Details
[Documentation] Backward compat: user_identity extracted from details when field is None
[Tags] observability audit identity
${result}= Run Process ${PYTHON} ${HELPER} user_identity_details_fallback
... cwd=${WORKSPACE}
Log ${result.stdout}
Log ${result.stderr}
Should Be Equal As Integers ${result.rc} 0
Should Contain ${result.stdout} IDENTITY_FALLBACK_OK
User Identity Field Precedence Over Details
[Documentation] DomainEvent.user_identity field takes precedence over details dict
[Tags] observability audit identity
${result}= Run Process ${PYTHON} ${HELPER} user_identity_field_precedence
... cwd=${WORKSPACE}
Log ${result.stdout}
Log ${result.stderr}
Should Be Equal As Integers ${result.rc} 0
Should Contain ${result.stdout} IDENTITY_PRECEDENCE_OK
Auth Middleware Emits Audit Events
[Documentation] End-to-end: TokenAuthMiddleware emits AUTH_SUCCESS/AUTH_FAILURE through audit pipeline
[Tags] observability audit auth
${result}= Run Process ${PYTHON} ${HELPER} auth_middleware_pipeline
... cwd=${WORKSPACE}
Log ${result.stdout}
Log ${result.stderr}
Should Be Equal As Integers ${result.rc} 0
Should Contain ${result.stdout} AUTH_PIPELINE_OK
Audit Subscriber Preserves Event Timestamp
[Documentation] DomainEvent.timestamp propagates to AuditLogEntry.created_at via AuditEventSubscriber
[Tags] observability audit
${result}= Run Process ${PYTHON} ${HELPER} preserve_event_timestamp
... cwd=${WORKSPACE}
Log ${result.stdout}
Log ${result.stderr}
Should Be Equal As Integers ${result.rc} 0
Should Contain ${result.stdout} TIMESTAMP_OK