Requires an AI engine: No HTTP handler reads, updates or deletes an object by a client-supplied identifier without binding it to the authenticated caller.
Inputs
Non-test code files of the checkout (JavaScript, TypeScript, Python, PHP). Route declarations of Express/Koa/Fastify/Hono, NestJS, Next.js, Django, Flask, FastAPI and Laravel with a 40-line handler window; handlers that take an id from params, query or body and use it in a data access are kept. Their files (at most 24, handlers without ownership signal first, first 96000 characters each) are sent line-numbered to the chosen engine through the platform ai service; at most 160000 estimated tokens per execution. Paths listed in excludes (test, spec and tmp files, __tests__, __mocks__, fixtures, test, tests and audit-scripts folders) are never analysed. Every model call (rubric, repository context and files) stays under 120000 characters; a candidate set that does not fit is sent in parts, each part bounded by what the chosen engine serves in one call (the engine publishes that budget; a call is never filled up to the transport cap).
Decision rule
Three applicability gates run before the model is asked and again over its answer, and a handler they catch is reported INFO with its reason, never blocking: (1) a handler whose window carries a ROLE or SERVICE gate (isAdmin, hasRole, hasDevRole, requireRole, requireAdmin, isService, requireService, verifyService, SERVICE_SECRET, x-service-secret, hasPermission, permission_required, IsAdminUser, AdminGuard, RolesGuard, @Roles, Gate::allows, abort_unless…) is not reachable by an arbitrary caller whatever id it sends; (2) a CATALOGUE resource of the release (services, plans, products, prices, categories, tags, countries, currencies, languages, translations, features, flags, settings, templates, themes, menus, roles, permissions, providers, regions, taxes, rates…) that no schema file of the checkout (models, entities, migrations, Prisma, SQL, DAOs) declares with an ownership field (user_id, owner_id, account_id, customer_id, author_id, tenant_id, created_by…) and whose handler names no such field has no owner: there is no other user's object to reach; (3) a PROVIDER WEBHOOK endpoint — a route whose path or file says webhook, callback, notification, notify, ipn or hook — whose handler VERIFIES the signature the external provider computed over this request (a gateway signature header such as stripe-signature, x-signature with x-request-id, x-hub-signature, svix-id, x-shopify-hmac-sha256, paypal-transmission-sig, x-razorpay-signature, x-twilio-signature, or a call to constructEvent, verifyWebhookSignature, validateWebhook, verify<Provider>Signature…) has no calling user: it is authenticated by the webhook secret, not by a session or a role, so no ownership signal can exist in its window, and the identifiers it carries belong to the provider and are used only after that verification. A webhook route that does NOT verify a provider signature is not gated and is judged as any other handler. Not applicable when no handler uses a client-supplied id in a data access. Handlers without an ownership or authorisation signal (caller id in the query, policy, authorize, scope, tenant, ORM scope) are reported as LOW and reviewed first. FAIL on a HIGH model finding with a cited line and confidence of at least 0.8 (another user's object read or written by changing the id); MEDIUM findings block only inside Extended suites. FAIL also when any engine answer is unparseable. PASS when every such handler was reviewed and nothing blocking was found. Without an AI engine the check is not executed (requires an AI engine).
Type
ai-assisted · engine chosen by the organisation · medium
1.0.6: `assess` mode cuts the candidate set to what the chosen ENGINE serves in ONE call (`ctx.ai.maxInputChars`, published by the engine per its catalogue budget; `ENGINE_CALL_MAX_CHARS` = 32 000 when an older sandbox does not publish it), instead of filling the call up to the TRANSPORT cap of the proxy (`spec.promptMaxChars`, unchanged at 120 000). On a 1 319-file checkout the old packing produced single prompts of 17 672 / 26 709 / 29 351 tokens and the platform GPU answered "CUDA out of memory": the check ended in `error`. Every call of the same run under ~24 000 characters was answered normally. The part aggregation of 1.0.1 is unchanged: a HIGH in any part is a FAIL, an unsatisfied part is a FAIL, undecided never passes. (1.0.5: false positive of the attestation of platform/apps/pay (2026-09-24), fixed in the script: una TERCERA puerta de aplicabilidad, en los dos mismos momentos: un endpoint de WEBHOOK de proveedor (ruta o fichero que dice webhook, callback, notification, ipn o hook) cuyo handler VERIFICA la firma que el proveedor externo calculó sobre esa petición (stripe-signature, x-signature + x-request-id, x-hub-signature, svix, x-shopify-hmac-sha256, paypal-transmission-sig, constructEvent, verifyWebhookSignature, verify<Proveedor>Signature…) no tiene usuario llamante: lo autentica el secreto del webhook, no una sesión ni un rol, así que en su ventana NUNCA puede aparecer una señal de pertenencia, y los identificadores que trae son del PROVEEDOR y sólo se usan después de esa verificación. Un webhook que no verifica firma alguna NO queda excusado y se juzga como cualquier otro handler. (1.0.4: false positive of the attestation of platform/apps/auth (2026-09-24), fixed in the script: dos puertas de aplicabilidad, antes de preguntar al modelo y otra vez sobre su respuesta, con el motivo en la traza y como INFO (nunca bloqueante): (1) un handler con puerta de ROL o SERVICIO en su ventana (isAdmin, hasRole, hasDevRole, requireService, SERVICE_SECRET, IsAdminUser, @Roles, Gate::allows, abort_unless…) no es alcanzable por un llamante cualquiera mande el id que mande; (2) un recurso de CATÁLOGO de la release (services, plans, products, categories, translations…) que ningún esquema del checkout declara con campo de dueño no tiene dueño: no hay objeto de otro usuario al que llegar. 1.0.3: requires an AI engine (spec.requiresEngine: true): the check needs judgement and is executed only when the organisation provides an engine; the core catalogue runs without one.))
// candidate = route handler window with ID_FROM_REQUEST (req.params.id, kwargs['pk'], $request->route('id'), @Param('id')...) AND DATA_ACCESS (findById, objects.get, ::find, UPDATE...)
const OWNERSHIP = /\b(?:user_?id|owner|tenant\w*|req\.user|current_user|Auth::user|policy|authorize|can\(|Gate::|scope|belongsTo)\b/i; // absent -> reviewed first
// gate 3 (1.0.5): a webhook endpoint that verifies the PROVIDER's signature has no calling user to compare an id against
const WEBHOOK_ENDPOINT = /(?:^|[/_.-])(?:webhooks?|callbacks?|notifications?|ipn|hooks?)(?:$|[/_.-])/i; // + a gateway signature header or verifier call -> INFO, never blocking
// FAIL on a HIGH model finding (idor-read | idor-write) with a cited line and confidence >= 0.8
The full script is disclosed on request in a read-only viewer (never published on GitHub); the attestation binds to this exact hash.