From 27ddd8fd8032bc39cb1b6da4fc42b7cb3cb34273 Mon Sep 17 00:00:00 2001 From: aaronagent <1115117931@qq.com> Date: Sun, 28 Jun 2026 15:25:25 -0700 Subject: [PATCH] fix(gateway): sanitize agent error messages, validate webhook gh args MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two of the three fixes from PR #6660 (the cli.py reopen_session change is moot — that raw _conn.execute reopen block no longer exists on main). - gateway/run.py: stop sending raw type(e).__name__ and str(e)[:300] to end users on chat platforms. Exception text from LLM providers can leak API URLs, file paths, and partial credentials. Return a generic message; keep curated status hints for known HTTP codes; full detail stays in logs. - gateway/platforms/webhook.py: validate pr_number (positive int) and repo (owner/name regex) before passing to the 'gh pr comment' subprocess. Payload-controlled values could otherwise inject gh flags (--help, a different --repo). List-form subprocess means this is arg injection, not shell injection, but validation is still correct. Co-authored-by: aaronagent <1115117931@qq.com> --- gateway/platforms/webhook.py | 23 ++++++++++++++++++++++- gateway/run.py | 8 +++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/gateway/platforms/webhook.py b/gateway/platforms/webhook.py index 7c77a96b5b8..9d236f2198b 100644 --- a/gateway/platforms/webhook.py +++ b/gateway/platforms/webhook.py @@ -938,13 +938,34 @@ class WebhookAdapter(BasePlatformAdapter): success=False, error="Missing repo or pr_number" ) + # --- Input validation (prevent CLI argument injection) --- + # pr_number must be a positive integer. + try: + pr_int = int(pr_number) + if pr_int <= 0: + raise ValueError("non-positive") + except (ValueError, TypeError): + logger.error( + "[webhook] invalid pr_number: %r", pr_number + ) + return SendResult( + success=False, error="Invalid pr_number" + ) + + # repo must match owner/name (alphanumeric, hyphens, underscores, dots). + if not re.fullmatch(r"[A-Za-z0-9._-]+/[A-Za-z0-9._-]+", repo): + logger.error("[webhook] invalid repo format: %r", repo) + return SendResult( + success=False, error="Invalid repo format" + ) + try: result = subprocess.run( [ "gh", "pr", "comment", - str(pr_number), + str(pr_int), "--repo", repo, "--body", diff --git a/gateway/run.py b/gateway/run.py index d449bdcf9f4..c32fbff0c87 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -10910,8 +10910,8 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew ) except Exception: logger.debug("Failed to persist inbound user message after agent exception", exc_info=True) - error_type = type(e).__name__ - error_detail = str(e)[:300] if str(e) else "no details available" + # Log full details server-side only; never expose raw exception + # types or messages to end users (info-leakage risk). status_hint = "" status_code = getattr(e, "status_code", None) _hist_len = len(history) if 'history' in locals() else 0 @@ -10955,9 +10955,7 @@ class GatewayRunner(GatewayAuthorizationMixin, GatewayKanbanWatchersMixin, Gatew elif status_code == 400: status_hint = " The request was rejected by the API." return ( - f"Sorry, I encountered an error ({error_type}).\n" - f"{error_detail}\n" - f"{status_hint}" + f"Sorry, I encountered an unexpected error.{status_hint}\n" "Try again or use /reset to start a fresh session." ) finally: