diff --git a/CHANGELOG.md b/CHANGELOG.md index 89f983561c..f72f723325 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,42 @@ # CHANGELOG -## [Unreleased](https://github.com/bridgecrewio/checkov/compare/3.2.487...HEAD) +## [Unreleased](https://github.com/bridgecrewio/checkov/compare/3.2.493...HEAD) + +## [3.2.493](https://github.com/bridgecrewio/checkov/compare/3.2.492...3.2.493) - 2025-11-12 + +### Feature + +- **general:** support skips for module for_each and count - [#7368](https://github.com/bridgecrewio/checkov/pull/7368) + +## [3.2.492](https://github.com/bridgecrewio/checkov/compare/3.2.491...3.2.492) - 2025-11-10 + +### Bug Fix + +- **terraform:** get_resource_tags handles more cases - [#7365](https://github.com/bridgecrewio/checkov/pull/7365) + +## [3.2.491](https://github.com/bridgecrewio/checkov/compare/3.2.490...3.2.491) - 2025-11-09 + +### Bug Fix + +- **terraform:** Graph report tags should be dict - [#7363](https://github.com/bridgecrewio/checkov/pull/7363) + +## [3.2.490](https://github.com/bridgecrewio/checkov/compare/3.2.489...3.2.490) - 2025-11-04 + +### Feature + +- **general:** Fix downloading of the external modules when ref is a shortened Git hash - [#7278](https://github.com/bridgecrewio/checkov/pull/7278) + +## [3.2.489](https://github.com/bridgecrewio/checkov/compare/3.2.488...3.2.489) - 2025-10-29 + +### Bug Fix + +- **helm:** Check HELM_NAMESPACE env var in CKV_K8S_21 - [#7355](https://github.com/bridgecrewio/checkov/pull/7355) + +## [3.2.488](https://github.com/bridgecrewio/checkov/compare/3.2.487...3.2.488) - 2025-10-27 + +### Feature + +- **terraform_plan:** add new cases for foreach in the presence of skips - [#7351](https://github.com/bridgecrewio/checkov/pull/7351) ## [3.2.487](https://github.com/bridgecrewio/checkov/compare/3.2.486...3.2.487) - 2025-10-23 diff --git a/checkov/common/goget/github/get_git.py b/checkov/common/goget/github/get_git.py index 29015d2dbc..0dd2652306 100644 --- a/checkov/common/goget/github/get_git.py +++ b/checkov/common/goget/github/get_git.py @@ -15,7 +15,7 @@ except ImportError as e: git_import_error = e -COMMIT_ID_PATTERN = re.compile(r"\?(ref=)(?P([0-9a-f]{40}))") +COMMIT_ID_PATTERN = re.compile(r"\?(ref=)(?P([0-9a-f]{5,40}))") TAG_PATTERN = re.compile(r'\?(ref=)(?P(.*))') # technically should be with ?ref=tags/ but this catches both BRANCH_PATTERN = re.compile(r'\?(ref=heads/)(?P(.*))') diff --git a/checkov/common/output/report.py b/checkov/common/output/report.py index d8da7c3b85..51af285332 100644 --- a/checkov/common/output/report.py +++ b/checkov/common/output/report.py @@ -574,6 +574,10 @@ def handle_skipped_checks( if record.resource_address and record.resource_address.startswith("module."): module_path = record.resource_address[module_address_len:record.resource_address.index('.', module_address_len + 1)] + # For module with for_each or count, the module path will be module.module_name[(.*)]. We can + # ignore the index and the for_each value and just use the module name as it's not possible to + # skip checks for a specific instance of a module + module_path = module_path.split('[')[0] module_enrichments = enriched_resources.get(module_path, {}) for module_skip in module_enrichments.get("skipped_checks", []): if record.check_id in module_skip["id"]: diff --git a/checkov/kubernetes/checks/resource/k8s/DefaultNamespace.py b/checkov/kubernetes/checks/resource/k8s/DefaultNamespace.py index 9f05328542..1583d6540f 100644 --- a/checkov/kubernetes/checks/resource/k8s/DefaultNamespace.py +++ b/checkov/kubernetes/checks/resource/k8s/DefaultNamespace.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os from typing import Any from checkov.common.models.enums import CheckCategories, CheckResult @@ -37,6 +38,8 @@ def scan_spec_conf(self, conf: dict[str, Any]) -> CheckResult: if metadata: if "namespace" in metadata and metadata["namespace"] != "default": return CheckResult.PASSED + if os.getenv('HELM_NAMESPACE') and os.getenv('HELM_NAMESPACE') != "default": + return CheckResult.PASSED # If namespace not defined it is default -> Ignore default Service account and kubernetes service if conf["kind"] == "ServiceAccount" and metadata["name"] == "default": diff --git a/checkov/terraform/base_runner.py b/checkov/terraform/base_runner.py index bdd1bec3a6..c42860b324 100644 --- a/checkov/terraform/base_runner.py +++ b/checkov/terraform/base_runner.py @@ -31,6 +31,7 @@ from checkov.terraform.graph_builder.local_graph import TerraformLocalGraph from checkov.terraform.graph_manager import TerraformGraphManager from checkov.terraform.image_referencer.manager import TerraformImageReferencerManager +from checkov.terraform.tag_providers import get_resource_tags from checkov.terraform.tf_parser import TFParser from checkov.common.util.env_vars_config import env_vars_config @@ -187,7 +188,7 @@ def get_graph_checks_report( entity_context.get("end_line", 1), ], resource=resource, - entity_tags=entity.get("tags", {}), + entity_tags=get_resource_tags(resource, entity_config), evaluations=None, check_class=check.__class__.__module__, file_abs_path=os.path.abspath(full_file_path), diff --git a/checkov/terraform/tag_providers/__init__.py b/checkov/terraform/tag_providers/__init__.py index ddbd5b33cb..173240dc4b 100644 --- a/checkov/terraform/tag_providers/__init__.py +++ b/checkov/terraform/tag_providers/__init__.py @@ -4,18 +4,28 @@ from checkov.terraform.tag_providers import azure from checkov.terraform.tag_providers import gcp -provider_tag_mapping = {"aws": aws.get_resource_tags, "azure": azure.get_resource_tags, "gcp": gcp.get_resource_tags} +provider_tag_mapping = {"aws": aws.get_resource_tags, "azure": azure.get_resource_tags, "gcp": gcp.get_resource_tags, + "google": gcp.get_resource_tags} def get_resource_tags(resource_type: str, entity_config: Dict[str, Any]) -> Optional[Dict[str, Any]]: if not isinstance(entity_config, dict): return None - if "_" not in resource_type: - return None # probably not a resource block - provider = resource_type[: resource_type.index("_")] - provider_tag_function = provider_tag_mapping.get(provider) + provider_tag = get_provider_tag(resource_type) + provider_tag_function = provider_tag_mapping.get(provider_tag) if provider_tag else None if provider_tag_function: return provider_tag_function(entity_config) else: return None + + +def get_provider_tag(resource_type: str) -> Optional[str]: + provider_tag = None + if 'aws' in resource_type: + provider_tag = "aws" + elif 'azure' in resource_type: + provider_tag = "azure" + elif 'gcp' in resource_type or 'google' in resource_type: + provider_tag = "gcp" + return provider_tag diff --git a/checkov/version.py b/checkov/version.py index e5b1b36645..1ade9a5b6c 100644 --- a/checkov/version.py +++ b/checkov/version.py @@ -1 +1 @@ -version = '3.2.488' +version = '3.2.493' diff --git a/kubernetes/requirements.txt b/kubernetes/requirements.txt index 0195ff9089..d26f47dc23 100644 --- a/kubernetes/requirements.txt +++ b/kubernetes/requirements.txt @@ -1 +1 @@ -checkov==3.2.488 +checkov==3.2.493 diff --git a/tests/common/goget/test_goget_github.py b/tests/common/goget/test_goget_github.py index 3b63e41195..8930483b5c 100644 --- a/tests/common/goget/test_goget_github.py +++ b/tests/common/goget/test_goget_github.py @@ -108,6 +108,17 @@ def test_parse_commit_id(self): self.assertEqual("aa218f56b14c9653891f9e74264a383fa43fefbd", getter.commit_id, "Parsed source commit_id is wrong") + def test_parse_shortened_commit_id(self): + """Test parsing of shortened git commit IDs (5-39 characters).""" + url = "https://my-git.com/owner/repository-name?ref=aa218" + getter = GitGetter(url) + git_url = getter.extract_git_ref(url) + + self.assertEqual( + "https://my-git.com/owner/repository-name", git_url, "Parsed source url is wrong for 5-char commit" + ) + self.assertEqual("aa218", getter.commit_id, "Parsed source commit_id is wrong for 5-char commit") + @patch('checkov.common.goget.github.get_git.Repo') @patch('shutil.copytree') @patch('os.makedirs') diff --git a/tests/common/runner_registry/test_runner_registry_plan_enrichment.py b/tests/common/runner_registry/test_runner_registry_plan_enrichment.py index c591867858..84cdfecf7f 100644 --- a/tests/common/runner_registry/test_runner_registry_plan_enrichment.py +++ b/tests/common/runner_registry/test_runner_registry_plan_enrichment.py @@ -132,12 +132,11 @@ def test_enrichment_of_plan_report_with_for_each(self): report = runner_registry.run(repo_root_for_plan_enrichment=[repo_root], files=[str(valid_plan_path)])[0] - # TODO: after fixing module enrichment with skipped checks the failed checks will become skipped - self.assertEqual(len(report.failed_checks), 3) + self.assertEqual(len(report.failed_checks), 0) self.assertEqual(len(report.passed_checks), 0) - self.assertEqual(len(report.skipped_checks), 2) + self.assertEqual(len(report.skipped_checks), 5) def test_skip_check(self): diff --git a/tests/kubernetes/checks/test_DefaultNamespace.py b/tests/kubernetes/checks/test_DefaultNamespace.py index 1f5788c253..8b64a4c78b 100644 --- a/tests/kubernetes/checks/test_DefaultNamespace.py +++ b/tests/kubernetes/checks/test_DefaultNamespace.py @@ -1,5 +1,6 @@ import os import unittest +from unittest import mock from checkov.kubernetes.checks.resource.k8s.DefaultNamespace import check from checkov.kubernetes.runner import Runner @@ -21,6 +22,19 @@ def test_summary(self): self.assertEqual(summary['skipped'], 0) self.assertEqual(summary['parsing_errors'], 0) + @mock.patch.dict(os.environ, {"HELM_NAMESPACE": "non-default"}) + def test_summary_with_env_var(self): + runner = Runner() + current_dir = os.path.dirname(os.path.realpath(__file__)) + test_files_dir = current_dir + "/example_DefaultNamespace" + report = runner.run(root_folder=test_files_dir, runner_filter=RunnerFilter(checks=[check.id])) + summary = report.get_summary() + + self.assertEqual(summary['passed'], 11) + self.assertEqual(summary['failed'], 0) + self.assertEqual(summary['skipped'], 0) + self.assertEqual(summary['parsing_errors'], 0) + if __name__ == '__main__': unittest.main() diff --git a/tests/terraform/test_provider_tags.py b/tests/terraform/test_provider_tags.py new file mode 100644 index 0000000000..84f989cb71 --- /dev/null +++ b/tests/terraform/test_provider_tags.py @@ -0,0 +1,13 @@ +import pytest + +from checkov.terraform.tag_providers import get_provider_tag + + +@pytest.mark.parametrize("resource_type, expected", [ + ("aws_instance.example", "aws"), + ("module.test.aws_instance.example", "aws"), + ("azure_instance.example", "azure"), + ("google_instance.example", "gcp"), +]) +def test_get_provider_tag(resource_type, expected) -> None: + assert get_provider_tag(resource_type) == expected