An entry for the streamplace vod showcase
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: validate endpoint inputs in runner, return 400 for validation errors

- Add input validation for unsandboxed (local dev) execution
- Create ValidationError class for proper error typing
- Return 400 status for validation errors instead of 500

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

+36 -3
+25 -2
packages/at-run/runner/src/index.ts
··· 10 10 11 11 import { Hono } from "hono" 12 12 import { cors } from "hono/cors" 13 - import { isEndpoint, type Endpoint } from "@at-run/runtime" 13 + import { isEndpoint, type Endpoint, v } from "@at-run/runtime" 14 14 import { AtpAgent } from "@atproto/api" 15 15 import { IdResolver } from "@atproto/identity" 16 16 import { AtUri } from "@atproto/syntax" ··· 18 18 extractBundleInfo, 19 19 executeInSandbox, 20 20 checkDenoAvailable, 21 + ValidationError, 21 22 type BundleInfo, 22 23 } from "./sandbox" 23 24 import { ··· 283 284 ) 284 285 } 285 286 286 - result = await endpoint.handler(args) 287 + // Validate input if schema is defined 288 + let validatedArgs = args 289 + if (endpoint.input) { 290 + try { 291 + validatedArgs = v.parse(endpoint.input, args) 292 + } catch (err) { 293 + return Response.json( 294 + { error: "Validation error", details: err instanceof Error ? err.message : String(err) }, 295 + { status: 400 } 296 + ) 297 + } 298 + } 299 + 300 + result = await endpoint.handler(validatedArgs) 287 301 } 288 302 289 303 if (result instanceof Response) return result ··· 352 366 }) 353 367 } catch (error) { 354 368 console.error("Error:", error) 369 + if (error instanceof ValidationError) { 370 + return c.json({ error: "Validation error", details: error.message }, 400) 371 + } 355 372 return c.json({ error: error instanceof Error ? error.message : "Unknown error" }, 500) 356 373 } 357 374 }) ··· 377 394 }) 378 395 } catch (error) { 379 396 console.error("Error:", error) 397 + if (error instanceof ValidationError) { 398 + return c.json({ error: "Validation error", details: error.message }, 400) 399 + } 380 400 return c.json({ error: error instanceof Error ? error.message : "Unknown error" }, 500) 381 401 } 382 402 }) ··· 406 426 }) 407 427 } catch (error) { 408 428 console.error("Error:", error) 429 + if (error instanceof ValidationError) { 430 + return c.json({ error: "Validation error", details: error.message }, 400) 431 + } 409 432 return c.json({ error: error instanceof Error ? error.message : "Unknown error" }, 500) 410 433 } 411 434 })
+11 -1
packages/at-run/runner/src/sandbox.ts
··· 16 16 import * as crypto from "crypto" 17 17 import * as fs from "fs" 18 18 19 + /** 20 + * Error thrown when input validation fails 21 + */ 22 + export class ValidationError extends Error { 23 + constructor(message: string) { 24 + super(message) 25 + this.name = "ValidationError" 26 + } 27 + } 28 + 19 29 export interface BundleInfo { 20 30 permissions: Permissions 21 31 limits?: ResourceLimits ··· 293 303 } 294 304 295 305 if (result.validationError) { 296 - throw new Error(`Input validation failed: ${result.validationError}`) 306 + throw new ValidationError(result.validationError) 297 307 } 298 308 299 309 if (!result.success) {