284 lines
6.3 KiB
Markdown
284 lines
6.3 KiB
Markdown
# Stella Policy DSL Grammar Specification
|
|
|
|
**Version**: stella-dsl@1.0
|
|
**Status**: Implemented
|
|
**Last Updated**: 2026-02-15
|
|
|
|
## Overview
|
|
|
|
The Stella Policy DSL is a domain-specific language for defining release policies that control software deployment decisions. Policies are evaluated against signal contexts to produce deterministic verdicts.
|
|
|
|
## File Extension
|
|
|
|
Policy files use the `.stella` extension.
|
|
|
|
## Lexical Structure
|
|
|
|
### Comments
|
|
|
|
```
|
|
// Single-line comment
|
|
|
|
/*
|
|
* Multi-line comment
|
|
*/
|
|
```
|
|
|
|
### Keywords
|
|
|
|
Reserved keywords (case-sensitive):
|
|
|
|
| Keyword | Description |
|
|
|---------|-------------|
|
|
| `policy` | Policy declaration |
|
|
| `syntax` | Syntax version declaration |
|
|
| `metadata` | Policy metadata block |
|
|
| `settings` | Policy settings block |
|
|
| `profile` | Profile declaration |
|
|
| `rule` | Rule declaration |
|
|
| `when` | Rule condition |
|
|
| `then` | Rule action (condition true) |
|
|
| `else` | Rule action (condition false) |
|
|
| `and` | Logical AND |
|
|
| `or` | Logical OR |
|
|
| `not` | Logical NOT |
|
|
| `true` | Boolean true |
|
|
| `false` | Boolean false |
|
|
| `null` | Null literal |
|
|
| `in` | Membership operator |
|
|
| `map` | Map literal |
|
|
| `env` | Environment binding |
|
|
|
|
### Operators
|
|
|
|
| Operator | Description |
|
|
|----------|-------------|
|
|
| `==` | Equality |
|
|
| `!=` | Inequality |
|
|
| `<` | Less than |
|
|
| `<=` | Less than or equal |
|
|
| `>` | Greater than |
|
|
| `>=` | Greater than or equal |
|
|
| `:=` | Definition |
|
|
| `=>` | Arrow (lambda/map) |
|
|
| `.` | Member access |
|
|
| `,` | Separator |
|
|
| `:` | Key-value separator |
|
|
| `=` | Assignment |
|
|
|
|
### Literals
|
|
|
|
#### Strings
|
|
```
|
|
"hello world"
|
|
"escaped \"quotes\""
|
|
```
|
|
|
|
#### Numbers
|
|
```
|
|
42
|
|
3.14
|
|
-1
|
|
0.5
|
|
```
|
|
|
|
#### Booleans
|
|
```
|
|
true
|
|
false
|
|
```
|
|
|
|
#### Arrays
|
|
```
|
|
[1, 2, 3]
|
|
["a", "b", "c"]
|
|
```
|
|
|
|
### Identifiers
|
|
|
|
Identifiers start with a letter or underscore, followed by letters, digits, or underscores:
|
|
|
|
```
|
|
identifier
|
|
_private
|
|
signal_name
|
|
cvss_score
|
|
```
|
|
|
|
## Grammar (EBNF)
|
|
|
|
```ebnf
|
|
document = policy-header "{" body "}" ;
|
|
|
|
policy-header = "policy" string-literal "syntax" string-literal ;
|
|
|
|
body = { metadata-block | settings-block | profile | rule } ;
|
|
|
|
metadata-block = "metadata" "{" { key-value } "}" ;
|
|
|
|
settings-block = "settings" "{" { key-value } "}" ;
|
|
|
|
key-value = identifier ":" literal ;
|
|
|
|
profile = "profile" identifier "{" { profile-item } "}" ;
|
|
|
|
profile-item = map-item | env-item | scalar-item ;
|
|
|
|
map-item = "map" identifier "=>" expression ;
|
|
|
|
env-item = "env" identifier "=>" string-literal ;
|
|
|
|
scalar-item = identifier ":=" expression ;
|
|
|
|
rule = "rule" identifier [ "(" priority ")" ] "{" rule-body "}" ;
|
|
|
|
priority = number-literal ;
|
|
|
|
rule-body = when-clause then-clause [ else-clause ] ;
|
|
|
|
when-clause = "when" expression ;
|
|
|
|
then-clause = "then" "{" { action } "}" ;
|
|
|
|
else-clause = "else" "{" { action } "}" ;
|
|
|
|
action = action-name [ "(" { argument } ")" ] ;
|
|
|
|
action-name = identifier ;
|
|
|
|
argument = expression | key-value ;
|
|
|
|
expression = or-expression ;
|
|
|
|
or-expression = and-expression { "or" and-expression } ;
|
|
|
|
and-expression = unary-expression { "and" unary-expression } ;
|
|
|
|
unary-expression = [ "not" ] primary-expression ;
|
|
|
|
primary-expression = literal
|
|
| identifier
|
|
| member-access
|
|
| "(" expression ")"
|
|
| comparison ;
|
|
|
|
comparison = primary-expression comparison-op primary-expression ;
|
|
|
|
comparison-op = "==" | "!=" | "<" | "<=" | ">" | ">=" | "in" ;
|
|
|
|
member-access = identifier { "." identifier } ;
|
|
|
|
literal = string-literal
|
|
| number-literal
|
|
| boolean-literal
|
|
| array-literal
|
|
| null-literal ;
|
|
|
|
string-literal = '"' { character } '"' ;
|
|
|
|
number-literal = [ "-" | "+" ] digit { digit } [ "." digit { digit } ] ;
|
|
|
|
boolean-literal = "true" | "false" ;
|
|
|
|
array-literal = "[" [ expression { "," expression } ] "]" ;
|
|
|
|
null-literal = "null" ;
|
|
```
|
|
|
|
## Example Policy
|
|
|
|
```stella
|
|
policy "Production Release Policy" syntax "stella-dsl@1" {
|
|
metadata {
|
|
author: "security-team@example.com"
|
|
version: "1.2.0"
|
|
description: "Governs production releases"
|
|
}
|
|
|
|
settings {
|
|
default_action: "block"
|
|
audit_mode: false
|
|
}
|
|
|
|
profile production {
|
|
env target => "prod"
|
|
map severity_threshold := 7.0
|
|
}
|
|
|
|
rule critical_cve_block (100) {
|
|
when cvss.score >= 9.0 and cve.reachable == true
|
|
then {
|
|
block("Critical CVE is reachable")
|
|
notify("security-oncall")
|
|
}
|
|
}
|
|
|
|
rule high_cve_warn (50) {
|
|
when cvss.score >= 7.0 and cvss.score < 9.0
|
|
then {
|
|
warn("High severity CVE detected")
|
|
}
|
|
else {
|
|
allow()
|
|
}
|
|
}
|
|
|
|
rule sbom_required (80) {
|
|
when not sbom.present
|
|
then {
|
|
block("SBOM attestation required")
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Signal Context
|
|
|
|
Policies are evaluated against a signal context containing runtime values:
|
|
|
|
| Signal | Type | Description |
|
|
|--------|------|-------------|
|
|
| `cvss.score` | number | CVSS score of vulnerability |
|
|
| `cve.reachable` | boolean | Whether CVE is reachable |
|
|
| `cve.id` | string | CVE identifier |
|
|
| `sbom.present` | boolean | SBOM attestation exists |
|
|
| `sbom.format` | string | SBOM format (cyclonedx, spdx) |
|
|
| `artifact.digest` | string | Artifact content digest |
|
|
| `artifact.tag` | string | Container image tag |
|
|
| `environment` | string | Target environment |
|
|
| `attestation.signed` | boolean | Has signed attestation |
|
|
|
|
## Compilation
|
|
|
|
The DSL compiles to a content-addressed IR (Intermediate Representation):
|
|
|
|
1. **Tokenize**: Source → Token stream
|
|
2. **Parse**: Tokens → AST
|
|
3. **Compile**: AST → PolicyIrDocument
|
|
4. **Serialize**: IR → Canonical JSON
|
|
5. **Hash**: JSON → SHA-256 checksum
|
|
|
|
The checksum provides deterministic policy identity for audit and replay.
|
|
|
|
## CLI Commands
|
|
|
|
```bash
|
|
# Lint a policy file
|
|
stella policy lint policy.stella
|
|
|
|
# Compile to IR JSON
|
|
stella policy compile policy.stella --output policy.ir.json
|
|
|
|
# Get deterministic checksum
|
|
stella policy compile policy.stella --checksum-only
|
|
|
|
# Simulate with signals
|
|
stella policy simulate policy.stella --signals context.json
|
|
```
|
|
|
|
## See Also
|
|
|
|
- [Policy Module Architecture](architecture.md)
|
|
- [PolicyDsl Implementation](../../../src/Policy/StellaOps.PolicyDsl/)
|
|
- [Signal Context Reference](signal-context-reference.md)
|