diff --git a/run_community_analyzer.py b/run_community_analyzer.py index 04abedf..b6ab0ac 100644 --- a/run_community_analyzer.py +++ b/run_community_analyzer.py @@ -57,17 +57,20 @@ def main(argv: list[str] | None = None) -> None: toolbox_path = os.getenv("TOOLBOX_PATH", "/toolbox") output_path = os.path.join(toolbox_path, "analysis_results.json") artifacts_path = os.getenv("ARTIFACTS_PATH", "/artifacts") + issue_map_path = None parser = argparse.ArgumentParser("sarif_parser") parser.add_argument( "--analyzer", help="Which community analyzer to run. Example: 'kube-linter'", - required=True, + required=False, ) - args = parser.parse_args(argv, namespace=CommunityAnalyzerArgs) + if argv: + args = parser.parse_args(argv, namespace=CommunityAnalyzerArgs) + # analyzer name is mandatory in case of community analyzers but not custom analyzers + if analyzer_name := args.analyzer: + issue_map_path = get_issue_map(analyzer_name) - analyzer_name = args.analyzer - issue_map_path = get_issue_map(analyzer_name) modified_files = get_files_to_analyze(code_path) run_sarif_parser( artifacts_path, output_path, issue_map_path, modified_files=modified_files diff --git a/tests/test_community_analyzer.py b/tests/test_community_analyzer.py index a5b3f99..6718284 100644 --- a/tests/test_community_analyzer.py +++ b/tests/test_community_analyzer.py @@ -181,6 +181,180 @@ "extra_data": {}, } +expected_result_no_issue_map = { + "is_passed": False, + "issues": [ + { + "issue_code": "no-read-only-root-fs", + "issue_text": 'container "kube-scheduler" does not have a read-only root file system\nobject: /test-release-kube-scheduler apps/v1, Kind=Deployment', + "location": { + "path": "charts/kube-scheduler/templates/deployment.yaml", + "position": { + "begin": {"column": 1, "line": 1}, + "end": {"column": 1, "line": 1}, + }, + }, + }, + { + "issue_code": "run-as-non-root", + "issue_text": 'container "kube-scheduler" is not set to runAsNonRoot\nobject: /test-release-kube-scheduler apps/v1, Kind=Deployment', + "location": { + "path": "charts/kube-scheduler/templates/deployment.yaml", + "position": { + "begin": {"column": 1, "line": 1}, + "end": {"column": 1, "line": 1}, + }, + }, + }, + { + "issue_code": "env-var-secret", + "issue_text": 'environment variable TASK_IMAGE_PULL_SECRET_NAME in container "runner" found\nobject: /test-release-runner apps/v1, Kind=Deployment', + "location": { + "path": "charts/runner/templates/deployment.yaml", + "position": { + "begin": {"column": 1, "line": 1}, + "end": {"column": 1, "line": 1}, + }, + }, + }, + { + "issue_code": "env-var-secret", + "issue_text": 'environment variable TASK_ARTIFACT_SECRET_NAME in container "runner" found\nobject: /test-release-runner apps/v1, Kind=Deployment', + "location": { + "path": "charts/runner/templates/deployment.yaml", + "position": { + "begin": {"column": 1, "line": 1}, + "end": {"column": 1, "line": 1}, + }, + }, + }, + { + "issue_code": "no-read-only-root-fs", + "issue_text": 'container "runner" does not have a read-only root file system\nobject: /test-release-runner apps/v1, Kind=Deployment', + "location": { + "path": "charts/runner/templates/deployment.yaml", + "position": { + "begin": {"column": 1, "line": 1}, + "end": {"column": 1, "line": 1}, + }, + }, + }, + { + "issue_code": "run-as-non-root", + "issue_text": 'container "runner" is not set to runAsNonRoot\nobject: /test-release-runner apps/v1, Kind=Deployment', + "location": { + "path": "charts/runner/templates/deployment.yaml", + "position": { + "begin": {"column": 1, "line": 1}, + "end": {"column": 1, "line": 1}, + }, + }, + }, + { + "issue_code": "no-read-only-root-fs", + "issue_text": 'container "runner-rqlite" does not have a read-only root file system\nobject: /test-release-runner-rqlite apps/v1, Kind=StatefulSet', + "location": { + "path": "charts/runner/templates/rqlite-statefulset.yaml", + "position": { + "begin": {"column": 1, "line": 1}, + "end": {"column": 1, "line": 1}, + }, + }, + }, + { + "issue_code": "run-as-non-root", + "issue_text": 'container "runner-rqlite" is not set to runAsNonRoot\nobject: /test-release-runner-rqlite apps/v1, Kind=StatefulSet', + "location": { + "path": "charts/runner/templates/rqlite-statefulset.yaml", + "position": { + "begin": {"column": 1, "line": 1}, + "end": {"column": 1, "line": 1}, + }, + }, + }, + { + "issue_code": "latest-tag", + "issue_text": 'The container "wget" is using an invalid container image, "busybox". Please use images that are not blocked by the `BlockList` criteria : [".*:(latest)$" "^[^:]*$" "(.*/[^:]+)$"]\nobject: /test-release-runner-test-connection /v1, Kind=Pod', + "location": { + "path": "charts/runner/templates/tests/test-connection.yaml", + "position": { + "begin": {"column": 1, "line": 1}, + "end": {"column": 1, "line": 1}, + }, + }, + }, + { + "issue_code": "no-read-only-root-fs", + "issue_text": 'container "wget" does not have a read-only root file system\nobject: /test-release-runner-test-connection /v1, Kind=Pod', + "location": { + "path": "charts/runner/templates/tests/test-connection.yaml", + "position": { + "begin": {"column": 1, "line": 1}, + "end": {"column": 1, "line": 1}, + }, + }, + }, + { + "issue_code": "run-as-non-root", + "issue_text": 'container "wget" is not set to runAsNonRoot\nobject: /test-release-runner-test-connection /v1, Kind=Pod', + "location": { + "path": "charts/runner/templates/tests/test-connection.yaml", + "position": { + "begin": {"column": 1, "line": 1}, + "end": {"column": 1, "line": 1}, + }, + }, + }, + { + "issue_code": "unset-cpu-requirements", + "issue_text": 'container "wget" has cpu request 0\nobject: /test-release-runner-test-connection /v1, Kind=Pod', + "location": { + "path": "charts/runner/templates/tests/test-connection.yaml", + "position": { + "begin": {"column": 1, "line": 1}, + "end": {"column": 1, "line": 1}, + }, + }, + }, + { + "issue_code": "unset-cpu-requirements", + "issue_text": 'container "wget" has cpu limit 0\nobject: /test-release-runner-test-connection /v1, Kind=Pod', + "location": { + "path": "charts/runner/templates/tests/test-connection.yaml", + "position": { + "begin": {"column": 1, "line": 1}, + "end": {"column": 1, "line": 1}, + }, + }, + }, + { + "issue_code": "unset-memory-requirements", + "issue_text": 'container "wget" has memory request 0\nobject: /test-release-runner-test-connection /v1, Kind=Pod', + "location": { + "path": "charts/runner/templates/tests/test-connection.yaml", + "position": { + "begin": {"column": 1, "line": 1}, + "end": {"column": 1, "line": 1}, + }, + }, + }, + { + "issue_code": "unset-memory-requirements", + "issue_text": 'container "wget" has memory limit 0\nobject: /test-release-runner-test-connection /v1, Kind=Pod', + "location": { + "path": "charts/runner/templates/tests/test-connection.yaml", + "position": { + "begin": {"column": 1, "line": 1}, + "end": {"column": 1, "line": 1}, + }, + }, + }, + ], + "errors": [], + "metrics": [], + "extra_data": {}, +} + def test_community_analyzer(tmp_path: Path) -> None: """Test for `run_community_analyzer.main()`, to test `issue_map.json` parsing.""" @@ -234,3 +408,57 @@ def test_community_analyzer(tmp_path: Path) -> None: issue["location"]["path"] == "charts/runner/templates/tests/test-connection.yaml" ) + + +def test_community_analyzer_without_issue_map(tmp_path: Path) -> None: + """Test for `run_community_analyzer.main()` when no analyzer is specified and thus `issue_map.json` is not there.""" + code_path = "/code" + toolbox_path = tmp_path.as_posix() + artifacts_path = os.path.join(os.path.dirname(__file__), "test_artifacts") + analysis_config_path = os.path.join(toolbox_path, "analysis_config.json") + modified_files = extract_filepaths_from_deepsource_json(code_path, expected_result) + os.environ["CODE_PATH"] = code_path + os.environ["TOOLBOX_PATH"] = toolbox_path + os.environ["ARTIFACTS_PATH"] = artifacts_path + + # Case: when analysis config is not present, it should raise an error. + with pytest.raises(ValueError) as exe: + run_community_analyzer.main() + assert ( + str(exe.value) == f"Could not find analysis config at {analysis_config_path}." + ) + + # Case: all files from the report are present in the analysis config. + with temp_analysis_config(analysis_config_path, modified_files): + run_community_analyzer.main() + + analysis_results = tmp_path / "analysis_results.json" + assert analysis_results.exists() + + with open(analysis_results) as file: + result = json.load(file) + + assert result == expected_result_no_issue_map + + # Case: only a subset of files from the report are present in the analysis config. + # Note: There are 7 issues in this file in our report fixture. + # See `expected_result`. + modified_files = [ + os.path.join(code_path, "charts/runner/templates/tests/test-connection.yaml"), + ] + with temp_analysis_config(analysis_config_path, modified_files): + run_community_analyzer.main() + + analysis_results = tmp_path / "analysis_results.json" + assert analysis_results.exists() + + with open(analysis_results) as file: + result = json.load(file) + + assert len(result["issues"]) == 7 + + for issue in result["issues"]: + assert ( + issue["location"]["path"] + == "charts/runner/templates/tests/test-connection.yaml" + )