Audit package attribute reads and choose fact/assertion shape.
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Build and Publish Container Image / build-and-push (push) Successful in 56s

FLEX-WP-0025-T01/T02: keep merged attributes, add registry/asserted
namespaces later, and declare the remaining ceiling keys so no
published allowlist is unbacked. T03 stays waiting on validate.

Assistant: grok
Assistant-Session: 01a09dc1-b21e-77e1-919e-fcad2f82b267
This commit is contained in:
tegwick 2026-09-14 04:47:52 +02:00
parent d39953bf1a
commit 86716ff263
4 changed files with 120 additions and 4 deletions

View file

@ -74,6 +74,90 @@ caller assertion at all, which one merged `attributes` map makes impossible.
allowlist MUST read a key its manifest declares**, and that is a review
obligation on every package.
## Package attribute-read audit (FLEX-WP-0025-T02)
Read every `input.{resource,subject}.attributes.<key>` (including
`object.get`) in the published packages. A ceiling or allowlist must be
declared on every resource/subject that package can be asked about.
First-class registry fields that enrichment copies into attributes (`roles`,
`groups`, `labels`, `trust_zone`, `owner`, `path`, `parent`, plus subject
`metadata`/`claims`) count as declared.
Classification: **ceiling/allowlist** must be facts; **caller-proposed** is
genuinely the request; **ambiguous** is the T01 input.
| Package | Key | Kind | Role | Declared |
| --- | --- | --- | --- | --- |
| ops-warden | resource.max_ttl_hours | ceiling | ceiling | yes (manifest + snapshots) |
| ops-warden | resource.allowed_subjects | allowlist | allowlist | yes |
| ops-warden | resource.allowed_principals | allowlist | allowlist | yes |
| ops-warden | resource.actor_id | identity | allowlist | yes |
| ops-warden | resource.actor_type | identity | allowlist | yes |
| ops-warden | resource.security_zone | posture | ceiling | yes (snapshots; added to `resource_manifest.yaml` 2026-09-14) |
| ops-warden | resource.security_zone_admission | posture | ceiling | yes |
| ops-warden | subject.actor_type | identity | allowlist | yes (`metadata.actor_type`, copied into attributes) |
| railiance-platform | resource.max_ttl_seconds | ceiling | ceiling | yes |
| railiance-platform | resource.allowed_actor_types | allowlist | allowlist | yes |
| railiance-platform | resource.allowed_purposes | allowlist | allowlist | yes |
| railiance-platform | resource.allowed_delivery_modes | allowlist | allowlist | yes |
| railiance-platform | resource.grant_id | identity | allowlist | yes |
| secrets-engine | — | — | no attribute reads | — |
| tenant-engine | — | — | no attribute reads | — |
| qonto-assistant | — | — | no attribute reads | — |
| user-engine | subject.roles | allowlist | allowlist | yes (first-class `roles`; snapshot now names a registration-applicant) |
| user-engine | subject.issuer | allowlist | allowlist | yes (`claims.issuer` on that subject) |
| markitect (example) | resource.labels | classification | allowlist | yes (first-class `labels`) |
| markitect (example) | subject.groups | membership | allowlist | yes (first-class `groups`) |
| markitect (example) | subject.roles | membership | allowlist | yes (first-class `roles`) |
| informed-decision-t03 (fixture) | subject.assurance, groups, principal_type_source, tenant_source | mixed | see T01 | fixture-only; not a published consumer package |
No published-package ceiling or allowlist remains unbacked after the two
declaration fixes above. `ttl_hours`, `purpose`, `requested_ttl_seconds`, and
`principals` are **context** fields (caller-proposed) and are not in this
table.
## Chosen input shape (FLEX-WP-0025-T01)
**Keep the merged `attributes` map. Add parallel namespaces. Do not break the
six packages.**
```text
input.resource.attributes # merged view (status quo)
input.resource.registry.* # registry facts only
input.resource.asserted.* # caller-supplied only
input.subject.attributes # merged view (status quo)
input.subject.registry.* # registry facts only
input.subject.asserted.* # caller-supplied only
```
Why not stop merging: three of the six published packages (`secrets-engine`,
`tenant-engine`, `qonto-assistant`) read no attributes at all; the other three
already have their ceilings declared. A breaking change across six packages
buys nothing on day one and invites a silent miss on a package that still
reads `attributes` by habit. The merged view stays so existing rules keep
their meaning; a ceiling rule **opts in** to `registry.*`.
**Vocabulary collision on `subject.type`:** do not overwrite it with the
CARING registry type (`Human`/`Agent`/`Automation`/`Service`). Packages
compare against the protected system's actor vocabulary (`service`, `adm`,
`agt`, `atm`). Put the CARING type at `input.subject.registry.type` (and the
caller value at `input.subject.asserted.type`). `input.subject.type` remains
the request's actor vocabulary, filled from the caller, defaulting only when
empty — the exception already in `enrichSubjectRef`.
Ambiguous set that T03's validate check must surface, because intent cannot
be inferred from the key name:
- `subject.roles` / `subject.groups` — first-class registry facts when the
subject is registered; caller input when it is not
- `subject.issuer` — allowlist for public registration; a fact only if the
directory wrote it
- `subject.type` — two vocabularies, one field (handled above, not by
precedence)
Package changes wait until T03 makes "read a declared registry key" a
`flex-auth validate` finding rather than a review note.
## Which digest a consumer can reproduce
| Field | Over | Consumer-computable |