Files
regorus/docs/knowledge/policy-evaluation-security.md
copilot-swe-agent[bot] 524aab5528 Add knowledge docs, agent definitions, and skill files
Add comprehensive documentation and GitHub Copilot configuration:

- docs/knowledge/: 17 deep-dive knowledge files covering value semantics,
  RVM architecture, builtins, FFI boundary, feature composition, error
  handling migration, policy evaluation security, Rego semantics,
  interpreter/compiler architecture, Azure Policy/RBAC, engine API,
  time builtins, language extension guide, tooling architecture,
  causality/partial eval, Rego compiler, Azure Policy aliases, and
  telemetry/diagnostics

- .github/agents/: 16 role-specific AI agent definitions (red-teamer,
  semantics-expert, architect, performance-engineer, test-engineer,
  verification-engineer, security-auditor, reliability-engineer,
  support-engineer, ci-engineer, refactorer, api-steward, program-manager,
  demo-engineer, dx-engineer, tech-lead)

- .github/skills/: 6 workflow skill definitions (thorough-review,
  design-alternatives, add-builtin, opa-conformance, security-review,
  verification)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Co-authored-by: anakrish <35780660+anakrish@users.noreply.github.com>
2026-04-25 21:20:10 +00:00

200 lines
7.3 KiB
Markdown

<!-- Copyright (c) Microsoft Corporation. All rights reserved. -->
<!-- Licensed under the MIT License. -->
# Knowledge: Policy Evaluation Security
Deep knowledge about security properties, DoS protection, resource limits,
and input validation in regorus. Read this before modifying evaluation paths,
parsers, or resource management.
## Threat Model
Regorus evaluates **untrusted policy code** against **untrusted data**. Both
may be adversarial. The engine must:
1. **Always terminate** — no infinite loops, no unbounded recursion
2. **Bound resource usage** — memory, CPU time, instruction count
3. **Return correct results** — a wrong result is a security vulnerability
4. **Never crash** — panics in daemon mode crash the service
5. **Not leak information** — error messages must not expose sensitive data
## Resource Limit Enforcement
### Instruction Budget (RVM)
The primary defense against computation-based DoS:
- **Default**: 25,000 instructions (`src/rvm/vm/machine.rs`)
- **Enforcement**: checked every iteration in the execution loop
- **Error**: `VmError::InstructionLimitExceeded`
- **Configurable**: `set_max_instructions(limit)`
### Execution Time Limits
Wall-clock enforcement via `ExecutionTimer` (`src/utils/limits/time.rs`):
- **Cooperative checking** — the timer is checked periodically, not preemptively
- **Amortized overhead** — accumulates work units before reading the clock
to avoid syscall overhead
- **Suspended time excluded** — `resume_from_elapsed()` preserves elapsed time
across VM suspensions, so only active computation counts
- **Per-instance override** — each VM can set its own timer config
- **Error**: `VmError::TimeLimitExceeded`
### Memory Limits
Global memory tracking via `src/utils/limits/memory.rs`:
- **Global atomic limit** — `GLOBAL_MEMORY_LIMIT: AtomicU64`
- **Throttled checking** — dual strategy to avoid contention:
- Stride-based: check every 16 iterations
- Delta-based: check when 32 KiB has been allocated since last check
- **Per-thread flushing** — auto-flush at 1 MiB threshold
- **Enforcement points**: Value construction, deserialization, parsing
- **Error**: `VmError::MemoryLimitExceeded`
The `allocator-memory-limits` feature uses mimalloc to enforce at the allocator
level.
## Input Validation
### Policy Source (`src/lexer.rs`)
Rego source is validated during lexing with configurable limits:
| Limit | Default | Purpose |
|-------|---------|---------|
| `max_col` | 1,024 chars | Lines exceeding this are likely minified/attack code |
| `max_file_bytes` | 1 MiB | Prevents memory exhaustion from huge files |
| `max_lines` | 20,000 | Prevents excessive parsing time |
Memory limit is also checked after each logical chunk during lexing.
### Parser Depth
The parser enforces expression nesting depth:
- **Default**: `MAX_EXPR_DEPTH = 32` (`src/parser.rs`)
- Prevents stack overflow from deeply nested expressions like `(((((...)))))`
- Returns error, not panic
### JSON/YAML Data
Data added via `add_data()` must be an object (checked by `engine.rs`).
Value construction during deserialization checks memory limits at each node.
### RVM Programs
Compiled programs validated by `validate_limits()` (`src/rvm/program/core.rs`):
| Resource | Limit |
|----------|-------|
| Instructions | 65,535 |
| Literals | 65,535 |
| Rules | 4,000 |
| Entry points | 1,000 |
| Source files | 256 |
| Builtins | 512 |
| Path depth | 32 |
These prevent adversarial serialized programs from consuming excessive resources
during deserialization or execution.
## Recursion Protection
- **Parser**: `MAX_EXPR_DEPTH = 32` for expression nesting
- **RVM**: `MAX_PATH_DEPTH = 32` for rule path depth
- **Virtual documents**: `needs_runtime_recursion_check` flag enables detection
when `VirtualDataDocumentLookup` instructions are present
- **Rule evaluation**: processed rules tracked in `self.processed` set to
prevent re-evaluation cycles
## DoS via Regular Expressions
Regorus uses the `regex` crate which compiles to a DFA — **no catastrophic
backtracking**. Protection is layered:
1. DFA-based regex engine (no exponential blowup)
2. Instruction budget limits total work
3. Execution time limits bound wall-clock
4. LRU cache prevents repeated compilation (256 patterns, hard cap 2^16)
## Undefined vs False
**This is a security-critical distinction.** In policy evaluation:
```rego
allow { input.role == "admin" }
```
If `input.role` is missing:
- `input.role == "admin"``Undefined` (not `false`)
- `allow``Undefined` (rule body didn't succeed)
- `not allow``true` (because `not Undefined = true`)
A bug that treats `Undefined` as `false` (or vice versa) can change policy
decisions. Every evaluation path must handle the three-valued logic correctly.
See `docs/knowledge/value-semantics.md` for detailed Undefined propagation rules.
## Supply Chain Security
### Dependency Auditing
The `dependency-audit.yml` workflow runs:
- **cargo-audit**: checks 6 Cargo.lock files (main + 5 bindings) against
RustSec advisories
- **cargo-deny**: checks 9 manifests for CVEs (advisories) and problematic
dependencies (bans)
- **Schedule**: PRs, main pushes, weekly (Mondays 6 AM), manual dispatch
### Dependency Management
- **Pinned action SHAs**: all GitHub Actions references use full commit SHAs,
not mutable tags — prevents supply chain attacks via tag mutation
- **Locked dependencies**: `Cargo.lock` committed, `cargo fetch --locked` /
`--frozen` in CI ensures reproducible builds
- **Dependabot**: automated weekly updates for Cargo, GitHub Actions, Maven,
NuGet, pip, npm, bundler, Go
- **Minimal dependency surface**: prefer `core`/`alloc` over external crates
### Spectre Mitigation
On Windows (MSVC), the optional `msvc_spectre_libs` dependency links with
Spectre-mitigated CRT and libraries.
## Panic Safety
The 80+ deny lints in `src/lib.rs` exist not just for style — they prevent
panics at compile time:
| Denied | Why |
|--------|-----|
| `clippy::unwrap_used` | `.unwrap()` panics on `None`/`Err` |
| `clippy::expect_used` | `.expect()` panics on `None`/`Err` |
| `clippy::indexing_slicing` | `vec[i]` panics on out-of-bounds |
| `clippy::arithmetic_side_effects` | `a + b` can overflow and panic |
| `clippy::panic` | Explicit `panic!()` |
| `clippy::unreachable` | Explicit `unreachable!()` |
| `clippy::todo` | Explicit `todo!()` |
In daemon mode, **any panic is a service crash**. The deny lints are the first
line of defense. The FFI layer's `with_unwind_guard()` is the second — it
catches panics and poisons the engine (see `docs/knowledge/ffi-boundary.md`).
But panic containment is a last resort. The goal is zero panics in all code
paths, including error paths, resource exhaustion, and adversarial input.
## Security Review Checklist
When reviewing code for security:
1. **Undefined handling** — does the code correctly distinguish Undefined from false?
2. **Resource limits** — does new code respect instruction budget, time, memory?
3. **Input validation** — is untrusted input validated before use?
4. **Panic paths** — can any code path panic (overflow, indexing, unwrap)?
5. **Error messages** — do errors avoid leaking policy content or data?
6. **Recursion** — is recursion bounded?
7. **Allocation** — can adversarial input cause unbounded allocation?
8. **Cache behavior** — can cache be poisoned or exhausted?