Skip to content

fix(api): guard validateInstallRequest against null/undefined + wrap install catch JSON.parse (PKT-472) - #405

Open
bugsyhewitt wants to merge 1 commit into
mainfrom
feat/472-api-validation-install-null-undefined-type-guard
Open

fix(api): guard validateInstallRequest against null/undefined + wrap install catch JSON.parse (PKT-472)#405
bugsyhewitt wants to merge 1 commit into
mainfrom
feat/472-api-validation-install-null-undefined-type-guard

Conversation

@bugsyhewitt

Copy link
Copy Markdown
Owner

Summary

Corrective re-author of rejected PKT-406. Fixes POST /v1/install returning 500 INTERNAL_ERROR when api_key or machine_hash is null/undefined in the request body.

Root cause: Two-layer failure:

  1. validation.ts:110-118isValidApiKeyFormat(req.api_key) calls .length on null/undefined, throwing TypeError
  2. server.ts:238JSON.parse((e as Error).message) throws SyntaxError on the non-JSON TypeError message, escaping the catch and falling through to the outer 500 handler

Fix (defense-in-depth):

  • Part A (validation.ts): Add typeof guards at the top of validateInstallRequest using const apiKey: unknown = req.api_key to handle null/undefined — returns INVALID_API_KEY_FORMAT / INVALID_MACHINE_HASH with received_type detail before reaching .length
  • Part B (server.ts): Wrap JSON.parse((e as Error).message) in a nested try/catch — structured throws (the handleInstall JSON-stringify convention) still reach the client as parsed objects; non-structured throws (TypeErrors, future storage plain Errors) log ${e} to stderr only and return a generic INSTALL_HANDLER_ERROR body (no e.message leak per PKT-406 rejection directive)

Tests (+5 net): 4 unit tests in validation.test.ts (null api_key, undefined api_key, null machine_hash, numeric api_key) + 1 integration test in ts-api-server-route-handler-defensive-paths.test.ts (null api_key → 400 via full server stack)

TDD: All 5 tests written and verified failing before implementation.

Ship-gate results

  • TS (vitest): 2166 passed, 46 skipped (+5 net from 2161 baseline)
  • Python (pytest): 1206 passed, 125 skipped (unchanged)
  • pnpm tsc --noEmit: pre-existing 3 errors in unrelated test files (unchanged)

Runtime verify

Confirmed on scratch port 19472 with live server:

POST /v1/install {"api_key": null, "machine_hash": "aaa...64"}
→ HTTP 400 {"error":{"code":"INVALID_API_KEY_FORMAT","message":"api_key must be a string.","details":{"received_type":"null"}}}

POST /v1/install {"api_key": "AAA...43", "machine_hash": null}
→ HTTP 400 {"error":{"code":"INVALID_MACHINE_HASH","message":"machine_hash must be a string.","details":{"received_type":"null"}}}

POST /v1/install {"api_key": 42, "machine_hash": "aaa...64"}
→ HTTP 400 {"error":{"code":"INVALID_API_KEY_FORMAT","message":"api_key must be a string.","details":{"received_type":"number"}}}

Files changed

  • src/alienclaw/api/validation.ts (~12 LOC added)
  • src/alienclaw/api/server.ts (~13 LOC changed)
  • test/api/validation.test.ts (4 tests added)
  • test/api/ts-api-server-route-handler-defensive-paths.test.ts (1 test added)

Binding architectural review: APPROVED (alienclaw-architect, Opus)

🤖 Generated with Claude Code

…install catch JSON.parse (PKT-472)

Part A: add typeof guards in validateInstallRequest before calling isValidApiKeyFormat/
isValidMachineHash, using local unknown-typed variables to avoid TS narrowing issues.
Returns INVALID_API_KEY_FORMAT/INVALID_MACHINE_HASH with received_type detail instead
of throwing a TypeError that escaped to the outer 500 handler.

Part B: wrap the catch arm's JSON.parse((e as Error).message) in a nested try/catch in
the /v1/install route. On inner parse failure (non-structured throw) writes to stderr
only and returns a safe generic INSTALL_HANDLER_ERROR body. No e.message in response body.

Adds 4 unit tests in test/api/validation.test.ts and 1 integration test in
test/api/ts-api-server-route-handler-defensive-paths.test.ts confirming 400 (not 500)
for null api_key. All 2166 TS tests pass; 1206 Python tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@bugsyhewitt bugsyhewitt added tester opened by a Tester agent auto-merge-ok Binding review passed + ship-gate green — cleared for auto-merge labels Aug 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

auto-merge-ok Binding review passed + ship-gate green — cleared for auto-merge tester opened by a Tester agent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant