diff --git a/apps/server/src/secrets/SecretRequests.test.ts b/apps/server/src/secrets/SecretRequests.test.ts index 026b9775b52d1..a8db8eb747127 100644 --- a/apps/server/src/secrets/SecretRequests.test.ts +++ b/apps/server/src/secrets/SecretRequests.test.ts @@ -341,7 +341,7 @@ it.effect("a ref only works in the project it was entered for", () => const elsewhere = yield* service .consume({ ref, projectId: ProjectId.make("project-other") }) .pipe(Effect.flip); - assert.include(elsewhere.message, "does not exist"); + assert.equal(elsewhere.reason, "ref_wrong_project"); assert.equal(yield* service.consume({ ref, projectId }), "ghp_secret"); }), ), diff --git a/apps/server/src/secrets/SecretRequests.ts b/apps/server/src/secrets/SecretRequests.ts index cef9191bbf3dc..0105e107dd1c3 100644 --- a/apps/server/src/secrets/SecretRequests.ts +++ b/apps/server/src/secrets/SecretRequests.ts @@ -246,8 +246,10 @@ const make = Effect.gen(function* () { const decoded = Option.flatMap(stored, (bytes) => decodeStored(new TextDecoder().decode(bytes)), ); - if (Option.isNone(decoded) || decoded.value.projectId !== input.projectId) { - return yield* new SecretRequestError({ reason: "ref_unavailable" }); + if (Option.isNone(decoded)) return yield* new SecretRequestError({ reason: "ref_unavailable" }); + // Before the expiry check, so a caller from another project never deletes the value. + if (decoded.value.projectId !== input.projectId) { + return yield* new SecretRequestError({ reason: "ref_wrong_project" }); } if ((yield* Clock.currentTimeMillis) - decoded.value.savedAt > SECRET_REF_TTL_MS) { yield* removeLogged(storeName(input.ref)); diff --git a/packages/contracts/src/secretRequest.ts b/packages/contracts/src/secretRequest.ts index ccd114c397e38..deabfccd3b167 100644 --- a/packages/contracts/src/secretRequest.ts +++ b/packages/contracts/src/secretRequest.ts @@ -49,6 +49,8 @@ const SECRET_REQUEST_FAILURE_MESSAGES = { read_failed: "Could not read the secret.", ref_unavailable: "That secretRef was already used or does not exist. Ask the user again with request_secret.", + ref_wrong_project: + "That secretRef was entered in another project. Ask with request_secret from a thread in the project that uses it.", ref_expired: "That secretRef expired. Ask the user again with request_secret.", consume_failed: "Could not use that secretRef. Try again.", not_a_file_request: