Policy Reference
Lexega policies control what happens when signals fire. Policies don't define detection logic—that's handled by builtin rules and custom rules. Policies only define enforcement: block, warn, or allow.
Quick Start
Run lexega-sql init to generate a baseline policy:
lexega-sql init
This creates:
.lexega/policy.yml— Permissive baseline (warns on critical/high, nothing blocked).lexega/exceptions.yml— Scaffold for approved overrides.lexega/baseline.sarif— SARIF evidence snapshot
The default policy is permissive so you can see what signals exist before enabling blocking. To tighten, edit severity_actions and change warn to block.
Policy Structure
# .lexega/policy.yml
schema_version: 1
policy_id: my-org-policy
policy_version: "1.0.0"
# Explicit rule-based policies (highest priority)
policies:
- rule_id: DML-WRITE-UNBOUNDED
action: allow # Override: allow this specific rule
envs: ["dev"] # Only in dev environment
- rule_id: DIFF-WRITE-WHERE-RMV
action: block
description: "Block WHERE clause removals in prod"
# Severity-based fallback (checked after explicit policies)
severity_actions:
- critical: block
high: warn
# Final fallback for unmatched signals
default_action: allow
Evaluation Order
For each signal, Lexega evaluates in order:
- Explicit policies — Match by
rule_id - Severity actions — Match by signal severity level
- Default action — Catch-all fallback
First match wins. This layering lets you:
- Set broad severity-based defaults
- Override specific rules as needed
- Never enumerate every possible rule
Severity Actions
severity_actions catches signals by severity level without requiring you to know every rule ID:
severity_actions:
- critical: block # Block all critical signals
high: warn # Warn on high signals
medium: allow # Explicitly allow medium
# low/info: not specified → falls through to default_action
Scoped Severity Actions
You can scope severity actions to specific paths, environments, or other constraints:
severity_actions:
# Lenient for tests (first match wins)
- scope:
path_patterns: ["**/tests/**"]
critical: warn # Only warn in tests
# Lenient for dev environment
- scope:
envs: ["dev"]
critical: warn
high: allow
# Strict default (no scope = global fallback)
- critical: block
high: warn
Available scope constraints:
envs— Environment names (case-insensitive)path_patterns— Glob patterns for file paths (e.g.,**/tests/**)paths— Exact file pathspath_prefixes— Path prefixesrepos/repo_prefixes— Repository namesroles— Snowflake execution roles (detected fromUSE ROLEin SQL)expires_at— Expiration timestamp (UTC)
Explicit Policies
For fine-grained control over specific rules:
policies:
- rule_id: DML-WRITE-UNBOUNDED # Unbounded DELETE
action: block
description: "Never allow unbounded deletes"
- rule_id: Q-JOIN-LEFT-FILT # LEFT JOIN nullable filter
action: warn
envs: ["prod", "staging"] # Only in these environments
- rule_id: C050 # Encryption disabled
action: block
requires_exception: true # Must have exception to proceed
Policy Fields
| Field | Required | Description |
|---|---|---|
rule_id | ✅ | Rule ID to match (e.g., DML-WRITE-UNBOUNDED, DIFF-WRITE-WHERE-RMV) |
action | ✅ | block, warn, or allow |
description | Human-readable explanation | |
envs | Environment filter (e.g., ["prod"]) | |
severity_override | Override the rule's default severity | |
message_override | Override the rule's message | |
requires_exception | If true and blocking, exception is required |
Exceptions
Exceptions grant temporary or scoped overrides to blocking policies:
# .lexega/exceptions.yml
schema_version: 1
exceptions:
- exception_id: EXC-001
policy_id: my-org-policy
rule_id: DML-WRITE-UNBOUNDED
scope:
scoped: # Required wrapper for scoped exceptions
paths: ["migrations/legacy_cleanup.sql"]
expires_at: "2026-06-01T00:00:00Z"
reason: "Approved legacy cleanup - JIRA-1234"
approved_by: "security-team"
approved_at: "2026-01-15T12:00:00Z" # Required timestamp
ticket: "JIRA-1234" # Required ticket reference
For blanket exceptions (use with caution):
exceptions:
- exception_id: EXC-002
policy_id: my-org-policy
rule_id: TBL-RAP-ADD
scope:
global: # Explicit global scope
expires_at: "2026-12-31T23:59:59Z" # Strongly recommended for global
reason: "Informational signal, not actionable"
approved_by: "security-team"
approved_at: "2026-01-15T12:00:00Z"
ticket: "SEC-5678"
Exceptions are matched against the signal's context (path, env, etc.) and recorded in the decision artifact for audit.
Decision Artifacts
When using --decision-out, Lexega writes a JSON decision record:
lexega-sql analyze file.sql \
--policy policy.yml \
--env prod \
--decision-out decisions/$GITHUB_RUN_ID/
Tip: When writing to a long-lived directory or cloud storage prefix, include a unique run identifier (like $GITHUB_RUN_ID) so new runs don’t overwrite older decision/report artifacts.
The decision record includes:
- Input hashes (SQL, policy, exceptions)
- All matched rules and their actions
- Exceptions used
- Final outcome (allowed/blocked)
- Timestamps for audit
Environment Context
Pass environment context via --env:
lexega-sql analyze file.sql --policy policy.yml --env prod
Flags for Scoping
These flags affect policy/exception matching:
| Flag | Description | Can scope by |
|---|---|---|
--env | Environment name (required with --policy) | envs in policies/exceptions |
--repo | Repository name | repos, repo_prefixes |
File paths are automatically captured and matched against paths, path_prefixes, and path_patterns.
Flags for Audit Trail Only
These flags are recorded in decision artifacts but cannot be used for scoping:
| Flag | Description |
|---|---|
--team | Team identifier |
--change-id | CI build/change ID |
--commit | Commit SHA |
--job-type | Job type (ad_hoc, scheduled) |
These appear in the decision artifact's env_context for audit purposes.
CI Integration
When a policy blocks execution, the CLI exits with code 2:
lexega-sql analyze file.sql \
--policy policy.yml \
--env prod \
--decision-out decisions/
# Exit codes:
# 0 = allowed
# 1 = error
# 2 = blocked by policy
In CI environments, set LEXEGA_CI=1 to require policy evaluation (prevents accidentally skipping policy checks):
export LEXEGA_CI=1
# Now --policy is mandatory; without it, the CLI will exit with an error
lexega-sql analyze file.sql --policy policy.yml --env prod
Schema Validation
Add schema references for IDE support:
# yaml-language-server: $schema=https://lexega.com/schemas/v1/policy.schema.json
schema_version: 1
policy_id: my-policy
# ...
Available schemas:
policy.schema.json— PolicyBundleexceptions.schema.json— ExceptionBundledecision.schema.json— DecisionRecordcustom_rules.schema.json— CustomRuleSet
Need Help?
Can't find what you're looking for? Check out our GitHub or reach out to support.