From 2d2c5af37db6837ebc4319a12d683b677a604750 Mon Sep 17 00:00:00 2001 From: Eric Bell Date: Fri, 9 Oct 2026 16:38:37 -0400 Subject: [PATCH] detectors 3 issues E --- ...ector-fact-check-rubric-claims.inputs.json | 4 +- .../detector-fact-check-rubric-claims.md | 48 +++++--- .../tests/holistic-rubric.md | 115 +++++++----------- 3 files changed, 76 insertions(+), 91 deletions(-) diff --git a/worker-toolkit-potion-polyglot-v1.0.1/harbor-tasks/potion-voice-user-ownership/detectors/detector-fact-check-rubric-claims.inputs.json b/worker-toolkit-potion-polyglot-v1.0.1/harbor-tasks/potion-voice-user-ownership/detectors/detector-fact-check-rubric-claims.inputs.json index ea318d4..37f4c45 100644 --- a/worker-toolkit-potion-polyglot-v1.0.1/harbor-tasks/potion-voice-user-ownership/detectors/detector-fact-check-rubric-claims.inputs.json +++ b/worker-toolkit-potion-polyglot-v1.0.1/harbor-tasks/potion-voice-user-ownership/detectors/detector-fact-check-rubric-claims.inputs.json @@ -1,6 +1,6 @@ { "version": 1, - "capturedAt": "2026-10-09T20:33:04.059Z", + "capturedAt": "2026-10-09T20:38:04.767Z", "capturedBy": "stamp", "inputs": { "prompt": "75042109a7aab36d9a50fe23f5ac417488f437efb25575a987c4fe35d8103b16", @@ -9,7 +9,7 @@ "workspacePatch": null, "gitref": "fcd8a9d", "graderGuidanceConsolidated": null, - "holisticRubric": "d77f527d018604f9edad3a2aab276169de7bae20418554b1ffd093129212f7f7", + "holisticRubric": "96fbf11f88d75df8536a78e973d0bf80d6fb6d7fd2cb13c26aa2621d1df2f1f5", "atomicRubric": null, "rubricsYaml": null, "graderContext": null diff --git a/worker-toolkit-potion-polyglot-v1.0.1/harbor-tasks/potion-voice-user-ownership/detectors/detector-fact-check-rubric-claims.md b/worker-toolkit-potion-polyglot-v1.0.1/harbor-tasks/potion-voice-user-ownership/detectors/detector-fact-check-rubric-claims.md index dcd4e9c..a0625a3 100644 --- a/worker-toolkit-potion-polyglot-v1.0.1/harbor-tasks/potion-voice-user-ownership/detectors/detector-fact-check-rubric-claims.md +++ b/worker-toolkit-potion-polyglot-v1.0.1/harbor-tasks/potion-voice-user-ownership/detectors/detector-fact-check-rubric-claims.md @@ -1,13 +1,13 @@ --- detector: detector-fact-check-rubric-claims -verdict: partial +verdict: fail confidence: MEDIUM claims: - id: c01 verdict: unclear loadBearing: true summary: "Both workers can obtain a trustworthy userId from incoming job context" - rubricQuote: "To satisfy multi-tenant isolation, trial agents must obtain `userId` directly from the incoming job message payload/context (e.g., `job.userId` or `job._doc.userId`)." + rubricQuote: "To satisfy multi-tenant isolation, trial agents must obtain `userId` directly from incoming job message context (e.g., `job.userId` or `job._doc.userId`)." sourceEvidence: " const job = JSON.parse(response.Messages[0].Body)" sourceProvenance: "harbor-tasks/potion-voice-user-ownership/environment/workspace/voice-synthsizer-job-handler/index.js (lines 68-82); voice-cloning-job-handler/index.js (lines 100-107); app/services/sqs/sqs_service.js (lines 51-55)" note: "Source-unavailable uncertainty: the prompt names the job's userId, so the agent can reach the intended identity source, but no local producer or message fixture proves that both payload shapes contain a trustworthy field. Both consumers parse a generic body and omit userId destructuring; safe rejection on missing identity is credited by the rubric." @@ -15,7 +15,7 @@ claims: verdict: pass loadBearing: true summary: "Unscoped profile lookup cannot verify job ownership" - rubricQuote: "An unscoped database lookup (such as `UserAudioProfile.findById(job.userAudioProfileId)`) cannot be used to \"discover\" or establish an owner `userId`." + rubricQuote: "An unscoped database lookup (such as `UserAudioProfile.findById(job.userAudioProfileId)`) cannot be used as an identity source to \"discover\" an owner `userId` before ownership is established." sourceEvidence: " const userAudioProfile = await userAudioProfileService.find({\n _id: userAudioProfileId,\n status: 'completed',\n })\n if (userAudioProfile) {\n const { training_model_path, userId } = userAudioProfile[0]" sourceProvenance: "harbor-tasks/potion-voice-user-ownership/environment/workspace/voice-synthsizer-job-handler/index.js (lines 96-101); voice-synthsizer-job-handler/user_audio_profile/user_audio_profile_service.js (lines 49-54)" note: "The baseline gets userId after selecting a profile by its unverified ID. Using that same record as proof of the job owner is circular; the userID must come from independent trusted context for the first ownership check." @@ -23,7 +23,7 @@ claims: verdict: pass loadBearing: true summary: "List and batch queries can be tenant-scoped without _id" - rubricQuote: "`_id` is NOT required or expected when a specific document ID is not part of the search criteria." + rubricQuote: "Document `_id` is **not** required or expected when a single document ID is not part of the query criteria." sourceEvidence: " const foundJobs = await Job.find({\n ...filter,\n deleted: false," sourceProvenance: "harbor-tasks/potion-voice-user-ownership/environment/workspace/voice-synthsizer-job-handler/job/job_service.js (lines 49-54, 104-114)" note: "The repository has generic find and updateMany wrappers that accept collection filters. A userId predicate can isolate a tenant without a single-document _id; the revised rule matches that query shape." @@ -31,34 +31,42 @@ claims: verdict: pass loadBearing: true summary: "Mongoose places query conditions in argument one" - rubricQuote: "`findOneAndUpdate` accepts positional arguments: `findOneAndUpdate(conditions, update, options, callback)`." + rubricQuote: "`findOneAndUpdate` accepts positional parameters `findOneAndUpdate(conditions, update, options, callback)`." sourceEvidence: " const updatedJob = await Job.findOneAndUpdate({ _id: job._id }, job, {" sourceProvenance: "harbor-tasks/potion-voice-user-ownership/environment/workspace/voice-synthsizer-job-handler/job/job_service.js (lines 66-70); package.json (mongoose dependency)" - note: "The local call uses conditions, update, and options in the first three positions; the optional callback is consistent with the declared Mongoose 6 API. The rubric now correctly distinguishes extra argument count from whether conditions contain userId." + note: "The local calls use conditions, update, and options in the first three positions; a callback is a fourth accepted argument in the rubric's own stated signature. The agent can inspect these call sites and the declared Mongoose 6 dependency." - id: c05 - verdict: pass + verdict: fail loadBearing: true - summary: "RecordingSalutation has an optional recordingId and an owner field" - rubricQuote: "`RecordingSalutation` links a personalized salutation (`salutationId`) to a parent recording (`recordingId`). `recordingId` is optional in schema (`required: false`)." - sourceEvidence: " recordingId: {\n type: Schema.Types.ObjectId,\n ref: 'Recordings',\n required: false\n }," - sourceProvenance: "harbor-tasks/potion-voice-user-ownership/environment/workspace/voice-synthsizer-job-handler/recording_salutation/recording_salutation_model.js (lines 6-20)" - note: "The schema includes required userId and optional recordingId. The worker selects this model by the job's salutationId. Both facts are reachable through the shipped workspace." + summary: "Four arguments allegedly exceed a three-argument Mongoose signature and are ignored" + rubricQuote: "Passing 4 or 5 arguments exceeds Mongoose's 3-argument signature and causes trailing arguments to be ignored." + sourceEvidence: " \"mongoose\": \"^6.8.0\"," + sourceProvenance: "harbor-tasks/potion-voice-user-ownership/environment/workspace/voice-synthsizer-job-handler/package.json (line 14); tests/holistic-rubric.md (line 24)" + note: "The same rubric sentence gives a four-position signature ending in callback, then says four arguments exceed three positions. That is internally contradictory; Mongoose 6 also supports the callback position. The repository identifies Mongoose 6, although its installed library source is not shipped. The categorical four-argument claim is false and can misgrade valid code." - id: c06 verdict: pass loadBearing: true - summary: "Both baseline workers delete the SQS message near entry" - rubricQuote: "In baseline `voice-cloning-job-handler/index.js` and `voice-synthsizer-job-handler/index.js`, `sqs.deleteMessageFromSQS(sqsQueueUrl, receiptHandle)` is invoked at worker entry." - sourceEvidence: " await sqs.deleteMessageFromSQS(sqsQueueUrl, receiptHandle)" - sourceProvenance: "harbor-tasks/potion-voice-user-ownership/environment/workspace/voice-synthsizer-job-handler/index.js (line 72); voice-cloning-job-handler/index.js (line 130)" - note: "Both workers call delete before the expensive job body, so the rubric correctly treats this as baseline behavior. The agent can see both calls." + summary: "RecordingSalutation has an optional recordingId and an owner field" + rubricQuote: "Note that `recordingId` is an optional schema field (`required: false`)." + sourceEvidence: " recordingId: {\n type: Schema.Types.ObjectId,\n ref: 'Recordings',\n required: false\n }," + sourceProvenance: "harbor-tasks/potion-voice-user-ownership/environment/workspace/voice-synthsizer-job-handler/recording_salutation/recording_salutation_model.js (lines 6-20)" + note: "The schema includes required userId and optional recordingId. The worker selects this model by the job's salutationId. Both facts are reachable through the shipped workspace." - id: c07 + verdict: pass + loadBearing: true + summary: "Both baseline workers delete the SQS message near entry" + rubricQuote: "In baseline code (`voice-synthsizer-job-handler/index.js` line 72 and `voice-cloning-job-handler/index.js` line 130), `sqs.deleteMessageFromSQS(sqsQueueUrl, receiptHandle)` is invoked near worker entry." + sourceEvidence: " await sqs.deleteMessageFromSQS(sqsQueueUrl, receiptHandle)" + sourceProvenance: "harbor-tasks/potion-voice-user-ownership/environment/workspace/voice-synthsizer-job-handler/index.js (line 72); voice-cloning-job-handler/index.js (line 130)" + note: "Both workers call delete before the expensive job body, so the rubric correctly treats this as baseline behavior. The agent can see both calls." + - id: c08 verdict: pass loadBearing: true summary: "Baseline service updates use ID-only Mongoose conditions" - rubricQuote: "The agent updates service methods (e.g., `user_audio_profile_service.js`, `voice_cloning_service.js`, `salutation_service.js`, `job_service.js`) by passing `{ _id: data._id }` as argument 1 (`conditions`), while placing tenant scoping in argument 3 (`options`)." + rubricQuote: "Queries rely solely on `_id` without `userId` validation, perform circular unscoped lookups to discover owner IDs, or place tenant filters into Mongoose `options` parameters leaving query conditions unscoped." sourceEvidence: " const updatedModel = await VoiceCloningModel.findOneAndUpdate(\n { _id: data._id },\n data,\n {\n new: true,\n }\n )" sourceProvenance: "harbor-tasks/potion-voice-user-ownership/environment/workspace/voice-cloning-job-handler/voice_cloning/voice_cloning_service.js (lines 68-75)" - note: "This failure-mode example is hypothetical, but the baseline service does contain an ID-only first argument. The agent can reach that call directly." + note: "The baseline service contains an ID-only first argument, and the synthesis worker discovers userId after an unscoped profile lookup. These are reachable examples of the failures the rubric describes." --- Assessed: harbor-tasks/potion-voice-user-ownership/tests/holistic-rubric.md @@ -67,4 +75,4 @@ Assessed: harbor-tasks/potion-voice-user-ownership/tests/holistic-rubric.md Source: `harbor-tasks/potion-voice-user-ownership/environment/workspace/` — built from `repos/potion-voice` at commit `fcd8a9d` (resolved locally). No `environment/workspace.patch` exists. -Checked 7 load-bearing claims. Six pass against the local source or declared Mongoose API. Claim c01 remains unclear: the prompt refers to the job's userId, but no shipped producer or sample SQS message proves that both worker payloads contain a trustworthy field. The rubric permits safe rejection when the identity is missing. A local message fixture or producer contract would resolve the remaining provenance uncertainty. +Checked 8 load-bearing claims. Six pass, c01 remains unclear because no shipped producer or sample SQS message confirms a trustworthy `userId` in both payload shapes, and c05 fails: the rubric's four-argument Mongoose signature contradicts its new claim that four arguments exceed a three-argument signature and are ignored. The rubric permits safe rejection when identity is missing; a local message fixture or producer contract would resolve c01. diff --git a/worker-toolkit-potion-polyglot-v1.0.1/harbor-tasks/potion-voice-user-ownership/tests/holistic-rubric.md b/worker-toolkit-potion-polyglot-v1.0.1/harbor-tasks/potion-voice-user-ownership/tests/holistic-rubric.md index e8b80a9..a4031c7 100755 --- a/worker-toolkit-potion-polyglot-v1.0.1/harbor-tasks/potion-voice-user-ownership/tests/holistic-rubric.md +++ b/worker-toolkit-potion-polyglot-v1.0.1/harbor-tasks/potion-voice-user-ownership/tests/holistic-rubric.md @@ -1,108 +1,85 @@ # Holistic Rubric: Multi-Tenant Authorization in Background Workers ### Task Context -The backend background workers (`voice-synthsizer-job-handler` and `voice-cloning-job-handler`) and database service wrappers in `potion-voice` currently retrieve MongoDB records using document IDs without verifying multi-tenant user ownership. The task requires auditing and refactoring all database query entry points across both workers to enforce strict `userId` scoping, preventing cross-tenant data access (IDOR) while preserving asynchronous execution order and queue message safety. +The goal is to audit and refactor background SQS worker handlers (`voice-synthsizer-job-handler/index.js` and `voice-cloning-job-handler/index.js`) and database service wrappers in `potion-voice` to enforce strict multi-tenant data isolation by scoping all MongoDB queries with `userId`. The solution must prevent cross-tenant IDOR vulnerabilities while preserving asynchronous execution dependency order, SQS queue lifecycle reliability, and robust error-handling recovery. --- ### Ground Truth + 1. **Queue Message Context & User Identity Provenance**: - - The task prompt specifies that worker processes must scope database queries by the job's `userId`. - - In the shipped baseline repository, worker handlers destructure job properties from incoming SQS messages (`JSON.parse(response.Messages[0].Body)`), but baseline code omits `userId` destructuring. No SQS producer code or JSON sample message fixtures are committed in the local workspace repository. - - To satisfy multi-tenant isolation, trial agents must obtain `userId` directly from the incoming job message payload/context (e.g., `job.userId` or `job._doc.userId`). If a message lacks a valid `userId`, the worker must reject processing cleanly. An unscoped database lookup (such as `UserAudioProfile.findById(job.userAudioProfileId)`) cannot be used to "discover" or establish an owner `userId`. + * The task prompt instructs trial agents to audit background job handlers and scope all database operations by `userId`. + * **Source-Unavailable Environment Fact**: The shipped `potion-voice` repository contains worker consumers (`voice-synthsizer-job-handler/index.js` and `voice-cloning-job-handler/index.js`) and queue service utilities (`app/services/sqs/sqs_service.js`), but does **not** include SQS producer code or sample message JSON fixtures. + * To satisfy multi-tenant isolation, trial agents must obtain `userId` directly from incoming job message context (e.g., `job.userId` or `job._doc.userId`). If an incoming queue message context lacks a `userId` field, workers must reject processing cleanly and safely without performing unscoped database lookups or mutating records. + * An unscoped database lookup (such as `UserAudioProfile.findById(job.userAudioProfileId)`) cannot be used as an identity source to "discover" an owner `userId` before ownership is established. -2. **Database Relationships & Key Dependencies**: - - `UserAudioProfile` represents user-owned cloned voice profiles (`_id`, `userId`, `status`). - - `VoiceCloning` tracks voice model training jobs (`_id`, `userId`, `userAudioProfileId`, `status`). - - `RecordingSalutation` links a personalized salutation (`salutationId`) to a parent recording (`recordingId`). `recordingId` is optional in schema (`required: false`). - - `Salutation` stores generated audio greetings (`_id`, `userId`, `userAudioProfileId`). - - `Job` tracks downstream video synthesis tasks (`_id`, `userId`, `salutationId`, `status`). - - `Recording` represents dynamic video templates (`_id`, `userId`, `status`). +2. **Database Relationships & Entity Lookups**: + * `RecordingSalutation` (`recording_salutation_model.js`) links a personalized salutation (`salutationId`) to a parent recording (`recordingId`). Note that `recordingId` is an optional schema field (`required: false`). + * `recordingId` can be provided directly in the job message payload or extracted from `salutationToUpdate.recordingId` after resolving `RecordingSalutation`. Both extraction paths are valid. + * `Salutation` (`salutation_model.js`), `Recording` (`recording_model.js`), `UserAudioProfile` (`user_audio_profile_model.js`), `VoiceCloning` (`voice_cloning_model.js`), and `Job` (`job_model.js`) store tenant ownership references. -3. **Mongoose Method & Parameter Signatures**: - - `findOneAndUpdate` accepts positional arguments: `findOneAndUpdate(conditions, update, options, callback)`. - - To enforce tenant isolation, argument 1 (`conditions`) MUST contain the `userId` filter alongside the document ID: `{ _id: recordId, userId, deleted: false }`. - - Placing tenant filters into argument 3 (`options`) or passing extra arguments leaves argument 1 (`conditions`) unscoped, allowing query execution against foreign tenant records. - - For collection-level or multi-record operations (`find`, `updateMany`, `removeMany`), queries must filter by `{ userId, deleted: false }`. `_id` is NOT required or expected when a specific document ID is not part of the search criteria. +3. **Mongoose Query Standards**: + * **Single-Record ID Lookups & Updates**: Every primary and secondary model operation targeting a specific document by ID (`findById`, `findOne` by record ID, `findOneAndUpdate`) must combine document ID and user ownership: `{ _id, userId, deleted: false }`. + * **Tenant-Wide & Collection Queries**: Generic collection searches or batch operations (`find`, `updateMany`) must filter by `{ userId, deleted: false }`. Document `_id` is **not** required or expected when a single document ID is not part of the query criteria. + * **Mongoose Function Signatures**: `findOneAndUpdate` accepts positional parameters `findOneAndUpdate(conditions, update, options, callback)`. Tenant scoping filters must be placed in argument 1 (`conditions`), e.g., `{ _id: recordId, userId }`. Placing tenant filters into argument 3 (`options`) leaves argument 1 (`conditions`) unscoped, bypassing user ownership checks. Passing 4 or 5 arguments exceeds Mongoose's 3-argument signature and causes trailing arguments to be ignored. -4. **SQS Queue Lifecycle & Execution Order**: - - In baseline `voice-cloning-job-handler/index.js` and `voice-synthsizer-job-handler/index.js`, `sqs.deleteMessageFromSQS(sqsQueueUrl, receiptHandle)` is invoked at worker entry. - - Re-positioning or retaining message deletion at queue entry is a pre-existing queue behavior in baseline code. Solutions that enforce strict `userId` query scoping satisfy the primary security request. +4. **Queue Lifecycle & Async Execution Order**: + * In baseline code (`voice-synthsizer-job-handler/index.js` line 72 and `voice-cloning-job-handler/index.js` line 130), `sqs.deleteMessageFromSQS(sqsQueueUrl, receiptHandle)` is invoked near worker entry. + * Solutions that enforce strict `userId` database query scoping satisfy the primary task mandate. SQS message durability and queue retention timing are evaluated under secondary reliability considerations without penalizing baseline deletion timing. + * Dependent lookups must respect execution chronology (e.g., resolving parent records before referencing child IDs). Grouping sequential dependent lookups into concurrent `Promise.all` blocks before child IDs resolve throws runtime `ReferenceError` exceptions. -5. **Error & Foreign Tenant Rejection Safety**: - - When a job is rejected due to missing/invalid authorization or foreign tenant mismatch, background workers must halt execution cleanly without mutating foreign tenant records. - - Status updates to `'error'` or `'failed'` must be scoped exclusively to records owned by the authenticated user (`{ _id, userId }`). - ---- - -### Key AI Failure Modes - -1. **Missing Utility Module Startup Crash (`MODULE_NOT_FOUND`)**: - - The agent adds import statements like `const { requireUserId, tenantFilter } = require('../worker_tenant')` in `voice-cloning-job-handler/index.js` or `voice-synthsizer-job-handler/index.js` without creating `worker_tenant.js` or `worker_tenant/index.js`. Node.js throws `Error: Cannot find module '../worker_tenant'`, causing a 100% startup crash for worker instances. - -2. **Unscoped Mongoose `findOneAndUpdate` Query Conditions (Security Bypass)**: - - The agent updates service methods (e.g., `user_audio_profile_service.js`, `voice_cloning_service.js`, `salutation_service.js`, `job_service.js`) by passing `{ _id: data._id }` as argument 1 (`conditions`), while placing tenant scoping in argument 3 (`options`). Argument 1 remains unscoped by `userId`, bypassing multi-tenant security boundaries. - -3. **Async Dependency Execution Crash (`ReferenceError: recordingId is not defined`)**: - - The agent groups dependent database lookups into a concurrent `Promise.all` block before parent documents resolve (e.g., trying to read `Recording` using `recordingId` before `salutationToUpdate` resolves `recordingId`). Referencing uninitialized variables inside `Promise.all` throws `ReferenceError: recordingId is not defined`. - -4. **Unscoped Profile Lookup to Discover Owner (Circular Auth Bypass)**: - - The agent fetches an unverified profile via `UserAudioProfile.findById(job.userAudioProfileId)` without `userId` scoping, and then reads `profile.userId` to establish the job's owner. Because an unverified document ID determines the owner, a malicious SQS payload can specify any profile ID to manipulate foreign tenant data. - -5. **Foreign Tenant Record Mutation on Rejection**: - - Upon encountering an unauthorized job message, the agent executes status updates (`status: 'error'`) without `userId` filters, mutating database records belonging to an unauthenticated foreign tenant. +5. **Error Transitions & Authorization Rejections**: + * When a job fails due to an error or authorization rejection, workers must halt processing without mutating unauthorized or foreign tenant records. + * Status updates (`'error'`) must be scoped strictly to the authenticated user's own job or profile records (`{ _id, userId }`). --- ### Grading Dimensions #### Narrow Correctness -- **PASS**: Background worker processes (`voice-synthsizer-job-handler` and `voice-cloning-job-handler`) execute cleanly without throwing `MODULE_NOT_FOUND`, `ReferenceError`, or Mongoose `CastError` exceptions. -- **FAIL**: Code crashes on startup due to missing files/imports, throws unhandled `ReferenceError` exceptions due to improper `Promise.all` concurrency, or fails syntax validation. +* **PASS**: The worker handlers execute cleanly without runtime exceptions, syntax errors, or unhandled promise rejections. +* **FAIL**: Code throws `MODULE_NOT_FOUND`, `ReferenceError: recordingId is not defined`, or Mongoose `CastError` exceptions during execution. #### Broader Correctness -- **PASS**: All database lookups and update operations across models (`UserAudioProfile`, `VoiceCloning`, `Salutation`, `Job`, `Recording`) explicitly enforce `userId` scoping, preventing cross-tenant data access. -- **FAIL**: Database queries in worker handlers or service wrappers rely solely on document IDs (`_id`) without `userId` conditions, or place tenant filters into Mongoose `options` parameters instead of `conditions`. +* **PASS**: All primary and secondary MongoDB queries and updates enforce `userId` scoping, preventing cross-tenant data access. +* **FAIL**: Queries rely solely on `_id` without `userId` validation, perform circular unscoped lookups to discover owner IDs, or place tenant filters into Mongoose `options` parameters leaving query conditions unscoped. #### Common Sense -- **PASS**: Asynchronous execution order respects document dependencies, resolving parent records before dependent child queries. -- **FAIL**: Grouping sequential dependent queries into concurrent `Promise.all` blocks before child IDs are resolved. +* **PASS**: Asynchronous execution follows proper chronological order (resolving parent documents before accessing dependent child properties). +* **FAIL**: Grouping dependent sequential queries into concurrent execution blocks or referencing uninitialized variables. #### Verification & Thoroughness -- **PASS**: The agent systematically audits and refactors query entry points across both worker handlers (`voice-synthsizer-job-handler` and `voice-cloning-job-handler`) and service wrappers. -- **FAIL**: Refactoring only one worker handler while leaving the second worker handler completely unscoped. +* **PASS**: The response verifies database query scoping across both worker handlers and service wrappers (`job_service.js`, `salutation_service.js`, `user_audio_profile_service.js`, `voice_cloning_service.js`). +* **FAIL**: Claiming complete multi-tenant scoping while leaving secondary model queries or service wrapper methods unscoped. #### Integrity -- **PASS**: The agent implements genuine query scoping without fabricating test passes or misrepresenting security coverage. -- **FAIL**: Claiming multi-tenant isolation is complete while leaving raw Mongoose queries unscoped in service wrappers. +* **PASS**: Honest reporting of implemented query scoping and verification steps. +* **FAIL**: Claiming multi-tenant authorization was verified when database queries remain unscoped by `userId`. #### Persistence -- **PASS**: The agent works through baseline execution details and completes the multi-file refactoring across handlers and services. -- **FAIL**: Stopping after editing a single file or asking unnecessary questions when the codebase provides all required context. +* **PASS**: Continues through code refactoring across both worker handlers and service layers. +* **FAIL**: Stopping after editing one worker handler while leaving the other worker handler unscoped. #### Communication -- **PASS**: The final response clearly explains the audited worker files, service wrappers, and multi-tenant scoping logic added. -- **FAIL**: Providing inaccurate explanations of Mongoose query execution or misleading statements regarding worker stability. +* **PASS**: Clear, accurate summary of queries updated and tenant isolation enforced. +* **FAIL**: Inaccurate or misleading summary of code changes. #### Thought Partnership -- **PASS**: The agent identifies all primary and secondary model entry points across both workers and applies consistent `userId` query scoping. -- **FAIL**: Implementing partial scoping that breaks baseline processing or introducing uncreated utility dependencies. +* **PASS**: Identifies multi-tenant access boundaries and handles missing or invalid tenant context safely. +* **FAIL**: Blindly executing database operations without checking user identity provenance. --- ### Heavy Penalties -1. **Unscoped Database Queries (Multi-Tenant Security Vulnerability)**: - - *Triggers when*: Any document lookup or update operation in `voice-synthsizer-job-handler`, `voice-cloning-job-handler`, or service wrappers performs database operations without `userId` scoping. - - *Target Dimension*: **Broader Correctness**. - - *Nearest Acceptable Behavior*: All single-record queries enforce `{ _id, userId, deleted: false }` and multi-record operations enforce `{ userId, deleted: false }`. +1. **Unscoped Database Queries (Broader Correctness & Narrow Correctness)** + * **Trigger**: Any document lookup or update operation by ID (`findOne`, `findOneAndUpdate`, `findById`, `update`) on primary or secondary models that omits `userId` from argument 1 (`conditions`). + * **Nearest Acceptable**: All ID-based document lookups and updates enforce `{ _id, userId, deleted: false }` in query conditions. -2. **Missing Utility Module Startup Crash (`MODULE_NOT_FOUND`)**: - - *Triggers when*: The agent adds imports for an uncreated file (e.g., `require('../worker_tenant')`), causing Node.js worker execution to crash instantly. - - *Target Dimension*: **Narrow Correctness**. - - *Nearest Acceptable Behavior*: Any helper utility created by the agent is committed as a valid file in the repository or inline logic is used. +2. **Missing Utility Module Startup Crash (Narrow Correctness)** + * **Trigger**: Importing uncreated utility files (e.g., `const { requireUserId } = require('../worker_tenant')` without creating `worker_tenant.js`), causing `MODULE_NOT_FOUND` runtime crashes. + * **Nearest Acceptable**: All imported utility modules exist and load cleanly. -3. **Unscoped Lookup Security Bypass**: - - *Triggers when*: An unscoped database query is used to "discover" an owner `userId` before performing authorization checks. - - *Target Dimension*: **Broader Correctness**. - - *Nearest Acceptable Behavior*: Trusted `userId` context originates directly from the incoming job message payload. +3. **Async Execution Order Crash (Common Sense & Narrow Correctness)** + * **Trigger**: Grouping dependent queries into `Promise.all` before parent IDs resolve, triggering `ReferenceError: recordingId is not defined` runtime crashes. + * **Nearest Acceptable**: Sequential document lookups resolve parent records before referencing child properties.