Isolate tenant readback cache and prepare verified runtime release
Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
This commit is contained in:
parent
c460e2b0df
commit
1bb7cacc98
10 changed files with 188 additions and 13 deletions
|
|
@ -31,18 +31,19 @@ jobs:
|
|||
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||
run: |
|
||||
set -eu
|
||||
REF="${GITHUB_SHA:-main}"
|
||||
REF="${GITHUB_SHA:?exact source revision is required}"
|
||||
echo "${REF}" | grep -Eq '^[0-9a-f]{40}$'
|
||||
SHORT="${REF:0:7}"
|
||||
mkdir -p buildctx "${HOME}/bin"
|
||||
wget -qO /tmp/repo.tar.gz \
|
||||
"https://forgejo.coulomb.social/${GITHUB_REPOSITORY}/archive/${SHORT}.tar.gz"
|
||||
"https://forgejo.coulomb.social/${GITHUB_REPOSITORY}/archive/${REF}.tar.gz"
|
||||
tar xzf /tmp/repo.tar.gz -C buildctx --strip-components=1
|
||||
wget -qO- https://download.docker.com/linux/static/stable/x86_64/docker-27.3.1.tgz \
|
||||
| tar xz --strip-components=1 -C "${HOME}/bin" docker/docker
|
||||
export PATH="${HOME}/bin:${PATH}"
|
||||
echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY}" -u "${REGISTRY_USER}" --password-stdin
|
||||
IMAGE="${REGISTRY}/${IMAGE_NAME}"
|
||||
docker build -f buildctx/Containerfile -t "${IMAGE}:latest" -t "${IMAGE}:main-${SHORT}" buildctx
|
||||
docker build --label "org.opencontainers.image.revision=${REF}" -f buildctx/Containerfile -t "${IMAGE}:latest" -t "${IMAGE}:main-${SHORT}" buildctx
|
||||
docker push "${IMAGE}:latest"
|
||||
docker push "${IMAGE}:main-${SHORT}"
|
||||
echo "pushed ${IMAGE}:latest and ${IMAGE}:main-${SHORT}"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
FROM python:3.12-slim@sha256:d764629ce0ddd8c71fd371e9901efb324a95789d2315a47db7e4d27e78f1b0e9
|
||||
FROM python:3.12-slim@sha256:a6e34c598f2467ed0e9a8d349809fcd8b5c603269512df273a0bb1784edc11b1
|
||||
|
||||
RUN useradd --create-home --uid 10001 user-engine
|
||||
WORKDIR /app
|
||||
|
|
|
|||
31
docs/conformance/2026-10-11-layer-v08-migration.md
Normal file
31
docs/conformance/2026-10-11-layer-v08-migration.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
type: conformance-record
|
||||
derived: true
|
||||
derived_from: net-kingdom/canon/standards/security-layer-model_v0.8.md
|
||||
derived_source_sha256: "3d8298560069ef471fba28d2b1245fb2309328f231dad1a667cdf0ce5a5a7c8b"
|
||||
updated: "2026-10-11"
|
||||
---
|
||||
|
||||
# user-engine accepted v0.8 checker migration
|
||||
|
||||
This owner applies GH-WP-0004-T11's post-flip migration to the confirmed
|
||||
accepted NetKingdom v0.8 cut, immutable accepted-1 artifact SHA256
|
||||
3d8298560069ef471fba28d2b1245fb2309328f231dad1a667cdf0ce5a5a7c8b.
|
||||
The checker names that artifact and its local scope in every run. INTENT
|
||||
frontmatter remains authoritative and the sidecar remains derived; a
|
||||
contradiction is a finding, never hidden by choosing one form. No declaration
|
||||
acquires a standard version.
|
||||
|
||||
The pin detector follows OpsWarden's A12 r3 reference semantics: standard and
|
||||
companion identity values (including nested lists), version-bearing paths and
|
||||
standard/companion version keys are pins; unrelated intent/schema/spec versions
|
||||
and bare prose provenance are not. Existing local Tooling/contact and capability
|
||||
detectors remain in place. Historical version strings in negative fixtures are
|
||||
retained. This source migration grants no native capability or runtime authority.
|
||||
|
||||
Validation includes the existing local declaration/contact suite and the same
|
||||
12-case pin/provenance corpus across all four assigned owners, with exact
|
||||
accepted-artifact and scope assertions. The local test counts and staged source
|
||||
digests are retained in the coordinating Custodian evidence receipt. Standalone
|
||||
checker runs continue to inspect only this repository; no estate-wide compliance
|
||||
claim or novel declaration is produced.
|
||||
31
docs/evidence/2026-10-11-runtime-base-refresh.md
Normal file
31
docs/evidence/2026-10-11-runtime-base-refresh.md
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# Runtime base refresh — 2026-10-11
|
||||
|
||||
The exact source-bound application readback candidate failed its independent
|
||||
scan with 88 HIGH and three CRITICAL OS findings. Preserve that scan and OCI
|
||||
index bea54ccf in the managed runtime release record. This remediation changes
|
||||
only the Containerfile's immutable official Python base index from d764629c
|
||||
to a6e34c59. It preserves Python 3.12, updates patch 3.12.13 to 3.12.15, and
|
||||
uses current Docker Official Image source commit
|
||||
2a3b794c223ab067d122719541cdd54a068732a5 (3.12/slim-trixie). The native new
|
||||
base contains perl-base 5.40.1-6+deb13u1, libc6 deb13u4 and OpenSSL 3.5.7.
|
||||
|
||||
Detailed registry/source, exact context, installed file hashes, dependency
|
||||
snapshot and unfiltered scan are in
|
||||
`rapp-user-engine/docs/evidence/2026-10-11-readback-base-refresh/`. All 47
|
||||
copied application inputs and all 45 installed package files match the prior
|
||||
source. Resolved Python package versions are unchanged. All 20 image readback
|
||||
tests pass; all 323 source tests pass with 8 optional skips and accepted-v0.8
|
||||
layer conformance passes. Native consumer acceptance remains USER-WP-0026-T03.
|
||||
|
||||
The refreshed scan has zero CRITICAL and 44 HIGH findings across 8 distinct
|
||||
CVEs, all without a published fixed version in the fresh recorded database.
|
||||
These findings remain visible; no severity reduction, ignore-unfixed option
|
||||
or suppression was applied. This does not claim a vulnerability-free or
|
||||
approved release. Clean accepted source commit, publication and independent
|
||||
release review remain required, alongside exact scoped live rollout permission.
|
||||
|
||||
CI source preparation also replaces short-revision archive lookup with the
|
||||
validated full 40-hex GITHUB_SHA and labels the built image with that exact
|
||||
revision. Both workflow shell blocks pass bash syntax checks. This workflow
|
||||
change is outside the local image runtime context and was not published or
|
||||
executed; owner CI must produce a new publication receipt from a clean commit.
|
||||
|
|
@ -31,12 +31,11 @@ sys.path.insert(0, str(ROOT / "src"))
|
|||
from user_engine.layer_yaml import load_mapping, load_mapping_text # noqa: E402
|
||||
|
||||
# What every run checks against, printed on every run (GH-DEC-2026-020 §4,
|
||||
# GH-DEC-2026-021 §2). The accepted text is v0.7 at net-kingdom@66dc491; the
|
||||
# decision records whose rulings this checker enforces beyond v0.7 are named with it.
|
||||
# GH-DEC-2026-021 §2). The accepted v0.8 cut incorporates A9-A13 and GH024;
|
||||
# its immutable publication artifact digest is named in each run.
|
||||
VALIDATED_AGAINST = (
|
||||
"net-kingdom/canon/standards/security-layer-model_v0.7.md (net-kingdom@66dc491) "
|
||||
"as amended by GH-DEC-2026-017, GH-DEC-2026-020 and GH-DEC-2026-021 "
|
||||
"(A9-A13, A12 r3; gate-house@39d9287)"
|
||||
"net-kingdom/canon/standards/security-layer-model_v0.8.md "
|
||||
"(accepted-1 sha256:3d8298560069ef471fba28d2b1245fb2309328f231dad1a667cdf0ce5a5a7c8b)"
|
||||
)
|
||||
|
||||
# What every run ranges over. Stance, claims and classification maps
|
||||
|
|
@ -148,6 +147,9 @@ def load_intent_layer() -> str:
|
|||
if "layer" not in front:
|
||||
print(f"FAIL: {INTENT.name} frontmatter has no 'layer:' key (§11)", file=sys.stderr)
|
||||
raise SystemExit(2)
|
||||
if fold(front.get("role", "")) != "pip":
|
||||
print("FAIL: INTENT.md governing role disagrees with derived Engine/PIP declaration", file=sys.stderr)
|
||||
raise SystemExit(2)
|
||||
return str(front["layer"])
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -137,12 +137,12 @@ class ApplicationAccessReader:
|
|||
self.bound_applications = bound_applications
|
||||
self.cache_ttl = cache_ttl
|
||||
self.clock = clock
|
||||
self._cache: dict[tuple[str, str, str], _CacheEntry] = {}
|
||||
self._cache: dict[tuple[str, str, str, str], _CacheEntry] = {}
|
||||
|
||||
def rows(self, actor: Actor, session_key: str) -> tuple[ApplicationAccessRow, ...] | None:
|
||||
"""Return rendered rows, or None when the catalogue itself is unreadable."""
|
||||
now = self.clock()
|
||||
key = (session_key, actor.issuer, actor.subject)
|
||||
key = (session_key, actor.issuer, actor.subject, actor.tenant)
|
||||
entry = self._cache.get(key)
|
||||
if entry is None or entry.reuse_until <= now:
|
||||
entry = self._read(actor, now)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import json
|
|||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
from dataclasses import replace
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from unittest.mock import patch
|
||||
from urllib.error import HTTPError, URLError
|
||||
|
|
@ -159,6 +160,21 @@ class ReaderTests(unittest.TestCase):
|
|||
reader.rows(_actor("alice"), "s1")
|
||||
self.assertEqual(len(readback.reads), 3)
|
||||
|
||||
def test_tenant_switch_cannot_reuse_another_tenants_allow(self):
|
||||
readback = _FakeReadback([
|
||||
ReadbackResult("informed-decision", "allow", NOW + timedelta(seconds=30), "decision:1"),
|
||||
])
|
||||
reader = self._reader(readback)
|
||||
actor = _actor()
|
||||
self.assertEqual(reader.rows(actor, "s1")[0].status, "available")
|
||||
readback.results = [ReadbackResult("informed-decision", "deny")]
|
||||
other_tenant = replace(actor, tenant="tenant-b")
|
||||
self.assertEqual(reader.rows(other_tenant, "s1")[0].status, "denied")
|
||||
self.assertEqual(len(readback.reads), 2)
|
||||
reader.forget("s1")
|
||||
reader.rows(actor, "s1")
|
||||
self.assertEqual(len(readback.reads), 3)
|
||||
|
||||
def test_read_failure_is_unchecked_and_not_reused(self):
|
||||
readback = _FakeReadback([], fail_read=True)
|
||||
reader = self._reader(readback)
|
||||
|
|
|
|||
46
tests/test_layer_v08_convergence.py
Normal file
46
tests/test_layer_v08_convergence.py
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
"""Accepted A12 r3 semantics, retaining local contact scanners and scope."""
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
SCRIPT = ROOT / "scripts/check_layer_conformance.py"
|
||||
|
||||
def checker():
|
||||
spec = importlib.util.spec_from_file_location("v08_checker", SCRIPT)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
@pytest.mark.parametrize("value,pinned", [
|
||||
({"intent_version": "0.3"}, False),
|
||||
({"spec_version": "0.7"}, False),
|
||||
({"note": "per v0.6 section 13"}, False),
|
||||
({"note": "revised 0.7 wording"}, False),
|
||||
({"schema_version": "0.1"}, False),
|
||||
({"standard": "security-layer-model 0.8"}, True),
|
||||
({"STANDARD": ["security-layer-model v0.8"]}, True),
|
||||
({"companion": [["SECURITY-COMPANION v0.3"]]}, True),
|
||||
({"nested": {"standard_version": "0.8"}}, True),
|
||||
({"nested": {"companion_version": "0.3"}}, True),
|
||||
({"references": ["security-layer-model@0.8"]}, True),
|
||||
({"references": ["security-layer-model_v0.7.md"]}, True),
|
||||
])
|
||||
def test_reference_pin_provenance_semantics(value, pinned):
|
||||
module = checker()
|
||||
assert bool(module.find_versions(value, "test")) is pinned
|
||||
|
||||
def test_run_names_exact_accepted_standard_and_scope():
|
||||
module = checker()
|
||||
assert "security-layer-model_v0.8.md" in module.VALIDATED_AGAINST
|
||||
assert "3d8298560069ef471fba28d2b1245fb2309328f231dad1a667cdf0ce5a5a7c8b" in module.VALIDATED_AGAINST
|
||||
assert "INTENT.md" in module.SCOPE
|
||||
|
||||
def test_governing_role_disagreement_is_a_finding(tmp_path, monkeypatch):
|
||||
module = checker()
|
||||
intent = tmp_path / "INTENT.md"
|
||||
intent.write_text("---\nlayer: Engine\nrole: PDP\n---\n")
|
||||
monkeypatch.setattr(module, "INTENT", intent)
|
||||
with pytest.raises(SystemExit) as caught:
|
||||
module.load_intent_layer()
|
||||
assert caught.value.code == 2
|
||||
30
workplans/ADHOC-2026-10-11.md
Normal file
30
workplans/ADHOC-2026-10-11.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
id: USER-WP-ADHOC-2026-10-11
|
||||
type: workplan
|
||||
title: "Refresh vulnerable readback candidate runtime base"
|
||||
domain: communication
|
||||
repo: user-engine
|
||||
status: finished
|
||||
flavor: implementation
|
||||
owner: codex
|
||||
topic_slug: user-engine
|
||||
created: "2026-10-11"
|
||||
updated: "2026-10-11"
|
||||
---
|
||||
|
||||
## Refresh immutable Python patch base and verify exact application preservation
|
||||
|
||||
```task
|
||||
id: USER-WP-ADHOC-2026-10-11-T01
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
Replace only the Containerfile base digest with the current official Python 3.12
|
||||
index; retain exact application bytes and resolved Python dependency versions.
|
||||
The bounded local candidate removes all 3 CRITICAL and44 of 88 HIGH OS findings.
|
||||
Retain remaining 44 unfixed HIGH findings in the unfiltered fresh scan. Exact
|
||||
source/image/provenance checks and 20 image / 323 source tests pass. Evidence:
|
||||
`docs/evidence/2026-10-11-runtime-base-refresh.md`. This source remediation
|
||||
finishes no deployment: USER-WP-0026-T03 and USER-WP-0028-T02 retain native
|
||||
consumer acceptance, clean source commit/publication and scoped rollout gates.
|
||||
|
|
@ -62,8 +62,8 @@ logout. This does not accept the remaining multi-user Vergabe pilot.
|
|||
```task
|
||||
id: USER-WP-0026-T03
|
||||
status: wait
|
||||
blocked_on: message-from:access-engine
|
||||
blocking_reason: "Needs access-engine's per-identity decision/grant/revocation/readback contract and a readable protected-system registry (with key-cape client binding) as the workload catalogue; the membership store cannot represent the allow list."
|
||||
blocked_on: message-from:rapp-user-engine
|
||||
blocking_reason: "FLEX-WP-0035-T04 delivered the admitted readback pin on 2026-10-10. Needs rapp-user-engine readback URL, separate projected caller token and exact-pin egress, an image containing the readback client, and native consumer allow/deny/unavailable acceptance against the live pin."
|
||||
priority: high
|
||||
state_hub_task_id: "95e9a6d4-7e57-5483-97c5-3f4a45bb6767"
|
||||
```
|
||||
|
|
@ -147,3 +147,21 @@ despite its `message-from:net-kingdom` qualifier (added 2026-09-28). Sent one
|
|||
now (message `db1b7d6e`): does NetKingdom own the workload catalogue/admission
|
||||
record and the scoped access-decision contract, and if not, who does. The
|
||||
qualifier stays on net-kingdom until it answers or names another owner.
|
||||
|
||||
2026-10-10 security consolidation review: FLEX-WP-0035-T04 is done. Its founder-run
|
||||
Helm upgrades placed lifecycle v2 and application-readback on image e8c57c18;
|
||||
Ready and anonymous 401/non-enforcement 404 checks passed. Those provider
|
||||
receipts do not establish user-engine caller acceptance. The canonical apply
|
||||
home, rapp-user-engine/manifests/runtime.yaml, currently has no readback URL
|
||||
or dedicated projected-token mount and permits flex-auth egress only to the
|
||||
enforcement pin. Remaining owner work: wire USER_ENGINE_READBACK_URL and
|
||||
USER_ENGINE_READBACK_TOKEN_FILE to a separate audience flex-auth projection,
|
||||
permit only the application-readback pod selector, promote the immutable image
|
||||
containing this client, and verify native consumer readback without disclosing
|
||||
a token. Reuse that acceptance for USER-WP-0028-T02; it is the same integration
|
||||
boundary. No grant, browser acceptance or live deployment is inferred here.
|
||||
|
||||
The local readback cache now binds each row set to tenant as well as session,
|
||||
issuer and subject. A tenant switch within the same session therefore performs
|
||||
a fresh read instead of displaying another tenant's unexpired allow. A focused
|
||||
regression exercises an allow in tenant-a followed by a deny in tenant-b.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue