From 72599710e0e2db35d31240dee95103f3837cc01f Mon Sep 17 00:00:00 2001 From: Guy Giat Date: Mon, 19 Jan 2026 15:14:12 +0200 Subject: [PATCH 1/2] feat: Add BC_CA_BUNDLE environment variable support for custom CA certificates This change adds support for the BC_CA_BUNDLE environment variable in the GitGetter._clone() function. When set, it configures GIT_SSL_CAINFO to use the specified CA bundle file, enabling git clone operations to work with on-prem GitLab/GitHub instances that use self-signed or custom CA certificates. Changes: - Modified _clone() to check for BC_CA_BUNDLE env var - When BC_CA_BUNDLE is set, GIT_SSL_CAINFO is configured with its value - PROXY_URL settings take precedence over BC_CA_BUNDLE - Added unit tests for the new functionality Usage: export BC_CA_BUNDLE=/path/to/ca-bundle.crt checkov -d . --download-external-modules true --- checkov/common/goget/github/get_git.py | 7 ++ tests/common/goget/test_goget_github.py | 91 +++++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/checkov/common/goget/github/get_git.py b/checkov/common/goget/github/get_git.py index 0dd2652306..a7fec73a36 100644 --- a/checkov/common/goget/github/get_git.py +++ b/checkov/common/goget/github/get_git.py @@ -85,6 +85,13 @@ def _clone(self, git_url: str, clone_dir: str) -> None: https_proxy=os.getenv('PROXY_URL'), GIT_CONFIG_PARAMETERS=f"'http.extraHeader={os.getenv('PROXY_HEADER_KEY')}:{os.getenv('PROXY_HEADER_VALUE')}'"): self._clone_helper(clone_dir, git_url) + return + ca_bundle = os.getenv('BC_CA_BUNDLE') + if ca_bundle: + self.logger.info(f'Using custom CA bundle from BC_CA_BUNDLE: {ca_bundle}') + with temp_environ(GIT_SSL_CAINFO=ca_bundle): + self._clone_helper(clone_dir, git_url) + return self._clone_helper(clone_dir, git_url) def _clone_helper(self, clone_dir: str, git_url: str) -> None: diff --git a/tests/common/goget/test_goget_github.py b/tests/common/goget/test_goget_github.py index 8930483b5c..c34757addc 100644 --- a/tests/common/goget/test_goget_github.py +++ b/tests/common/goget/test_goget_github.py @@ -174,5 +174,96 @@ def test_do_get_import_error(self): getter.do_get() self.assertEqual("Unable to load git module (is the git executable available?)", str(context.exception)) + @patch('checkov.common.goget.github.get_git.Repo') + @patch.dict(os.environ, {'BC_CA_BUNDLE': '/path/to/ca-bundle.crt'}, clear=False) + def test_clone_with_bc_ca_bundle(self, mock_repo): + """Test that BC_CA_BUNDLE env var sets GIT_SSL_CAINFO for git clone.""" + # Arrange + url = "https://my-git.com/repo" + getter = GitGetter(url, create_clone_and_result_dirs=False) + getter.temp_dir = "/tmp/test" + mock_repo_instance = Mock() + mock_repo.clone_from.return_value = mock_repo_instance + + captured_env = {} + + def capture_env(*args, **kwargs): + captured_env['GIT_SSL_CAINFO'] = os.environ.get('GIT_SSL_CAINFO') + return mock_repo_instance + + mock_repo.clone_from.side_effect = capture_env + + # Act + getter.do_get() + + # Assert + self.assertEqual('/path/to/ca-bundle.crt', captured_env.get('GIT_SSL_CAINFO')) + mock_repo.clone_from.assert_called_once() + + @patch('checkov.common.goget.github.get_git.Repo') + @patch.dict(os.environ, {}, clear=False) + def test_clone_without_bc_ca_bundle(self, mock_repo): + """Test that clone works without BC_CA_BUNDLE env var.""" + # Ensure BC_CA_BUNDLE is not set + if 'BC_CA_BUNDLE' in os.environ: + del os.environ['BC_CA_BUNDLE'] + + # Arrange + url = "https://my-git.com/repo" + getter = GitGetter(url, create_clone_and_result_dirs=False) + getter.temp_dir = "/tmp/test" + mock_repo_instance = Mock() + mock_repo.clone_from.return_value = mock_repo_instance + + captured_env = {} + + def capture_env(*args, **kwargs): + captured_env['GIT_SSL_CAINFO'] = os.environ.get('GIT_SSL_CAINFO') + return mock_repo_instance + + mock_repo.clone_from.side_effect = capture_env + + # Act + getter.do_get() + + # Assert + self.assertIsNone(captured_env.get('GIT_SSL_CAINFO')) + mock_repo.clone_from.assert_called_once() + + @patch('checkov.common.goget.github.get_git.Repo') + @patch.dict(os.environ, { + 'PROXY_URL': 'http://proxy.example.com:8080', + 'PROXY_CA_PATH': '/path/to/proxy-ca.crt', + 'PROXY_HEADER_KEY': 'X-Custom-Header', + 'PROXY_HEADER_VALUE': 'custom-value', + 'BC_CA_BUNDLE': '/path/to/ca-bundle.crt' + }, clear=False) + def test_clone_proxy_takes_precedence_over_bc_ca_bundle(self, mock_repo): + """Test that PROXY_URL settings take precedence over BC_CA_BUNDLE.""" + # Arrange + url = "https://my-git.com/repo" + getter = GitGetter(url, create_clone_and_result_dirs=False) + getter.temp_dir = "/tmp/test" + mock_repo_instance = Mock() + mock_repo.clone_from.return_value = mock_repo_instance + + captured_env = {} + + def capture_env(*args, **kwargs): + captured_env['GIT_SSL_CAINFO'] = os.environ.get('GIT_SSL_CAINFO') + captured_env['https_proxy'] = os.environ.get('https_proxy') + return mock_repo_instance + + mock_repo.clone_from.side_effect = capture_env + + # Act + getter.do_get() + + # Assert - PROXY_CA_PATH should be used, not BC_CA_BUNDLE + self.assertEqual('/path/to/proxy-ca.crt', captured_env.get('GIT_SSL_CAINFO')) + self.assertEqual('http://proxy.example.com:8080', captured_env.get('https_proxy')) + mock_repo.clone_from.assert_called_once() + + if __name__ == '__main__': unittest.main() From 84860e052d9361f9918cee1cf3ddc44d6d593a9d Mon Sep 17 00:00:00 2001 From: Guy Giat Date: Mon, 19 Jan 2026 15:38:21 +0200 Subject: [PATCH 2/2] refactor: Use env_vars_config instead of os.getenv for environment variables Updated the code to use env_vars_config from checkov.common.util.env_vars_config instead of directly calling os.getenv(). This follows the project's pattern for handling environment variables. Changes: - Import env_vars_config instead of os - Use env_vars_config.BC_CA_BUNDLE instead of os.getenv('BC_CA_BUNDLE') - Use env_vars_config.PROXY_* instead of os.getenv('PROXY_*') - Updated tests to mock env_vars_config instead of os.environ --- checkov/common/goget/github/get_git.py | 14 +++++----- tests/common/goget/test_goget_github.py | 34 +++++++++++++------------ 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/checkov/common/goget/github/get_git.py b/checkov/common/goget/github/get_git.py index a7fec73a36..982c7f2659 100644 --- a/checkov/common/goget/github/get_git.py +++ b/checkov/common/goget/github/get_git.py @@ -3,11 +3,11 @@ import logging import re import shutil -import os from checkov.common.goget.base_getter import BaseGetter from checkov.common.resource_code_logger_filter import add_resource_code_filter_to_logger from checkov.common.util.contextmanagers import temp_environ +from checkov.common.util.env_vars_config import env_vars_config try: from git import Repo @@ -79,14 +79,14 @@ def do_get(self) -> str: def _clone(self, git_url: str, clone_dir: str) -> None: self.logger.info(f"cloning {git_url} to {clone_dir}") with temp_environ(GIT_TERMINAL_PROMPT="0"): # disables user prompts originating from GIT - if os.getenv('PROXY_URL'): - self.logger.info(f'Performing clone through proxy - {os.getenv("PROXY_URL")}') - with temp_environ(GIT_SSL_CAINFO=os.getenv('PROXY_CA_PATH'), - https_proxy=os.getenv('PROXY_URL'), - GIT_CONFIG_PARAMETERS=f"'http.extraHeader={os.getenv('PROXY_HEADER_KEY')}:{os.getenv('PROXY_HEADER_VALUE')}'"): + if env_vars_config.PROXY_URL: + self.logger.info(f'Performing clone through proxy - {env_vars_config.PROXY_URL}') + with temp_environ(GIT_SSL_CAINFO=env_vars_config.PROXY_CA_PATH, + https_proxy=env_vars_config.PROXY_URL, + GIT_CONFIG_PARAMETERS=f"'http.extraHeader={env_vars_config.PROXY_HEADER_KEY}:{env_vars_config.PROXY_HEADER_VALUE}'"): self._clone_helper(clone_dir, git_url) return - ca_bundle = os.getenv('BC_CA_BUNDLE') + ca_bundle = env_vars_config.BC_CA_BUNDLE if ca_bundle: self.logger.info(f'Using custom CA bundle from BC_CA_BUNDLE: {ca_bundle}') with temp_environ(GIT_SSL_CAINFO=ca_bundle): diff --git a/tests/common/goget/test_goget_github.py b/tests/common/goget/test_goget_github.py index c34757addc..c9f144bca7 100644 --- a/tests/common/goget/test_goget_github.py +++ b/tests/common/goget/test_goget_github.py @@ -175,10 +175,13 @@ def test_do_get_import_error(self): self.assertEqual("Unable to load git module (is the git executable available?)", str(context.exception)) @patch('checkov.common.goget.github.get_git.Repo') - @patch.dict(os.environ, {'BC_CA_BUNDLE': '/path/to/ca-bundle.crt'}, clear=False) - def test_clone_with_bc_ca_bundle(self, mock_repo): + @patch('checkov.common.goget.github.get_git.env_vars_config') + def test_clone_with_bc_ca_bundle(self, mock_env_vars_config, mock_repo): """Test that BC_CA_BUNDLE env var sets GIT_SSL_CAINFO for git clone.""" # Arrange + mock_env_vars_config.BC_CA_BUNDLE = '/path/to/ca-bundle.crt' + mock_env_vars_config.PROXY_URL = None + url = "https://my-git.com/repo" getter = GitGetter(url, create_clone_and_result_dirs=False) getter.temp_dir = "/tmp/test" @@ -201,14 +204,13 @@ def capture_env(*args, **kwargs): mock_repo.clone_from.assert_called_once() @patch('checkov.common.goget.github.get_git.Repo') - @patch.dict(os.environ, {}, clear=False) - def test_clone_without_bc_ca_bundle(self, mock_repo): + @patch('checkov.common.goget.github.get_git.env_vars_config') + def test_clone_without_bc_ca_bundle(self, mock_env_vars_config, mock_repo): """Test that clone works without BC_CA_BUNDLE env var.""" - # Ensure BC_CA_BUNDLE is not set - if 'BC_CA_BUNDLE' in os.environ: - del os.environ['BC_CA_BUNDLE'] - # Arrange + mock_env_vars_config.BC_CA_BUNDLE = None + mock_env_vars_config.PROXY_URL = None + url = "https://my-git.com/repo" getter = GitGetter(url, create_clone_and_result_dirs=False) getter.temp_dir = "/tmp/test" @@ -231,16 +233,16 @@ def capture_env(*args, **kwargs): mock_repo.clone_from.assert_called_once() @patch('checkov.common.goget.github.get_git.Repo') - @patch.dict(os.environ, { - 'PROXY_URL': 'http://proxy.example.com:8080', - 'PROXY_CA_PATH': '/path/to/proxy-ca.crt', - 'PROXY_HEADER_KEY': 'X-Custom-Header', - 'PROXY_HEADER_VALUE': 'custom-value', - 'BC_CA_BUNDLE': '/path/to/ca-bundle.crt' - }, clear=False) - def test_clone_proxy_takes_precedence_over_bc_ca_bundle(self, mock_repo): + @patch('checkov.common.goget.github.get_git.env_vars_config') + def test_clone_proxy_takes_precedence_over_bc_ca_bundle(self, mock_env_vars_config, mock_repo): """Test that PROXY_URL settings take precedence over BC_CA_BUNDLE.""" # Arrange + mock_env_vars_config.PROXY_URL = 'http://proxy.example.com:8080' + mock_env_vars_config.PROXY_CA_PATH = '/path/to/proxy-ca.crt' + mock_env_vars_config.PROXY_HEADER_KEY = 'X-Custom-Header' + mock_env_vars_config.PROXY_HEADER_VALUE = 'custom-value' + mock_env_vars_config.BC_CA_BUNDLE = '/path/to/ca-bundle.crt' + url = "https://my-git.com/repo" getter = GitGetter(url, create_clone_and_result_dirs=False) getter.temp_dir = "/tmp/test"