feat: Complete target system with C# bindings and resource inference (#458)

* feat: Add Schema Registry and Validation Framework

This commit introduces a comprehensive schema registry and validation framework, providing schema-based validation of resources and policy effects.

- Thread-safe, in-memory registry for schema storage and management
- Global registry patterns for effects and resources
- Concurrent access with proper error handling
- Unicode schema names support

- JSON Schema-compliant validation for all primitive types
- Advanced constraint validation (patterns, ranges, length limits)
- Discriminated union support with anyOf schemas
- Detailed error reporting with nested validation paths
- Discriminated subobject validation for polymorphic schemas

- **Registry Tests**: All registry operations
- **Effect Tests**: Policy effect validation
- **Resource Tests**: Resource validation
- **Validation Tests**: Core validation engine
- Thread-safety, error handling, integration scenarios, edge cases

- **Dependencies**: dashmap, once_cell, regex
- **Thread Safety**: Minimal locking with Rc<Schema> sharing
- **Error Types**: TypeMismatch, OutOfRange, PatternMismatch, etc.

- Complete schema registry and validation subsystem
- Comprehensive test coverage
- Foundation for policy validation in Regorus

Benchmarks:

- Criterion benchmarks for basic types, effects and Azure resources
- Performance range: 3.22ns (string) to 34.74µs (Azure VM resource schema validation)
- String withs patterns validation: 30.2µs. Need to explore whether regex caching helps
  bring this down.
- Azure policy effects: 188ns-1.4µs

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>

* feat: Complete target system with C# bindings and resource inference

- Add comprehensive target system with TargetRegistry and target-aware compilation
- Implement resource type inference from policy equality expressions
- Create modular C# bindings with separate wrapper classes for each concept
- Add thread-safe CompiledPolicy with reference counting for safe disposal
- Enhance FFI with detailed error propagation and target functionality
- Create TargetExampleApp demonstrating Azure Policy integration
- Add CI/CD pipeline testing for all C# applications
- Support target definitions with schema validation and resource selectors
- Implement PolicyModule struct and target-aware compilation methods
- Add comprehensive test coverage for target functionality

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>

---------

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
Anand Krishnamoorthi
2025-08-19 20:23:43 -05:00
committed by GitHub
parent 3c33d31d08
commit cc917ea75d
71 changed files with 10278 additions and 1000 deletions

View File

@@ -0,0 +1,291 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
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""]";
// 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);
}
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);
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}");
}
}
}

View File

@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<!-- If the environment variable is set (such as in a Github Action run), append the suffix to the version number -->
<RegorusPackageVersionSuffix Condition="'$(VersionSuffix)' != ''">-$(VersionSuffix)</RegorusPackageVersionSuffix>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Regorus" Version="0.6.0$(RegorusPackageVersionSuffix)"/>
</ItemGroup>
<ItemGroup>
<Content Include="azure_policy.target.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,125 @@
{
"name": "target.tests.azure_policy",
"description": "Azure Policy target for comprehensive policy evaluation testing",
"version": "1.0.0",
"resource_schema_selector": "type",
"resource_schemas": [
{
"type": "object",
"properties": {
"type": { "const": "Microsoft.Resources/subscriptions" },
"subscriptionId": { "type": "string" },
"tenantId": { "type": "string" },
"displayName": { "type": "string" }
},
"required": ["type", "subscriptionId"]
},
{
"type": "object",
"properties": {
"type": { "const": "Microsoft.Storage/storageAccounts" },
"name": { "type": "string" },
"location": { "type": "string" },
"kind": { "enum": ["Storage", "StorageV2", "BlobStorage", "FileStorage", "BlockBlobStorage"] },
"properties": {
"type": "object",
"properties": {
"supportsHttpsTrafficOnly": { "type": "boolean" },
"minimumTlsVersion": { "enum": ["TLS1_0", "TLS1_1", "TLS1_2"] },
"allowBlobPublicAccess": { "type": "boolean" },
"encryption": {
"type": "object",
"properties": {
"services": {
"type": "object",
"properties": {
"blob": { "type": "object", "properties": { "enabled": { "type": "boolean" } } },
"file": { "type": "object", "properties": { "enabled": { "type": "boolean" } } }
}
}
}
}
}
},
"tags": { "type": "object" }
},
"required": ["type", "name", "location"]
},
{
"type": "object",
"properties": {
"type": { "const": "Microsoft.Network/networkSecurityGroups" },
"name": { "type": "string" },
"location": { "type": "string" },
"properties": {
"type": "object",
"properties": {
"securityRules": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"properties": {
"type": "object",
"properties": {
"direction": { "enum": ["Inbound", "Outbound"] },
"access": { "enum": ["Allow", "Deny"] },
"protocol": { "enum": ["Tcp", "Udp", "*"] },
"sourcePortRange": { "type": "string" },
"destinationPortRange": { "type": "string" },
"sourceAddressPrefix": { "type": "string" },
"destinationAddressPrefix": { "type": "string" },
"priority": { "type": "integer", "minimum": 100, "maximum": 4096 }
}
}
}
}
}
}
}
},
"required": ["type", "name", "location"]
}
],
"effects": {
"allow": { "type": "boolean" },
"deny": {
"type": "object",
"properties": {
"message": { "type": "string" }
}
},
"audit": {
"type": "object",
"properties": {
"level": { "enum": ["info", "warning", "error"] },
"message": { "type": "string" },
"complianceState": { "enum": ["Compliant", "NonCompliant", "Unknown"] }
}
},
"modify": {
"type": "object",
"properties": {
"operations": {
"type": "array",
"items": {
"type": "object",
"properties": {
"operation": { "enum": ["add", "replace", "remove"] },
"field": { "type": "string" },
"value": { "type": "any" }
}
}
}
}
},
"deployIfNotExists": {
"type": "object",
"properties": {
"template": { "type": "object" },
"parameters": { "type": "object" }
}
}
}
}