diff --git a/docs/api/cloudflare-schema.yaml b/docs/api/cloudflare-schema.yaml index 00bdcf18..b3447891 100644 --- a/docs/api/cloudflare-schema.yaml +++ b/docs/api/cloudflare-schema.yaml @@ -4049,127 +4049,6 @@ components: unreachable: type: integer description: Number of unreachable URLs. - responses: - BadRequest: - description: Bad request - input validation failed - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - example: false - error: - type: string - example: 'requestId: Invalid requestId format' - Unauthorized: - description: Unauthorized - authentication required - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - example: false - error: - type: string - example: Authentication required - RateLimited: - description: Rate limit exceeded - headers: - Retry-After: - schema: - type: integer - description: Seconds until retry allowed - X-RateLimit-Limit: - schema: - type: integer - X-RateLimit-Remaining: - schema: - type: integer - X-RateLimit-Reset: - schema: - type: integer - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - example: false - error: - type: string - example: Rate limit exceeded. - BadRequestError: - description: Bad request - request body failed schema validation - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - example: false - error: - type: string - example: Invalid request body - RateLimitError: - description: Rate limit exceeded - headers: - Retry-After: - schema: - type: integer - description: Seconds until retry allowed - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - example: false - error: - type: string - example: Rate limit exceeded. Maximum 10 requests per minute. - CompilationError: - description: Compilation failed - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - example: false - error: - type: string - UnauthorizedError: - description: Unauthorized - missing or invalid admin key - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - example: false - error: - type: string - ServiceUnavailable: - description: Service unavailable - required binding not configured - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - example: false - error: - type: string ConvertRuleRequest: type: object required: @@ -4832,6 +4711,140 @@ components: additionalProperties: type: integer description: Per-route request counts for this day + responses: + BadRequest: + description: Bad request - input validation failed + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: false + error: + type: string + example: 'requestId: Invalid requestId format' + Unauthorized: + description: Unauthorized - authentication required + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: false + error: + type: string + example: Authentication required + RateLimited: + description: Rate limit exceeded + headers: + Retry-After: + schema: + type: integer + description: Seconds until retry allowed + X-RateLimit-Limit: + schema: + type: integer + X-RateLimit-Remaining: + schema: + type: integer + X-RateLimit-Reset: + schema: + type: integer + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: false + error: + type: string + example: Rate limit exceeded. + BadRequestError: + description: Bad request - request body failed schema validation + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: false + error: + type: string + example: Invalid request body + RateLimitError: + description: Rate limit exceeded + headers: + Retry-After: + schema: + type: integer + description: Seconds until retry allowed + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: false + error: + type: string + example: Rate limit exceeded. Maximum 10 requests per minute. + CompilationError: + description: Compilation failed + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: false + error: + type: string + UnauthorizedError: + description: Unauthorized - missing or invalid admin key + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: false + error: + type: string + ForbiddenError: + description: Forbidden - insufficient permissions + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: false + error: + type: string + example: Forbidden + ServiceUnavailable: + description: Service unavailable - required binding not configured + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: false + error: + type: string securitySchemes: Turnstile: type: apiKey diff --git a/docs/api/openapi.yaml b/docs/api/openapi.yaml index 13232357..e2d634c0 100644 --- a/docs/api/openapi.yaml +++ b/docs/api/openapi.yaml @@ -3952,119 +3952,6 @@ components: type: integer description: Number of unreachable URLs. - responses: - BadRequest: - description: Bad request - input validation failed - content: - application/json: - schema: - type: object - properties: - success: { type: boolean, example: false } - error: { type: string, example: 'requestId: Invalid requestId format' } - - Unauthorized: - description: Unauthorized - authentication required - content: - application/json: - schema: - type: object - properties: - success: { type: boolean, example: false } - error: { type: string, example: 'Authentication required' } - - RateLimited: - description: Rate limit exceeded - headers: - Retry-After: - schema: { type: integer } - description: Seconds until retry allowed - X-RateLimit-Limit: - schema: { type: integer } - X-RateLimit-Remaining: - schema: { type: integer } - X-RateLimit-Reset: - schema: { type: integer } - content: - application/json: - schema: - type: object - properties: - success: { type: boolean, example: false } - error: { type: string, example: 'Rate limit exceeded.' } - - BadRequestError: - description: Bad request - request body failed schema validation - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - example: false - error: - type: string - example: Invalid request body - - RateLimitError: - description: Rate limit exceeded - headers: - Retry-After: - schema: - type: integer - description: Seconds until retry allowed - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - example: false - error: - type: string - example: Rate limit exceeded. Maximum 10 requests per minute. - - CompilationError: - description: Compilation failed - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - example: false - error: - type: string - - UnauthorizedError: - description: Unauthorized - missing or invalid admin key - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - example: false - error: - type: string - - ServiceUnavailable: - description: Service unavailable - required binding not configured - content: - application/json: - schema: - type: object - properties: - success: - type: boolean - example: false - error: - type: string - # Rule Validation Schemas ConvertRuleRequest: type: object @@ -4700,6 +4587,129 @@ components: type: integer description: Per-route request counts for this day + responses: + BadRequest: + description: Bad request - input validation failed + content: + application/json: + schema: + type: object + properties: + success: { type: boolean, example: false } + error: { type: string, example: 'requestId: Invalid requestId format' } + + Unauthorized: + description: Unauthorized - authentication required + content: + application/json: + schema: + type: object + properties: + success: { type: boolean, example: false } + error: { type: string, example: 'Authentication required' } + + RateLimited: + description: Rate limit exceeded + headers: + Retry-After: + schema: { type: integer } + description: Seconds until retry allowed + X-RateLimit-Limit: + schema: { type: integer } + X-RateLimit-Remaining: + schema: { type: integer } + X-RateLimit-Reset: + schema: { type: integer } + content: + application/json: + schema: + type: object + properties: + success: { type: boolean, example: false } + error: { type: string, example: 'Rate limit exceeded.' } + + BadRequestError: + description: Bad request - request body failed schema validation + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: false + error: + type: string + example: Invalid request body + + RateLimitError: + description: Rate limit exceeded + headers: + Retry-After: + schema: + type: integer + description: Seconds until retry allowed + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: false + error: + type: string + example: Rate limit exceeded. Maximum 10 requests per minute. + + CompilationError: + description: Compilation failed + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: false + error: + type: string + + UnauthorizedError: + description: Unauthorized - missing or invalid admin key + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: false + error: + type: string + + ForbiddenError: + description: Forbidden - insufficient permissions + content: + application/json: + schema: + type: object + properties: + success: { type: boolean, example: false } + error: { type: string, example: 'Forbidden' } + + ServiceUnavailable: + description: Service unavailable - required binding not configured + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + example: false + error: + type: string + securitySchemes: Turnstile: type: apiKey diff --git a/docs/postman/postman-collection.json b/docs/postman/postman-collection.json index ff736082..a4ee4d10 100644 --- a/docs/postman/postman-collection.json +++ b/docs/postman/postman-collection.json @@ -3060,7 +3060,7 @@ }, "body": { "mode": "raw", - "raw": "{}", + "raw": "{\n \"currentPassword\": \"string\",\n \"newPassword\": \"string\"\n}", "options": { "raw": { "language": "json" diff --git a/scripts/generate-cloudflare-schema.ts b/scripts/generate-cloudflare-schema.ts index f26bd981..f8b5d0cf 100644 --- a/scripts/generate-cloudflare-schema.ts +++ b/scripts/generate-cloudflare-schema.ts @@ -50,6 +50,59 @@ interface OpenAPISpec { [key: string]: any; } +/** + * Walk the spec and return every local JSON-pointer `$ref` (starting with `#/`) + * that cannot be resolved within the document. + * + * @param spec - The parsed OpenAPI spec object. + * @returns An array of unresolved ref strings. + */ +function validateLocalRefs(spec: OpenAPISpec): string[] { + const unresolvedRefs: string[] = []; + const seen = new Set(); + + function resolvePointer(pointer: string): boolean { + // pointer = "#/components/responses/ForbiddenError" + // Per RFC 6901 ยง3, each segment must be unescaped: ~1 โ†’ / then ~0 โ†’ ~ + const segments = pointer.slice(2).split('/').map((s) => s.replaceAll('~1', '/').replaceAll('~0', '~')); + let current: unknown = spec; + for (const segment of segments) { + if (current == null || typeof current !== 'object') { + return false; + } + current = (current as Record)[segment]; + } + return current !== undefined; + } + + function walk(node: unknown): void { + if (node == null || typeof node !== 'object') { + return; + } + if (Array.isArray(node)) { + for (const item of node) { + walk(item); + } + return; + } + for (const [key, value] of Object.entries(node as Record)) { + if (key === '$ref' && typeof value === 'string' && value.startsWith('#/')) { + if (!seen.has(value)) { + seen.add(value); + if (!resolvePointer(value)) { + unresolvedRefs.push(value); + } + } + } else { + walk(value); + } + } + } + + walk(spec); + return unresolvedRefs; +} + async function generateCloudflareSchema(): Promise { console.log('๐Ÿš€ Generating Cloudflare API Shield schema...\n'); @@ -105,6 +158,18 @@ async function generateCloudflareSchema(): Promise { console.log('โœ… No x-* extensions found in operations'); } + // Validate all local $refs resolve within the document + const unresolvedRefs = validateLocalRefs(spec); + if (unresolvedRefs.length > 0) { + console.error('โŒ Unresolved local $ref(s) found in OpenAPI spec:'); + for (const ref of unresolvedRefs) { + console.error(` ${ref}`); + } + console.error('Fix the missing definitions before regenerating the schema.'); + Deno.exit(1); + } + console.log('โœ… All local $refs resolve correctly'); + // Add header comment const header = `# Auto-generated Cloudflare API Shield Schema # Generated from docs/api/openapi.yaml