diff --git a/src/confcom/azext_confcom/custom.py b/src/confcom/azext_confcom/custom.py index 89d727f4f21..a1b04c70ccd 100644 --- a/src/confcom/azext_confcom/custom.py +++ b/src/confcom/azext_confcom/custom.py @@ -97,7 +97,8 @@ def acipolicygen_confcom( # gather information about the fragments being used in the new policy if include_fragments: fragments_list = os_util.load_json_from_file(fragments_json or input_path) - fragments_list = fragments_list.get("fragments", []) or fragments_list + if isinstance(fragments_list, dict): + fragments_list = fragments_list.get("fragments", []) # convert to list if it's just a dict if not isinstance(fragments_list, list): @@ -165,11 +166,13 @@ def acipolicygen_confcom( fragment_imports.extend(policy.get_fragments()) for container in policy.get_images(): container_names.append(container.get_container_image()) + # get all the fragments that are being used in the policy fragment_policy_list = get_all_fragment_contents(container_names, fragment_imports) for policy in container_group_policies: policy.set_fragment_contents(fragment_policy_list) for count, policy in enumerate(container_group_policies): + # this is where parameters and variables are populated policy.populate_policy_content_for_all_images( individual_image=bool(image_name), tar_mapping=tar_mapping, faster_hashing=faster_hashing ) diff --git a/src/confcom/azext_confcom/oras_proxy.py b/src/confcom/azext_confcom/oras_proxy.py index c232a46d3f3..3fd95882996 100644 --- a/src/confcom/azext_confcom/oras_proxy.py +++ b/src/confcom/azext_confcom/oras_proxy.py @@ -16,6 +16,38 @@ host_os = platform.system() machine = platform.machine() +from knack.log import get_logger + +logger = get_logger(__name__) + + +def prepend_docker_registry(image_name: str) -> str: + """ + Normalize a Docker image reference by adding `docker.io/library` if necessary. + + Args: + image (str): The Docker image reference (e.g., `nginx:latest` or `myrepo/myimage`). + + Returns: + str: The normalized Docker image reference. + """ + # Split the image into name and tag + if ":" in image_name: + name, _ = image_name.rsplit(":", 1) + else: + name, _ = image_name, "latest" + + registry = "" + # Check if the image name contains a registry (e.g., docker.io, custom registry) + if "/" not in name or "." not in name.split("/")[0]: + # If no registry is specified, assume docker.io/library + if "/" not in name: + # Add the `library` namespace for official images + registry = f"library/" + # Add the default `docker.io` registry + registry = f"docker.io/" + registry + + return f"{registry}{image_name}" def call_oras_cli(args, check=False): return subprocess.run(args, check=check, capture_output=True, timeout=120) @@ -26,10 +58,14 @@ def call_oras_cli(args, check=False): def discover( image: str, ) -> List[str]: + # normalize the name in case the docker registry is implied + image = prepend_docker_registry(image) + arg_list = ["oras", "discover", image, "-o", "json", "--artifact-type", ARTIFACT_TYPE] item = call_oras_cli(arg_list, check=False) hashes = [] + logger.info(f"Discovering fragments for {image}: {item.stdout.decode('utf-8')}") if item.returncode == 0: json_output = json.loads(item.stdout.decode("utf-8")) manifests = json_output.get("manifests", []) diff --git a/src/confcom/azext_confcom/security_policy.py b/src/confcom/azext_confcom/security_policy.py index 34dbb7eddc9..d9fa3f4240e 100644 --- a/src/confcom/azext_confcom/security_policy.py +++ b/src/confcom/azext_confcom/security_policy.py @@ -136,6 +136,7 @@ def __init__( container_image = UserContainerImage.from_json(c, is_vn2=is_vn2) else: container_image = ContainerImage.from_json(c) + container_image.parse_all_parameters_and_variables(self.all_params, self.all_vars) container_results.append(container_image) self._images = container_results @@ -431,7 +432,6 @@ def populate_policy_content_for_all_images( message_queue = [] # populate regular container images(s) for image in container_images: - image.parse_all_parameters_and_variables(AciPolicy.all_params, AciPolicy.all_vars) image_name = f"{image.base}:{image.tag}" image_info, tar = get_image_info(progress, message_queue, tar_mapping, image) @@ -517,6 +517,7 @@ def populate_policy_content_for_all_images( } image.set_user(user) + # should have all fragments before this point if self._fragment_contents and self.should_eliminate_container_covered_by_fragments(image): # these containers will get taken out later in the function # since they are covered by a fragment @@ -694,8 +695,8 @@ def load_policy_from_arm_template_str( ] = infrastructure_svn if rego_imports: # error check the rego imports for invalid data types - process_fragment_imports(rego_imports) - rego_fragments.extend(rego_imports) + processed_imports = process_fragment_imports(rego_imports) + rego_fragments.extend(processed_imports) volumes = ( case_insensitive_dict_get( diff --git a/src/confcom/azext_confcom/template_util.py b/src/confcom/azext_confcom/template_util.py index b28424de3a0..b7df4572c48 100644 --- a/src/confcom/azext_confcom/template_util.py +++ b/src/confcom/azext_confcom/template_util.py @@ -509,6 +509,8 @@ def process_env_vars_from_config(container) -> List[Dict[str, str]]: def process_fragment_imports(rego_imports) -> None: for rego_import in rego_imports: + if not rego_import: + continue feed = case_insensitive_dict_get( rego_import, config.POLICY_FIELD_CONTAINERS_ELEMENTS_REGO_FRAGMENTS_FEED ) @@ -550,6 +552,8 @@ def process_fragment_imports(rego_imports) -> None: + "can only be a list value." ) + return rego_imports + def process_mounts(image_properties: dict, volumes: List[dict]) -> List[Dict[str, str]]: mount_source_table_keys = config.MOUNT_SOURCE_TABLE.keys()