Files
regorus/tests/rvm/vm/suites/load_context_metadata.yaml
Anand Krishnamoorthi e5ac9a2734 feat(rvm): new instructions and loop semantics for Azure Policy support (#659)
The Rego VM was designed around Rego's semantics, but Azure Policy needs
a few things Rego doesn't: host-supplied context alongside input/data,
undefined-to-null coercion for missing fields, skip-undefined collection
behavior for wildcard aliases, and non-vacuous iteration over non-array
values.

This commit adds five new instructions to bridge those gaps:

  LoadContext / LoadMetadata — give programs access to host-supplied
  evaluation context and cached program metadata at runtime.

  ArrayPushDefined — like ArrayPush but silently drops undefined values,
  so wildcard alias collection (field[*].property) excludes absent
  nested properties instead of leaking undefined entries into the array.

  ReturnUndefinedIfNotTrue — early return with Undefined when a guard
  condition isn't satisfied, without tripping a VM assertion failure.
  This models "condition doesn't match" cleanly.

  CoalesceUndefinedToNull — turns Undefined into Null in-place so that
  downstream builtins see null rather than short-circuiting on undefined.

The loop engine also gains an Azure Policy mode: when the source language
is "azure_policy", an Every loop over a non-array value (scalars, null,
objects) iterates once over a virtual Null element instead of being
vacuously true.  This matches how field[*] behaves on non-array fields
in Azure Policy — the condition body runs once against Null, which
typically evaluates to false.

On the plumbing side: the VM gets a context field with set_context(),
metadata is cached as a Value on program load, and map_limit_error is
inlined into memory_check since it had only one call site.

Four new YAML test suites (~880 lines) cover the new instructions and
context/metadata loading, along with instruction parser, display, and
assembly listing support for everything added here.
2026-04-06 15:40:41 -05:00

390 lines
12 KiB
YAML

# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# LoadContext and LoadMetadata Test Suite
# Tests LoadContext and LoadMetadata instructions
# Covers: no context, populated context, context indexing, metadata fields,
# annotations, metadata round-trip, and interaction with other loads
cases:
# ── LoadContext ────────────────────────────────────────────────────────────
- note: load_context_undefined
description: LoadContext returns Undefined when no context is set
literals: []
instructions:
- "LoadContext { dest: 0 }"
- "Return { value: 0 }"
want_result: "#undefined"
- note: load_context_empty_object
description: LoadContext returns empty object
context: {}
literals: []
instructions:
- "LoadContext { dest: 0 }"
- "Return { value: 0 }"
want_result: {}
- note: load_context_string
description: LoadContext returns a string value
context: "hello world"
literals: []
instructions:
- "LoadContext { dest: 0 }"
- "Return { value: 0 }"
want_result: "hello world"
- note: load_context_number
description: LoadContext returns a numeric value
context: 42
literals: []
instructions:
- "LoadContext { dest: 0 }"
- "Return { value: 0 }"
want_result: 42
- note: load_context_boolean
description: LoadContext returns a boolean value
context: true
literals: []
instructions:
- "LoadContext { dest: 0 }"
- "Return { value: 0 }"
want_result: true
- note: load_context_with_values
description: LoadContext returns a populated object
context:
api_version: "2021-06-01"
tenant_id: "abc-123"
literals: []
instructions:
- "LoadContext { dest: 0 }"
- "Return { value: 0 }"
want_result:
api_version: "2021-06-01"
tenant_id: "abc-123"
- note: load_context_nested
description: LoadContext returns deeply nested data
context:
level1:
level2:
value: "deep"
literals: []
instructions:
- "LoadContext { dest: 0 }"
- "Return { value: 0 }"
want_result:
level1:
level2:
value: "deep"
- note: load_context_and_index
description: LoadContext followed by indexing into context data
context:
api_version: "2021-06-01"
tenant_id: "abc-123"
literals:
- "api_version"
instructions:
- "LoadContext { dest: 0 }"
- "Load { dest: 1, literal_idx: 0 }"
- "Index { dest: 2, container: 0, key: 1 }"
- "Return { value: 2 }"
want_result: "2021-06-01"
- note: load_context_index_missing_key
description: Loading context and indexing a missing key yields Undefined
context:
api_version: "2021-06-01"
literals:
- "nonexistent"
instructions:
- "LoadContext { dest: 0 }"
- "Load { dest: 1, literal_idx: 0 }"
- "Index { dest: 2, container: 0, key: 1 }"
- "Return { value: 2 }"
want_result: "#undefined"
- note: load_context_deep_index
description: LoadContext with chained indexing into nested object
context:
request:
headers:
content_type: "application/json"
literals:
- "request"
- "headers"
- "content_type"
instructions:
- "LoadContext { dest: 0 }"
- "Load { dest: 1, literal_idx: 0 }"
- "Index { dest: 2, container: 0, key: 1 }"
- "Load { dest: 3, literal_idx: 1 }"
- "Index { dest: 4, container: 2, key: 3 }"
- "Load { dest: 5, literal_idx: 2 }"
- "Index { dest: 6, container: 4, key: 5 }"
- "Return { value: 6 }"
want_result: "application/json"
- note: load_context_with_array
description: LoadContext with an array value
context:
tags:
- "production"
- "east-us"
literals:
- "tags"
instructions:
- "LoadContext { dest: 0 }"
- "Load { dest: 1, literal_idx: 0 }"
- "Index { dest: 2, container: 0, key: 1 }"
- "Count { dest: 3, collection: 2 }"
- "Return { value: 3 }"
want_result: 2
- note: load_context_multiple_times
description: Loading context multiple times returns the same value
context:
value: 42
literals:
- "value"
instructions:
- "LoadContext { dest: 0 }"
- "LoadContext { dest: 1 }"
- "Eq { dest: 2, left: 0, right: 1 }"
- "Return { value: 2 }"
want_result: true
- note: load_context_independent_of_input
description: Context and Input are independent
input:
input_key: "from_input"
context:
context_key: "from_context"
literals:
- "context_key"
- "input_key"
- "from_context"
- "from_input"
instructions:
# Load context["context_key"] into r2
- "LoadContext { dest: 0 }"
- "Load { dest: 1, literal_idx: 0 }"
- "Index { dest: 2, container: 0, key: 1 }"
# Load input["input_key"] into r5
- "LoadInput { dest: 3 }"
- "Load { dest: 4, literal_idx: 1 }"
- "Index { dest: 5, container: 3, key: 4 }"
# Verify context value == "from_context"
- "Load { dest: 6, literal_idx: 2 }"
- "Eq { dest: 7, left: 2, right: 6 }"
# Verify input value == "from_input"
- "Load { dest: 8, literal_idx: 3 }"
- "Eq { dest: 9, left: 5, right: 8 }"
# Both must be true
- "And { dest: 10, left: 7, right: 9 }"
- "Return { value: 10 }"
want_result: true
- note: load_context_independent_of_data
description: Context and Data are independent
data:
data_key: "from_data"
context:
context_key: "from_context"
literals:
- "context_key"
instructions:
- "LoadContext { dest: 0 }"
- "Load { dest: 1, literal_idx: 0 }"
- "Index { dest: 2, container: 0, key: 1 }"
- "Return { value: 2 }"
want_result: "from_context"
# ── LoadMetadata ────────────────────────────────────────────────────────────
- note: load_metadata_default
description: LoadMetadata returns object with metadata fields - check by indexing language
literals:
- "language"
instructions:
- "LoadMetadata { dest: 0 }"
- "Load { dest: 1, literal_idx: 0 }"
- "Index { dest: 2, container: 0, key: 1 }"
- "Return { value: 2 }"
# Default language is empty string
want_result: ""
- note: load_metadata_language
description: LoadMetadata returns language field when set
metadata_language: "azure_policy"
literals:
- "language"
instructions:
- "LoadMetadata { dest: 0 }"
- "Load { dest: 1, literal_idx: 0 }"
- "Index { dest: 2, container: 0, key: 1 }"
- "Return { value: 2 }"
want_result: "azure_policy"
- note: load_metadata_annotations_string
description: LoadMetadata includes string annotations
metadata_language: "azure_policy"
metadata_annotations:
policy_mode: "indexed"
policy_category: "Security"
literals:
- "annotations"
- "policy_mode"
instructions:
- "LoadMetadata { dest: 0 }"
- "Load { dest: 1, literal_idx: 0 }"
- "Index { dest: 2, container: 0, key: 1 }"
- "Load { dest: 3, literal_idx: 1 }"
- "Index { dest: 4, container: 2, key: 3 }"
- "Return { value: 4 }"
want_result: "indexed"
- note: load_metadata_annotations_number
description: LoadMetadata includes numeric annotations
metadata_annotations:
max_retries: 3
literals:
- "annotations"
- "max_retries"
instructions:
- "LoadMetadata { dest: 0 }"
- "Load { dest: 1, literal_idx: 0 }"
- "Index { dest: 2, container: 0, key: 1 }"
- "Load { dest: 3, literal_idx: 1 }"
- "Index { dest: 4, container: 2, key: 3 }"
- "Return { value: 4 }"
want_result: 3
- note: load_metadata_annotations_boolean
description: LoadMetadata includes boolean annotations
metadata_annotations:
is_audit: true
literals:
- "annotations"
- "is_audit"
instructions:
- "LoadMetadata { dest: 0 }"
- "Load { dest: 1, literal_idx: 0 }"
- "Index { dest: 2, container: 0, key: 1 }"
- "Load { dest: 3, literal_idx: 1 }"
- "Index { dest: 4, container: 2, key: 3 }"
- "Return { value: 4 }"
want_result: true
- note: load_metadata_annotations_nested
description: LoadMetadata includes nested annotations (object)
metadata_annotations:
compliance:
framework: "nist"
controls:
- "AC-1"
- "AC-2"
literals:
- "annotations"
- "compliance"
- "framework"
instructions:
- "LoadMetadata { dest: 0 }"
- "Load { dest: 1, literal_idx: 0 }"
- "Index { dest: 2, container: 0, key: 1 }"
- "Load { dest: 3, literal_idx: 1 }"
- "Index { dest: 4, container: 2, key: 3 }"
- "Load { dest: 5, literal_idx: 2 }"
- "Index { dest: 6, container: 4, key: 5 }"
- "Return { value: 6 }"
want_result: "nist"
- note: load_metadata_annotations_absent
description: LoadMetadata without annotations - indexing annotations gives Undefined
literals:
- "annotations"
instructions:
- "LoadMetadata { dest: 0 }"
- "Load { dest: 1, literal_idx: 0 }"
- "Index { dest: 2, container: 0, key: 1 }"
- "Return { value: 2 }"
want_result: "#undefined"
- note: load_metadata_multiple_times
description: Loading metadata multiple times returns same value
metadata_language: "rego"
literals:
- "language"
instructions:
- "LoadMetadata { dest: 0 }"
- "Load { dest: 1, literal_idx: 0 }"
- "Index { dest: 2, container: 0, key: 1 }"
- "LoadMetadata { dest: 3 }"
- "Load { dest: 4, literal_idx: 0 }"
- "Index { dest: 5, container: 3, key: 4 }"
- "Eq { dest: 6, left: 2, right: 5 }"
- "Return { value: 6 }"
want_result: true
# ── Combined LoadContext + LoadMetadata ──────────────────────────────────
- note: context_and_metadata_independent
description: Context and metadata are independent values
context:
env: "production"
metadata_language: "rego"
literals:
- "env"
- "language"
instructions:
- "LoadContext { dest: 0 }"
- "Load { dest: 1, literal_idx: 0 }"
- "Index { dest: 2, container: 0, key: 1 }"
# r2 = "production"
- "LoadMetadata { dest: 3 }"
- "Load { dest: 4, literal_idx: 1 }"
- "Index { dest: 5, container: 3, key: 4 }"
# r5 = "rego"
- "Ne { dest: 6, left: 2, right: 5 }"
- "Return { value: 6 }"
want_result: true
- note: all_four_loads
description: LoadData, LoadInput, LoadContext, LoadMetadata all coexist
data:
role: "admin"
input:
action: "read"
context:
region: "us-east"
metadata_language: "test"
literals:
- "role"
- "action"
- "region"
- "language"
instructions:
- "LoadData { dest: 0 }"
- "Load { dest: 1, literal_idx: 0 }"
- "Index { dest: 2, container: 0, key: 1 }"
# r2 = "admin"
- "LoadInput { dest: 3 }"
- "Load { dest: 4, literal_idx: 1 }"
- "Index { dest: 5, container: 3, key: 4 }"
# r5 = "read"
- "LoadContext { dest: 6 }"
- "Load { dest: 7, literal_idx: 2 }"
- "Index { dest: 8, container: 6, key: 7 }"
# r8 = "us-east"
- "LoadMetadata { dest: 9 }"
- "Load { dest: 10, literal_idx: 3 }"
- "Index { dest: 11, container: 9, key: 10 }"
# r11 = "test"
# Return context region as witness
- "Return { value: 8 }"
want_result: "us-east"