Skip to content

fix(agent): suppress internal exception details in selfRegister error responses#259

Closed
sebastiondev wants to merge 2 commits into
HKUDS:mainfrom
sebastiondev:fix/cwe209-routes-agent-internal-afa6
Closed

fix(agent): suppress internal exception details in selfRegister error responses#259
sebastiondev wants to merge 2 commits into
HKUDS:mainfrom
sebastiondev:fix/cwe209-routes-agent-internal-afa6

Conversation

@sebastiondev

Copy link
Copy Markdown

Summary

The /api/claw/agents/selfRegister endpoint in service/server/routes_agent.py returns raw Python exception messages to HTTP clients on internal errors. When an unexpected exception occurs during agent registration, the handler at line 724 passes str(exc) directly into the HTTPException detail field:

except Exception as exc:
    conn.close()
    raise HTTPException(status_code=500, detail=str(exc))

This is an instance of CWE-209: Generation of Error Message Containing Sensitive Information. Depending on the exception type, the leaked message can include database file paths, SQLite schema details, connection strings, internal hostnames, or Python tracebacks — all useful for reconnaissance.

This endpoint is particularly sensitive because it requires no authentication. Unlike similar patterns in routes_signals.py, routes_experiments.py, routes_challenges.py, and routes_team_missions.py (which are behind auth), selfRegister is publicly accessible and deployed at ai4trade.ai.

Proof of Concept

An attacker can trigger internal errors and read the exception details from the response. For example, if the database is temporarily unavailable or a schema migration leaves a column missing:

# Send a registration request that triggers an internal error
# (e.g., database constraint violation, missing column, disk full)
curl -X POST https://ai4trade.ai/api/claw/agents/selfRegister \
  -H "Content-Type: application/json" \
  -d '{"name": "test-agent", "password": "pass123", "initial_balance": 100000}'

If any Exception is raised inside the try block (lines 566–724 of routes_agent.py), the current code returns the raw exception string in the JSON response body:

{"detail": "sqlite3.OperationalError: no such table: agents"}

or:

{"detail": "OperationalError: (psycopg2.OperationalError) could not connect to server: Connection refused\n\tIs the server running on host \"internal-db.cluster.local\" ..."}

This reveals database technology, internal hostnames, table names, and schema details to unauthenticated users.

Fix

The fix replaces str(exc) with a static generic message 'Agent registration failed' and logs the actual exception server-side via print() so developers can still diagnose issues:

except Exception as exc:
    conn.close()
    print(f'[Agent Registration Error] {exc}')
    raise HTTPException(status_code=500, detail='Agent registration failed')

This preserves the existing error handling flow (connection cleanup, 500 status code) while preventing information leakage. The HTTPException re-raise for known errors (e.g., 400 for duplicate names) is unaffected — that handler is in a separate except HTTPException block above.

Testing

The PR includes a test file (service/server/tests/test_cwe209_agent_registration.py) with four tests:

  1. test_successful_registration_still_works — Verifies normal registration is unaffected by the fix.
  2. test_duplicate_registration_returns_409_not_internal_details — Confirms duplicate agent names still return proper 400/409 errors.
  3. test_internal_error_returns_generic_message — Patches hash_password to raise a RuntimeError containing a simulated secret (SECRET_DB_CONNECTION_STRING_12345) and asserts the 500 response contains only the generic message, not the secret.
  4. test_internal_error_does_not_leak_traceback — Patches hash_password to raise an exception mimicking a SQLite error and asserts no database internals (sqlite3, OperationalError, Traceback) appear in the response.

All four tests pass.

Security Analysis

The vulnerability is exploitable because:

  • The endpoint requires no authentication — any internet user can reach it.
  • The except Exception handler is a catch-all covering ~160 lines of code (lines 566–724), meaning many different failure modes can produce varied and informative exception messages.
  • FastAPI serializes the detail field directly into the JSON response body with no sanitization.
  • The service is publicly deployed at ai4trade.ai.

Before submitting, we considered whether existing protections mitigate this. The CORS middleware is permissive (allow_origins=CORS_ORIGINS, allow_methods=['*']), and there is no global error handler or middleware that sanitizes 500 responses. The endpoint has no rate limiting or authentication gate. The information disclosure is real and reachable from the public internet.

Severity is low-to-medium: the leaked information aids reconnaissance (revealing database technology, internal network topology, table schemas) but is not directly exploitable for data theft or code execution on its own.

Note: Similar str(exc) patterns exist in other route files (routes_signals.py, routes_experiments.py, routes_challenges.py, routes_team_missions.py), but those endpoints are behind authentication. This PR targets the single unauthenticated instance as the highest-priority fix.


Submitted via the Sebastion AI GitHub App.

…e (CWE-209)

The /api/claw/agents/selfRegister endpoint returned str(exc) in the HTTP
500 response body, leaking database schema, table names, and internal
paths to unauthenticated callers.

Replace with a generic error message and log the exception server-side.
The original tests patched routes_agent.get_db_connection which is called
BEFORE the try/except block (line 563), so the exception bypassed the
fix's handler entirely.  Patching hash_password instead (called at line
571, inside the try) exercises the actual CWE-209 fix and all 4 tests
now pass.
@sebastiondev

Copy link
Copy Markdown
Author

Closing this as inactive — no maintainer response after 14 days.

The security finding and fix remain valid. If this is still relevant, I'm happy to reopen, rebase, or re-submit against a different branch. Just drop a comment.

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