Files
cleveragents-core/robot/security_audit.robot
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

101 lines
4.1 KiB
Plaintext

*** Settings ***
Documentation SEC7 - Audit logging integration smoke tests
Library OperatingSystem
Library Process
*** Variables ***
${AUDIT_SERVICE} ${CURDIR}/../src/cleveragents/application/services/audit_service.py
${AUDIT_CLI} ${CURDIR}/../src/cleveragents/cli/commands/audit.py
${AUDIT_MODEL} ${CURDIR}/../src/cleveragents/infrastructure/database/models.py
${AUDIT_DOC} ${CURDIR}/../docs/reference/audit_logging.md
*** Test Cases ***
Audit Service Module Exists
[Documentation] Verify the audit service module is present
File Should Exist ${AUDIT_SERVICE}
Audit CLI Module Exists
[Documentation] Verify the audit CLI module is present
File Should Exist ${AUDIT_CLI}
Audit Log Model Defined
[Documentation] Verify AuditLogModel is defined in models.py
${content}= Get File ${AUDIT_MODEL}
Should Contain ${content} class AuditLogModel
Should Contain ${content} audit_log
Should Contain ${content} event_type
Should Contain ${content} plan_id
Should Contain ${content} details
Should Contain ${content} created_at
Audit Service Has Record Method
[Documentation] Verify the audit service has a record method
${content}= Get File ${AUDIT_SERVICE}
Should Contain ${content} def record(
Should Contain ${content} event_type
Should Contain ${content} VALID_EVENT_TYPES
Audit Service Has List Method
[Documentation] Verify the audit service has a list_entries method
${content}= Get File ${AUDIT_SERVICE}
Should Contain ${content} def list_entries(
Should Contain ${content} plan_id
Should Contain ${content} project_name
Should Contain ${content} since
Audit Service Has Prune Method
[Documentation] Verify the audit service has a prune method
${content}= Get File ${AUDIT_SERVICE}
Should Contain ${content} def prune(
Should Contain ${content} retention_days
Audit Service Has Thread Safe Session Init
[Documentation] Verify _ensure_session() uses threading.Lock (bug #991 fix)
${content}= Get File ${AUDIT_SERVICE}
Should Contain ${content} import threading
Should Contain ${content} _session_lock
Should Contain ${content} threading.Lock()
Audit Service Has Close And Context Manager
[Documentation] Verify the audit service supports close() and context manager
${content}= Get File ${AUDIT_SERVICE}
Should Contain ${content} def close(
Should Contain ${content} def __enter__(
Should Contain ${content} def __exit__(
Audit CLI Has List Command
[Documentation] Verify the audit CLI has a list command
${content}= Get File ${AUDIT_CLI}
Should Contain ${content} name="list"
Should Contain ${content} --plan
Should Contain ${content} --project
Should Contain ${content} --since
Audit CLI Has Show Command
[Documentation] Verify the audit CLI has a show command
${content}= Get File ${AUDIT_CLI}
Should Contain ${content} name="show"
Should Contain ${content} audit_id
Audit CLI Has Prune Command
[Documentation] Verify the audit CLI has a prune command
${content}= Get File ${AUDIT_CLI}
Should Contain ${content} name="prune"
Should Contain ${content} --days
Should Contain ${content} --yes
Audit Documentation Exists
[Documentation] Verify the audit logging reference doc exists
File Should Exist ${AUDIT_DOC}
${content}= Get File ${AUDIT_DOC}
Should Contain ${content} Audit Logging
Should Contain ${content} agents audit list
Should Contain ${content} agents audit show
Should Contain ${content} agents audit prune
Audit Service Record Accepts Timestamp Parameter
[Documentation] Verify AuditService.record() has a timestamp parameter (issue #719)
${content}= Get File ${AUDIT_SERVICE}
Should Contain ${content} timestamp: datetime | None = None
Should Contain ${content} audit_retention_days