diff --git a/.github/workflows/test-csharp.yml b/.github/workflows/test-csharp.yml
index 67f16d1..ce566e6 100644
--- a/.github/workflows/test-csharp.yml
+++ b/.github/workflows/test-csharp.yml
@@ -19,8 +19,8 @@ jobs:
- name: Build
run: dotnet build
- working-directory: ./bindings/csharp
+ working-directory: ./bindings/csharp/net8.0
- name: Run
run: LD_LIBRARY_PATH=. dotnet run
- working-directory: ./bindings/csharp
+ working-directory: ./bindings/csharp/net8.0
diff --git a/.github/workflows/test-csharp40.yml b/.github/workflows/test-csharp40.yml
new file mode 100644
index 0000000..bb06ac9
--- /dev/null
+++ b/.github/workflows/test-csharp40.yml
@@ -0,0 +1,28 @@
+name: bindings/csharp40
+
+on:
+ push:
+ branches: [ "main" ]
+ pull_request:
+ branches: [ "main" ]
+
+jobs:
+ test:
+ runs-on: windows-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: "5.0.x"
+
+ - name: Build
+ run: dotnet build
+ working-directory: ./bindings/csharp/net40
+
+ - name: Run
+ run: dotnet run
+ working-directory: ./bindings/csharp/net40
diff --git a/.gitignore b/.gitignore
index 8c2c60a..e25d801 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,8 @@
# Generated by Cargo
# will have compiled files and executables
/target/
+**/wheels/
+**/__pycache__/
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
diff --git a/bindings/csharp/net40/Program.cs b/bindings/csharp/net40/Program.cs
new file mode 100644
index 0000000..6236564
--- /dev/null
+++ b/bindings/csharp/net40/Program.cs
@@ -0,0 +1,65 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c)2012 Microsoft. All rights reserved.
+//
+//
+// Contains code to test the Regorus Policy Engine base class for C#
+// and .NET4.0 bindings. It can be built and tested in Windows only.
+//
+//-----------------------------------------------------------------------
+using System;
+using System.Text;
+
+using System.Diagnostics;
+using Microsoft.WindowsAzure.Regorus.IaaS;
+
+namespace regoregorus_test
+{
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ long nanosecPerTick = (1000L * 1000L * 1000L) / Stopwatch.Frequency;
+ var w = new Stopwatch();
+ w.Restart();
+
+ var engine = new RegorusPolicyEngine();
+
+ w.Stop();
+ var newEngineTicks = w.ElapsedTicks;
+
+
+ w.Restart();
+
+ // Load policies and data.
+ engine.AddPolicyFromFile("../../../examples/extension_list/agent_extension_policy.rego");
+ engine.AddDataFromJsonFile("../../../examples/extension_list/agent-extension-data-allow-only.json");
+
+
+ w.Stop();
+ var loadPoliciesTicks = w.ElapsedTicks;
+
+
+ w.Restart();
+
+ // Set input and eval query.
+ engine.SetInputFromJsonFile("../../../examples/extension_list/agent-extension-input.json");
+ var results = engine.EvalQuery("data.agent_extension_policy.extensions_to_download=x");
+ Console.WriteLine("Download query test: \n {0}", results);
+
+ results = engine.EvalQuery("data.agent_extension_policy.extensions_validated");
+
+ Console.WriteLine("Signing validation test: \n {0}", results);
+
+ engine.Dispose();
+
+ w.Stop();
+ var evalTicks = w.ElapsedTicks;
+
+ Console.WriteLine("Engine creation took {0} msecs", (newEngineTicks * nanosecPerTick) / (1000.0 * 1000.0));
+ Console.WriteLine("Load policies and data took {0} msecs", (loadPoliciesTicks * nanosecPerTick) / (1000.0 * 1000.0));
+ Console.WriteLine("EvalQuery and print results took {0} msecs", (evalTicks * nanosecPerTick) / (1000.0 * 1000.0));
+ }
+ }
+}
+
diff --git a/bindings/csharp/net40/README b/bindings/csharp/net40/README
new file mode 100644
index 0000000..68bf4d3
--- /dev/null
+++ b/bindings/csharp/net40/README
@@ -0,0 +1,4 @@
+The Regorus C# binding library can be built via command "dotnet build". We can use the Regorus C# binding library built from this
+directory to create a Nuget. This Nuget will contain the Regorus C# binding library with definitions that
+work for .NET framework 4.0 (net40) and above. Note the Nuget can only be created after the binding library has been built.
+RegorusCsharp-Lib-x64.nuspec is built for x64 architecture.
\ No newline at end of file
diff --git a/bindings/csharp/net40/Regorus.cs b/bindings/csharp/net40/Regorus.cs
new file mode 100644
index 0000000..42a6cd4
--- /dev/null
+++ b/bindings/csharp/net40/Regorus.cs
@@ -0,0 +1,203 @@
+//-----------------------------------------------------------------------
+//
+// Copyright (c)2012 Microsoft. All rights reserved.
+//
+//
+// Contains code for the Regorus Policy Engine base class for C# and
+// .NET4.0 bindings. Currently this base class is not thread-safe. Make
+// sure we use it in a signle-threaded environment or add additional
+// protection when using it.
+//
+//-----------------------------------------------------------------------
+
+
+using System;
+using System.Text;
+using System.IO;
+using System.Threading;
+
+namespace Microsoft.WindowsAzure.Regorus.IaaS
+{
+
+ public class RegorusPolicyEngine : ICloneable, IDisposable
+ {
+ unsafe private RegorusFFI.RegorusEngine* E;
+
+ public RegorusPolicyEngine()
+ {
+ unsafe
+ {
+ E = RegorusFFI.API.regorus_engine_new();
+ }
+ }
+
+
+ public void Dispose()
+ {
+ unsafe
+ {
+ if (E != null)
+ {
+ RegorusFFI.API.regorus_engine_drop(E);
+ // to avoid Dispose() being called multiple times by mistake.
+ E = null;
+ }
+
+ }
+
+ }
+
+ public object Clone()
+ {
+ var clone = (RegorusPolicyEngine)this.MemberwiseClone();
+ unsafe
+ {
+ clone.E = RegorusFFI.API.regorus_engine_clone(E);
+ }
+ return clone;
+
+ }
+
+ public void AddPolicy(string path, string rego)
+ {
+ var pathBytes = Encoding.UTF8.GetBytes(path);
+ var regoBytes = Encoding.UTF8.GetBytes(rego);
+
+ unsafe
+ {
+ fixed (byte* pathPtr = pathBytes)
+ {
+ fixed(byte* regoPtr = regoBytes)
+ {
+ CheckAndDropResult(RegorusFFI.API.regorus_engine_add_policy(E, pathPtr, regoPtr));
+ }
+ }
+ }
+ }
+
+ public void AddPolicyFromFile(string path)
+ {
+ var pathBytes = Encoding.UTF8.GetBytes(path);
+
+ unsafe
+ {
+ fixed (byte* pathPtr = pathBytes)
+ {
+ CheckAndDropResult(RegorusFFI.API.regorus_engine_add_policy_from_file(E, pathPtr));
+ }
+ }
+ }
+
+ public void AddPolicyFromPath(string path)
+ {
+ if (!Directory.Exists(path))
+ {
+ return;
+ }
+
+ string[] regoFiles = Directory.GetFiles(path, "*.rego", SearchOption.AllDirectories);
+ foreach (string file in regoFiles)
+ {
+ AddPolicyFromFile(file);
+ }
+ }
+
+ public void AddDataJson(string data)
+ {
+ var dataBytes = Encoding.UTF8.GetBytes(data);
+
+ unsafe
+ {
+ fixed (byte* dataPtr = dataBytes)
+ {
+ CheckAndDropResult(RegorusFFI.API.regorus_engine_add_data_json(E, dataPtr));
+
+ }
+ }
+ }
+
+ public void AddDataFromJsonFile(string path)
+ {
+ var pathBytes = Encoding.UTF8.GetBytes(path);
+
+ unsafe
+ {
+ fixed (byte* pathPtr = pathBytes)
+ {
+ CheckAndDropResult(RegorusFFI.API.regorus_engine_add_data_from_json_file(E, pathPtr));
+
+ }
+ }
+ }
+
+ public void SetInputJson(string input)
+ {
+ var inputBytes = Encoding.UTF8.GetBytes(input);
+
+ unsafe
+ {
+ fixed (byte* inputPtr = inputBytes)
+ {
+ CheckAndDropResult(RegorusFFI.API.regorus_engine_set_input_json(E, inputPtr));
+
+ }
+ }
+ }
+
+ public void SetInputFromJsonFile(string path)
+ {
+ var pathBytes = Encoding.UTF8.GetBytes(path);
+
+ unsafe
+ {
+ fixed (byte* pathPtr = pathBytes)
+ {
+ CheckAndDropResult(RegorusFFI.API.regorus_engine_set_input_from_json_file(E, pathPtr));
+
+ }
+ }
+ }
+
+ public string EvalQuery(string query)
+ {
+ var queryBytes = Encoding.UTF8.GetBytes(query);
+
+ var resultJson = "";
+ unsafe
+ {
+ fixed (byte* queryPtr = queryBytes)
+ {
+ var result = RegorusFFI.API.regorus_engine_eval_query(E, queryPtr);
+ if (result.status == RegorusFFI.RegorusStatus.RegorusStatusOk) {
+ if (result.output != null) {
+ resultJson = System.Runtime.InteropServices.Marshal.PtrToStringAnsi((IntPtr)result.output);
+ }
+ RegorusFFI.API.regorus_result_drop(result);
+ } else {
+ CheckAndDropResult(result);
+ }
+
+ }
+ }
+ if (resultJson != null) {
+ return resultJson;
+ } else {
+ return "";
+ }
+ }
+
+ void CheckAndDropResult(RegorusFFI.RegorusResult result)
+ {
+ if (result.status != RegorusFFI.RegorusStatus.RegorusStatusOk) {
+ unsafe {
+ var message = System.Runtime.InteropServices.Marshal.PtrToStringAnsi((IntPtr)result.error_message);
+ var ex = new Exception(message);
+ RegorusFFI.API.regorus_result_drop(result);
+ throw ex;
+ }
+ }
+ RegorusFFI.API.regorus_result_drop(result);
+ }
+
+ }
+}
diff --git a/bindings/csharp/net40/RegorusCsharp-Lib-x64.nuspec b/bindings/csharp/net40/RegorusCsharp-Lib-x64.nuspec
new file mode 100644
index 0000000..8f8956c
--- /dev/null
+++ b/bindings/csharp/net40/RegorusCsharp-Lib-x64.nuspec
@@ -0,0 +1,22 @@
+
+
+
+ RegorusCsharp-Lib-x64
+ 0.2.1
+ RegorusCsharp-Lib-x64
+ yangjie@microsoft.com
+ yangjie@microsoft.com
+ https://www.microsoft.com
+ false
+ Regorus C# library for x64
+ remove Regorus.cs from Nuget
+ Copyright (C) Microsoft Corp
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/bindings/csharp/net40/regorus-test.csproj b/bindings/csharp/net40/regorus-test.csproj
new file mode 100644
index 0000000..a131fa7
--- /dev/null
+++ b/bindings/csharp/net40/regorus-test.csproj
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Exe
+ net40
+ regorus_test
+ regoregorus_test.Program
+ enable
+ true
+
+
+
+
+
diff --git a/bindings/csharp/Program.cs b/bindings/csharp/net8.0/Program.cs
similarity index 61%
rename from bindings/csharp/Program.cs
rename to bindings/csharp/net8.0/Program.cs
index ea98a62..ab52979 100644
--- a/bindings/csharp/Program.cs
+++ b/bindings/csharp/net8.0/Program.cs
@@ -1,4 +1,14 @@
-using System.Diagnostics;
+//-----------------------------------------------------------------------
+//
+// Copyright (c)2012 Microsoft. All rights reserved.
+//
+//
+// Contains code to test the Regorus class for C#
+// and .NET 8.0 bindings.
+//
+//-----------------------------------------------------------------------
+
+using System.Diagnostics;
long nanosecPerTick = (1000L*1000L*1000L) / Stopwatch.Frequency;
var w = new Stopwatch();
@@ -21,10 +31,10 @@ var newEngineTicks = w.ElapsedTicks;
w.Restart();
// Load policies and data.
-engine.AddPolicyFromFile("../../tests/aci/framework.rego");
-engine.AddPolicyFromFile("../../tests/aci/api.rego");
-engine.AddPolicyFromFile("../../tests/aci/policy.rego");
-engine.AddDataFromJsonFile("../../tests/aci/data.json");
+engine.AddPolicyFromFile("../../../tests/aci/framework.rego");
+engine.AddPolicyFromFile("../../../tests/aci/api.rego");
+engine.AddPolicyFromFile("../../../tests/aci/policy.rego");
+engine.AddDataFromJsonFile("../../../tests/aci/data.json");
w.Stop();
@@ -34,7 +44,7 @@ var loadPoliciesTicks = w.ElapsedTicks;
w.Restart();
// Set input and eval rule.
-engine.SetInputFromJsonFile("../../tests/aci/input.json");
+engine.SetInputFromJsonFile("../../../tests/aci/input.json");
var value = engine.EvalQuery("data.framework.mount_overlay");
var valueDoc = System.Text.Json.JsonDocument.Parse(value);
diff --git a/bindings/csharp/Regorus.cs b/bindings/csharp/net8.0/Regorus.cs
similarity index 100%
rename from bindings/csharp/Regorus.cs
rename to bindings/csharp/net8.0/Regorus.cs
diff --git a/bindings/csharp/regorus-test.csproj b/bindings/csharp/net8.0/regorus-test.csproj
similarity index 67%
rename from bindings/csharp/regorus-test.csproj
rename to bindings/csharp/net8.0/regorus-test.csproj
index 0267fed..a64247c 100644
--- a/bindings/csharp/regorus-test.csproj
+++ b/bindings/csharp/net8.0/regorus-test.csproj
@@ -1,10 +1,10 @@
-
-
+
+
-
+
diff --git a/bindings/python/test_extensions.py b/bindings/python/test_extensions.py
new file mode 100644
index 0000000..ef77909
--- /dev/null
+++ b/bindings/python/test_extensions.py
@@ -0,0 +1,214 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+import json
+import pytest
+import regorus
+
+TEST_EXT_NAME = "Microsoft.Azure.ActiveDirectory.AADSSHLoginForLinux"
+
+
+@pytest.fixture(name="engine", scope="function")
+def engine_fixture():
+ """
+ Fixture to handle creation and cleanup of a default policy engine.
+ New engine is created for each test case.
+ """
+ engine = regorus.Engine()
+ engine.add_policy_from_file('../../examples/extension_list/agent_extension_policy.rego')
+ yield engine
+
+
+@pytest.fixture(name="input_data")
+def input_data_fixture():
+ """
+ Fixture to handle creation and cleanup of a default input data.
+ New input data is created for each test case.
+ """
+ input_data = {
+ "extensions": {
+ TEST_EXT_NAME: {
+ "signingInfo": {
+ "extensionSigned": False
+ }
+ }
+ }
+ }
+ input_json = json.dumps(input_data)
+ yield input_json
+
+
+@pytest.fixture(name="default_data")
+def default_data_fixture():
+ """Fixture for default data"""
+ data_json = {
+ "azureGuestAgentPolicy": {
+ "policyVersion": "0.1.0",
+ "signingRules": {
+ "extensionSigned": False
+ },
+ "allowListOnly": False
+ }
+ }
+ data_json = json.dumps(data_json)
+ yield data_json
+
+
+def test_default_data_json(engine, input_data):
+ """Test the default data in json format for extension policy."""
+ data_json = {
+ "azureGuestAgentPolicy": {
+ "policyVersion": "0.1.0",
+ "signingRules": {
+ "extensionSigned": False
+ },
+ "allowListOnly": False
+ }
+ }
+ data_json = json.dumps(data_json)
+ engine.add_data_json(data_json)
+ engine.set_input_json(input_data)
+ # Eval query
+ results = engine.eval_query('data.agent_extension_policy')
+ assert results['result'][0]['expressions'][0]['value']['extensions_to_download'][TEST_EXT_NAME]['downloadAllowed']
+
+
+def test_default_data_file(engine, input_data):
+ """Test the default data in file format for extension policy."""
+ data_default_path = "../../examples/extension_list/agent-extension-default-data.json"
+ engine.add_data_from_json_file(data_default_path)
+ engine.set_input_json(input_data)
+ # Eval query
+ results = engine.eval_query('data.agent_extension_policy')
+ assert results['result'][0]['expressions'][0]['value']['extensions_to_download'][TEST_EXT_NAME]['downloadAllowed']
+
+
+def test_allow_all(engine, input_data):
+ """Test the policy engine with allow all policy."""
+ data_json = {
+ "azureGuestAgentPolicy": {
+ "policyVersion": "0.1.0",
+ "signingRules": {
+ "extensionSigned": False
+ },
+ "allowListOnly": False
+ }
+ }
+ data_json = json.dumps(data_json)
+ engine.add_data_json(data_json)
+ engine.set_input_json(input_data)
+ # Eval query
+ results = engine.eval_query('data.agent_extension_policy')
+ assert results['result'][0]['expressions'][0]['value']['extensions_to_download'][TEST_EXT_NAME]['downloadAllowed']
+
+
+def test_name_only_input(engine, default_data):
+ """Test input with only the extension name."""
+ input_data = {
+ "extensions": {
+ TEST_EXT_NAME: {
+ }
+ }
+ }
+ input_json = json.dumps(input_data)
+ engine.add_data_json(default_data)
+ engine.set_input_json(input_json)
+ # Eval query
+ results = engine.eval_query('data.agent_extension_policy')
+ assert results['result'][0]['expressions'][0]['value']['extensions_to_download'][TEST_EXT_NAME]['downloadAllowed']
+
+
+@pytest.mark.parametrize("input_signed, extension_signed", [
+ (True, True),
+ (True, False),
+ (False, True),
+ (False, False)
+])
+def test_extension_signed_rule(engine, input_signed, extension_signed):
+ """
+ Test extension signing rule. Engine should be able to handle
+ both signed and unsigned extensions, with extensionSigned rule set
+ to either true or false.
+ """
+ data_json = {
+ "azureGuestAgentPolicy": {
+ "policyVersion": "0.1.0",
+ "signingRules": {
+ "extensionSigned": extension_signed
+ },
+ "allowListOnly": False
+ }
+ }
+ input_data = {
+ "extensions": {
+ TEST_EXT_NAME: {
+ "signingInfo": {
+ "extensionSigned": input_signed
+ }
+ }
+ }
+ }
+ data_json = json.dumps(data_json)
+ input_data = json.dumps(input_data)
+ engine.add_data_json(data_json)
+ engine.set_input_json(input_data)
+ # Eval query
+ results = engine.eval_query('data.agent_extension_policy')
+
+ # assert results
+ if extension_signed:
+ assert results['result'][0]['expressions'][0]['value']['extensions_validated'][TEST_EXT_NAME]['signingValidated'] == input_signed
+ else:
+ assert results['result'][0]['expressions'][0]['value']['extensions_validated'][TEST_EXT_NAME]['signingValidated']
+ assert results['result'][0]['expressions'][0]['value']['extensions_to_download'][TEST_EXT_NAME]['downloadAllowed']
+
+
+@pytest.mark.parametrize("ext_allowed, allow_rule", [
+ (True, True),
+ (True, False),
+ (False, True),
+ (False, False)
+])
+def test_allowlist_rule(engine, ext_allowed, allow_rule):
+ """
+ Test allowListOnly rule. Engine should be able to handle
+ both allowed and disallowed extensions, with allowListOnly rule
+ set to either true or false.
+ """
+ if ext_allowed:
+ ext_name = TEST_EXT_NAME
+ else:
+ ext_name = "random_disallowed_extension"
+
+ input_json = {
+ "extensions": {
+ ext_name: {
+ "signingInfo": {
+ "extensionSigned": False
+ }
+ }
+ }
+ }
+ data_json = {
+ "azureGuestAgentPolicy": {
+ "signingRules": {
+ "extensionSigned": False
+ },
+ "allowListOnly": allow_rule
+ },
+ "azureGuestExtensionsPolicy": {
+ "Microsoft.CPlat.Core.RunCommandLinux": {
+ },
+ TEST_EXT_NAME: {
+ }
+ }
+ }
+ input_json = json.dumps(input_json)
+ data_json = json.dumps(data_json)
+ engine.add_data_json(data_json)
+ engine.set_input_json(input_json)
+ # Eval query
+ results = engine.eval_query('data.agent_extension_policy')
+ if allow_rule:
+ assert results['result'][0]['expressions'][0]['value']['extensions_to_download'][ext_name]['downloadAllowed'] == ext_allowed
+ else:
+ assert results['result'][0]['expressions'][0]['value']['extensions_to_download'][ext_name]['downloadAllowed']
diff --git a/examples/extension_list/agent-extension-data-allow-only.json b/examples/extension_list/agent-extension-data-allow-only.json
new file mode 100644
index 0000000..312e467
--- /dev/null
+++ b/examples/extension_list/agent-extension-data-allow-only.json
@@ -0,0 +1,19 @@
+{
+ "azureGuestAgentPolicy": {
+ "signingRules": {
+ "extensionSigned": true
+ },
+ "allowListOnly": true
+ },
+ "azureGuestExtensionsPolicy": {
+ "test3": {
+ "runtimeRules": {}
+ },
+ "test2": {
+ "signingRules": {
+ "extensionSigned": false
+ },
+ "runtimeRules": {}
+ }
+ }
+}
diff --git a/examples/extension_list/agent-extension-default-data.json b/examples/extension_list/agent-extension-default-data.json
new file mode 100644
index 0000000..e8c4ea8
--- /dev/null
+++ b/examples/extension_list/agent-extension-default-data.json
@@ -0,0 +1,9 @@
+{
+ "azureGuestAgentPolicy": {
+ "policyVersion": "0.1.0",
+ "signingRules": {
+ "extensionSigned": false
+ },
+ "allowListOnly": false
+ }
+}
diff --git a/examples/extension_list/agent-extension-input.json b/examples/extension_list/agent-extension-input.json
new file mode 100644
index 0000000..96af0f4
--- /dev/null
+++ b/examples/extension_list/agent-extension-input.json
@@ -0,0 +1,28 @@
+{
+ "extensions": {
+ "Microsoft.Azure.ActiveDirectory.AADSSHLoginForLinux": {
+ "signingInfo": {
+
+ "extensionSigned": false
+
+ }
+
+ },
+ "test2": {
+ "signingInfo": {
+ "extensionSigned": true
+ }
+ },
+ "test3": {
+ "signingInfo": {
+ "extensionSigned": false
+ }
+ },
+ "test1": {
+ "signingInfo": {
+ "extensionSigned": false
+ }
+ },
+ "test4": {}
+ }
+}
diff --git a/examples/extension_list/agent_extension_policy.rego b/examples/extension_list/agent_extension_policy.rego
new file mode 100644
index 0000000..99643e6
--- /dev/null
+++ b/examples/extension_list/agent_extension_policy.rego
@@ -0,0 +1,125 @@
+# Copyright (c) Microsoft Corporation.
+# Licensed under the MIT License.
+
+package agent_extension_policy
+
+import rego.v1
+
+policy_version := "0.1.0"
+
+default default_global_rules := {
+ "allowListOnly": false,
+ "signingRules": {
+ "extensionSigned": false,
+ "signingDetails": {},
+ },
+ "updateAllowed": true,
+ "uninstallAllowed": true,
+}
+
+default global_rules := {
+ "allowListOnly": false,
+ "signingRules": {
+ "extensionSigned": false,
+ "signingDetails": {},
+ },
+ "updateAllowed": true,
+ "uninstallAllowed": true,
+}
+
+global_rules := object.union(default_global_rules, data.azureGuestAgentPolicy) if {
+ data.azureGuestAgentPolicy
+}
+
+default any_extension_allowed := true
+
+any_extension_allowed := false if {
+ global_rules.allowListOnly
+}
+
+default default_signing_info := {"signingInfo": {}}
+
+# Download rule 1: if the extension is in the list and download rule satisfied: download allowed
+extensions_to_download[name] := extension if {
+ some name, input_extension in input.extensions
+ data.azureGuestExtensionsPolicy[name]
+ download_rule_validated(input_extension, data.azureGuestExtensionsPolicy[name])
+ extension := object.union(input_extension, {"downloadAllowed": true})
+}
+
+# Download rule 2: if the extension is in the list and download rule not satisfied: download denied
+extensions_to_download[name] := extension if {
+ some name, input_extension in input.extensions
+ data.azureGuestExtensionsPolicy[name]
+ not download_rule_validated(input_extension, data.azureGuestExtensionsPolicy[name])
+ extension := object.union(input_extension, {"downloadAllowed": false})
+}
+
+# Download rule 3: if the extension is not in the list: depending on allowListOnly on or off
+extensions_to_download[name] := extension if {
+ some name, input_extension in input.extensions
+ not data.azureGuestExtensionsPolicy[name]
+ extension := object.union(input_extension, {"downloadAllowed": any_extension_allowed})
+}
+
+# Validate rule 1: if individual signing rule exists, signing rule validated according to the rules
+extensions_validated[name] := extension if {
+ some name, input_extension in input.extensions
+ data.azureGuestExtensionsPolicy[name]
+
+ extension_global_rules := object.union(global_rules, data.azureGuestExtensionsPolicy[name])
+ extension_signing_info := object.union(extension_global_rules, default_signing_info)
+ output := object.union(input_extension, extension_signing_info)
+ signing_validated(output.signingInfo, output.signingRules)
+ extension := object.union(output, {"signingValidated": true})
+}
+
+# Validate rule 2: if indivual signing rule exists, signing rule not validated according to the rules
+extensions_validated[name] := extension if {
+ some name, input_extension in input.extensions
+ data.azureGuestExtensionsPolicy[name]
+
+ extension_global_rules := object.union(global_rules, data.azureGuestExtensionsPolicy[name])
+ extension_signing_info := object.union(extension_global_rules, default_signing_info)
+ output := object.union(input_extension, extension_signing_info)
+ not signing_validated(output.signingInfo, output.signingRules)
+ extension := object.union(output, {"signingValidated": false})
+}
+
+# Validate rule 3: if individual signing rule doesn't exist, signing rule validated according to global signing rule
+extensions_validated[name] := extension if {
+ some name, input_extension in input.extensions
+ not data.azureGuestExtensionsPolicy[name]
+ extension_global_rules := object.union(input_extension, global_rules)
+ output := object.union(extension_global_rules, default_signing_info)
+ signing_validated(output.signingInfo, output.signingRules)
+ extension := object.union(output, {"signingValidated": true})
+}
+
+# Validate rule 4: if individual signing rule doesn't exist, signing rule not validated according to the global rules
+extensions_validated[name] := extension if {
+ some name, input_extension in input.extensions
+ not data.azureGuestExtensionsPolicy[name]
+ extension_global_rules := object.union(input_extension, global_rules)
+ output := object.union(extension_global_rules, default_signing_info)
+ not signing_validated(output.signingInfo, output.signingRules)
+ extension := object.union(output, {"signingValidated": false})
+}
+
+# Currently if download rules doesn't exist, allow the extension because its name is in the list.
+# In the future additional rules can be checked with downloadRules present.
+download_rule_validated(_, rules) if {
+ not rules.downloadRules
+}
+
+# Signing is validated if input comes with extension signed, or the input of signing information is matching the
+# rules in data.
+signing_validated(signingInfo, signingRules) if {
+ signingInfo
+ signingRules
+ signingInfo.extensionSigned
+} else if {
+ signingInfo
+ signingRules
+ signingInfo.extensionSigned == signingRules.extensionSigned
+}
diff --git a/examples/example.rego b/examples/server/allowed_server.rego
similarity index 100%
rename from examples/example.rego
rename to examples/server/allowed_server.rego
diff --git a/examples/input.json b/examples/server/input.json
similarity index 100%
rename from examples/input.json
rename to examples/server/input.json