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