Skip to content

feat: Phase 5 — exporter, 5 tools, SSRF transport#9

Merged
prosdev merged 5 commits intomainfrom
feat/phase-5-exporter-tools
Mar 15, 2026
Merged

feat: Phase 5 — exporter, 5 tools, SSRF transport#9
prosdev merged 5 commits intomainfrom
feat/phase-5-exporter-tools

Conversation

@prosdev
Copy link
Contributor

@prosdev prosdev commented Mar 14, 2026

Summary

  • SSRF-hardened transport: PinnedDNSBackend overrides DNS resolution at the httpcore level, pinning resolved IPs to prevent DNS rebinding while preserving TLS SNI. validate_url() now returns (error, resolved_ip) tuple. SSRFSafeTransport subclasses httpx.HTTPTransport and forwards ssl_context.
  • 5 new tools: web_search (Tavily + DuckDuckGo fallback), wikipedia (MediaWiki opensearch API + wikipediaapi with user_agent), file_read/file_write (sandboxed with O_NOFOLLOW + realpath, UTF-8 only), weather (Open-Meteo, lat/lon detection)
  • File sandbox: Shared sandbox.py with two-layer defense — realpath pre-check for parent dirs + O_NOFOLLOW for leaf symlinks
  • Exporter: Generates standalone Python from GraphSchema — TypedDict state class with correct reducer annotations, node functions, routing, if __name__ block, and pip requirements
  • Export route: Upgraded from 501 stub to 200 returning ExportResponse
  • Registry: Expanded from 3 to 8 tools

Manual tests run (30-37)

# Test Result
30 url_fetch real HTTPS + SSRF blocks
31 web_search DDG fallback
32 web_search Tavily (with API key)
33 Wikipedia search + page (real API)
34 Weather current + forecast (Open-Meteo)
35 File sandbox (roundtrip, traversal, symlinks)
36 Exporter exec + AST validation
37 Export route end-to-end

Test plan

  • 302 unit tests passing
  • 8 manual tests passing against real APIs
  • ruff check + format clean
  • All existing tests unaffected (old 501 test updated to 200)

🤖 Generated with Claude Code

prosdev and others added 5 commits March 14, 2026 14:55
Covers 3 parts: SSRF-hardened httpx transport (PinnedDNSBackend),
5 remaining tools (web_search, wikipedia, file_read, file_write,
weather), and Python code exporter. Plan reviewed through 4 passes
of plan-reviewer. Also updates research-planner agent with ASCII
diagram guidance and plan-review handoff loop, and marks Phase 4
as merged in READMEs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Phase 5 of the execution layer:

- SSRF transport: PinnedDNSBackend pins resolved IPs at httpcore
  level, preventing DNS rebinding while preserving TLS SNI.
  validate_url() now returns (error, resolved_ip) tuple.
- 5 new tools: web_search (Tavily + DDG fallback), wikipedia
  (MediaWiki opensearch + wikipediaapi), file_read, file_write
  (sandboxed with O_NOFOLLOW + realpath), weather (Open-Meteo).
- Exporter: generates standalone Python from GraphSchema with
  TypedDict state, node functions, routing, and requirements.
- Export route upgraded from 501 stub to 200 with ExportResponse.
- Registry expanded from 3 to 8 tools.

302 unit tests, 8 manual tests (30-37) all passing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Exporter: validate all identifiers against safe pattern before
  interpolation into generated Python (prevents code injection).
  Apply _escape() to all string values in generated code.
- url_fetch: guard against empty getaddrinfo results returning
  (None, None) — now returns an explicit error.
- file_read: fix fd leak if fstat raises unexpectedly — added
  finally block with ownership tracking.
- file_write: wrap os.makedirs in try/except to return proper
  error envelope instead of uncaught 500.
- web_search: wrap int(max_results) in try/except to handle
  non-numeric input gracefully.
- exporter: _python_default uses .get() fallback for unknown types.
- Added cross-owner isolation test for export route.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Success responses include {success, result, source, truncated}.
Error responses include {success, error, recoverable}.
recoverable only applies to errors — matches schema spec and
all existing tool implementations.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Exporter: validate condition_branch/label values from edges
  against safe identifier pattern (closes remaining injection gap).
- Add test_symlink_inside_sandbox_blocked_by_onofollow to verify
  the O_NOFOLLOW defense layer independently from the realpath
  pre-check (platform-aware: macOS allows read-only symlinks).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@prosdev prosdev merged commit 6d30245 into main Mar 15, 2026
4 checks passed
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