diff --git a/strix/report/state.py b/strix/report/state.py index 16f4ac1..f7acaba 100644 --- a/strix/report/state.py +++ b/strix/report/state.py @@ -438,30 +438,37 @@ class ReportState: targets = self.run_record.get("targets_info") or [] if not isinstance(targets, list): return None - for target in targets: - if not isinstance(target, dict) or target.get("type") != "repository": - continue - details = target.get("details") or {} - if not isinstance(details, dict): - continue - uri = details.get("target_repo") - if not isinstance(uri, str) or not uri.strip(): - continue + repo_targets = [ + target + for target in targets + if isinstance(target, dict) and target.get("type") == "repository" + ] + # Provenance binds the whole run to one repo; with multiple repo targets + # that's ambiguous, so omit it rather than mis-attributing later repos' + # findings to the first repo's URI/commit. + if len(repo_targets) != 1: + return None + target = repo_targets[0] + details = target.get("details") or {} + if not isinstance(details, dict): + return None + uri = details.get("target_repo") + if not isinstance(uri, str) or not uri.strip(): + return None - context: dict[str, Any] = {"repositoryUri": uri.strip()} - full_name = _parse_repo_full_name(uri) - if full_name: - context["repositoryFullName"] = full_name - cloned = details.get("cloned_repo_path") - if isinstance(cloned, str) and cloned.strip(): - commit, branch = _git_head(cloned.strip()) - if commit: - context["commitSha"] = commit - if branch: - context["branch"] = branch - context["ref"] = f"refs/heads/{branch}" - return context - return None + context: dict[str, Any] = {"repositoryUri": uri.strip()} + full_name = _parse_repo_full_name(uri) + if full_name: + context["repositoryFullName"] = full_name + cloned = details.get("cloned_repo_path") + if isinstance(cloned, str) and cloned.strip(): + commit, branch = _git_head(cloned.strip()) + if commit: + context["commitSha"] = commit + if branch: + context["branch"] = branch + context["ref"] = f"refs/heads/{branch}" + return context def _sync_llm_usage_record(self) -> None: self.run_record["llm_usage"] = self._build_llm_usage_record() diff --git a/tests/test_state_repo_context.py b/tests/test_state_repo_context.py index bca9e24..b0a6eb3 100644 --- a/tests/test_state_repo_context.py +++ b/tests/test_state_repo_context.py @@ -41,6 +41,15 @@ def test_repository_context_uri_only_without_clone() -> None: } +def test_repository_context_none_for_multiple_repository_targets() -> None: + state = ReportState(run_name="t") + state.run_record["targets_info"] = [ + {"type": "repository", "details": {"target_repo": "https://github.com/acme/widget"}}, + {"type": "repository", "details": {"target_repo": "https://github.com/acme/api"}}, + ] + assert state._sarif_repository_context() is None + + def test_repository_context_derives_commit_and_branch_from_clone(tmp_path: Path) -> None: repo = tmp_path / "widget" repo.mkdir()