mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
* fix(ffi): eliminate aliasing UB via to_shared_ref migration Add to_shared_ref() helper that creates &T (shared reference) from raw pointers instead of &mut T. This eliminates undefined behavior caused by violating Rust's aliasing invariant when C# SafeHandle permits concurrent FFI calls on the same handle. With &mut T, the compiler may assume exclusive (noalias) access and reorder or elide reads/writes — a miscompilation risk when another thread holds a reference to the same object. Switching to &T removes that assumption; actual mutation is mediated by the interior RwLock inside Handle<T>, which is the sole synchronization mechanism. Migrated sites: - rvm.rs: 20 non-drop call sites - engine.rs: 30 non-drop call sites + with_unwind_guard for timer fns - compiled_policy.rs: 2 call sites - Fix null-data UB in regorus_program_deserialize_binary Drop paths retain to_ref() where exclusive access is guaranteed by the caller contract (preventing use-after-free). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * feat(ffi): add Azure Policy JSON compilation FFI and C# bindings - AliasRegistry builder pattern: RegorusAliasRegistryBuilder (mutable, single-threaded) + RegorusAliasRegistry (immutable, Arc-wrapped) - Azure Policy JSON compilation: regorus_compile_azure_policy_rule and regorus_compile_azure_policy_definition with alias registry support - regorus_rvm_set_context for host-supplied ambient data - C# AliasRegistryBuilder and AliasRegistry classes with convenience factories (FromJson, FromManifest, Empty) - C# AzurePolicyCompiler static class for policy rule/definition compilation - Compile functions take *const RegorusAliasRegistry (read-only via to_shared_ref for concurrent compilation safety) - Fix pre-existing clippy warnings across multiple crates Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
575 lines
21 KiB
C#
575 lines
21 KiB
C#
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
using System.Linq;
|
|
using System.Text.Json;
|
|
|
|
namespace TargetExampleApp;
|
|
|
|
class Program
|
|
{
|
|
// Policy definition constants
|
|
private const string AZURE_STORAGE_POLICY_DEFINITION = @"
|
|
package policy
|
|
|
|
import rego.v1
|
|
|
|
# Target declaration for Azure Policy
|
|
__target__ := ""target.tests.azure_policy""
|
|
|
|
default parameters.requiredTLSVersion = """"
|
|
default parameters.allowedPorts = []
|
|
|
|
# Policy rules for storage accounts
|
|
default allow := false
|
|
|
|
# Allow storage accounts with HTTPS-only traffic and proper encryption
|
|
allow if {
|
|
input.type == ""Microsoft.Storage/storageAccounts""
|
|
input.properties.supportsHttpsTrafficOnly == true
|
|
input.properties.encryption.services.blob.enabled == true
|
|
input.properties.minimumTlsVersion in [parameters.requiredTLSVersion]
|
|
}
|
|
|
|
# Allow network security groups with proper inbound rules
|
|
allow if {
|
|
input.type == ""Microsoft.Network/networkSecurityGroups""
|
|
count([rule |
|
|
rule := input.properties.securityRules[_]
|
|
rule.properties.direction == ""Inbound""
|
|
rule.properties.access == ""Allow""
|
|
rule.properties.sourceAddressPrefix == ""*""
|
|
rule.properties.destinationPortRange in [parameters.allowedPorts]
|
|
]) == 0
|
|
}";
|
|
|
|
private const string AZURE_STORAGE_POLICY_ASSIGNMENT = @"
|
|
package policy
|
|
|
|
import rego.v1
|
|
|
|
parameters.requiredTLSVersion = ""TLS1_2""
|
|
parameters.allowedPorts = [""22"", ""3389""]";
|
|
|
|
private const string EXECUTION_TIMER_POLICY = @"
|
|
package limits.timer
|
|
import rego.v1
|
|
|
|
triplet_count := count([1 |
|
|
x := data.values[_]
|
|
y := data.values[_]
|
|
z := data.values[_]
|
|
])
|
|
";
|
|
|
|
private const string EXECUTION_TIMER_QUERY = "data.limits.timer.triplet_count";
|
|
private const int EXECUTION_TIMER_VALUE_COUNT = 40;
|
|
|
|
private const string RVM_POLICY = """
|
|
package demo
|
|
import rego.v1
|
|
|
|
default allow := false
|
|
|
|
allow if {
|
|
input.user == "alice"
|
|
some role in data.roles[input.user]
|
|
role == "admin"
|
|
}
|
|
""";
|
|
|
|
private const string RVM_DATA = """
|
|
{
|
|
"roles": {
|
|
"alice": ["admin", "reader"]
|
|
}
|
|
}
|
|
""";
|
|
|
|
private const string RVM_INPUT = """
|
|
{
|
|
"user": "alice"
|
|
}
|
|
""";
|
|
|
|
private const string HOST_AWAIT_POLICY = """
|
|
package demo
|
|
import rego.v1
|
|
|
|
default allow := false
|
|
|
|
allow if {
|
|
input.account.active == true
|
|
details := __builtin_host_await(input.account.id, "account")
|
|
details.tier == "gold"
|
|
}
|
|
""";
|
|
|
|
private const string HOST_AWAIT_INPUT = """
|
|
{
|
|
"account": {
|
|
"id": "acct-1",
|
|
"active": true
|
|
}
|
|
}
|
|
""";
|
|
|
|
// Test data constants
|
|
private const string COMPLIANT_STORAGE_ACCOUNT = @"{
|
|
""type"": ""Microsoft.Storage/storageAccounts"",
|
|
""name"": ""compliantstorageacct"",
|
|
""location"": ""eastus"",
|
|
""kind"": ""StorageV2"",
|
|
""properties"": {
|
|
""supportsHttpsTrafficOnly"": true,
|
|
""minimumTlsVersion"": ""TLS1_2"",
|
|
""allowBlobPublicAccess"": false,
|
|
""encryption"": {
|
|
""services"": {
|
|
""blob"": { ""enabled"": true },
|
|
""file"": { ""enabled"": true }
|
|
}
|
|
}
|
|
},
|
|
""tags"": {
|
|
""environment"": ""production""
|
|
}
|
|
}";
|
|
|
|
private const string NON_COMPLIANT_STORAGE_ACCOUNT = @"{
|
|
""type"": ""Microsoft.Storage/storageAccounts"",
|
|
""name"": ""insecurestorageacct"",
|
|
""location"": ""westus"",
|
|
""kind"": ""Storage"",
|
|
""properties"": {
|
|
""supportsHttpsTrafficOnly"": false,
|
|
""minimumTlsVersion"": ""TLS1_0"",
|
|
""allowBlobPublicAccess"": true,
|
|
""encryption"": {
|
|
""services"": {
|
|
""blob"": { ""enabled"": false },
|
|
""file"": { ""enabled"": false }
|
|
}
|
|
}
|
|
}
|
|
}";
|
|
|
|
static void Main(string[] args)
|
|
{
|
|
Console.WriteLine("=== Regorus Target Example Application ===\n");
|
|
|
|
try
|
|
{
|
|
DemonstrateTargetFunctionality();
|
|
Console.WriteLine("\n=== Target demonstration completed successfully! ===");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Error: {ex.Message}");
|
|
Environment.Exit(1);
|
|
}
|
|
}
|
|
|
|
static void DemonstrateTargetFunctionality()
|
|
{
|
|
Console.WriteLine("REGORUS TARGET FUNCTIONALITY DEMONSTRATION");
|
|
Console.WriteLine("==========================================");
|
|
|
|
// 1. Register target using JSON from file
|
|
var targetJsonPath = Path.Combine(AppContext.BaseDirectory, "azure_policy.target.json");
|
|
var targetJson = File.ReadAllText(targetJsonPath);
|
|
|
|
Console.WriteLine("1. Registering target from JSON file:");
|
|
Console.WriteLine(targetJson);
|
|
|
|
Regorus.TargetRegistry.RegisterFromJson(targetJson);
|
|
Console.WriteLine($"Target registered. Registry contains {Regorus.TargetRegistry.Count} target(s)");
|
|
Console.WriteLine($"Registered targets: {Regorus.TargetRegistry.ListNames()}");
|
|
|
|
// 2. Compile policy for target
|
|
var policyModules = new List<Regorus.PolicyModule>
|
|
{
|
|
new Regorus.PolicyModule($"definition-{Guid.NewGuid():N}", AZURE_STORAGE_POLICY_DEFINITION),
|
|
new Regorus.PolicyModule($"assignment-{Guid.NewGuid():N}", AZURE_STORAGE_POLICY_ASSIGNMENT)
|
|
};
|
|
|
|
var policyDataJson = "{}";
|
|
|
|
Console.WriteLine("\n2. Compiling policy for target...");
|
|
using var compiledPolicy = Regorus.Compiler.CompilePolicyForTarget(policyDataJson, policyModules);
|
|
Console.WriteLine("Policy compiled successfully!");
|
|
|
|
// 2.5. Demonstrate policy information retrieval
|
|
Console.WriteLine("\n2.5. Retrieving policy information:");
|
|
DemonstratePolicyInfo(compiledPolicy);
|
|
|
|
// 3. Evaluate with different inputs
|
|
Console.WriteLine("\n3. Testing policy evaluation:");
|
|
Console.WriteLine("Compliant storage account:");
|
|
Console.WriteLine(COMPLIANT_STORAGE_ACCOUNT);
|
|
|
|
var compliantResult = compiledPolicy.EvalWithInput(COMPLIANT_STORAGE_ACCOUNT);
|
|
Console.WriteLine($"Result: {compliantResult}");
|
|
|
|
Console.WriteLine("\nNon-compliant storage account:");
|
|
Console.WriteLine(NON_COMPLIANT_STORAGE_ACCOUNT);
|
|
|
|
var nonCompliantResult = compiledPolicy.EvalWithInput(NON_COMPLIANT_STORAGE_ACCOUNT);
|
|
Console.WriteLine($"Result: {nonCompliantResult}");
|
|
|
|
// 4. Demonstrate thread-safe concurrent evaluation
|
|
Console.WriteLine("\n4. Testing concurrent evaluation from multiple threads:");
|
|
DemonstrateConcurrentEvaluation(compiledPolicy);
|
|
|
|
Console.WriteLine("\n5. Execution timer configuration:");
|
|
DemonstrateExecutionTimer();
|
|
|
|
Console.WriteLine("\n6. RVM program execution:");
|
|
DemonstrateRvmUsage();
|
|
|
|
Console.WriteLine("\n7. RVM program compilation from engine:");
|
|
DemonstrateRvmCompileFromEngine();
|
|
|
|
Console.WriteLine("\n8. RVM host await (suspend/resume):");
|
|
DemonstrateRvmHostAwait();
|
|
|
|
Console.WriteLine("\n9. Azure Policy JSON compilation:");
|
|
DemonstrateAzurePolicyJsonCompilation();
|
|
}
|
|
|
|
static void DemonstrateConcurrentEvaluation(Regorus.CompiledPolicy compiledPolicy)
|
|
{
|
|
var testInputs = new[]
|
|
{
|
|
("Thread-1-Compliant", COMPLIANT_STORAGE_ACCOUNT),
|
|
("Thread-2-NonCompliant", NON_COMPLIANT_STORAGE_ACCOUNT),
|
|
("Thread-3-Compliant", COMPLIANT_STORAGE_ACCOUNT.Replace("compliantstorageacct", "thread3storage")),
|
|
("Thread-4-NonCompliant", NON_COMPLIANT_STORAGE_ACCOUNT.Replace("insecurestorageacct", "thread4storage")),
|
|
("Thread-5-Compliant", COMPLIANT_STORAGE_ACCOUNT.Replace("compliantstorageacct", "thread5storage"))
|
|
};
|
|
|
|
Console.WriteLine($"Starting {testInputs.Length} concurrent evaluations...");
|
|
|
|
var tasks = testInputs.Select(input =>
|
|
Task.Run(() =>
|
|
{
|
|
var (threadName, json) = input;
|
|
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
|
|
|
|
// Multiple evaluations per thread to stress test
|
|
var results = new List<string>();
|
|
for (int i = 0; i < 1000; i++)
|
|
{
|
|
var result = compiledPolicy.EvalWithInput(json)
|
|
?? throw new System.InvalidOperationException("Expected EvalWithInput to return a JSON value.");
|
|
results.Add(result);
|
|
}
|
|
|
|
stopwatch.Stop();
|
|
var microseconds = stopwatch.ElapsedTicks * 1000000 / System.Diagnostics.Stopwatch.Frequency;
|
|
|
|
// Verify all results are identical (thread safety)
|
|
var firstResult = results[0];
|
|
var allIdentical = results.All(r => r == firstResult);
|
|
|
|
Console.WriteLine($"✓ {threadName}: {results.Count} evaluations in {microseconds}μs, " +
|
|
$"Results consistent: {allIdentical}");
|
|
|
|
return (threadName, results.Count, microseconds, allIdentical);
|
|
})
|
|
).ToArray();
|
|
|
|
// Wait for all threads to complete
|
|
var results = Task.WhenAll(tasks).Result;
|
|
|
|
Console.WriteLine("\nConcurrency test results:");
|
|
var totalEvaluations = results.Sum(r => r.Item2);
|
|
var maxTime = results.Max(r => r.Item3);
|
|
var allConsistent = results.All(r => r.allIdentical);
|
|
|
|
Console.WriteLine($"✓ Total evaluations: {totalEvaluations}");
|
|
Console.WriteLine($"✓ Max thread time: {maxTime}μs");
|
|
Console.WriteLine($"✓ All threads consistent: {allConsistent}");
|
|
Console.WriteLine($"✓ Approximate throughput: {totalEvaluations * 1000000.0 / maxTime:F0} evaluations/second");
|
|
Console.WriteLine("✓ No locks required - CompiledPolicy is thread-safe!");
|
|
}
|
|
|
|
static void DemonstratePolicyInfo(Regorus.CompiledPolicy compiledPolicy)
|
|
{
|
|
Console.WriteLine("Getting policy metadata using GetPolicyInfo()...");
|
|
|
|
try
|
|
{
|
|
var policyInfo = compiledPolicy.GetPolicyInfo();
|
|
|
|
Console.WriteLine($"✓ Policy Information Retrieved:");
|
|
Console.WriteLine($" Target Name: {policyInfo.TargetName ?? "None"}");
|
|
Console.WriteLine($" Effect Rule: {policyInfo.EffectRule ?? "None"}");
|
|
Console.WriteLine($" Entrypoint Rule: {policyInfo.EntrypointRule}");
|
|
|
|
Console.WriteLine($" Module IDs ({policyInfo.ModuleIds.Count}):");
|
|
foreach (var moduleId in policyInfo.ModuleIds)
|
|
{
|
|
Console.WriteLine($" - {moduleId}");
|
|
}
|
|
|
|
Console.WriteLine($" Applicable Resource Types ({policyInfo.ApplicableResourceTypes.Count}):");
|
|
foreach (var resourceType in policyInfo.ApplicableResourceTypes)
|
|
{
|
|
Console.WriteLine($" - {resourceType}");
|
|
}
|
|
|
|
if (policyInfo.Parameters != null && policyInfo.Parameters.Count > 0)
|
|
{
|
|
Console.WriteLine($" Policy Parameters:");
|
|
foreach (var parameterSet in policyInfo.Parameters)
|
|
{
|
|
Console.WriteLine($" From '{parameterSet.SourceFile}':");
|
|
Console.WriteLine($" Parameters ({parameterSet.Parameters.Count}):");
|
|
foreach (var param in parameterSet.Parameters)
|
|
{
|
|
Console.WriteLine($" - {param.Name} ({param.Type})");
|
|
if (param.Default != null)
|
|
{
|
|
Console.WriteLine($" Default: {param.Default}");
|
|
}
|
|
if (!string.IsNullOrEmpty(param.Description))
|
|
{
|
|
Console.WriteLine($" Description: {param.Description}");
|
|
}
|
|
}
|
|
|
|
if (parameterSet.Modifiers.Count > 0)
|
|
{
|
|
Console.WriteLine($" Modifiers ({parameterSet.Modifiers.Count}):");
|
|
foreach (var modifier in parameterSet.Modifiers)
|
|
{
|
|
Console.WriteLine($" - {modifier.Name}: {modifier.Value}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine(" No parameter information available");
|
|
}
|
|
|
|
// Demonstrate JSON serialization of policy info
|
|
Console.WriteLine("\n✓ Policy Info as JSON:");
|
|
var jsonOptions = new JsonSerializerOptions
|
|
{
|
|
WriteIndented = true,
|
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
|
};
|
|
var policyInfoJson = JsonSerializer.Serialize(policyInfo, jsonOptions);
|
|
Console.WriteLine(policyInfoJson);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"✗ Failed to get policy info: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
static void DemonstrateExecutionTimer()
|
|
{
|
|
var dataJson = JsonSerializer.Serialize(new
|
|
{
|
|
values = Enumerable.Range(0, EXECUTION_TIMER_VALUE_COUNT).ToArray()
|
|
});
|
|
|
|
var fallback = new Regorus.ExecutionTimerConfig(TimeSpan.FromMilliseconds(2), checkInterval: 1);
|
|
var relaxed = new Regorus.ExecutionTimerConfig(TimeSpan.FromMilliseconds(1000), checkInterval: 1);
|
|
|
|
Console.WriteLine($" Configuring fallback timer (limit={fallback.Limit.TotalMilliseconds:F0} ms, interval={fallback.CheckInterval})...");
|
|
|
|
Regorus.Engine.SetFallbackExecutionTimerConfig(fallback);
|
|
try
|
|
{
|
|
using var engine = new Regorus.Engine();
|
|
engine.AddPolicy("limits_timer.rego", EXECUTION_TIMER_POLICY);
|
|
engine.AddDataJson(dataJson);
|
|
|
|
Console.WriteLine(" Evaluating under fallback limit (expected failure)...");
|
|
try
|
|
{
|
|
engine.EvalRule(EXECUTION_TIMER_QUERY);
|
|
Console.WriteLine(" ⚠ Evaluation unexpectedly succeeded under fallback limit.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($" ✓ Fallback enforced: {ex.Message}");
|
|
}
|
|
|
|
Console.WriteLine($" Applying per-engine override ({relaxed.Limit.TotalMilliseconds:F0} ms) and retrying...");
|
|
engine.SetExecutionTimerConfig(relaxed);
|
|
var result = engine.EvalRule(EXECUTION_TIMER_QUERY);
|
|
Console.WriteLine($" ✓ Override succeeded; triplet_count = {result}");
|
|
|
|
Console.WriteLine(" Clearing engine override to restore fallback...");
|
|
engine.ClearExecutionTimerConfig();
|
|
try
|
|
{
|
|
engine.EvalRule(EXECUTION_TIMER_QUERY);
|
|
Console.WriteLine(" ⚠ Evaluation unexpectedly succeeded after clearing override.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($" ✓ Fallback restored: {ex.Message}");
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
Regorus.Engine.ClearFallbackExecutionTimerConfig();
|
|
}
|
|
}
|
|
|
|
static void DemonstrateRvmUsage()
|
|
{
|
|
var modules = new List<Regorus.PolicyModule>
|
|
{
|
|
new Regorus.PolicyModule("demo.rego", RVM_POLICY)
|
|
};
|
|
var entryPoints = new[] { "data.demo.allow" };
|
|
|
|
using var program = Regorus.Program.CompileFromModules(RVM_DATA, modules, entryPoints);
|
|
var binary = program.SerializeBinary();
|
|
using var rehydrated = Regorus.Program.DeserializeBinary(binary, out var isPartial);
|
|
if (isPartial)
|
|
{
|
|
throw new InvalidOperationException("RVM program deserialization returned a partial program.");
|
|
}
|
|
|
|
Console.WriteLine($"Serialized program size: {binary.Length} bytes");
|
|
|
|
var listing = rehydrated.GenerateListing();
|
|
|
|
Console.WriteLine("RVM listing:");
|
|
Console.WriteLine(listing);
|
|
|
|
using var vm = new Regorus.Rvm();
|
|
vm.LoadProgram(rehydrated);
|
|
vm.SetDataJson(RVM_DATA);
|
|
vm.SetInputJson(RVM_INPUT);
|
|
|
|
var result = vm.Execute();
|
|
Console.WriteLine($"RVM result: {result}");
|
|
}
|
|
|
|
static void DemonstrateRvmCompileFromEngine()
|
|
{
|
|
using var engine = new Regorus.Engine();
|
|
engine.AddPolicy("demo.rego", RVM_POLICY);
|
|
engine.AddDataJson(RVM_DATA);
|
|
|
|
var entryPoints = new[] { "data.demo.allow" };
|
|
using var program = Regorus.Program.CompileFromEngine(engine, entryPoints);
|
|
|
|
using var vm = new Regorus.Rvm();
|
|
vm.LoadProgram(program);
|
|
vm.SetDataJson(RVM_DATA);
|
|
vm.SetInputJson(RVM_INPUT);
|
|
|
|
var result = vm.ExecuteEntryPoint("data.demo.allow");
|
|
Console.WriteLine($"RVM result from engine-compiled program: {result}");
|
|
}
|
|
|
|
static void DemonstrateRvmHostAwait()
|
|
{
|
|
var modules = new List<Regorus.PolicyModule>
|
|
{
|
|
new Regorus.PolicyModule("host_await.rego", HOST_AWAIT_POLICY)
|
|
};
|
|
var entryPoints = new[] { "data.demo.allow" };
|
|
|
|
using var program = Regorus.Program.CompileFromModules("{}", modules, entryPoints);
|
|
using var vm = new Regorus.Rvm();
|
|
vm.SetExecutionMode(1);
|
|
vm.LoadProgram(program);
|
|
vm.SetInputJson(HOST_AWAIT_INPUT);
|
|
|
|
var initial = vm.Execute();
|
|
var state = vm.GetExecutionState();
|
|
Console.WriteLine($"HostAwait initial result: {initial}");
|
|
Console.WriteLine($"Execution state: {state}");
|
|
|
|
var resumed = vm.Resume("{\"tier\":\"gold\"}");
|
|
Console.WriteLine($"HostAwait resumed result: {resumed}");
|
|
}
|
|
|
|
// Azure Policy JSON constants
|
|
private const string STORAGE_ALIASES_JSON = @"[{
|
|
""namespace"": ""Microsoft.Storage"",
|
|
""resourceTypes"": [{
|
|
""resourceType"": ""storageAccounts"",
|
|
""capabilities"": ""SupportsTags, SupportsLocation"",
|
|
""aliases"": [
|
|
{
|
|
""name"": ""Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly"",
|
|
""defaultPath"": ""properties.supportsHttpsTrafficOnly"",
|
|
""paths"": []
|
|
}
|
|
]
|
|
}]
|
|
}]";
|
|
|
|
private const string HTTPS_DENY_RULE = @"{
|
|
""if"": {
|
|
""allOf"": [
|
|
{ ""field"": ""type"", ""equals"": ""Microsoft.Storage/storageAccounts"" },
|
|
{ ""field"": ""Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly"", ""equals"": false }
|
|
]
|
|
},
|
|
""then"": { ""effect"": ""deny"" }
|
|
}";
|
|
|
|
static void DemonstrateAzurePolicyJsonCompilation()
|
|
{
|
|
// 1. Set up alias registry
|
|
using var registry = Regorus.AliasRegistry.FromJson(STORAGE_ALIASES_JSON);
|
|
Console.WriteLine("Loaded storage account aliases");
|
|
|
|
// 2. Compile the JSON policy rule directly (no Rego needed)
|
|
using var program = Regorus.AzurePolicyCompiler.CompilePolicyRule(registry, HTTPS_DENY_RULE);
|
|
Console.WriteLine("Compiled Azure Policy JSON rule to RVM program");
|
|
|
|
// 3. Normalize an ARM resource
|
|
var armResource = @"{
|
|
""type"": ""Microsoft.Storage/storageAccounts"",
|
|
""name"": ""insecurestorage"",
|
|
""location"": ""eastus"",
|
|
""properties"": { ""supportsHttpsTrafficOnly"": false }
|
|
}";
|
|
var envelope = registry.NormalizeAndWrap(armResource, apiVersion: null, contextJson: "{}", parametersJson: "{}");
|
|
Console.WriteLine($"Normalized ARM resource to evaluation envelope");
|
|
|
|
// 4. Execute in the RVM
|
|
// Note: For policies using context functions (subscription(), resourceGroup()),
|
|
// call vm.SetContextJson(contextJson) before execution. The context from
|
|
// NormalizeAndWrap is in the envelope but must also be set on the VM separately.
|
|
using var vm = new Regorus.Rvm();
|
|
vm.LoadProgram(program);
|
|
vm.SetInputJson(envelope!);
|
|
// vm.SetContextJson(contextJson); // ← required for context-dependent policies
|
|
var result = vm.ExecuteEntryPoint("main");
|
|
Console.WriteLine($"Evaluation result (non-compliant): {result}");
|
|
|
|
// 5. Test with a compliant resource
|
|
var compliantResource = @"{
|
|
""type"": ""Microsoft.Storage/storageAccounts"",
|
|
""name"": ""securestorage"",
|
|
""location"": ""eastus"",
|
|
""properties"": { ""supportsHttpsTrafficOnly"": true }
|
|
}";
|
|
var compliantEnvelope = registry.NormalizeAndWrap(compliantResource, apiVersion: null, contextJson: "{}", parametersJson: "{}");
|
|
using var vm2 = new Regorus.Rvm();
|
|
vm2.LoadProgram(program);
|
|
vm2.SetInputJson(compliantEnvelope!);
|
|
var compliantResult = vm2.ExecuteEntryPoint("main");
|
|
Console.WriteLine($"Evaluation result (compliant): {compliantResult}");
|
|
|
|
// 6. Demonstrate program serialization
|
|
var binary = program.SerializeBinary();
|
|
Console.WriteLine($"Serialized program size: {binary.Length} bytes");
|
|
}
|
|
}
|