diff --git a/docs/Docusaurus/docs/modules/engine_core.md b/docs/Docusaurus/docs/modules/engine_core.md index a6dd7cefd..1aa7a7c7e 100644 --- a/docs/Docusaurus/docs/modules/engine_core.md +++ b/docs/Docusaurus/docs/modules/engine_core.md @@ -715,6 +715,39 @@ devsecops-engine-tools \ --image_to_scan myimage:latest ``` +## Remote Config Environment Variables + +These variables are used to resolve authentication and repository context when loading remote configuration files. + +```bash +DET_REMOTE_ORG="my-azure-organization" +DET_REMOTE_PROJ="my-azure-project" +DET_REMOTE_TOKEN="***" +DET_GITHUB_REPOSITORY="owner/repository" +``` + +- **DET_REMOTE_ORG** + - **Use:** Azure DevOps organization used to build the remote config URL. + - **Applies to:** `--remote_config_source azure`. + - **Fallback if missing:** `System.TeamFoundationCollectionUri`. + +- **DET_REMOTE_PROJ** + - **Use:** Azure DevOps project used in the remote config URL path. + - **Applies to:** `--remote_config_source azure`. + - **Fallback if missing:** `System.TeamProject`. + +- **DET_REMOTE_TOKEN** + - **Use:** Token to authenticate against the remote config source. + - **Applies to:** `--remote_config_source azure` and `--remote_config_source github`. + - **Fallback if missing:** + - Azure: `System.AccessToken`. + - GitHub: `github.access.token`. + +- **DET_GITHUB_REPOSITORY** + - **Use:** GitHub repository context used to resolve owner/repository (accepts `owner/repo` or full GitHub URL). + - **Applies to:** `--remote_config_source github`. + - **Fallback if missing:** `github.repository`. + ## Extensibility - New tools and modules can be added by extending the adapters and use cases. diff --git a/tools/devsecops_engine_tools/engine_core/src/applications/runner_engine_core.py b/tools/devsecops_engine_tools/engine_core/src/applications/runner_engine_core.py index 64fa38c26..8b0e25b84 100755 --- a/tools/devsecops_engine_tools/engine_core/src/applications/runner_engine_core.py +++ b/tools/devsecops_engine_tools/engine_core/src/applications/runner_engine_core.py @@ -260,7 +260,6 @@ def get_inputs_from_cli(args): required=False, help="Address of the Docker daemon to connect to." ) - TOOLS = { "engine_iac": ["checkov", "kics", "kubescape"], "engine_secret": ["trufflehog", "gitleaks", "all_tools"], @@ -312,6 +311,7 @@ def get_inputs_from_cli(args): def application_core(): tb = True + devops_platform_gateway = None try: # Get inputs from CLI args = get_inputs_from_cli(sys.argv[1:]) diff --git a/tools/devsecops_engine_tools/engine_core/src/infrastructure/driven_adapters/azure/azure_devops.py b/tools/devsecops_engine_tools/engine_core/src/infrastructure/driven_adapters/azure/azure_devops.py index 0263b7927..5beb8d7f1 100644 --- a/tools/devsecops_engine_tools/engine_core/src/infrastructure/driven_adapters/azure/azure_devops.py +++ b/tools/devsecops_engine_tools/engine_core/src/infrastructure/driven_adapters/azure/azure_devops.py @@ -1,3 +1,4 @@ +import os from dataclasses import dataclass from devsecops_engine_tools.engine_core.src.domain.model.gateway.devops_platform_gateway import ( DevopsPlatformGateway, @@ -26,14 +27,17 @@ @dataclass class AzureDevops(DevopsPlatformGateway): def get_remote_config(self, repository, path, branch=""): + organization = os.environ.get("DET_REMOTE_ORG") or SystemVariables.System_TeamFoundationCollectionUri.value() + project = os.environ.get("DET_REMOTE_PROJ") or SystemVariables.System_TeamProject.value() + pat = os.environ.get("DET_REMOTE_TOKEN") or SystemVariables.System_AccessToken.value() base_compact_remote_config_url = ( - f"https://{SystemVariables.System_TeamFoundationCollectionUri.value().rstrip('/').split('/')[-1].replace('.visualstudio.com','')}" - f".visualstudio.com/{SystemVariables.System_TeamProject.value()}/_git/" - f"{repository}?path={path}" + f"https://{organization.rstrip('/').split('/')[-1].replace('.visualstudio.com','')}" + f".visualstudio.com/{project}/_git/{repository}?path={path}" ) + utils_azure = AzureDevopsApi( - personal_access_token=SystemVariables.System_AccessToken.value(), + personal_access_token=pat, compact_remote_config_url=base_compact_remote_config_url, ) connection = utils_azure.get_azure_connection() diff --git a/tools/devsecops_engine_tools/engine_core/src/infrastructure/driven_adapters/github/github_actions.py b/tools/devsecops_engine_tools/engine_core/src/infrastructure/driven_adapters/github/github_actions.py index cda9940d3..2bae6a4c8 100755 --- a/tools/devsecops_engine_tools/engine_core/src/infrastructure/driven_adapters/github/github_actions.py +++ b/tools/devsecops_engine_tools/engine_core/src/infrastructure/driven_adapters/github/github_actions.py @@ -1,3 +1,4 @@ +import os from dataclasses import dataclass from devsecops_engine_tools.engine_core.src.domain.model.gateway.devops_platform_gateway import ( DevopsPlatformGateway, @@ -25,13 +26,18 @@ class GithubActions(DevopsPlatformGateway): ICON_SUCCESS = "\u2714" def get_remote_config(self, repository, path, branch=""): + token = os.environ.get("DET_REMOTE_TOKEN") or SystemVariables.github_access_token.value() + github_repository = os.environ.get("DET_GITHUB_REPOSITORY") or SystemVariables.github_repository.value() + + # Accept both owner/repo and full URL formats. + if github_repository.startswith("https://") or github_repository.startswith("http://"): + github_repository = github_repository.rstrip("/").split("github.com/")[-1] - github_repository = SystemVariables.github_repository.value() split = github_repository.split("/") owner = split[0] utils_github = GithubApi() - git_client = utils_github.get_github_connection(SystemVariables.github_access_token.value()) + git_client = utils_github.get_github_connection(token) json_config = utils_github.get_remote_json_config(git_client, owner, repository, path, branch) return json_config diff --git a/tools/devsecops_engine_tools/engine_core/test/infrastructure/driven_adapters/azuredevopscore/test_azure_devops.py b/tools/devsecops_engine_tools/engine_core/test/infrastructure/driven_adapters/azuredevopscore/test_azure_devops.py index 37026b98a..7591798a0 100644 --- a/tools/devsecops_engine_tools/engine_core/test/infrastructure/driven_adapters/azuredevopscore/test_azure_devops.py +++ b/tools/devsecops_engine_tools/engine_core/test/infrastructure/driven_adapters/azuredevopscore/test_azure_devops.py @@ -8,30 +8,26 @@ class TestAzureDevops(unittest.TestCase): + @mock.patch('devsecops_engine_tools.engine_core.src.infrastructure.driven_adapters.azure.azure_devops.AzureDevopsApi', autospec=True) @mock.patch('devsecops_engine_tools.engine_core.src.infrastructure.driven_adapters.azure.azure_devops.SystemVariables', autospec=True) def test_get_remote_config(self, mock_system_variables, mock_azure_devops_api): - azure_devops = AzureDevops() - # Set up mock values for SystemVariables mock_system_variables.System_TeamFoundationCollectionUri.value.return_value = "System_TeamFoundationCollectionUri" - # Mock the AzureDevopsApi class mock_azure_devops_api_instance = MagicMock() mock_azure_devops_api_instance.get_azure_connection.return_value = "MockedConnection" mock_azure_devops_api_instance.get_remote_json_config.return_value = {'key': 'value'} mock_azure_devops_api.return_value = mock_azure_devops_api_instance - remote_config_repo = "my_repo" remote_config_path = "my_path" - result = azure_devops.get_remote_config(remote_config_repo, remote_config_path) + result = azure_devops.get_remote_config(remote_config_repo, remote_config_path) assert result == {"key": "value"} def test_message(self): azure_devops = AzureDevops() - assert azure_devops.message("succeeded", "message") == "##[section]message" assert azure_devops.message("info", "message") == "##[command]message" assert azure_devops.message("warning", "message") == "##[warning]message" @@ -39,7 +35,6 @@ def test_message(self): def test_result_pipeline(self): azure_devops = AzureDevops() - assert azure_devops.result_pipeline("failed") == "##vso[task.complete result=Failed;]DONE" assert azure_devops.result_pipeline("succeeded") == "##vso[task.complete result=Succeeded;]DONE" @@ -47,12 +42,10 @@ def test_result_pipeline(self): @mock.patch('devsecops_engine_tools.engine_core.src.infrastructure.driven_adapters.azure.azure_devops.BuildVariables', autospec=True) def test_get_source_code_management_uri(self, mock_build_variables, mock_system_variables): azure_devops = AzureDevops() - mock_system_variables.System_TeamFoundationCollectionUri.value.return_value = "System_TeamFoundationCollectionUri" mock_system_variables.System_TeamProject.value.return_value = "Build_Project_Name" mock_build_variables.Build_Repository_Name.value.return_value = "Build_Repository_Name" mock_build_variables.Build_Repository_Provider.value.return_value = "tfsgit" - assert azure_devops.get_source_code_management_uri() == "System_TeamFoundationCollectionUriBuild_Project_Name/_git/Build_Repository_Name" @@ -60,55 +53,42 @@ def test_get_source_code_management_uri(self, mock_build_variables, mock_system_ @mock.patch('devsecops_engine_tools.engine_core.src.infrastructure.driven_adapters.azure.azure_devops.BuildVariables', autospec=True) def test_get_build_pipeline_execution_url(self, mock_build_variables, mock_system_variables): azure_devops = AzureDevops() - mock_system_variables.System_TeamFoundationCollectionUri.value.return_value = "System_TeamFoundationCollectionUri" mock_system_variables.System_TeamProject.value.return_value = "Build_Project_Name" mock_build_variables.Build_BuildId.value.return_value = "Build_BuildId" - - assert azure_devops.get_build_pipeline_execution_url() == "System_TeamFoundationCollectionUriBuild_Project_Name/_build?buildId=Build_BuildId" + @mock.patch('devsecops_engine_tools.engine_core.src.infrastructure.driven_adapters.azure.azure_devops.SystemVariables', autospec=True) @mock.patch('devsecops_engine_tools.engine_core.src.infrastructure.driven_adapters.azure.azure_devops.BuildVariables', autospec=True) @mock.patch('devsecops_engine_tools.engine_core.src.infrastructure.driven_adapters.azure.azure_devops.ReleaseVariables', autospec=True) def test_get_variable(self, mock_release_variables, mock_build_variables, mock_system_variables): azure_devops = AzureDevops() - # Mock the BuildVariables class mock_build_variables.Build_SourceBranchName.value.return_value = "Build_SourceBranchName" mock_build_variables.Build_BuildNumber.value.return_value = "Build_BuildNumber" mock_build_variables.Build_BuildId.value.return_value = "Build_BuildId" mock_build_variables.Build_SourceVersion.value.return_value = "Build_SourceVersion" mock_build_variables.Build_SourceBranch.value.return_value = "Build_SourceBranch" - # Mock the ReleaseVariables class mock_release_variables.Environment.value.return_value = "Environment" mock_release_variables.Release_Releaseid.value.return_value = "Release_ReleaseId" - # Mock the SystemVariables class mock_system_variables.System_AccessToken.value.return_value = "System_AccessToken" - result = azure_devops.get_variable("branch_name") assert result == "Build_SourceBranchName" - result = azure_devops.get_variable("build_id") assert result == "Build_BuildNumber" - result = azure_devops.get_variable("build_execution_id") assert result == "Build_BuildId" - result = azure_devops.get_variable("commit_hash") assert result == "Build_SourceVersion" - result = azure_devops.get_variable("environment") assert result == "Environment" - result = azure_devops.get_variable("release_id") assert result == "Release_ReleaseId" - result = azure_devops.get_variable("branch_tag") assert result == "Build_SourceBranch" - result = azure_devops.get_variable("access_token") assert result == "System_AccessToken" @@ -236,5 +216,6 @@ def test_get_source_code_management_uri_value_error_returns_none(self, mock_buil self.assertIsNone(result) + if __name__ == '__main__': unittest.main() \ No newline at end of file