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,
|
||||
request, decision, registry, audit, and Rego-in-Markdown policy package
|
||||
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) {
|
||||
doc := inlinePolicy(false, "allow")
|
||||
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) {
|
||||
t.Helper()
|
||||
|
||||
|
||||
@@ -82,6 +82,15 @@ func TestDecisionAndAuditExamplesParse(t *testing.T) {
|
||||
if 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) {
|
||||
|
||||
@@ -230,7 +230,7 @@ Add a minimal service skeleton only after CLI/library behavior is stable.
|
||||
|
||||
```task
|
||||
id: FLEX-WP-0002-T008
|
||||
status: todo
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "6cbe572a-2877-4936-8ef3-63b79900fae2"
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user