Auditor
Long-term and session keys are generated on the client; private keys never leave the device.
1.0.9: SPEED PASS: each file goes to the model as the EXCERPT around the lines the deterministic part marked relevant to THIS requirement (key-generation calls, private or secret key material, uploads that carry key material, the protocol library), 12 lines on each side (the whole file when that is most of it); the omitted spans are marked in place and a finding on a line that was not sent is dropped. Every key-management file has such lines by construction, so none is dropped. Up to 48 files, most relevant first, instead of 24 whole ones. STABLE PARTS: the files are packed by path with content-defined part boundaries (decided by the path, never by the content) and the repository context lists no files, so an unchanged file lands in a byte-identical part the engine may answer from its verdict cache (per organisation, declared in the trace and in the attestation). The parts are sent in parallel up to what the engine admits (ctx.ai.maxConcurrentCalls) and aggregated in part order, so the verdict never depends on which answer arrives first. The assessment reason is ONE sentence of at most 30 words (the platform GPU spends 46 ms per output token and the reason was most of every answer). Requirement, categories and decision rule unchanged. (1.0.8: the rubric did not say which keys a recognised protocol publishes by design: the public identity, signing and signed one-time keys of Olm/Signal stored by the server were reported as private keys leaving the device, and a backup encrypted on the device with the user recovery phrase as a leak. The rubric now names that public material, accepts a device-encrypted backup under a user-held secret, and a private-key finding must name the line where private material is generated by the server or placed in an upload; a "may/could" finding is speculative and not reported. (1.0.7: WHERE A FILE RUNS is decided from the FILE (and, when the file alone does not say, from the import graph of the checkout), never from a list of folder names of one particular repository, and a file whose server nature is UNPROVEN is never reported as server code. A monorepo has a SHARED layer — a package the browser bundle and the services both import — and the old rule read every ambiguous file as server: WebCrypto of the browser (`crypto.subtle` in a shared e2ee service that a client component imports) came out as "key generation in server code", once per product sharing the layer. The evidence is now, strongest first: the language (Python, PHP); a `server-only`/`client-only` import or a `'use server'`/`'use client'` directive; a location the framework declares as server (route.ts, middleware, pages/api, app/api, *.server.*, +server, wsgi/asgi); a browser runtime in the code (DOM member, Web Worker scope, `new Worker(`, a React client hook, a browser-environment polyfill such as fake-indexeddb or jsdom); a server runtime in the code (Node built-in, server-only package such as express/mongodb/winston/@aws-sdk, HTTP response sink, Lambda handler export); the IMPORT GRAPH (a module a client-declared file imports runs in the browser, a module that imports a server-proven module runs on a server); the folder convention (`workers/` is no longer one of them: a worker folder is a browser convention as often as a server one); a universal Web API of the browser with no server evidence at all; otherwise unproven, which is never server. Found by the attestation campaign of the platform (2026-09-25): 73 of the 252 high findings of every project were this one classification. (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/auth (2026-09-24), fixed in the script: el check juzga claves de MENSAJERÍA: un sitio de generación no se juzga (motivo en la traza, nunca se manda al modelo) si vive en un script de build u operaciones (scripts/, tools/, bin/, ops/, infra/, deploy/, ci/, build/) o si su fichero no es de mensajería, no nombra librería de protocolo ni material de clave de mensaje y sí nombra las claves de firma y de servicio de la release (jose/JWT/JWKS, RS256/ES256, OAuth, TLS, certificados, API, KMS, base de datos); una línea de import/require/use no es un sitio de generación. (1.0.4: the browser asset folders of the server-rendered frameworks (static/, wwwroot/ and Laravel resources/js/) are classified as browser code, like public/ and assets/ already were: browser JavaScript of a Django, Flask or Go application was being analysed as server code (false positive found by the control bench of the audit scripts). 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.)))))
| const KEY_GENERATION = /\b(?:generateKey(?:Pair)?|crypto\.subtle\.generateKey|generateIdentityKeyPair|generatePreKeys?|nacl\.box\.keyPair|crypto_box_keypair|X25519PrivateKey\.generate|sodium_crypto_box_keypair)\b/; |
| const PRIVATE_KEY_EXFIL = /(?:fetch\(|axios\.post\(|emit\(|send\(|body\s*:)[\s\S]{0,300}?\b(?:privateKey|private_key|secretKey|privKey|signingKey|masterKey|seed|mnemonic)\b/i; |
| // key generation in a server file -> FAIL; private key in an upload -> FAIL; then one assessment call over the key-management files (>= 0.8) or FAIL |
The full script is disclosed on request in a read-only viewer (never published on GitHub); the attestation binds to this exact hash.