Auditor
Account deletion propagates to backups, caches, search indexes and third parties within the declared window.
1.1.1: a callee whose root is a name that `Object.prototype` also carries (`build().toString()` tokenizes to the bare callee `toString`; also `constructor`, `valueOf`, `hasOwnProperty`) was looked up in the plain object that holds the imported names, so it resolved to the INHERITED FUNCTION instead of to nothing and the whole check died with "name.includes is not a function". Only a real binding counts now, and the name of a unit is always read as a string. Found on a 1 319-file checkout (2fa-available and session-expiry-rotation, 2026-09-24); the same line was in the 15 scripts that walk the call graph, so all fifteen ship the fix. (1.1.0: deterministic: the rule runs over the static analysis kit (tokens, function units, calls, imports, routes) with no AI engine; exact decisionRule and languages published; not-applicable with the reason when the checkout gives nothing to evaluate. The deletion flow is a call graph from the named entry units (DELETE /users, /accounts, /me handlers, user-controller destroy actions, user-deletion listeners) followed 3 module hops plus emitted events resolved to listeners by name and dispatched job classes resolved to handle/perform; each store class in use (cache, search index, object storage, third-party processor) must be reached by a propagation call in that graph. Not judged: backup expiry and rotation (informative LOW when documented), opaque-identifier third parties, per-entry-point propagation, conditional calls. (1.0.2: never analyses temporary files (*.tmp.*; spec.excludes follows AUDITOR_ANALYSIS_EXCLUDES). (1.0.1: never analyses test, spec, fixture and mock paths nor the auditor's own scripts (spec.excludes = AUDITOR_ANALYSIS_EXCLUDES); a fixture-looking secret (sk_test_, example, dummy) in real code is LOW, informative; every model call stays under the engine prompt cap (spec.promptMaxChars = AUDITOR_AI_PROMPT_MAX_CHARS): assess mode sends the candidate set in parts and aggregates the verdicts (a HIGH in any part is a FAIL, an unsatisfied part is a FAIL).)))
| // entry units: deleteAccount/deleteUser/closeAccount…, DELETE /users|/accounts|/me handlers, UsersController.destroy, listeners of user.deleted / post_delete / UserDeleted |
| // flow = call graph (<= 3 module hops) + listeners of emitted events (emit('user.deleted') -> on('user.deleted', fn)) + dispatched job classes (dispatch(new PurgeUserData) -> PurgeUserData.handle) |
| // classes: cache (redis/memcached/framework cache) · search-index (algolia/elasticsearch/meilisearch/typesense/Scout) · object-storage (S3/GCS/Azure/cloudinary) · third-party-records (stripe customers, sendgrid, mailchimp, hubspot, intercom, segment…) |
| // a class in use with no propagation call (del/forget · deleteObject/deleteByQuery/unsearchable · DeleteObjectCommand/Storage::delete · customers.del/contacts.delete/suppress/gdpr) in any unit of the flow -> HIGH (index, third party) / MEDIUM (cache, storage); no declared window -> MEDIUM; backups documented -> LOW, not judged |
The full script is disclosed on request in a read-only viewer (never published on GitHub); the attestation binds to this exact hash.