Audit package attribute reads and choose fact/assertion shape.
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:
parent
d39953bf1a
commit
86716ff263
4 changed files with 120 additions and 4 deletions
|
|
@ -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
|
allowlist MUST read a key its manifest declares**, and that is a review
|
||||||
obligation on every package.
|
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
|
## Which digest a consumer can reproduce
|
||||||
|
|
||||||
| Field | Over | Consumer-computable |
|
| Field | Over | Consumer-computable |
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ resources:
|
||||||
- platform
|
- platform
|
||||||
- root
|
- root
|
||||||
max_ttl_hours: 8
|
max_ttl_hours: 8
|
||||||
|
security_zone: z2-protected
|
||||||
|
security_zone_admission: satisfied
|
||||||
- id: ssh-cert:actor/ci-deploy-agent
|
- id: ssh-cert:actor/ci-deploy-agent
|
||||||
type: ssh-certificate
|
type: ssh-certificate
|
||||||
labels:
|
labels:
|
||||||
|
|
@ -35,6 +37,8 @@ resources:
|
||||||
- deploy
|
- deploy
|
||||||
- git
|
- git
|
||||||
max_ttl_hours: 2
|
max_ttl_hours: 2
|
||||||
|
security_zone: z2-protected
|
||||||
|
security_zone_admission: satisfied
|
||||||
- id: ssh-cert:actor/backup-automation
|
- id: ssh-cert:actor/backup-automation
|
||||||
type: ssh-certificate
|
type: ssh-certificate
|
||||||
labels:
|
labels:
|
||||||
|
|
@ -51,6 +55,8 @@ resources:
|
||||||
allowed_principals:
|
allowed_principals:
|
||||||
- backup
|
- backup
|
||||||
max_ttl_hours: 1
|
max_ttl_hours: 1
|
||||||
|
security_zone: z2-protected
|
||||||
|
security_zone_admission: satisfied
|
||||||
actions:
|
actions:
|
||||||
- sign
|
- sign
|
||||||
caring_profile: caring-0.4.0-rc2
|
caring_profile: caring-0.4.0-rc2
|
||||||
|
|
|
||||||
|
|
@ -14,5 +14,21 @@
|
||||||
"actions": [],
|
"actions": [],
|
||||||
"metadata": {"flex_auth_contract":"protected-system-v0","dynamic_actions":true}
|
"metadata": {"flex_auth_contract":"protected-system-v0","dynamic_actions":true}
|
||||||
}],
|
}],
|
||||||
"resource_manifests": [], "tenants": [], "subjects": [], "groups": [], "relationships": []
|
"resource_manifests": [],
|
||||||
|
"tenants": [],
|
||||||
|
"subjects": [
|
||||||
|
{
|
||||||
|
"id": "registration-applicant",
|
||||||
|
"type": "Human",
|
||||||
|
"display_name": "Public registration applicant",
|
||||||
|
"organization_relation": "Customer",
|
||||||
|
"roles": ["registration-applicant"],
|
||||||
|
"tenant": "tenant:friendly:binky",
|
||||||
|
"claims": {
|
||||||
|
"issuer": "urn:netkingdom:public-registration"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"groups": [],
|
||||||
|
"relationships": []
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ related_workplans:
|
||||||
- FLEX-WP-0022
|
- FLEX-WP-0022
|
||||||
- FLEX-WP-0024
|
- FLEX-WP-0024
|
||||||
created: "2026-09-07"
|
created: "2026-09-07"
|
||||||
updated: "2026-09-07"
|
updated: "2026-09-14"
|
||||||
state_hub_workstream_id: "f9a657ce-67b4-5d25-9933-e0fcb2c20b1c"
|
state_hub_workstream_id: "f9a657ce-67b4-5d25-9933-e0fcb2c20b1c"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -54,7 +54,7 @@ while the value it produces is never read.**
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: FLEX-WP-0025-T01
|
id: FLEX-WP-0025-T01
|
||||||
status: todo
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "05c6a85d-ee3d-5875-bea4-e9a9c5382e2e"
|
state_hub_task_id: "05c6a85d-ee3d-5875-bea4-e9a9c5382e2e"
|
||||||
```
|
```
|
||||||
|
|
@ -79,11 +79,16 @@ and which are ambiguous. The ambiguous set is the real design input.
|
||||||
Gate: the chosen shape is written down with the vocabulary collision addressed,
|
Gate: the chosen shape is written down with the vocabulary collision addressed,
|
||||||
before any package changes.
|
before any package changes.
|
||||||
|
|
||||||
|
**Done 2026-09-14.** Shape recorded in `docs/request-enrichment.md`: keep merged
|
||||||
|
`attributes`, add `registry.*` / `asserted.*`, leave `subject.type` as caller
|
||||||
|
vocabulary and put CARING type at `input.subject.registry.type`. No package
|
||||||
|
rule changes.
|
||||||
|
|
||||||
## 2. Audit every package for ceilings read from undeclared keys
|
## 2. Audit every package for ceilings read from undeclared keys
|
||||||
|
|
||||||
```task
|
```task
|
||||||
id: FLEX-WP-0025-T02
|
id: FLEX-WP-0025-T02
|
||||||
status: todo
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "fc61c8a7-347b-59d2-b6a6-a72c5c1cdb08"
|
state_hub_task_id: "fc61c8a7-347b-59d2-b6a6-a72c5c1cdb08"
|
||||||
```
|
```
|
||||||
|
|
@ -102,6 +107,11 @@ changing the rule.
|
||||||
Gate: a table of package × key × declared-in-manifest, with no ceiling or
|
Gate: a table of package × key × declared-in-manifest, with no ceiling or
|
||||||
allowlist unbacked. Add it to `docs/request-enrichment.md`.
|
allowlist unbacked. Add it to `docs/request-enrichment.md`.
|
||||||
|
|
||||||
|
**Done 2026-09-14.** Table is in `docs/request-enrichment.md`. Declaration
|
||||||
|
fixes: `security_zone` / `security_zone_admission` on the ops-warden resource
|
||||||
|
manifest; `claims.issuer` plus `roles` on a user-engine registration-applicant
|
||||||
|
subject. Secrets-engine, tenant-engine and qonto-assistant read no attributes.
|
||||||
|
|
||||||
## 3. Make the review obligation enforceable rather than written
|
## 3. Make the review obligation enforceable rather than written
|
||||||
|
|
||||||
```task
|
```task
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue