partly or unimplemented features - now implemented
This commit is contained in:
283
docs/modules/policy/dsl-grammar-specification.md
Normal file
283
docs/modules/policy/dsl-grammar-specification.md
Normal file
@@ -0,0 +1,283 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user