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
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
151 lines
6.9 KiB
Gherkin
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
|