feat(audit): emit Cedar deny events to the durable chain#64
Merged
Conversation
The Cedar engine's audit_decision / audit_field_decision functions only
write to tracing — denials never reach the hash-chained audit ledger.
For NIST 800-53 AU-2 every denied access attempt needs a chain entry
that an incident responder can pull verbatim.
This change adds chain emission at the route-handler boundaries (rather
than threading an AuditLogger through the pure engine API):
routes/files.rs:
forge.access.denied every check_schema_access deny on mint_upload_url
/ confirm_upload / download_file, with the full
schema/entity/field triple the engine doesn't see.
forge.access.denied scan_complete's platform_admin gate when a
non-admin caller tries to flip a file's status.
routes/entities.rs:
forge.access.denied every record-level can_modify deny in
update_entity and patch_entity.
forge.access.denied every record-level can_delete deny in
delete_entity.
filter_entity_fields now returns the dropped-field list so a future
change can roll up field-level denials into a single chain entry per
request. Existing callers ignore the return; no behaviour change.
Fourth and final PR from the production-audit gap review.
b0a92da to
13ac7e2
Compare
3 tasks
rrrodzilla
added a commit
that referenced
this pull request
May 26, 2026
Release rolling up the production-audit gap PRs (#61, #62, #63, #64). BREAKING (pre-1.0 minor): schema-forge-backend 0.11 → 0.12 AuthStore trait gains required `record_login(username, at)` method. Downstream impls must add it. schema-forge-acton 0.30 → 0.31 DynAuthStore trait gains required `record_login` shim. `access::filter_entity_fields` now returns `Vec<String>` (dropped field names) instead of `()`. Callers binding the return value must update.
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.
Summary
audit_decision/audit_field_decisionhelpers only write totracing— denials never reach the BLAKE3-chained audit ledger. For NIST 800-53 AU-2 every denied access attempt needs a chain entry with the schema/entity/field triple the engine doesn't see. Emission goes at the route-handler boundaries (wherestate.audit_logger()is in scope) rather than threading anAuditLoggerthrough the engine API.routes/files.rs:forge.access.deniedon everycheck_schema_accessdeny acrossmint_upload_url,confirm_upload,download_file, and onscan_complete'splatform_admingate.routes/entities.rs:forge.access.deniedon every record-levelcan_modifydeny inupdate_entity/patch_entityandcan_deletedeny indelete_entity.filter_entity_fieldsnow returns the dropped-field list so a future change can roll up field-level denials into a single chain entry per request. Existing callers ignore the return; no behaviour change.Fourth and final PR from the production-audit gap review.
Test plan
cargo nextest run --workspace --features schema-forge-acton/surrealdb— 1672 passedcargo clippy --workspace --features schema-forge-acton/surrealdb --all-targets— clean