feat: RFC-008 capability class support (ALLOW path)#65
Conversation
- Add CapabilityClassRule struct to policy config with dot-notation class names - Add config validation for class names, trust levels, DID conflicts, duplicates - Add DenyOnUnknownClass field to PIP DecisionRequest - Wire capability_class and deny_on_unknown_class through buildOPAInput - Add proto field 15 (optional bool deny_on_unknown_class) to EvaluateToolAccessRequest - Wire gRPC handler to pass capability_class and deny_on_unknown_class to EvaluateToolAccessInput - Add CapabilityClass and DenyOnUnknownClass fields to mcp.EvaluateToolAccessInput - Add comprehensive tests for config parsing, validation (14+ subtests), and ToMap serialization Part of: RFC-008 §5.5 capability class enforcement
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Pull request overview
Adds RFC-008 “capability class” support plumbing (ALLOW path) across policy config, PIP/PDP input, and the MCP gRPC surface so callers can scope policy by capability class and control “unknown class” behavior.
Changes:
- Extend policy config schema/validation to support
capability_classesrules and serialize them into the OPA data document. - Add
deny_on_unknown_classto PIPDecisionRequestand wire it into OPA input construction. - Extend MCP proto + server handler + service input types to carry
deny_on_unknown_class(and capability class).
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| proto/capiscio/v1/mcp.proto | Adds optional bool deny_on_unknown_class = 15 to the MCP EvaluateToolAccess request. |
| pkg/rpc/gen/capiscio/v1/mcp.pb.go | Regenerated Go bindings for the proto change. |
| pkg/policy/config.go | Adds CapabilityClassRule, validation (regex, trust levels, DID conflicts), and capability_classes serialization in ToMap. |
| pkg/policy/config_test.go | Adds parsing/validation/serialization tests for capability classes. |
| pkg/pip/types.go | Adds DenyOnUnknownClass *bool to DecisionRequest. |
| pkg/pdp/opa_client.go | Includes deny_on_unknown_class in OPA input when present. |
| pkg/mcp/service.go | Extends EvaluateToolAccessInput with CapabilityClass and DenyOnUnknownClass. |
| internal/rpc/mcp_service.go | Wires request fields into EvaluateToolAccessInput. |
Comments suppressed due to low confidence (1)
pkg/mcp/service.go:58
- EvaluateToolAccessInput now carries CapabilityClass and DenyOnUnknownClass, but Service.EvaluateToolAccess ignores both when calling guard.EvaluateToolAccess (it only forwards toolName/paramsHash/origin/credential/config). This means the new RFC-008 fields never affect PDP input / policy decisions. Consider extending the guard/service evaluation path to propagate these fields into the PIP DecisionRequest (e.g., Action.CapabilityClass and top-level deny_on_unknown_class) so the ALLOW-path behavior is actually enforced.
// EvaluateToolAccess evaluates tool access using RFC-006 §6.2-6.4
func (s *Service) EvaluateToolAccess(
ctx context.Context,
input *EvaluateToolAccessInput,
) (*EvaluateResult, error) {
return s.guard.EvaluateToolAccess(
ctx,
input.ToolName,
input.ParamsHash,
input.Origin,
input.Credential,
input.Config,
)
}
| // capabilityClassPattern validates RFC-008 §7.1 dot-notation class names. | ||
| // Allowed characters: lowercase letters, digits, hyphens, and dots as separators. | ||
| var capabilityClassPattern = regexp.MustCompile(`^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$`) |
| } | ||
| if ccSeen[cc.Class] { | ||
| errs = append(errs, fmt.Sprintf("capability_classes[%d]: duplicate class %q", i, cc.Class)) | ||
| } | ||
| ccSeen[cc.Class] = true |
| string constraints_json = 13; // reserved for envelope | ||
| string parent_constraints_json = 14; // reserved for envelope | ||
|
|
||
| // RFC-008: PEP-level unknown capability class behavior (default: deny) |
- Reuse envelope.ValidateCapabilityClass() instead of local regex (fixes regex mismatch: policy allowed hyphens, envelope allowed underscores) - Extract validateCapabilityClasses() helper to reduce cyclomatic complexity - Fix duplicate-class detection to skip empty/invalid class names - Clarify proto deny_on_unknown_class comment re: proto3 bool defaults - Update tests to use RFC-compliant names (underscores, not hyphens)
There was a problem hiding this comment.
Pull request overview
Adds RFC-008 capability class support plumbing across policy config, MCP RPC/API surfaces, and PDP/OPA input to enable “ALLOW path” capability-class-aware authorization decisions.
Changes:
- Extend policy config schema with
capability_classesrules + validation andToMapserialization. - Add
deny_on_unknown_class(presence-aware) to MCP proto and wire it through MCP RPC types. - Add
DenyOnUnknownClassto PIPDecisionRequestand include it in OPA input construction.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| proto/capiscio/v1/mcp.proto | Adds optional bool deny_on_unknown_class = 15 with documented fail-closed intent. |
| pkg/rpc/gen/capiscio/v1/mcp.pb.go | Regenerated Go bindings to include presence-aware DenyOnUnknownClass *bool. |
| pkg/policy/config.go | Introduces CapabilityClassRule, validates class names via envelope.ValidateCapabilityClass, serializes to OPA data map. |
| pkg/policy/config_test.go | Adds parsing/validation/serialization tests for capability classes. |
| pkg/pip/types.go | Extends PDP request model with DenyOnUnknownClass *bool. |
| pkg/pdp/opa_client.go | Adds deny_on_unknown_class to OPA input when explicitly set. |
| pkg/mcp/service.go | Extends MCP service input type with CapabilityClass and DenyOnUnknownClass. |
| internal/rpc/mcp_service.go | Maps proto request fields into MCP service input. |
| CapabilityClass string // RFC-008: capability class for policy evaluation | ||
| DenyOnUnknownClass *bool // RFC-008: PEP-level unknown class behavior (nil = deny) | ||
| } |
| Credential: cred, | ||
| Config: config, | ||
| CapabilityClass: req.CapabilityClass, | ||
| DenyOnUnknownClass: req.DenyOnUnknownClass, | ||
| } |
| // RFC-008: PEP-level flag for unknown capability class behavior | ||
| if req.DenyOnUnknownClass != nil { | ||
| input["deny_on_unknown_class"] = *req.DenyOnUnknownClass | ||
| } |
| // Proto3 default for bool is false, but the server treats an unset field | ||
| // as "deny" (fail-closed). Set explicitly to false to allow unknown classes | ||
| // (e.g. in development/staging environments). |
Summary
Adds RFC-008 §5.5 capability class support to capiscio-core (ALLOW path).
Changes
pkg/policy/config.go): AddCapabilityClassRulestruct withClass,MinTrustLevel,AllowedDIDs,DeniedDIDsfields. Dot-notation class name validation via regex.pkg/pip/types.go): AddDenyOnUnknownClass *booltoDecisionRequest.pkg/pdp/opa_client.go): WireDenyOnUnknownClassinto OPA input.proto/capiscio/v1/mcp.proto): Add field 15optional bool deny_on_unknown_classtoEvaluateToolAccessRequest.internal/rpc/mcp_service.go): WireCapabilityClassandDenyOnUnknownClassintoEvaluateToolAccessInput.pkg/mcp/service.go): ExtendEvaluateToolAccessInputwithCapabilityClassandDenyOnUnknownClass.Linked PRs
feat/rfc008-capability-class-allow-pathfeat/rfc008-capability-class-allow-pathfeat/rfc008-capability-class-allow-pathTesting