generated from coulomb/repo-seed
Add CARING examples and coverage
This commit is contained in:
@@ -6,3 +6,7 @@ Small fixtures for the executable CARING 0.4.0-RC2 profile used by
|
|||||||
These are intentionally compact. They prove that the canonical descriptor,
|
These are intentionally compact. They prove that the canonical descriptor,
|
||||||
request, decision, registry, audit, and Rego-in-Markdown policy package
|
request, decision, registry, audit, and Rego-in-Markdown policy package
|
||||||
shapes can round-trip through `pkg/api` and `internal/policy`.
|
shapes can round-trip through `pkg/api` and `internal/policy`.
|
||||||
|
|
||||||
|
The set includes local subjects, groups, teams, project resources, inherited
|
||||||
|
relationship facts, exposure events, allow/deny fixtures, and a
|
||||||
|
redact-with-obligation policy package.
|
||||||
|
|||||||
20
examples/caring/exposure_event.json
Normal file
20
examples/caring/exposure_event.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"id": "exposure:tenant-alpha-support-001",
|
||||||
|
"type": "X-Support",
|
||||||
|
"actor": "user:alice",
|
||||||
|
"subject": "user:bob",
|
||||||
|
"scope": {
|
||||||
|
"level": "Resource",
|
||||||
|
"id": "document:alpha-plan",
|
||||||
|
"tenant": "tenant:alpha",
|
||||||
|
"resource": "document:alpha-plan"
|
||||||
|
},
|
||||||
|
"planes": ["Data"],
|
||||||
|
"exposure_modes": ["Masked"],
|
||||||
|
"reason": "Support review of masked project plan",
|
||||||
|
"decision_id": "decision:tenant-alpha-support-001",
|
||||||
|
"timestamp": "2026-05-17T00:00:00Z",
|
||||||
|
"metadata": {
|
||||||
|
"source": "examples/caring/exposure_event.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
38
examples/caring/inherited_relationships.yaml
Normal file
38
examples/caring/inherited_relationships.yaml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
- id: rel:reviewers-project-reviewer
|
||||||
|
system: markitect-tool
|
||||||
|
subject: team:project-reviewers
|
||||||
|
relation: reviewer
|
||||||
|
object: project:alpha-redesign
|
||||||
|
tenant: tenant:alpha
|
||||||
|
conditions:
|
||||||
|
- Logged
|
||||||
|
caring:
|
||||||
|
id: descriptor:tenant-alpha-project-reviewer
|
||||||
|
profile: caring-0.4.0-rc2
|
||||||
|
subject_type: Group
|
||||||
|
organization_relation: Customer
|
||||||
|
canonical_role: Verifier
|
||||||
|
scope:
|
||||||
|
level: Project
|
||||||
|
id: project:alpha-redesign
|
||||||
|
tenant: tenant:alpha
|
||||||
|
resource: project:alpha-redesign
|
||||||
|
planes:
|
||||||
|
- Data
|
||||||
|
capabilities:
|
||||||
|
- Review
|
||||||
|
exposure_modes:
|
||||||
|
- Masked
|
||||||
|
conditions:
|
||||||
|
- Logged
|
||||||
|
restrictions:
|
||||||
|
- ExportBlocked
|
||||||
|
- id: rel:alpha-plan-inherits-project-reviewer
|
||||||
|
system: markitect-tool
|
||||||
|
subject: document:alpha-plan
|
||||||
|
relation: inherits
|
||||||
|
object: project:alpha-redesign
|
||||||
|
tenant: tenant:alpha
|
||||||
|
metadata:
|
||||||
|
inheritance: parent
|
||||||
|
source: examples/caring/inherited_relationships.yaml
|
||||||
27
examples/caring/project_resource_manifest.yaml
Normal file
27
examples/caring/project_resource_manifest.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
id: markitect-project-resources
|
||||||
|
system: markitect-tool
|
||||||
|
resources:
|
||||||
|
- id: project:alpha-redesign
|
||||||
|
type: project
|
||||||
|
path: /projects/alpha-redesign
|
||||||
|
labels:
|
||||||
|
- project
|
||||||
|
trust_zone: internal
|
||||||
|
owner: team:project-reviewers
|
||||||
|
- id: document:alpha-plan
|
||||||
|
type: document
|
||||||
|
path: /projects/alpha-redesign/plan
|
||||||
|
parent: project:alpha-redesign
|
||||||
|
labels:
|
||||||
|
- internal
|
||||||
|
- pii
|
||||||
|
trust_zone: internal
|
||||||
|
owner: team:project-reviewers
|
||||||
|
actions:
|
||||||
|
- read
|
||||||
|
- review
|
||||||
|
- export
|
||||||
|
caring_profile: caring-0.4.0-rc2
|
||||||
|
metadata:
|
||||||
|
flex_auth_contract: resource-registration-v0
|
||||||
|
source: examples/caring/project_resource_manifest.yaml
|
||||||
132
examples/caring/redact_policy_package.md
Normal file
132
examples/caring/redact_policy_package.md
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
---
|
||||||
|
id: markitect.documents.mask-pii
|
||||||
|
name: Markitect masked PII read
|
||||||
|
namespace: markitect:document
|
||||||
|
version: v1
|
||||||
|
status: draft
|
||||||
|
package: flexauth.markitect.redact
|
||||||
|
actions:
|
||||||
|
- read
|
||||||
|
owner: team:project-reviewers
|
||||||
|
caring:
|
||||||
|
profile: caring-0.4.0-rc2
|
||||||
|
enforce: false
|
||||||
|
canonical_roles:
|
||||||
|
- Verifier
|
||||||
|
organization_relations:
|
||||||
|
- Customer
|
||||||
|
scopes:
|
||||||
|
- level: Resource
|
||||||
|
id: document:alpha-plan
|
||||||
|
tenant: tenant:alpha
|
||||||
|
planes:
|
||||||
|
- Data
|
||||||
|
capabilities:
|
||||||
|
- View
|
||||||
|
- Mask
|
||||||
|
exposure_modes:
|
||||||
|
- Masked
|
||||||
|
conditions:
|
||||||
|
- Logged
|
||||||
|
restrictions:
|
||||||
|
- ExportBlocked
|
||||||
|
metadata:
|
||||||
|
source: examples/caring/redact_policy_package.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# Markitect Masked PII Read
|
||||||
|
|
||||||
|
This package returns a redaction decision when a verifier may inspect a
|
||||||
|
document only through masked fields.
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
```rego
|
||||||
|
import future.keywords.if
|
||||||
|
import future.keywords.in
|
||||||
|
|
||||||
|
default decision := {"effect": "deny", "reason": "no_matching_rule"}
|
||||||
|
|
||||||
|
decision := {
|
||||||
|
"effect": "redact",
|
||||||
|
"reason": "masked_pii",
|
||||||
|
"obligations": [{
|
||||||
|
"type": "mask_fields",
|
||||||
|
"parameters": {"fields": ["email", "phone"]}
|
||||||
|
}]
|
||||||
|
} if {
|
||||||
|
input.action == "read"
|
||||||
|
input.resource.id == "document:alpha-plan"
|
||||||
|
"Mask" in input.caring_context.capabilities
|
||||||
|
"Masked" in input.caring_context.exposure_modes
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
```rego test
|
||||||
|
package flexauth.markitect.redact_test
|
||||||
|
|
||||||
|
import future.keywords.if
|
||||||
|
import data.flexauth.markitect.redact
|
||||||
|
|
||||||
|
test_masked_reader_gets_redaction if {
|
||||||
|
redact.decision.effect == "redact" with input as {
|
||||||
|
"action": "read",
|
||||||
|
"resource": {"id": "document:alpha-plan"},
|
||||||
|
"caring_context": {
|
||||||
|
"capabilities": ["View", "Mask"],
|
||||||
|
"exposure_modes": ["Masked"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Fixtures
|
||||||
|
|
||||||
|
```yaml fixture
|
||||||
|
id: fixture:masked-pii-redact
|
||||||
|
request:
|
||||||
|
id: check:masked-pii
|
||||||
|
subject:
|
||||||
|
id: user:bob
|
||||||
|
type: Human
|
||||||
|
tenant: tenant:alpha
|
||||||
|
action: read
|
||||||
|
resource:
|
||||||
|
id: document:alpha-plan
|
||||||
|
type: document
|
||||||
|
system: markitect-tool
|
||||||
|
tenant: tenant:alpha
|
||||||
|
caring_context:
|
||||||
|
id: descriptor:tenant-alpha-masked-pii-reviewer
|
||||||
|
profile: caring-0.4.0-rc2
|
||||||
|
subject_type: Human
|
||||||
|
organization_relation: Customer
|
||||||
|
canonical_role: Verifier
|
||||||
|
scope:
|
||||||
|
level: Resource
|
||||||
|
id: document:alpha-plan
|
||||||
|
tenant: tenant:alpha
|
||||||
|
resource: document:alpha-plan
|
||||||
|
planes:
|
||||||
|
- Data
|
||||||
|
capabilities:
|
||||||
|
- View
|
||||||
|
- Mask
|
||||||
|
exposure_modes:
|
||||||
|
- Masked
|
||||||
|
conditions:
|
||||||
|
- Logged
|
||||||
|
restrictions:
|
||||||
|
- ExportBlocked
|
||||||
|
expect:
|
||||||
|
effect: redact
|
||||||
|
reason: masked_pii
|
||||||
|
obligations:
|
||||||
|
- type: mask_fields
|
||||||
|
parameters:
|
||||||
|
fields:
|
||||||
|
- email
|
||||||
|
- phone
|
||||||
|
```
|
||||||
37
examples/caring/team_subject_manifest.yaml
Normal file
37
examples/caring/team_subject_manifest.yaml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
id: tenant-alpha-project-team
|
||||||
|
tenants:
|
||||||
|
- id: tenant:alpha
|
||||||
|
name: Tenant Alpha
|
||||||
|
subjects:
|
||||||
|
- id: user:alice
|
||||||
|
type: Human
|
||||||
|
display_name: Alice Example
|
||||||
|
organization_relation: Customer
|
||||||
|
roles:
|
||||||
|
- Doer
|
||||||
|
groups:
|
||||||
|
- group:platform-architecture
|
||||||
|
tenant: tenant:alpha
|
||||||
|
- id: user:bob
|
||||||
|
type: Human
|
||||||
|
display_name: Bob Example
|
||||||
|
organization_relation: Customer
|
||||||
|
roles:
|
||||||
|
- Verifier
|
||||||
|
groups:
|
||||||
|
- team:project-reviewers
|
||||||
|
tenant: tenant:alpha
|
||||||
|
groups:
|
||||||
|
- id: group:platform-architecture
|
||||||
|
display_name: Platform Architecture
|
||||||
|
members:
|
||||||
|
- user:alice
|
||||||
|
tenant: tenant:alpha
|
||||||
|
teams:
|
||||||
|
- id: team:project-reviewers
|
||||||
|
display_name: Project Reviewers
|
||||||
|
members:
|
||||||
|
- user:bob
|
||||||
|
tenant: tenant:alpha
|
||||||
|
metadata:
|
||||||
|
source: examples/caring/team_subject_manifest.yaml
|
||||||
@@ -50,6 +50,30 @@ func TestLoadPolicyPackageMarkdownValidates(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRedactPolicyPackageMarkdownValidates(t *testing.T) {
|
||||||
|
pkg, err := policy.LoadAndValidateFile(context.Background(), filepath.Join("..", "..", "examples", "caring", "redact_policy_package.md"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("LoadAndValidateFile: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pkg.Valid {
|
||||||
|
t.Fatalf("pkg.Valid = false\n%s", formatValidation(pkg.Validation))
|
||||||
|
}
|
||||||
|
if len(pkg.Validation.Fixtures) != 1 {
|
||||||
|
t.Fatalf("Validation.Fixtures len = %d; want 1", len(pkg.Validation.Fixtures))
|
||||||
|
}
|
||||||
|
fixture := pkg.Validation.Fixtures[0]
|
||||||
|
if !fixture.Passed {
|
||||||
|
t.Fatalf("fixture failed: %s\nactual: %+v", fixture.Error, fixture.Actual)
|
||||||
|
}
|
||||||
|
if fixture.Actual.Effect != api.DecisionEffectRedact {
|
||||||
|
t.Fatalf("fixture.Actual.Effect = %q; want redact", fixture.Actual.Effect)
|
||||||
|
}
|
||||||
|
if len(fixture.Actual.Obligations) != 1 || fixture.Actual.Obligations[0].Type != "mask_fields" {
|
||||||
|
t.Fatalf("fixture.Actual.Obligations = %+v; want mask_fields", fixture.Actual.Obligations)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCaringFindingsAreAdvisoryUntilEnforced(t *testing.T) {
|
func TestCaringFindingsAreAdvisoryUntilEnforced(t *testing.T) {
|
||||||
doc := inlinePolicy(false, "allow")
|
doc := inlinePolicy(false, "allow")
|
||||||
pkg, err := policy.Load([]byte(doc), "inline-policy.md")
|
pkg, err := policy.Load([]byte(doc), "inline-policy.md")
|
||||||
|
|||||||
@@ -30,6 +30,32 @@ func TestRegistryManifestsParse(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestExpandedRegistryExamplesParse(t *testing.T) {
|
||||||
|
var subjects api.SubjectManifest
|
||||||
|
loadYAML(t, filepath.Join("..", "..", "examples", "caring", "team_subject_manifest.yaml"), &subjects)
|
||||||
|
if len(subjects.Teams) != 1 || subjects.Teams[0].ID != "team:project-reviewers" {
|
||||||
|
t.Fatalf("subjects.Teams = %+v; want team:project-reviewers", subjects.Teams)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resources api.ResourceManifest
|
||||||
|
loadYAML(t, filepath.Join("..", "..", "examples", "caring", "project_resource_manifest.yaml"), &resources)
|
||||||
|
if len(resources.Resources) != 2 || resources.Resources[1].Parent != "project:alpha-redesign" {
|
||||||
|
t.Fatalf("resources = %+v; want document inheriting from project", resources.Resources)
|
||||||
|
}
|
||||||
|
|
||||||
|
var relationships []api.RelationshipFact
|
||||||
|
loadYAML(t, filepath.Join("..", "..", "examples", "caring", "inherited_relationships.yaml"), &relationships)
|
||||||
|
if len(relationships) != 2 {
|
||||||
|
t.Fatalf("relationships len = %d; want 2", len(relationships))
|
||||||
|
}
|
||||||
|
if relationships[0].Caring == nil || relationships[0].Caring.CanonicalRole != api.CanonicalRoleVerifier {
|
||||||
|
t.Fatalf("relationships[0].Caring = %+v; want Verifier descriptor", relationships[0].Caring)
|
||||||
|
}
|
||||||
|
if relationships[1].Relation != "inherits" {
|
||||||
|
t.Fatalf("relationships[1].Relation = %q; want inherits", relationships[1].Relation)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func loadYAML(t *testing.T, path string, out any) {
|
func loadYAML(t *testing.T, path string, out any) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
|
|||||||
@@ -82,6 +82,15 @@ func TestDecisionAndAuditExamplesParse(t *testing.T) {
|
|||||||
if audit.DecisionID != decision.ID {
|
if audit.DecisionID != decision.ID {
|
||||||
t.Errorf("Audit.DecisionID = %q; want %q", audit.DecisionID, decision.ID)
|
t.Errorf("Audit.DecisionID = %q; want %q", audit.DecisionID, decision.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var exposure api.CaringExposureEvent
|
||||||
|
loadJSON(t, filepath.Join("..", "..", "examples", "caring", "exposure_event.json"), &exposure)
|
||||||
|
if exposure.Type != api.ExposureEventSupport {
|
||||||
|
t.Errorf("Exposure.Type = %q; want X-Support", exposure.Type)
|
||||||
|
}
|
||||||
|
if len(exposure.ExposureModes) != 1 || exposure.ExposureModes[0] != api.ExposureModeMasked {
|
||||||
|
t.Errorf("Exposure.ExposureModes = %v; want [Masked]", exposure.ExposureModes)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSchemaFilesAreJSON(t *testing.T) {
|
func TestSchemaFilesAreJSON(t *testing.T) {
|
||||||
|
|||||||
@@ -230,7 +230,7 @@ Add a minimal service skeleton only after CLI/library behavior is stable.
|
|||||||
|
|
||||||
```task
|
```task
|
||||||
id: FLEX-WP-0002-T008
|
id: FLEX-WP-0002-T008
|
||||||
status: todo
|
status: done
|
||||||
priority: high
|
priority: high
|
||||||
state_hub_task_id: "6cbe572a-2877-4936-8ef3-63b79900fae2"
|
state_hub_task_id: "6cbe572a-2877-4936-8ef3-63b79900fae2"
|
||||||
```
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user