From d146ec27f18a7a173be7ce66561b736a81e7f677 Mon Sep 17 00:00:00 2001 From: openhands Date: Thu, 6 Aug 2026 13:39:02 +0000 Subject: [PATCH] fix(security-scan): improve release security scan comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three adjustments to the release security-scan workflow comment: 1. Status line says "finding(s)" instead of "blocking finding(s)" — the findings are already marked as blocking by the emoji/verdict; the extra word is redundant. 2. The merged-head SHA in approval-drift rows is now a markdown link to the GitHub compare page between the last reviewed commit and the merged head, so reviewers can jump straight to what landed after approval. 3. Internal openhands-* package bumps are excluded from the external-dep count (they bump on every release) but still displayed in a separate collapsible section at the bottom of the dependency-diff report. Co-authored-by: openhands --- .github/scripts/check_approval_drift.py | 20 +++++++++++++-- .github/scripts/check_dependency_diff.py | 32 ++++++++++++++++++++---- .github/scripts/security_scan_common.py | 2 +- 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/.github/scripts/check_approval_drift.py b/.github/scripts/check_approval_drift.py index 7202eb9eac..739aba06c5 100644 --- a/.github/scripts/check_approval_drift.py +++ b/.github/scripts/check_approval_drift.py @@ -123,12 +123,15 @@ def audit_pr( if not reviewed_head: latest = approvals[-1] reviewer = (latest.get("user") or {}).get("login", "?") - approved_on = _short(latest.get("commit_id")) + approved_sha = latest.get("commit_id") + approved_on = _short(approved_sha) + merged_head = _short(head_sha) + diff_link = _compare_link(repo, approved_sha, head_sha, merged_head) return ( "changed-after-approval", ( f"last approval by @{reviewer} was on {approved_on}, but merged " - f"head was {_short(head_sha)} — commits landed after review" + f"head was {diff_link} — commits landed after review" ), ) @@ -139,6 +142,19 @@ def _short(sha: object) -> str: return str(sha)[:9] if sha else "?" +def _compare_link( + repo: str, base: object, head: object, label: str +) -> str: + """Markdown link to the GitHub compare page between *base* and *head*. + + Falls back to plain text if either SHA is missing so the report never + breaks on a sparse API response. + """ + if not base or not head: + return label + return f"[{label}](https://github.com/{repo}/compare/{base}...{head})" + + def main() -> int: token = os.environ.get("GITHUB_TOKEN") or os.environ.get("GH_TOKEN") repo = resolve_repo() diff --git a/.github/scripts/check_dependency_diff.py b/.github/scripts/check_dependency_diff.py index b61c271538..e049aeb269 100644 --- a/.github/scripts/check_dependency_diff.py +++ b/.github/scripts/check_dependency_diff.py @@ -138,11 +138,27 @@ def main() -> int: if before[n]["version"] != after[n]["version"] } + # First-party openhands-* packages are version-bumped on every release; + # exclude them from the external-dep count but still surface them below. + def _is_openhands(name: str) -> bool: + return name.lower().startswith("openhands-") + + bumped_external = { + n: v for n, v in bumped.items() if not _is_openhands(n) + } + bumped_openhands = { + n: v for n, v in bumped.items() if _is_openhands(n) + } + report.add(f"Baseline: `{baseline}`") report.add( - f"Added: **{len(added)}**, bumped: **{len(bumped)}**, " + f"Added: **{len(added)}**, bumped: **{len(bumped_external)}**, " f"removed: **{len(removed)}**." ) + if bumped_openhands: + report.add( + f"_(plus {len(bumped_openhands)} internal `openhands-*` bump(s))_" + ) report.add("") # --- non-registry sources on new/changed packages (supply-chain surface) -- @@ -199,11 +215,17 @@ def main() -> int: for name in sorted(added): report.add(f"- `{name}=={added[name]['version']}`") report.add("\n") - if bumped: + if bumped_external: report.add("
Bumped dependencies\n") - for name in sorted(bumped): - old, new = bumped[name] - report.add(f"- `{name}`: {old} → {new}") + for name in sorted(bumped_external): + old_v, new_v = bumped_external[name] + report.add(f"- `{name}`: {old_v} → {new_v}") + report.add("\n
") + if bumped_openhands: + report.add("
Internal `openhands-*` bumps\n") + for name in sorted(bumped_openhands): + old_v, new_v = bumped_openhands[name] + report.add(f"- `{name}`: {old_v} → {new_v}") report.add("\n
") sys.stdout.write(report.render()) diff --git a/.github/scripts/security_scan_common.py b/.github/scripts/security_scan_common.py index 2238863e00..fac274966a 100644 --- a/.github/scripts/security_scan_common.py +++ b/.github/scripts/security_scan_common.py @@ -149,7 +149,7 @@ def warn(self, reason: str) -> None: def render(self) -> str: if self.blocking: - status = f"❌ {len(self.blocking)} blocking finding(s)" + status = f"❌ {len(self.blocking)} finding(s)" elif self.warnings: status = f"⚠️ {len(self.warnings)} warning(s), nothing blocking" else: