security: sign this service's own native audit:events payloads (HIPAA PR3b) - #8
Merged
Merged
Conversation
HIPAA PR3b: closes producer-side signing for the sixth and final real
audit:events producer the HIPAA audit-integrity assessment found
unsigned -- this repo's own AuditLogger.log(), used by
api/routes_audit.py for platform-admin API actions.
Unlike every other producer (api-gateway, tes, workflow-bundles,
control-center, rag -- all separate deployables that hand-port
sign_audit_event()), AuditLogger.log() lives in the same repo as
audit.signing itself and imports sign_audit_event directly -- no drift
risk, no parallel copy to keep in sync.
data = json.dumps(payload) is computed exactly once, from the already-
model_dump()'d payload, and that exact string is both signed and
published as {"data": data, "sig": ...}. service is read from
payload.get("service") -- the identity actually present in the signed
bytes -- not event.service, so a hypothetical future divergence between
the AuditEvent object and its own serialization can never desync the
two. Secret reused unchanged: AuditConfig.EVENT_SIGNING_SECRET
(audit/config.py), defined by PR2 specifically for this eventual purpose
-- no new secret, no new config path.
Existing event schema, event types, event IDs, timestamps, and stream
name unchanged -- purely additive sig field. All 10 pre-existing
tests/test_logger.py tests pass unmodified.
7 new tests (tests/test_logger_signing.py): signs the exact wire string
(verified against the real verify_audit_event, not a re-derived value),
wrong-secret and tampered-data both correctly fail verification, missing-
service still publishes unsigned rather than dropping the event, and
secret non-leakage on a forced xadd exception. Full suite: 249 passed (242
baseline + 7 new), 0 regressions.
This closes producer-side signing across all six real audit:events
producers on the platform (api-gateway, tes, workflow-bundles,
control-center, rag, and this repo's own native producer) -- each in its
own separate PR per repo, this being the sixth and last.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Lint-only, no behavior change. Fixes the 3 ruff errors this PR's own CI
run reported once audit/logger.py entered the changed-file set (ruff
lints changed files whole, not just changed lines, so this also caught
two pre-existing issues the file already had before PR3b's signing
change touched it):
- I001: import block un-sorted -- auto-fixed via `ruff check --fix`,
grouping stdlib/third-party/first-party with blank lines between,
matching this repo's implicit isort convention elsewhere.
- F401: `typing.Optional` imported but unused -- pre-existing, never
referenced anywhere in this file even before PR3b; removed.
- BLE001: blind `except Exception` -- pre-existing, and deliberate:
this is the same "NEVER break core system" fire-and-forget contract
every audit-write call site in this platform already documents for
itself (worker/main.py's three identical except-Exception-print
blocks, audit_service.log_event elsewhere). Suppressed with a
minimal inline `# noqa: BLE001` plus a one-line reason, matching
this repo's existing noqa style (alembic/env.py, tests/conftest.py's
`# noqa: CODE -- reason` comments) -- not a broad/file-level
suppression, and no repo-wide BLE001 precedent existed to follow, so
this establishes the narrowest one for this exact line only.
Zero signing-behavior change: data/sig construction, secret source,
event schema, and control flow are byte-for-byte identical to before
this commit -- confirmed by re-running tests/test_logger_signing.py (7
tests) and tests/test_logger.py (10 tests) unmodified, all still
passing. Full suite: 249 passed, 0 regressions (unchanged from the
commit this fixes).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
This service's own native producer (
AuditLogger.log(), used byapi/routes_audit.pyfor platform-admin API actions) is the sixth and last of six realaudit:eventsproducers the HIPAA audit-integrity assessment found unsigned.Fix
audit/logger.py::AuditLogger.log()now signs the exactdatastring before publishing:Unlike the other five producers (separate deployables that each hand-port
sign_audit_event), this one imports it directly fromaudit.signing— same repo, no drift risk.serviceis read from the already-serializedpayload(notevent.service) so the signature always covers the identity actually present in the signed bytes.Secret:
AuditConfig.EVENT_SIGNING_SECRET(audit/config.py) — defined by PR2 specifically for this eventual purpose. No new secret, no new config path.Tests
All 10 pre-existing
tests/test_logger.pytests pass unmodified. 7 new tests intests/test_logger_signing.py: signs the exact wire string (verified against the realverify_audit_event, not a re-derived value), wrong-secret and tampered-data both correctly fail verification, missing-service still publishes unsigned rather than dropping the event, secret non-leakage on a forcedxaddexception. Full suite: 249 passed (242 baseline + 7 new), 0 regressions.Closes producer-side signing platform-wide
This is the sixth and final PR of HIPAA PR3b — every real
audit:eventsproducer now signs (omnibioai-api-gateway#10,omnibioai-tes#23,omnibioai-workflow-bundles#25,omnibioai-control-center#43,omnibioai-rag#21, and this PR).Non-scope
No SDK redesign, no GHCR/image work, no consumer changes beyond this repo's own already-merged PR2, no audit schema changes, no unrelated fixes.
🤖 Generated with Claude Code
Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com