From 08a57f262ade7132a6315bba6575ad6f7bd9ebb0 Mon Sep 17 00:00:00 2001 From: Dominic Ayre Date: Mon, 10 Nov 2025 14:31:46 +0000 Subject: [PATCH 01/12] Add a --with-containers flag to policy and fragment gen --- src/confcom/.gitignore | 2 + src/confcom/azext_confcom/_params.py | 16 +++ src/confcom/azext_confcom/_validators.py | 9 +- src/confcom/azext_confcom/custom.py | 31 ++++- src/confcom/azext_confcom/lib/policy.py | 124 +++++++++++++++++++ src/confcom/azext_confcom/security_policy.py | 13 +- 6 files changed, 188 insertions(+), 7 deletions(-) create mode 100644 src/confcom/azext_confcom/lib/policy.py diff --git a/src/confcom/.gitignore b/src/confcom/.gitignore index 562e4134172..6d554baf4b2 100644 --- a/src/confcom/.gitignore +++ b/src/confcom/.gitignore @@ -36,3 +36,5 @@ azext_confcom/bin/* **/.coverage **/htmlcov + +!lib/ diff --git a/src/confcom/azext_confcom/_params.py b/src/confcom/azext_confcom/_params.py index 08bf87e28fc..12d886664fe 100644 --- a/src/confcom/azext_confcom/_params.py +++ b/src/confcom/azext_confcom/_params.py @@ -198,6 +198,14 @@ def load_arguments(self, _): required=False, help="Exclude default fragments in the generated policy", ) + c.argument( + "container_definitions", + options_list=['--with-containers'], + action='append', + required=False, + default=[], + help='Container definitions to include in the policy' + ) with self.argument_context("confcom acifragmentgen") as c: c.argument( @@ -345,6 +353,14 @@ def load_arguments(self, _): help="Path to JSON file to write fragment import information. This is used with --generate-import. If not specified, the import statement will print to the console", validator=validate_fragment_json, ) + c.argument( + "container_definitions", + options_list=['--with-containers'], + action='append', + required=False, + default=[], + help='Container definitions to include in the policy' + ) with self.argument_context("confcom katapolicygen") as c: c.argument( diff --git a/src/confcom/azext_confcom/_validators.py b/src/confcom/azext_confcom/_validators.py index be669f70d8d..a35fb2af057 100644 --- a/src/confcom/azext_confcom/_validators.py +++ b/src/confcom/azext_confcom/_validators.py @@ -38,7 +38,8 @@ def validate_aci_source(namespace): namespace.input_path, namespace.arm_template, namespace.image_name, - namespace.virtual_node_yaml_path + namespace.virtual_node_yaml_path, + namespace.container_definitions != [], ])) != 1: raise CLIError("Can only generate CCE policy from one source at a time") @@ -71,7 +72,11 @@ def validate_fragment_key_and_chain(namespace): def validate_fragment_source(namespace): - if not namespace.generate_import and sum(map(bool, [namespace.image_name, namespace.input_path])) != 1: + if not namespace.generate_import and sum(map(bool, [ + namespace.image_name, + namespace.input_path, + namespace.container_definitions != [], + ])) != 1: raise CLIError("Must provide either an image name or an input file to generate a fragment") diff --git a/src/confcom/azext_confcom/custom.py b/src/confcom/azext_confcom/custom.py index acd513f2038..0476940976f 100644 --- a/src/confcom/azext_confcom/custom.py +++ b/src/confcom/azext_confcom/custom.py @@ -11,13 +11,13 @@ from azext_confcom._validators import resolve_stdio from azext_confcom.config import ( DEFAULT_REGO_FRAGMENTS, POLICY_FIELD_CONTAINERS_ELEMENTS_REGO_FRAGMENTS, - REGO_IMPORT_FILE_STRUCTURE) + REGO_IMPORT_FILE_STRUCTURE, ACI_FIELD_VERSION, ACI_FIELD_CONTAINERS) from azext_confcom.cose_proxy import CoseSignToolProxy from azext_confcom.errors import eprint from azext_confcom.fragment_util import get_all_fragment_contents from azext_confcom.init_checks import run_initial_docker_checks from azext_confcom.kata_proxy import KataPolicyGenProxy -from azext_confcom.security_policy import OutputType +from azext_confcom.security_policy import AciPolicy, OutputType from azext_confcom.template_util import ( get_image_name, inject_policy_into_template, inject_policy_into_yaml, pretty_print_func, print_existing_policy_from_arm_template, @@ -37,6 +37,7 @@ def acipolicygen_confcom( virtual_node_yaml_path: str, infrastructure_svn: str, tar_mapping_location: str, + container_definitions: list, approve_wildcards: str = False, outraw: bool = False, outraw_pretty_print: bool = False, @@ -147,6 +148,16 @@ def acipolicygen_confcom( exclude_default_fragments=exclude_default_fragments, infrastructure_svn=infrastructure_svn, ) + elif container_definitions: + container_group_policies = AciPolicy( + { + ACI_FIELD_VERSION: "1.0", + ACI_FIELD_CONTAINERS: [], + }, + debug_mode=debug_mode, + disable_stdio=disable_stdio, + container_definitions=container_definitions, + ) exit_code = 0 @@ -227,6 +238,7 @@ def acifragmentgen_confcom( key: str, chain: str, minimum_svn: str, + container_definitions: list, image_target: str = "", algo: str = "ES384", fragment_path: str = None, @@ -299,13 +311,24 @@ def acifragmentgen_confcom( policy = security_policy.load_policy_from_image_name( image_name, debug_mode=debug_mode, disable_stdio=(not stdio_enabled) ) - else: + elif input_path: # this is using --input if not tar_mapping: tar_mapping = os_util.load_tar_mapping_from_config_file(input_path) policy = security_policy.load_policy_from_json_file( input_path, debug_mode=debug_mode, disable_stdio=(not stdio_enabled) ) + elif container_definitions: + policy = AciPolicy( + { + ACI_FIELD_VERSION: "1.0", + ACI_FIELD_CONTAINERS: [], + }, + debug_mode=debug_mode, + disable_stdio=disable_stdio, + container_definitions=container_definitions, + ) + # get all of the fragments that are being used in the policy # and associate them with each container group fragment_policy_list = [] @@ -321,7 +344,7 @@ def acifragmentgen_confcom( # make sure we have images to generate a fragment policy_images = policy.get_images() - if not policy_images: + if not policy_images and not container_definitions: eprint("No images found in the policy or all images are covered by fragments") if not feed: diff --git a/src/confcom/azext_confcom/lib/policy.py b/src/confcom/azext_confcom/lib/policy.py new file mode 100644 index 00000000000..1ae413a51f6 --- /dev/null +++ b/src/confcom/azext_confcom/lib/policy.py @@ -0,0 +1,124 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from dataclasses import dataclass, field, is_dataclass +import inspect +import sys +from typing import Literal, Optional + + +def get_default_capabilities(): + return [ + "CAP_AUDIT_WRITE", + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FOWNER", + "CAP_FSETID", + "CAP_KILL", + "CAP_MKNOD", + "CAP_NET_BIND_SERVICE", + "CAP_NET_RAW", + "CAP_SETFCAP", + "CAP_SETGID", + "CAP_SETPCAP", + "CAP_SETUID", + "CAP_SYS_CHROOT" + ] + +@dataclass +class ContainerCapabilities: + ambient: list[str] = field(default_factory=list) + bounding: list[str] = field(default_factory=get_default_capabilities) + effective: list[str] = field(default_factory=get_default_capabilities) + inheritable: list[str] = field(default_factory=list) + permitted: list[str] = field(default_factory=get_default_capabilities) + + +@dataclass +class ContainerRule: + pattern: str + strategy: str + required: Optional[bool] = False + + +@dataclass +class ContainerExecProcesses: + command: list[str] + signals: Optional[list[str]] = None + allow_stdio_access: bool = True + + +@dataclass +class ContainerMount: + destination: str + source: str + type: str + options: list[str] = field(default_factory=list) + + +@dataclass +class ContainerUser: + group_idnames: list[ContainerRule] = field(default_factory=lambda: [ContainerRule(pattern="", strategy="any")]) + umask: str = "0022" + user_idname: ContainerRule = field(default_factory=lambda: ContainerRule(pattern="", strategy="any")) + + +@dataclass +class FragmentReference: + feed: str + issuer: str + minimum_svn: str + includes: list[Literal["containers", "fragments", "namespace", "external_processes"]] + path: Optional[str] = None + + +@dataclass +class Container: + allow_elevated: bool = False + allow_stdio_access: bool = True + capabilities: ContainerCapabilities = field(default_factory=ContainerCapabilities) + command: Optional[list[str]] = None + env_rules: list[ContainerRule] = field(default_factory=list) + exec_processes: list[ContainerExecProcesses] = field(default_factory=list) + id: Optional[str] = None + layers: list[str] = field(default_factory=list) + mounts: list[ContainerMount] = field(default_factory=list) + name: Optional[str] = None, + no_new_privileges: bool = False + seccomp_profile_sha256: str = "" + signals: list[str] = field(default_factory=list) + user: ContainerUser = field(default_factory=ContainerUser) + working_dir: str = "/" + + +@dataclass +class Policy: + package: str = "policy" + api_version: str = "0.10.0" + framework_version: str = "0.2.3" + fragments: list[FragmentReference] = field(default_factory=list) + containers: list[Container] = field(default_factory=list) + allow_properties_access: bool = True + allow_dump_stacks: bool = False + allow_runtime_logging: bool = False + allow_environment_variable_dropping: bool = True + allow_unencrypted_scratch: bool = False + allow_capability_dropping: bool = True + + +@dataclass +class Fragment: + package: str = "fragment" + svn: str = "0" + framework_version: str = "0.2.3" + fragments: list[FragmentReference] = field(default_factory=list) + containers: list[Container] = field(default_factory=list) + + +POLICY_CLASSES = [ + cls + for _, cls in inspect.getmembers(sys.modules[__name__], inspect.isclass) + if is_dataclass(cls) and cls.__module__ == sys.modules[__name__].__name__ +] \ No newline at end of file diff --git a/src/confcom/azext_confcom/security_policy.py b/src/confcom/azext_confcom/security_policy.py index 6dc65771612..9ec1ef30088 100644 --- a/src/confcom/azext_confcom/security_policy.py +++ b/src/confcom/azext_confcom/security_policy.py @@ -4,13 +4,16 @@ # -------------------------------------------------------------------------------------------- import copy +from dataclasses import asdict import json +import tempfile import warnings from enum import Enum, auto -from typing import Any, Dict, List, Tuple, Union +from typing import Any, Dict, List, Optional, Tuple, Union import deepdiff from azext_confcom import config, os_util +from azext_confcom.lib.policy import Container from azext_confcom.container import ContainerImage, UserContainerImage from azext_confcom.errors import eprint from azext_confcom.fragment_util import sanitize_fragment_fields @@ -65,6 +68,7 @@ def __init__( disable_stdio: bool = False, is_vn2: bool = False, fragment_contents: Any = None, + container_definitions: Optional[list] = None, ) -> None: self._rootfs_proxy = None self._policy_str = None @@ -74,6 +78,7 @@ def __init__( self._existing_fragments = existing_rego_fragments self._api_version = config.API_VERSION self._fragment_contents = fragment_contents + self._container_definitions = container_definitions or [] if debug_mode: self._allow_properties_access = config.DEBUG_MODE_SETTINGS.get( @@ -399,6 +404,12 @@ def _policy_serialization(self, pretty_print=False, include_sidecars: bool = Tru for container in policy: container[config.POLICY_FIELD_CONTAINERS_ELEMENTS_ALLOW_STDIO_ACCESS] = False + if self._container_definitions: + policy += [ + asdict(Container(**json.loads(c))) + for c in self._container_definitions + ] + if pretty_print: return pretty_print_func(policy) return print_func(policy) From dcdfeff02af20906b1402deba54523aae02ca477 Mon Sep 17 00:00:00 2001 From: Dominic Ayre Date: Mon, 10 Nov 2025 14:48:22 +0000 Subject: [PATCH 02/12] Style fixes --- src/confcom/azext_confcom/custom.py | 3 +++ src/confcom/azext_confcom/lib/policy.py | 3 ++- src/confcom/azext_confcom/security_policy.py | 1 - 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/confcom/azext_confcom/custom.py b/src/confcom/azext_confcom/custom.py index 0476940976f..7748227aa7c 100644 --- a/src/confcom/azext_confcom/custom.py +++ b/src/confcom/azext_confcom/custom.py @@ -328,6 +328,9 @@ def acifragmentgen_confcom( disable_stdio=disable_stdio, container_definitions=container_definitions, ) + else: + eprint("Either --image-name, --input, or --container-definitions must be provided", exit_code=2) + return # get all of the fragments that are being used in the policy # and associate them with each container group diff --git a/src/confcom/azext_confcom/lib/policy.py b/src/confcom/azext_confcom/lib/policy.py index 1ae413a51f6..be1f2e04cab 100644 --- a/src/confcom/azext_confcom/lib/policy.py +++ b/src/confcom/azext_confcom/lib/policy.py @@ -27,6 +27,7 @@ def get_default_capabilities(): "CAP_SYS_CHROOT" ] + @dataclass class ContainerCapabilities: ambient: list[str] = field(default_factory=list) @@ -121,4 +122,4 @@ class Fragment: cls for _, cls in inspect.getmembers(sys.modules[__name__], inspect.isclass) if is_dataclass(cls) and cls.__module__ == sys.modules[__name__].__name__ -] \ No newline at end of file +] diff --git a/src/confcom/azext_confcom/security_policy.py b/src/confcom/azext_confcom/security_policy.py index 9ec1ef30088..76ce4c5f5bc 100644 --- a/src/confcom/azext_confcom/security_policy.py +++ b/src/confcom/azext_confcom/security_policy.py @@ -6,7 +6,6 @@ import copy from dataclasses import asdict import json -import tempfile import warnings from enum import Enum, auto from typing import Any, Dict, List, Optional, Tuple, Union From cb2499f9c4636bc33522d0178f15ebe9c59ec5f7 Mon Sep 17 00:00:00 2001 From: Dominic Ayre Date: Mon, 10 Nov 2025 14:50:43 +0000 Subject: [PATCH 03/12] Update HISTORY.rst --- src/confcom/HISTORY.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/confcom/HISTORY.rst b/src/confcom/HISTORY.rst index f45806e4666..f703f21eef7 100644 --- a/src/confcom/HISTORY.rst +++ b/src/confcom/HISTORY.rst @@ -3,6 +3,10 @@ Release History =============== +1.3.1 +++++++ +* Add --with-containers flag to acipolicygen and acifragmentgen to allow passing container policy definitions directly + 1.3.0 ++++++ * Add a new --enable-stdio flag, with a warning if neither this or --disable-stdio is set From 912fd6f7a7b0c877336f553d84848de22c18f2ca Mon Sep 17 00:00:00 2001 From: Dominic Ayre Date: Mon, 10 Nov 2025 14:53:59 +0000 Subject: [PATCH 04/12] Bump confcom version --- src/confcom/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/confcom/setup.py b/src/confcom/setup.py index 2b623cbd2c0..a5a28dc8782 100644 --- a/src/confcom/setup.py +++ b/src/confcom/setup.py @@ -19,7 +19,7 @@ logger.warn("Wheel is not available, disabling bdist_wheel hook") -VERSION = "1.3.1" +VERSION = "1.3.2" # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers From 68e9aee31580de9ba470ed014768487e2068fc53 Mon Sep 17 00:00:00 2001 From: Dominic Ayre Date: Mon, 10 Nov 2025 15:16:17 +0000 Subject: [PATCH 05/12] Support more kinds of container defs --- src/confcom/azext_confcom/_params.py | 5 +++-- src/confcom/azext_confcom/security_policy.py | 14 +++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/confcom/azext_confcom/_params.py b/src/confcom/azext_confcom/_params.py index 12d886664fe..ccbea8d0091 100644 --- a/src/confcom/azext_confcom/_params.py +++ b/src/confcom/azext_confcom/_params.py @@ -4,6 +4,7 @@ # -------------------------------------------------------------------------------------------- # pylint: disable=line-too-long +import json from knack.arguments import CLIArgumentType from azext_confcom._validators import ( validate_params_file, @@ -202,8 +203,8 @@ def load_arguments(self, _): "container_definitions", options_list=['--with-containers'], action='append', + type=json.loads, required=False, - default=[], help='Container definitions to include in the policy' ) @@ -358,7 +359,7 @@ def load_arguments(self, _): options_list=['--with-containers'], action='append', required=False, - default=[], + type=json.loads, help='Container definitions to include in the policy' ) diff --git a/src/confcom/azext_confcom/security_policy.py b/src/confcom/azext_confcom/security_policy.py index 4a8ef88f665..73219e21518 100644 --- a/src/confcom/azext_confcom/security_policy.py +++ b/src/confcom/azext_confcom/security_policy.py @@ -79,6 +79,14 @@ def __init__( self._fragment_contents = fragment_contents self._container_definitions = container_definitions or [] + self._container_definitions = [] + if container_definitions: + for container_definition in container_definitions: + if isinstance(container_definition, list): + self._container_definitions.extend(container_definition) + else: + self._container_definitions.append(container_definition) + if debug_mode: self._allow_properties_access = config.DEBUG_MODE_SETTINGS.get( "allowPropertiesAccess" @@ -403,11 +411,7 @@ def _policy_serialization(self, pretty_print=False, include_sidecars: bool = Tru for container in policy: container[config.POLICY_FIELD_CONTAINERS_ELEMENTS_ALLOW_STDIO_ACCESS] = False - if self._container_definitions: - policy += [ - asdict(Container(**json.loads(c))) - for c in self._container_definitions - ] + policy += [asdict(Container(**c)) for c in self._container_definitions] if pretty_print: return pretty_print_func(policy) From b81b71806c61a22dfa2b36fbdc76543902e16116 Mon Sep 17 00:00:00 2001 From: Dominic Ayre Date: Mon, 10 Nov 2025 15:17:03 +0000 Subject: [PATCH 06/12] Update version --- src/confcom/HISTORY.rst | 2 +- src/confcom/setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/confcom/HISTORY.rst b/src/confcom/HISTORY.rst index f5a09d5191b..5c8c7d2bc04 100644 --- a/src/confcom/HISTORY.rst +++ b/src/confcom/HISTORY.rst @@ -3,7 +3,7 @@ Release History =============== -1.3.2 +1.4.0 ++++++ * Add --with-containers flag to acipolicygen and acifragmentgen to allow passing container policy definitions directly diff --git a/src/confcom/setup.py b/src/confcom/setup.py index a5a28dc8782..feffafbb021 100644 --- a/src/confcom/setup.py +++ b/src/confcom/setup.py @@ -19,7 +19,7 @@ logger.warn("Wheel is not available, disabling bdist_wheel hook") -VERSION = "1.3.2" +VERSION = "1.4.0" # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers From feb5e558cf4e8d1ab9cc4ae535a6fc9c816d0dcb Mon Sep 17 00:00:00 2001 From: Dominic Ayre Date: Mon, 10 Nov 2025 15:18:25 +0000 Subject: [PATCH 07/12] Cleanup --- src/confcom/azext_confcom/lib/policy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/confcom/azext_confcom/lib/policy.py b/src/confcom/azext_confcom/lib/policy.py index be1f2e04cab..fc02bea9d9b 100644 --- a/src/confcom/azext_confcom/lib/policy.py +++ b/src/confcom/azext_confcom/lib/policy.py @@ -86,7 +86,7 @@ class Container: id: Optional[str] = None layers: list[str] = field(default_factory=list) mounts: list[ContainerMount] = field(default_factory=list) - name: Optional[str] = None, + name: Optional[str] = None no_new_privileges: bool = False seccomp_profile_sha256: str = "" signals: list[str] = field(default_factory=list) From 0f2e156d9c47e06dbf8bec856da5451fe6379928 Mon Sep 17 00:00:00 2001 From: Dominic Ayre Date: Mon, 10 Nov 2025 15:21:32 +0000 Subject: [PATCH 08/12] Remove unused array --- src/confcom/azext_confcom/lib/policy.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/confcom/azext_confcom/lib/policy.py b/src/confcom/azext_confcom/lib/policy.py index fc02bea9d9b..f8257b423eb 100644 --- a/src/confcom/azext_confcom/lib/policy.py +++ b/src/confcom/azext_confcom/lib/policy.py @@ -116,10 +116,3 @@ class Fragment: framework_version: str = "0.2.3" fragments: list[FragmentReference] = field(default_factory=list) containers: list[Container] = field(default_factory=list) - - -POLICY_CLASSES = [ - cls - for _, cls in inspect.getmembers(sys.modules[__name__], inspect.isclass) - if is_dataclass(cls) and cls.__module__ == sys.modules[__name__].__name__ -] From ab47ad55de2fcb51f107d86daf5d9ac153a8e684 Mon Sep 17 00:00:00 2001 From: Dominic Ayre Date: Mon, 10 Nov 2025 16:26:38 +0000 Subject: [PATCH 09/12] Fix existing tests --- src/confcom/azext_confcom/custom.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/confcom/azext_confcom/custom.py b/src/confcom/azext_confcom/custom.py index 7748227aa7c..e617866980f 100644 --- a/src/confcom/azext_confcom/custom.py +++ b/src/confcom/azext_confcom/custom.py @@ -37,7 +37,7 @@ def acipolicygen_confcom( virtual_node_yaml_path: str, infrastructure_svn: str, tar_mapping_location: str, - container_definitions: list, + container_definitions: Optional[list] = None, approve_wildcards: str = False, outraw: bool = False, outraw_pretty_print: bool = False, @@ -65,6 +65,9 @@ def acipolicygen_confcom( "For additional information, see http://aka.ms/clisecrets. \n", ) + if container_definitions is None: + container_definitions = [] + stdio_enabled = resolve_stdio(enable_stdio, disable_stdio) if print_existing_policy and arm_template: From ec3166296a752259ea930ab54ee28bd85085e93b Mon Sep 17 00:00:00 2001 From: Dominic Ayre Date: Mon, 10 Nov 2025 16:26:44 +0000 Subject: [PATCH 10/12] Add new basic tests --- .../latest/test_confcom_acipolicygen_arm.py | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/src/confcom/azext_confcom/tests/latest/test_confcom_acipolicygen_arm.py b/src/confcom/azext_confcom/tests/latest/test_confcom_acipolicygen_arm.py index d7dff77f2dd..991201ef6e3 100644 --- a/src/confcom/azext_confcom/tests/latest/test_confcom_acipolicygen_arm.py +++ b/src/confcom/azext_confcom/tests/latest/test_confcom_acipolicygen_arm.py @@ -252,4 +252,45 @@ def test_acipolicygen_arm_diff_with_allow_all(): } +@pytest.mark.parametrize( + "container_definitions", + [ + ["{}"], # Single empty container definition (use all default values) + ["{}", "{}"], # Two empty container definitions + ["[{}]", "{}"], # Two empty container definitions, one in subarray + ["[{}, {}]", "{}"], # Three empty container definitions, two in subarray + ['{"id": "test"}'], # Single container definition a field changed + ] +) +def test_acipolicygen_with_containers(container_definitions): + + acipolicygen_confcom( + input_path=None, + arm_template=None, + arm_template_parameters=None, + image_name=None, + virtual_node_yaml_path=None, + infrastructure_svn=None, + tar_mapping_location=None, + outraw=True, + container_definitions=[json.loads(c) for c in container_definitions] + ) + +def test_acipolicygen_with_containers_field_changed(): + + buffer = io.StringIO() + with contextlib.redirect_stdout(buffer): + acipolicygen_confcom( + input_path=None, + arm_template=None, + arm_template_parameters=None, + image_name=None, + virtual_node_yaml_path=None, + infrastructure_svn=None, + tar_mapping_location=None, + outraw=True, + container_definitions=[json.loads('{"id": "test"}')] + ) + actual_policy = buffer.getvalue() + assert '"id":"test"' in actual_policy From b7bd7bdeab17ebdf950f529938f0b3ba1661a7de Mon Sep 17 00:00:00 2001 From: Dominic Ayre Date: Mon, 10 Nov 2025 16:32:27 +0000 Subject: [PATCH 11/12] Fix running without --with-containers --- src/confcom/azext_confcom/_validators.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/confcom/azext_confcom/_validators.py b/src/confcom/azext_confcom/_validators.py index a35fb2af057..bcc7f13b465 100644 --- a/src/confcom/azext_confcom/_validators.py +++ b/src/confcom/azext_confcom/_validators.py @@ -39,7 +39,7 @@ def validate_aci_source(namespace): namespace.arm_template, namespace.image_name, namespace.virtual_node_yaml_path, - namespace.container_definitions != [], + namespace.container_definitions is not None, ])) != 1: raise CLIError("Can only generate CCE policy from one source at a time") @@ -75,7 +75,7 @@ def validate_fragment_source(namespace): if not namespace.generate_import and sum(map(bool, [ namespace.image_name, namespace.input_path, - namespace.container_definitions != [], + namespace.container_definitions is not None, ])) != 1: raise CLIError("Must provide either an image name or an input file to generate a fragment") From 3e99378f3ff5a20c85092b6842c539731b043ee5 Mon Sep 17 00:00:00 2001 From: Dominic Ayre Date: Tue, 11 Nov 2025 09:17:28 +0000 Subject: [PATCH 12/12] Fix acifragmentgen with no containers --- src/confcom/azext_confcom/custom.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/confcom/azext_confcom/custom.py b/src/confcom/azext_confcom/custom.py index e617866980f..2f90c796bbd 100644 --- a/src/confcom/azext_confcom/custom.py +++ b/src/confcom/azext_confcom/custom.py @@ -241,7 +241,7 @@ def acifragmentgen_confcom( key: str, chain: str, minimum_svn: str, - container_definitions: list, + container_definitions: Optional[list] = None, image_target: str = "", algo: str = "ES384", fragment_path: str = None, @@ -256,6 +256,8 @@ def acifragmentgen_confcom( no_print: bool = False, fragments_json: str = "", ): + if container_definitions is None: + container_definitions = [] stdio_enabled = resolve_stdio(enable_stdio, disable_stdio)