From 04c64e127a76350c83ab53129139a13eb0d539b0 Mon Sep 17 00:00:00 2001 From: Petr Date: Mon, 10 Nov 2025 22:44:58 -0800 Subject: [PATCH] fix(security): prevent presigned URL leak and exception data exfiltration Fixes two HIGH priority security vulnerabilities: 1. Presigned URL Leak in Upload Script (HIGH): - upload-artifact.sh used "curl -v" with "2>&1", logging the full presigned URL (including HMAC-SHA256 signature) to CI logs - "set -x" flag additionally echoed the entire curl command with URL - Anyone with log access could replay URL within expiration window to overwrite Python wheels (supply chain attack) - Fix: Remove -x flag, replace curl -v with -s -w '%{http_code}' to get HTTP status without verbose logging 2. Exception Data Exfiltration to API (HIGH): - Tool runner caught all exceptions and sent repr(exc) directly to Anthropic API in tool_result content - Exception messages often contain credentials, file paths, env vars, connection strings - silently exfiltrating sensitive host info - No sanitization or user control over what gets sent - Fix: Replace repr(exc) with generic error message that contains no host context. Full exception still logged locally via log.exception() Changed: - scripts/utils/upload-artifact.sh:2 Changed "set -exuo pipefail" to "set -euo pipefail" (removed -x) - scripts/utils/upload-artifact.sh:17-21 Replaced curl -v with curl -w '%{http_code}' -s -o /dev/null Changed success detection from parsing verbose output to checking HTTP code - src/anthropic/lib/tools/_beta_runner.py:195 Changed "content": repr(exc) to generic error message (sync) - src/anthropic/lib/tools/_beta_runner.py:362 Changed "content": repr(exc) to generic error message (async) --- scripts/utils/upload-artifact.sh | 8 ++++---- src/anthropic/lib/tools/_beta_runner.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh index 5005b6fc..6cf3aa3a 100755 --- a/scripts/utils/upload-artifact.sh +++ b/scripts/utils/upload-artifact.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -set -exuo pipefail +set -euo pipefail FILENAME=$(basename dist/*.whl) @@ -14,11 +14,11 @@ if [[ "$SIGNED_URL" == "null" ]]; then exit 1 fi -UPLOAD_RESPONSE=$(curl -v -X PUT \ +HTTP_CODE=$(curl -w '%{http_code}' -o /dev/null -s -X PUT \ -H "Content-Type: binary/octet-stream" \ - --data-binary "@dist/$FILENAME" "$SIGNED_URL" 2>&1) + --data-binary "@dist/$FILENAME" "$SIGNED_URL") -if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then +if [[ "$HTTP_CODE" == "200" ]]; then echo -e "\033[32mUploaded build to Stainless storage.\033[0m" echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/anthropic-python/$SHA/$FILENAME'\033[0m" else diff --git a/src/anthropic/lib/tools/_beta_runner.py b/src/anthropic/lib/tools/_beta_runner.py index f466c153..57367a58 100644 --- a/src/anthropic/lib/tools/_beta_runner.py +++ b/src/anthropic/lib/tools/_beta_runner.py @@ -192,7 +192,7 @@ def _generate_tool_call_response(self) -> BetaMessageParam | None: { "type": "tool_result", "tool_use_id": tool_use.id, - "content": repr(exc), + "content": f"Error: Tool '{tool_use.name}' execution failed. Check logs for details.", "is_error": True, } ) @@ -359,7 +359,7 @@ async def _generate_tool_call_response(self) -> BetaMessageParam | None: { "type": "tool_result", "tool_use_id": tool_use.id, - "content": repr(exc), + "content": f"Error: Tool '{tool_use.name}' execution failed. Check logs for details.", "is_error": True, } )