Files
cleveragents-core/features/observability/audit_service_wiring.feature
T
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

151 lines
6.9 KiB
Gherkin

Feature: AuditService EventBus Wiring
As a security-conscious system
I want domain operations to automatically generate audit log entries
So that all security-relevant events are tracked
Scenario: Plan applied event generates audit log entry
Given an event bus with an audit subscriber
When a plan_applied event is emitted
Then the audit log should contain a plan_applied entry
Scenario: Plan cancelled event generates audit log entry
Given an event bus with an audit subscriber
When a plan_cancelled event is emitted
Then the audit log should contain a plan_cancelled entry
Scenario: Config changed event generates audit log entry
Given an event bus with an audit subscriber
When a config_changed event is emitted
Then the audit log should contain a config_changed entry
Scenario: Entity deleted event generates audit log entry
Given an event bus with an audit subscriber
When an entity_deleted event is emitted
Then the audit log should contain an entity_deleted entry
Scenario: Session created event generates audit log entry
Given an event bus with an audit subscriber
When a session_created event is emitted
Then the audit log should contain a session_created entry
Scenario: Correction applied event generates audit log entry
Given an event bus with an audit subscriber
When a correction_applied event is emitted
Then the audit log should contain a correction_applied entry
Scenario: Resource modified event generates audit log entry
Given an event bus with an audit subscriber
When a resource_modified event is emitted
Then the audit log should contain a resource_modified entry
Scenario: Auth success event generates audit log entry
Given an event bus with an audit subscriber
When an auth_success event is emitted
Then the audit log should contain an auth_success entry
Scenario: Auth failure event generates audit log entry
Given an event bus with an audit subscriber
When an auth_failure event is emitted
Then the audit log should contain an auth_failure entry
Scenario: Sensitive data in event details is redacted in audit log
Given an event bus with an audit subscriber
When an event with sensitive details is emitted
Then the audit log entry should have redacted values
Scenario: Multiple events are all logged
Given an event bus with an audit subscriber
When multiple security events are emitted
Then all events should appear in the audit log
Scenario: Non-security events are not logged
Given an event bus with an audit subscriber
When a non-security event is emitted
Then the audit log should not contain the event
Scenario: Subscriber maps event type enum values to audit type strings
Given an event bus with an audit subscriber
When a plan_applied event is emitted
Then the audit entry event_type should be the string "plan_applied"
Scenario: Event plan_id is propagated to audit entry
Given an event bus with an audit subscriber
When a plan_applied event with plan_id "PLAN-123" is emitted
Then the audit entry should have plan_id "PLAN-123"
Scenario: Event actor_name is propagated to audit entry
Given an event bus with an audit subscriber
When a config_changed event with actor_name "admin-user" is emitted
Then the audit entry should have actor_name "admin-user"
Scenario: Event session_id is propagated to audit entry details
Given an event bus with an audit subscriber
When a session_created event with session_id "SES-456" is emitted
Then the audit entry details should contain session_id "SES-456"
Scenario: Event correlation_id is propagated to audit entry details
Given an event bus with an audit subscriber
When a plan_applied event with correlation_id "CORR-789" is emitted
Then the audit entry details should contain correlation_id "CORR-789"
Scenario: Subscriber handles recording errors gracefully
Given an event bus with a failing audit service
When a plan_applied event is emitted on the failing bus
Then no audit subscriber exception should propagate
Scenario: Subscriber recovers after a recording failure
Given an event bus with a transiently failing audit service
When a plan_applied event is emitted causing a transient failure
And a plan_cancelled event is emitted after the transient failure
Then the audit log should contain the plan_cancelled entry after recovery
Scenario: Multiple non-security event types are filtered out
Given an event bus with an audit subscriber
When non-security events PLAN_CREATED and ACTOR_INVOKED are emitted
Then the audit log should remain empty
Scenario: Nested sensitive data in event details is redacted
Given an event bus with an audit subscriber
When an event with nested sensitive details is emitted
Then the nested audit log values should be redacted
Scenario: Auto-generated correlation_id is propagated to audit entry
Given an event bus with an audit subscriber
When a plan_applied event without explicit correlation_id is emitted
Then the audit entry details should contain a non-empty correlation_id
Scenario: User identity is extracted from event details to audit column
Given an event bus with an audit subscriber
When a session_created event with user_identity in details is emitted
Then the audit entry should have user_identity "test-user@example.com"
Scenario: User identity from DomainEvent field is forwarded to audit entry
Given an event bus with an audit subscriber
When a session_created event with user_identity field "field-user@example.com" is emitted
Then the audit entry should have user_identity "field-user@example.com"
Scenario: DomainEvent field user_identity takes precedence over details
Given an event bus with an audit subscriber
When a session_created event with both user_identity field "field@example.com" and details identity "details@example.com" is emitted
Then the audit entry should have user_identity "field@example.com"
Scenario: Audit entry user_identity is None when not provided anywhere
Given an event bus with an audit subscriber
When a plan_applied event is emitted
Then the audit entry user_identity should be None
Scenario: Event timestamp is preserved in audit entry via subscriber
Given an event bus with an audit subscriber
When a plan_applied event with a known timestamp is emitted
Then the audit entry created_at should match the event timestamp
Scenario: AuditService.record with explicit timestamp
Given a direct audit service instance
When record is called with an explicit timestamp
Then the audit entry created_at should equal the explicit timestamp
Scenario: AuditService.record without timestamp auto-generates created_at
Given a direct audit service instance
When record is called without a timestamp
Then the audit entry created_at should be close to now