History

Validate cached OpenAPI payloads before rehydration

Rhys Sullivan committed 8 months ago

Commit 501720b1 parent 91ec1fe1 file · +33 −3Browse files

Unified · Split

executor/packages/database/src/runtime/tool_source_loading.ts+33−3
@@ -29,6 +29,33 @@ const OPENAPI_SPEC_CACHE_TTL_MS = 5 * 60 * 60_000; · 28 unmodified lines
2929 /** Cache version - bump when tool snapshot/registry/type-hint semantics change. */
3030 const TOOL_SOURCE_CACHE_VERSION = "v25";
3131
32+function toPreparedOpenApiSpec(value: unknown): PreparedOpenApiSpec | null {
33+ const record = asPayload(value);
34+ if (!Array.isArray(record.servers)) {
35+ return null;
36+ }
37+ if (!record.paths || typeof record.paths !== "object" || Array.isArray(record.paths)) {
38+ return null;
39+ }
40+
41+ const servers = record.servers.filter((entry): entry is string => typeof entry === "string");
42+ const warnings = Array.isArray(record.warnings)
43+ ? record.warnings.filter((entry): entry is string => typeof entry === "string")
44+ : [];
45+ const dtsStatus =
46+ record.dtsStatus === "ready" || record.dtsStatus === "failed" || record.dtsStatus === "skipped"
47+ ? record.dtsStatus
48+ : undefined;
49+
50+ return {
51+ servers,
52+ paths: asPayload(record.paths),
53+ warnings,
54+ dts: typeof record.dts === "string" ? record.dts : undefined,
55+ dtsStatus,
56+ };
57+}
58+
3259 export function sourceSignature(workspaceId: string, sources: Array<{ id: string; updatedAt: number; enabled: boolean }>): string {
3360 const parts = sources
3461 .map((source) => `${source.id}:${source.updatedAt}:${source.enabled ? 1 : 0}`)
@@ -235,9 +262,12 @@ async function loadCachedOpenApiSpec( · 200 unmodified lines
235262 const blob = await ctx.storage.get(entry.storageId);
236263 if (blob) {
237264 const json = await blob.text();
238− const prepared = JSON.parse(json) as PreparedOpenApiSpec;
239− if (!includeDts || getDtsStatus(prepared) !== "skipped") {
240− return prepared;
265+ const parsedResult = Result.try(() => JSON.parse(json));
266+ if (parsedResult.isOk()) {
267+ const prepared = toPreparedOpenApiSpec(parsedResult.value);
268+ if (prepared && (!includeDts || getDtsStatus(prepared) !== "skipped")) {
269+ return prepared;
270+ }
241271 }
242272 }
243273 }