14 KiB
14 KiB
Policy Studio API and Rule Syntax
Sprint: SPRINT_20251226_017_AI_policy_copilot Task: POLICY-26
This guide documents the Policy Studio API for AI-powered policy authoring, converting natural language to lattice rules.
Overview
Policy Studio enables:
- Natural Language → Policy Intent: Parse human intent from plain English
- Intent → Lattice Rules: Generate K4 lattice-compatible rules
- Validation: Detect conflicts, unreachable conditions, loops
- Test Synthesis: Auto-generate test cases for policy validation
- Compilation: Bundle rules into signed, versioned policy packages
API Endpoints
Parse Natural Language
Convert natural language to structured policy intent.
POST /api/v1/policy/studio/parse
Content-Type: application/json
{
"input": "Block all critical vulnerabilities in production services unless they have a vendor VEX stating not affected",
"scope": "production"
}
Response:
{
"intent": {
"intentId": "intent-20251226-001",
"intentType": "OverrideRule",
"originalInput": "Block all critical vulnerabilities in production services unless they have a vendor VEX stating not affected",
"conditions": [
{
"field": "severity",
"operator": "equals",
"value": "critical",
"connector": "and"
},
{
"field": "scope",
"operator": "equals",
"value": "production",
"connector": "and"
},
{
"field": "has_vex",
"operator": "equals",
"value": false,
"connector": null
}
],
"actions": [
{
"actionType": "set_verdict",
"parameters": {
"verdict": "block",
"reason": "Critical vulnerability without VEX exception"
}
}
],
"scope": "production",
"scopeId": null,
"priority": 100,
"confidence": 0.92,
"alternatives": null,
"clarifyingQuestions": null
},
"success": true,
"modelId": "claude-sonnet-4-20250514",
"parsedAt": "2025-12-26T10:30:00Z"
}
Clarifying Questions
When intent is ambiguous, the API returns clarifying questions:
{
"intent": {
"intentId": "intent-20251226-002",
"intentType": "ThresholdRule",
"confidence": 0.65,
"clarifyingQuestions": [
"Should this rule apply to all environments or just production?",
"What should happen when the threshold is exceeded: block or escalate?"
],
"alternatives": [
{ "...alternative interpretation 1..." },
{ "...alternative interpretation 2..." }
]
},
"success": true
}
Generate Rules
Convert policy intent to K4 lattice rules.
POST /api/v1/policy/studio/generate
Content-Type: application/json
{
"intentId": "intent-20251226-001"
}
Response:
{
"rules": [
{
"ruleId": "rule-20251226-001",
"name": "block-critical-no-vex",
"description": "Block critical vulnerabilities in production without VEX exception",
"latticeExpression": "Present ∧ ¬Mitigated ∧ severity=critical ∧ scope=production → Block",
"conditions": [
{ "field": "severity", "operator": "equals", "value": "critical" },
{ "field": "scope", "operator": "equals", "value": "production" },
{ "field": "has_vex", "operator": "equals", "value": false }
],
"disposition": "Block",
"priority": 100,
"scope": "production",
"enabled": true
}
],
"success": true,
"warnings": [],
"intentId": "intent-20251226-001",
"generatedAt": "2025-12-26T10:30:00Z"
}
Validate Rules
Check rules for conflicts and issues.
POST /api/v1/policy/studio/validate
Content-Type: application/json
{
"rules": [
{ "ruleId": "rule-20251226-001", "..." },
{ "ruleId": "rule-20251226-002", "..." }
],
"existingRuleIds": ["rule-existing-001", "rule-existing-002"]
}
Response:
{
"valid": false,
"conflicts": [
{
"ruleId1": "rule-20251226-001",
"ruleId2": "rule-existing-002",
"description": "Both rules match critical vulnerabilities but produce different dispositions (Block vs Allow)",
"suggestedResolution": "Add priority ordering or more specific conditions to disambiguate",
"severity": "error"
}
],
"unreachableConditions": [
"Rule rule-20251226-002 condition 'severity=low AND severity=high' is always false"
],
"potentialLoops": [],
"coverage": 0.85
}
Compile Policy Bundle
Bundle validated rules into a signed policy package.
POST /api/v1/policy/studio/compile
Content-Type: application/json
{
"rules": [
{ "ruleId": "rule-20251226-001", "..." }
],
"bundleName": "production-security-policy",
"version": "1.0.0",
"sign": true
}
Response:
{
"bundleId": "bundle-20251226-001",
"bundleName": "production-security-policy",
"version": "1.0.0",
"ruleCount": 5,
"digest": "sha256:bundledigest...",
"signed": true,
"signatureKeyId": "stellaops-policy-signer-2025",
"compiledAt": "2025-12-26T10:30:00Z",
"downloadUrl": "/api/v1/policy/bundle/bundle-20251226-001"
}
Policy Intent Types
| Type | Description | Example |
|---|---|---|
OverrideRule |
Override default verdict | "Block all critical CVEs" |
EscalationRule |
Escalate findings | "Escalate CVSS ≥9.0 to security team" |
ExceptionCondition |
Bypass rules | "Except internal-only services" |
MergePrecedence |
Priority ordering | "VEX takes precedence over CVSS" |
ThresholdRule |
Automatic thresholds | "Allow max 10 high-severity per service" |
ScopeRestriction |
Scope limits | "Only apply to production" |
Rule Syntax
Lattice Expression Format
Rules use K4 lattice logic:
<atoms> → <disposition>
Security Atoms
| Atom | Meaning |
|---|---|
Present |
Vulnerability is present in artifact |
Applies |
Vulnerability applies to this context |
Reachable |
Vulnerable code is reachable |
Mitigated |
Mitigation exists (VEX, WAF, etc.) |
Fixed |
Fix is available |
Misattributed |
False positive |
Operators
| Operator | Symbol | Example |
|---|---|---|
| AND | ∧ |
Present ∧ Reachable |
| OR | ∨ |
Fixed ∨ Mitigated |
| NOT | ¬ |
¬Mitigated |
| Implies | → |
Present → Block |
Dispositions
| Disposition | Meaning |
|---|---|
Block |
Fail the build/gate |
Warn |
Warning only |
Allow |
Pass with no action |
Review |
Require human review |
Escalate |
Escalate to security team |
Examples
# Block critical unmitigated vulnerabilities
Present ∧ Reachable ∧ ¬Mitigated ∧ severity=critical → Block
# Allow if vendor says not affected
Present ∧ Mitigated ∧ vex_status=not_affected → Allow
# Escalate CVSS ≥9.0
Present ∧ cvss_score>=9.0 → Escalate
# Warn on high severity with fix available
Present ∧ severity=high ∧ Fixed → Warn
Condition Fields
| Field | Type | Values |
|---|---|---|
severity |
string | critical, high, medium, low, none |
cvss_score |
number | 0.0 - 10.0 |
reachable |
boolean | true, false |
has_vex |
boolean | true, false |
vex_status |
string | not_affected, affected, fixed, under_investigation |
has_fix |
boolean | true, false |
fix_version |
string | Version string |
scope |
string | production, staging, development |
age_days |
number | Days since disclosure |
exploit_available |
boolean | true, false |
in_kev |
boolean | In CISA KEV catalog |
Condition Operators
| Operator | Description | Example |
|---|---|---|
equals |
Exact match | severity equals critical |
not_equals |
Not equal | scope not_equals development |
greater_than |
Greater than | cvss_score greater_than 7.0 |
less_than |
Less than | age_days less_than 30 |
greater_or_equal |
≥ | cvss_score greater_or_equal 9.0 |
less_or_equal |
≤ | cvss_score less_or_equal 3.9 |
contains |
String contains | component contains lodash |
in |
In list | severity in [critical, high] |
not_in |
Not in list | scope not_in [development, test] |
Test Case Format
Generated Test Cases
Policy Studio auto-generates test cases:
{
"testCases": [
{
"testId": "test-001",
"type": "positive",
"description": "Critical unmitigated vulnerability should be blocked",
"input": {
"severity": "critical",
"reachable": true,
"has_vex": false,
"scope": "production"
},
"expectedDisposition": "Block",
"matchedRuleId": "rule-20251226-001"
},
{
"testId": "test-002",
"type": "negative",
"description": "Critical vulnerability with VEX should not match block rule",
"input": {
"severity": "critical",
"reachable": true,
"has_vex": true,
"vex_status": "not_affected",
"scope": "production"
},
"expectedDisposition": "Allow",
"shouldNotMatch": "rule-20251226-001"
},
{
"testId": "test-003",
"type": "boundary",
"description": "CVSS exactly at threshold",
"input": {
"cvss_score": 9.0,
"severity": "critical"
},
"expectedDisposition": "Escalate"
},
{
"testId": "test-004",
"type": "conflict",
"description": "Input matching multiple conflicting rules",
"input": {
"severity": "high",
"reachable": true,
"has_fix": true
},
"possibleDispositions": ["Warn", "Block"],
"conflictingRules": ["rule-001", "rule-002"]
}
]
}
Test Types
| Type | Purpose | Auto-Generated |
|---|---|---|
positive |
Should match rule and produce expected disposition | Yes |
negative |
Should NOT match rule (boundary conditions) | Yes |
boundary |
Edge cases at thresholds | Yes |
conflict |
Triggers multiple rules | Yes |
manual |
User-defined custom cases | No |
Natural Language Examples
Override Rules
Input: "Block all critical vulnerabilities"
→ Present ∧ severity=critical → Block
Input: "Allow vulnerabilities with VEX not_affected status"
→ Present ∧ vex_status=not_affected → Allow
Input: "Block exploitable vulnerabilities older than 30 days"
→ Present ∧ exploit_available=true ∧ age_days>30 → Block
Escalation Rules
Input: "Escalate anything in the KEV catalog to security team"
→ Present ∧ in_kev=true → Escalate
Input: "Escalate CVSS 9.0 or above"
→ Present ∧ cvss_score>=9.0 → Escalate
Exception Conditions
Input: "Except for development environments"
→ Adds: ∧ scope!=development to existing rules
Input: "Unless there's a VEX from the vendor"
→ Adds: ∧ ¬(has_vex=true ∧ vex_status=not_affected)
Threshold Rules
Input: "Allow maximum 5 high-severity vulnerabilities per service"
→ Creates threshold counter with Block when exceeded
CLI Commands
# Parse natural language
stella policy parse "Block all critical CVEs in production"
# Generate rules from intent
stella policy generate intent-20251226-001
# Validate rules
stella policy validate rules.yaml
# Run test cases
stella policy test rules.yaml --cases tests.yaml
# Compile bundle
stella policy compile rules.yaml \
--name production-policy \
--version 1.0.0 \
--sign
# Apply policy
stella policy apply bundle-20251226-001.tar.gz
Configuration
policyStudio:
# Maximum conditions per rule
maxConditionsPerRule: 10
# Auto-generate test cases
autoGenerateTests: true
# Test case types to generate
testTypes:
- positive
- negative
- boundary
- conflict
# Minimum test coverage
minTestCoverage: 0.80
# Require human approval for production policies
requireApproval:
production: true
staging: false
development: false
# Number of approvers required
requiredApprovers: 2
# Sign compiled bundles
signBundles: true
Policy Bundle Format
Compiled policy bundles are tar.gz archives:
production-policy-1.0.0.tar.gz
├── manifest.json # Bundle metadata
├── rules/
│ ├── rule-001.yaml # Individual rule files
│ ├── rule-002.yaml
│ └── ...
├── tests/
│ ├── test-001.yaml # Test cases
│ └── ...
├── signature.dsse.json # DSSE signature
└── checksums.sha256 # File checksums
Manifest Schema
{
"bundleId": "bundle-20251226-001",
"bundleName": "production-security-policy",
"version": "1.0.0",
"createdAt": "2025-12-26T10:30:00Z",
"createdBy": "policy-studio",
"rules": [
{
"ruleId": "rule-001",
"name": "block-critical",
"file": "rules/rule-001.yaml"
}
],
"testCount": 15,
"coverage": 0.92,
"signed": true,
"signatureKeyId": "stellaops-policy-signer-2025"
}
Attestation Format
Policy drafts are attested using DSSE:
{
"_type": "https://stellaops.org/attestation/policy-draft/v1",
"bundleId": "bundle-20251226-001",
"bundleName": "production-security-policy",
"version": "1.0.0",
"authority": "Validated",
"rules": {
"count": 5,
"ruleIds": ["rule-001", "rule-002", "..."]
},
"validation": {
"valid": true,
"conflictCount": 0,
"testsPassed": 15,
"coverage": 0.92
},
"model": {
"modelId": "claude-sonnet-4-20250514",
"parseConfidence": 0.95
},
"createdAt": "2025-12-26T10:30:00Z"
}
Error Handling
Parse Errors
{
"success": false,
"error": "ambiguous_intent",
"message": "Could not determine whether 'block' means verdict or action",
"suggestions": [
"Try: 'Set verdict to block for critical vulnerabilities'",
"Try: 'Fail the build for critical vulnerabilities'"
]
}
Validation Errors
{
"valid": false,
"conflicts": [
{
"severity": "error",
"description": "Rule A and Rule B have contradicting dispositions for the same conditions"
}
]
}
Compilation Errors
{
"success": false,
"error": "compilation_failed",
"message": "Cannot compile bundle with unresolved conflicts",
"unresolvedConflicts": 2
}