Skip to content

fix: security hardening across SDK, CLI, and MCP#2

Closed
Hovessian wants to merge 1 commit intofetchai:mainfrom
Hovessian:fix/security-hardening
Closed

fix: security hardening across SDK, CLI, and MCP#2
Hovessian wants to merge 1 commit intofetchai:mainfrom
Hovessian:fix/security-hardening

Conversation

@Hovessian
Copy link
Collaborator

Summary

  • MCP agentverse (#84): Validate agentFile path is within process.cwd() to prevent arbitrary file reads via path traversal
  • MCP scaffold (#85): Validate outputDir is within process.cwd() for both scaffoldAgent and scaffoldSwarm to prevent arbitrary file writes
  • SDK handoff (#87): Validate Ethereum address format (/^0x[a-fA-F0-9]{40}$/) in generateTradeLink to prevent URL injection
  • SDK client (#89): Cap Retry-After header wait to 30 seconds to prevent a malicious server from stalling the client indefinitely
  • CLI tokenize (#90): Validate --image URL scheme is HTTP/HTTPS to prevent SSRF via file:// or other protocols

Issue #86 (default URL points to staging) is already fixed in urls.ts — production is the default when AGENT_LAUNCH_ENV is not set to dev.

Test plan

  • Verify MCP deploy_to_agentverse rejects paths outside cwd (e.g., ../../etc/passwd)
  • Verify MCP scaffold_agent / scaffold_swarm reject outputDir outside cwd
  • Verify generateTradeLink throws on malformed addresses
  • Verify SDK client retries are capped at 30s even with large Retry-After header
  • Verify CLI tokenize --image file:///etc/passwd is rejected

Addresses: fetchai/launchpadDAO#84, fetchai/launchpadDAO#85, fetchai/launchpadDAO#87, fetchai/launchpadDAO#89, fetchai/launchpadDAO#90

🤖 Generated with Claude Code

- MCP agentverse: validate agentFile path is within cwd (path traversal)
- MCP scaffold: validate outputDir is within cwd for both scaffoldAgent and scaffoldSwarm
- SDK handoff: validate Ethereum address format in generateTradeLink
- SDK client: cap Retry-After wait to 30s to prevent unbounded sleep
- CLI tokenize: validate --image URL scheme is HTTP/HTTPS (SSRF prevention)

Addresses: fetchai/launchpadDAO#84, #85, #87, #89, #90

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment on lines +161 to +163
const waitMs = retryAfter
? Math.min(parseInt(retryAfter, 10) * 1000, MAX_RETRY_WAIT_MS)
: delayMs;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: parseInt on a non-numeric Retry-After header returns NaN, causing sleep(NaN) which results in an immediate retry instead of waiting.
Severity: MEDIUM

Suggested Fix

Validate the result of parseInt(retryAfter, 10) before using it. If the result is NaN, the logic should fall back to the default exponential backoff delay (delayMs) instead of using the invalid value. A check like !isNaN(parsedRetryAfter) should be added.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: packages/sdk/src/client.ts#L161-L163

Potential issue: The code calculates `waitMs` using `parseInt(retryAfter, 10)`. If the
`Retry-After` header contains a non-numeric value, such as an HTTP-date string,
`parseInt` returns `NaN`. This `NaN` value propagates through the `Math.min`
calculation, resulting in a call to `sleep(NaN)`. The underlying `setTimeout` treats
`NaN` as a 0ms delay, causing the client to bypass the intended rate-limiting and enter
a tight retry loop. This can lead to client-side resource exhaustion and excessive
requests to the server when a malformed header is received.

Did we get this right? 👍 / 👎 to inform future reviews.

@Hovessian
Copy link
Collaborator Author

Closing — all 5 security fixes are already on main with equivalent or better implementations (extracted helper functions, stricter HTTPS-only policy for image URLs). Thanks for the thorough security audit; the issues identified were valid and important.

@Hovessian Hovessian closed this Mar 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant