Commit Graph

6 Commits

Author SHA1 Message Date
chaim 901e9c75b0 fix: deploy crash from MutableHeaders.pop + missing wget
Two issues blocked the security-fix deploy:

1. Starlette's MutableHeaders has no .pop() — del-via-membership is the
   correct mapping op. The previous code crashed every request with
   AttributeError, taking the container's healthcheck down with it.

2. Coolify's in-container healthcheck falls back to wget which wasn't
   installed in our slim base. Add wget alongside curl.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 18:22:36 +00:00
chaim 0340a670ed security: address all findings from vulnerability assessment
Implements the fixes catalogued under Task Master tasks 1-19, derived
from the security audit at .claude/plans/adaptive-sleeping-diffie.md.

Critical
- C-1 SSRF userinfo bypass: PDF proxy now rejects URLs containing
  userinfo or non-default ports, and asserts netloc == hostname.
- C-2 No authentication: optional NADLAN_API_KEY gate guards
  /api/search/*, /api/appraisals/pdf and /mcp until Traefik mTLS
  is wired up. Default behavior unchanged when env unset.

High
- H-1 Error info leak: HTTPException details replaced with generic
  string; full exceptions still logged via logger.exception.
- H-2 Security headers middleware: HSTS, X-Frame-Options, nosniff,
  Referrer-Policy, Permissions-Policy, CSP, server-banner stripped.
- H-3 Swagger gated: /api/docs and /api/openapi.json hidden when
  ENVIRONMENT=production.
- H-4 PDF proxy DoS: streams in 8KB chunks, rejects upstream above
  50MB Content-Length, also rejects mid-stream overrun. Magic-byte
  check moved before StreamingResponse so we can 502 cleanly.
- H-5 Per-IP rate limit: token-bucket per (path, IP) on /mcp,
  /api/search/*, /api/appraisals/pdf. Reads X-Forwarded-For for
  client IP behind Traefik.

Medium
- M-1 CORS: ALLOWED_ORIGINS env, allow_headers narrowed, no creds.
- M-2 Deal model: extra="ignore" + explicit fields for gushNum,
  parcelNum, subParcelNum, streetNameHeb, houseNum, deal_source,
  deal_type, deal_type_description, distance_meters. Frontend now
  reads snake_case.
- M-3 Input validators: regex on block, plot, appraiser names,
  dates, address, free-text — Hebrew + Latin + safe punctuation.
- M-4 COOLIFY_UUID moved out of deploy.yaml into Gitea Actions
  secret.
- M-5 Base images pinned to sha256 digests.
- M-6 Runtime deps tightened to ~= compatible-release.
- M-7 1MB request-body size limit middleware.

Low / privacy
- L-1 Heebo self-hosted via @fontsource. Removed Google Fonts links
  and CSP entries. Added meta referrer + meta robots noindex.
- L-2 Cache-Control: immutable for /assets, no-cache for HTML.
- L-3 Logger no longer prints user-supplied addresses; emits length
  only.
- L-4 Dockerfile healthcheck uses literal port 8000.
- L-5 PDF proxy stream cleanup uses contextlib.suppress.

Tests: 331 passed, 17 skipped. Frontend typecheck passes.
Findings file: .claude/plans/adaptive-sleeping-diffie.md
Tasks: .taskmaster/tasks/tasks.json

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 17:59:10 +00:00
chaim 97bbb852ec Fix: install curl in runtime image for Coolify healthcheck
The container ran fine (uvicorn started on :8000) but Coolify's healthcheck
runs `curl` / `wget` inside the container, neither of which ships in
python:3.13-slim. Container marked unhealthy → rolled back.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 13:00:57 +00:00
chaim 5e9963f51b Add: Web UI (React 19 + Vite + shadcn/ui) + FastAPI REST layer
Adds a self-contained web interface that wraps the MCP tools so end users
don't need to issue MCP commands. Single Docker container serves:

  /              → React SPA (RTL Hebrew, Heebo font, shadcn/ui components)
  /api/*         → FastAPI REST endpoints used by the SPA
  /api/docs      → OpenAPI / Swagger
  /mcp           → existing FastMCP streamable-http endpoint
  /health        → liveness probe

REST endpoints (in nadlan_mcp/web_app.py):
  GET  /api/search/address?q=...  — autocomplete + best-effort gush/חלקה
                                    via Govmap PARCEL_ALL layer
  POST /api/search/deals          — Govmap deals around an address
  POST /api/search/appraisals     — Decisive-appraiser search (gov.il)
  GET  /api/appraisals/pdf?url=.. — Stream PDFs through this server,
                                    carrying the upstream x-client-id
                                    header (a normal browser fetch fails)

UI flow:
  - Address search → resolves gush/חלקה → shows BOTH deals (Govmap) AND
    appraiser decisions (gov.il) for the same parcel side-by-side in tabs.
  - Block+plot search → goes straight to appraisals.
  - Appraiser-name search → all decisions by that appraiser.

Stack chosen for RTL-first usability and longevity: React 19, Vite 6,
TypeScript, Tailwind, shadcn/ui (Radix primitives), TanStack Query,
Lucide icons. ~295KB gzipped JS, no SSR overhead.

Multi-stage Dockerfile: Node 22 builds web/ in stage 1, Python 3.13
runtime serves the dist + the FastAPI app in stage 2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 12:35:08 +00:00
Nitzan P 5be68a5b04 Modernize to pyproject.toml and fix test assertion
- Remove requirements.txt and requirements-dev.txt (duplicates pyproject.toml)
- Update Dockerfile to use 'pip install .' instead of requirements.txt
- Update README.md and GitHub workflows to use 'pip install -e .[dev]'
- Fix test_get_valuation_comparables: check for 'deal_date' instead of non-existent 'asset_room_num'
  (rooms field is optional and excluded when None due to exclude_none=True)
- Fix fastmcp version constraint in pyproject.toml (>=2.13.0,<3.0.0)
- Add vcrpy to dev dependencies

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 23:00:03 +02:00
Nitzan P 87b0a355f8 Add HTTP transport support for cloud deployment
Enables deployment to cloud platforms (Render, Railway, etc.) while maintaining
backward compatibility with existing stdio transport for Claude Desktop.

New features:
- HTTP server entry point (run_http_server.py) using uvicorn
- Docker containerization with Python 3.13
- Health check endpoint at /health
- Comprehensive deployment documentation for Render, Railway, and Docker

Technical changes:
- Added uvicorn dependency for ASGI server
- Created Dockerfile with optimized multi-stage build (343MB)
- Added .dockerignore for efficient Docker builds
- Implemented /health endpoint using Starlette JSONResponse
- Updated README.md and DEPLOYMENT.md with HTTP deployment guides

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 09:43:18 +02:00