-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add governed audit evidence review boundary #102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
seonghobae
wants to merge
32
commits into
develop
Choose a base branch
from
feat/audit-evidence-review
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
32 commits
Select commit
Hold shift + click to select a range
3e6ccd2
test(audit-review): define package quality contract
seonghobae 8a46f1c
test(audit-review): require purpose-bound verified audit read
seonghobae 592cd8f
test(audit-review): add exact-head quality workflow
seonghobae 75bedd0
feat(audit-review): implement purpose-bound evidence review
seonghobae 29408ee
feat(audit-review): export governed review contracts
seonghobae 1e23586
test(audit-review): cover authorization and integrity edges
seonghobae 6e60f5a
docs(audit-review): document governed review boundary
seonghobae b80eb57
docs(audit-review): record initial governed review contract
seonghobae b490ee2
docs(audit-review): record purpose-bound review decision
seonghobae a1c5da3
docs(audit-review): record primary-source evidence
seonghobae 705536a
docs(audit-review): trace executable evidence boundary
seonghobae 1aa81b2
test(audit-review): reject post-storage envelope widening
seonghobae fb668be
fix(audit-review): preserve PII-minimized envelope shape
seonghobae 3d0fad6
docs(audit-review): record envelope-widening repair
seonghobae eaccec5
docs(audit-review): trace privacy-integrity repair
seonghobae b501d07
docs(audit-review): document exact envelope privacy check
seonghobae ce9d372
docs(audit-review): align audit-review authority and privacy controls
seonghobae 27dae80
test(audit-review): cover exact audit envelope variants
seonghobae b9385db
test(audit-review): reject post-construction trust mutation
seonghobae 5005c9f
fix(audit-review): revalidate live trust objects before use
seonghobae 6471199
docs(audit-review): record live trust-object revalidation
seonghobae 4009b45
docs(audit-review): trace runtime-integrity repair
seonghobae d1e8708
docs(audit-review): document live evidence revalidation
seonghobae df289f8
test(audit): require tenant-bound PostgreSQL evidence reader
seonghobae b7269c6
feat(audit): add tenant-bound PostgreSQL evidence reader
seonghobae c5daf3a
feat(audit): expose PostgreSQL evidence reader
seonghobae da0e676
docs(audit): document PostgreSQL evidence reader contract
seonghobae 6133453
docs(audit): record PostgreSQL reader delivery
seonghobae 8664395
docs(audit): trace PostgreSQL reader evidence
seonghobae d87cb05
docs(audit): accept least-privileged PostgreSQL reader boundary
seonghobae 67b8446
fix(audit-review): validate persisted envelope value types
seonghobae 5344d77
fix: isolate audit review callback queries
seonghobae File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| name: Audit Evidence Review Quality | ||
|
|
||
| on: | ||
| pull_request: | ||
| branches: | ||
| - develop | ||
| paths: | ||
| - "packages/audit-evidence-review/**" | ||
| - ".github/requirements/foundation-test.txt" | ||
| - ".github/workflows/audit-evidence-review-quality.yml" | ||
| - "docs/adr/0102-governed-audit-evidence-review.md" | ||
| - "docs/doctoring/audit-evidence-review-references.md" | ||
| - "docs/traceability/audit-evidence-review.md" | ||
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| group: audit-evidence-review-quality-${{ github.event.pull_request.number || github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| unit: | ||
| name: Audit evidence review contract and 100% coverage | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 10 | ||
| steps: | ||
| - name: Checkout exact candidate | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| ref: ${{ github.event.pull_request.head.sha || github.sha }} | ||
| persist-credentials: false | ||
| - name: Prove exact candidate checkout | ||
| env: | ||
| ORGMETRA_EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} | ||
| run: test "$(git rev-parse HEAD)" = "$ORGMETRA_EXPECTED_HEAD_SHA" | ||
| - name: Set up Python | ||
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | ||
| with: | ||
| python-version: "3.14" | ||
| check-latest: false | ||
| - name: Install reviewed test and build toolchain | ||
| run: | | ||
| python -m pip install --require-hashes --no-deps --only-binary=:all: -r .github/requirements/foundation-test.txt | ||
| printf '%s\n' 'setuptools==84.0.0 --hash=sha256:51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670' > /tmp/orgmetra-audit-evidence-review-build.txt | ||
| python -m pip install --require-hashes --no-deps --only-binary=:all: -r /tmp/orgmetra-audit-evidence-review-build.txt | ||
| python -m pip check | ||
| - name: Compile audit evidence review package | ||
| run: python -m compileall -q packages/audit-evidence-review/src packages/audit-evidence-review/tests | ||
| - name: Build and install exact package artifact | ||
| run: | | ||
| rm -rf /tmp/orgmetra-audit-evidence-review-build-tree /tmp/orgmetra-audit-evidence-review-dist /tmp/orgmetra-audit-evidence-review-venv | ||
| cp -a packages/audit-evidence-review /tmp/orgmetra-audit-evidence-review-build-tree | ||
| mkdir -p /tmp/orgmetra-audit-evidence-review-dist | ||
| python -m pip wheel --no-deps --no-build-isolation --wheel-dir /tmp/orgmetra-audit-evidence-review-dist /tmp/orgmetra-audit-evidence-review-build-tree | ||
| test "$(find /tmp/orgmetra-audit-evidence-review-dist -maxdepth 1 -type f -name '*.whl' | wc -l)" -eq 1 | ||
| python -m venv /tmp/orgmetra-audit-evidence-review-venv | ||
| /tmp/orgmetra-audit-evidence-review-venv/bin/python -m pip install --require-hashes --no-deps --only-binary=:all: -r "$GITHUB_WORKSPACE/.github/requirements/foundation-test.txt" | ||
| wheel_path="$(find /tmp/orgmetra-audit-evidence-review-dist -maxdepth 1 -type f -name '*.whl' -print -quit)" | ||
| wheel_sha="$(sha256sum "$wheel_path" | awk '{print $1}')" | ||
| printf 'orgmetra-audit-evidence-review[test] @ file://%s --hash=sha256:%s\n' "$wheel_path" "$wheel_sha" > /tmp/orgmetra-audit-evidence-review-install.txt | ||
| /tmp/orgmetra-audit-evidence-review-venv/bin/python -m pip install --require-hashes --no-deps -r /tmp/orgmetra-audit-evidence-review-install.txt | ||
| /tmp/orgmetra-audit-evidence-review-venv/bin/python -m pip check | ||
|
seonghobae marked this conversation as resolved.
|
||
| - name: Test installed audit evidence review artifact with exact statement and branch coverage | ||
| env: | ||
| COVERAGE_FILE: /tmp/orgmetra-audit-evidence-review.coverage | ||
| run: | | ||
| cd /tmp | ||
| /tmp/orgmetra-audit-evidence-review-venv/bin/python -m pytest -c "$GITHUB_WORKSPACE/packages/audit-evidence-review/pyproject.toml" "$GITHUB_WORKSPACE/packages/audit-evidence-review/tests" | ||
| - name: Require clean checkout | ||
| run: | | ||
| git diff --exit-code | ||
| test -z "$(git status --porcelain)" | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| # ADR 0102: Governed audit evidence review boundary | ||
|
|
||
| Status: Proposed | ||
|
|
||
| ## Context | ||
|
|
||
| Protected `develop` already persists a PII-minimized CloudEvents-compatible canonical audit envelope and SHA-256 digest in `audit_event_record` under forced tenant RLS. The repository did not yet define an executable read-side trust boundary for audit review. A caller could otherwise implement ad hoc reads that fetch evidence before authorization, fail to bind authorization to the exact tenant/request/purpose, return unbounded history, or trust persisted bytes without rechecking their digest, row identity, and PII-minimized envelope shape. | ||
|
|
||
| NIST SP 800-53 Rev. 5 AU-6 calls for review and analysis of system audit records; the current NIST control catalog update reviewed for this decision is 5.2.0. CloudEvents v1.0.2 defines the stable event context contract used by Orgmetra's existing audit envelope. These sources motivate reviewability and interoperability; they do not make this package a certification or a complete security-monitoring system. | ||
|
|
||
| ## Decision | ||
|
|
||
| Introduce an Orgmetra-owned, transport-neutral `audit-evidence-review` package with four explicit boundaries: | ||
|
|
||
| 1. `AuditEvidenceQuery` fixes tenant scope, pseudonymous request/requester correlations, the closed `audit_evidence_review` purpose, a maximum 90-day system-recorded interval, and a maximum 200-row page. | ||
| 2. `AuditEvidenceReadAuthority` must be invoked before any audit-store read. Its authorization result must exactly match tenant, query reference, requester and purpose and must explicitly permit the read. | ||
| 3. `AuditEvidenceRowReader` is the read-only storage protocol. The included `PostgresAuditEvidenceRowReader` reads only the existing Orgmetra `public.audit_event_record` relation. It enters a read-only transaction, proves `CURRENT_USER` is neither `SUPERUSER` nor `BYPASSRLS`, sets transaction-local `orgmetra.tenant_record_id`, and executes a static parameterized query over the authorized half-open system-recorded interval with deterministic ordering and the authorized limit. Deployment composition owns TLS, credentials, pooling and the least-privileged login role. | ||
| 4. Every returned `PersistedAuditEvidenceRow` re-verifies valid bounded UTF-8, standard finite JSON values, exact canonical JSON bytes, SHA-256, the protected-main PII-minimized top-level/nested envelope key sets and value types, CloudEvents version/media type, persisted tenant/event identity, authorized time window, result count and deterministic ordering before evidence reaches the reviewer. | ||
|
|
||
| The package returns the already PII-minimized canonical audit envelope and does not query application-table HR values or another service database. A privileged storage rewrite cannot make extra top-level HR fields or nested rating-like fields acceptable merely by recomputing a matching digest. The package does not grant or imply employment-decision authority. An empty page means the authorized bounded reader returned no rows; an adapter failure must remain a failure rather than being converted to an empty page. | ||
|
|
||
| ## Consequences | ||
|
|
||
| The buyer gains an executable, testable control boundary for audit review without turning audit evidence into a shadow HR system of record. The PostgreSQL adapter makes least-privilege role state, tenant RLS binding, read-only transaction semantics, bounded parameterized SQL and stable ordering executable rather than leaving them only as host documentation. Host composition still retains responsibility for identity/policy resolution, credentials/TLS/pooling, observability and auditing the review itself. A customer-facing audit UI/API may be added later only if it preserves authorization-before-read and exact evidence verification. | ||
|
|
||
| ## Alternatives rejected | ||
|
|
||
| - **Expose `canonical_event_json` directly from arbitrary SQL.** Rejected because authorization ordering, bounds and integrity verification would be host-specific and easy to omit. | ||
| - **Let a privileged or RLS-bypass database role perform audit review.** Rejected because it would make tenant isolation depend on application predicates alone rather than the existing forced-RLS defense in depth. | ||
| - **Copy HR fields into an audit-search index.** Rejected because it creates a second sensitive system of record and weakens minimization/retention boundaries. | ||
| - **Treat a successful database read as proof of evidence integrity.** Rejected because read-time digest, exact-envelope-shape and row-identity verification provide defense in depth against corruption or privileged storage tampering. |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| # Audit evidence review references | ||
|
|
||
| Reviewed 2026-08-24 against current authoritative sources. | ||
|
|
||
| ## References (APA 7) | ||
|
|
||
| Cloud Native Computing Foundation. (2022). *CloudEvents specification (Version 1.0.2)*. https://github.com/cloudevents/spec/tree/v1.0.2 | ||
|
|
||
| Joint Task Force. (2020). *Security and privacy controls for information systems and organizations (NIST Special Publication 800-53, Revision 5; updated release catalog 5.2.0)*. National Institute of Standards and Technology. https://doi.org/10.6028/NIST.SP.800-53r5 | ||
|
|
||
| ## Design use | ||
|
|
||
| CloudEvents v1.0.2 remains the latest stable CloudEvents core release reviewed for the existing Orgmetra audit envelope. The package therefore rechecks `specversion = 1.0` and the existing JSON media-type contract rather than adopting a working draft. | ||
|
|
||
| NIST SP 800-53 Rev. 5 AU-6 identifies audit-record review, analysis and reporting as an organizational control capability. Orgmetra uses that as a design input for purpose-bound reviewability and accountable access. The reference does not imply NIST certification, FedRAMP authorization, SOC 2 attestation, or that this bounded package implements the entire AU family. |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| # Audit evidence review traceability | ||
|
|
||
| ## Protected-main truth | ||
|
|
||
| `develop@9e3e4847510e1e612b48474ba42b177b8ed824df` already owns immutable, PII-minimized audit/outbox persistence: `AuditOutboxEvent` produces deterministic CloudEvents-compatible canonical JSON and SHA-256, and `database/migrations/0003_audit_outbox_persistence.sql` stores it in forced-RLS `audit_event_record` with append-only mutation guards. Protected main does not yet contain this review package or its PostgreSQL reader. | ||
|
|
||
| ## Active-PR truth | ||
|
|
||
| PR #102 adds the transport-neutral `orgmetra-audit-evidence-review` boundary and its Orgmetra-owned PostgreSQL reader. The package requires authorization before store access; exact authorization scope; bounded system-recorded interval and result count; exact runtime trust primitives; standard finite JSON value types; SHA-256 and canonical-JSON revalidation; row/event/tenant binding; in-window tenant-only rows; and strict `(recorded_at, audit_event_record_id)` order. It contains no HR application-table query and no employment-decision authority. | ||
|
|
||
| `PostgresAuditEvidenceRowReader` is the concrete read-only adapter for the existing `public.audit_event_record` relation. Before audit evidence is selected it opens `SET TRANSACTION READ ONLY`, verifies `CURRENT_USER` resolves to a PostgreSQL role with both `rolsuper=false` and `rolbypassrls=false`, and binds the exact tenant through transaction-local `orgmetra.tenant_record_id`. The evidence query is static and parameterized, applies the authorized half-open `recorded_at` interval and limit, and orders by `recorded_at, audit_event_record_id`. Deployment composition owns TLS, credentials, pooling and the actual least-privileged login; this adapter does not query another service database or an HR application table. | ||
|
|
||
| The first contract head `592cd8f097cb1d45cd901b85f542b590f9ebb546` intentionally had no production package and is the test-first predecessor. Its workflows were queued before follow-up commits, so it is not claimed as terminal hosted RED evidence and no predecessor status is transferred. | ||
|
|
||
| Fresh self-review then found a distinct privacy-integrity defect after the first implementation: canonical JSON plus a recomputed digest could carry additional top-level HR data or extra nested `data` fields because read-time verification checked the digest, CloudEvents version, event id and tenant but not the exact existing PII-minimized envelope shape. RED regression `1aa81b250669373ffd63c2b398925773c6cf967c` requires both widened shapes to fail closed and also requires malformed unencodable text to become a stable validation failure. Root repair `fb668be543d93c2bc7a1d0a930d5f889a916fd7b` binds read-time verification to the exact protected-main audit key sets and hashes a single verified UTF-8 byte sequence. | ||
|
|
||
| A second runtime-integrity self-review found that Python frozen dataclasses can still be rewritten with `object.__setattr__` after successful construction. Without boundary revalidation, a once-valid query could have its limit widened, a once-valid authorization could have `permitted` rewritten to a truthy non-boolean, and a once-valid persisted row could have both canonical JSON and digest replaced after construction. RED regression `b9385dbe9c9501dd1748cca6261c4d4c118ac1dd` fixes those attack paths as acceptance tests. Root repair `5005c9f58c3f35c6a08b8134362478418d011bcc` reconstructs live query, authorization, and row fields through their governed constructors and returns detached verified row snapshots, so authorization/store access and returned evidence never rely on a merely once-valid mutable Python object. | ||
|
|
||
| The PostgreSQL host-adapter slice began with regression head `df289f8506e170d290340c5d3dab32a31812e2ce`, which requires a public `PostgresAuditEvidenceRowReader`, read-only transaction entry, least-privileged role proof, transaction-local tenant binding, a static parameterized bounded audit query, query revalidation before opening a connection, and fail-closed unexpected row shape. Its initial hosted workflows queued before implementation, so no terminal RED is claimed. Root implementation began at `b7269c6b9fc091ab141c85e98c9c2c00580295bf`; exact-current-head evidence must be evaluated fresh after documentation and public-contract alignment. | ||
|
|
||
| ## Planned host surfaces | ||
|
|
||
| A customer-facing audit UI/API may consume the verified review page through an application boundary. That surface is still planned and must not bypass `read_audit_evidence()` authorization ordering or the PostgreSQL reader's least-privilege/RLS contract. | ||
|
|
||
| ## Out of scope | ||
|
|
||
| This PR does not create a second audit store, copy HR values into an index, query another service's application tables, mutate any dedicated-writer dependency repository, authorize employment decisions, provide SIEM correlation, or claim NIST/SOC 2 certification. |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| # Changelog | ||
|
|
||
| ## Unreleased | ||
|
|
||
| - Add a purpose-bound audit evidence review request with a 90-day system-recorded window and 200-row maximum. | ||
| - Require authoritative exact-scope authorization before the audit-store reader is invoked. | ||
| - Add `PostgresAuditEvidenceRowReader` for the existing forced-RLS `public.audit_event_record`: it uses a read-only transaction, verifies the current PostgreSQL role is `NOSUPERUSER NOBYPASSRLS`, binds transaction-local tenant context, applies the authorized half-open recorded-time window and limit with parameterized SQL, and returns governed row snapshots only. | ||
| - Revalidate live query, authorization, and persisted-row fields into detached governed snapshots before authority/store use or evidence return, so post-construction `object.__setattr__` mutation cannot widen request bounds, forge permission, or bypass evidence checks. | ||
| - Re-verify persisted canonical JSON, SHA-256 digest, CloudEvents version/media type, tenant/event identity, review window, result count, and deterministic `(recorded_at, audit_event_record_id)` ordering before evidence is returned. | ||
| - Fail closed if stored canonical evidence is widened beyond the existing PII-minimized audit envelope/data key sets, even when a privileged rewrite recomputes a matching SHA-256 digest. | ||
| - Normalize unencodable canonical text to a stable validation failure before hashing or parsing. | ||
| - Keep HR application values, cross-service SQL, and employment-decision authority outside the review contract. | ||
| - Add exact statement and branch coverage over the installed wheel plus clean-checkout enforcement. |
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.