| 1 | import * as NodeServices from "@effect/platform-node/NodeServices"; |
| 2 | import { assert, it } from "@effect/vitest"; |
| 3 | import * as DateTime from "effect/DateTime"; |
| 4 | import * as Duration from "effect/Duration"; |
| 5 | import * as Effect from "effect/Effect"; |
| 6 | import * as FileSystem from "effect/FileSystem"; |
| 7 | import * as Layer from "effect/Layer"; |
| 8 | import * as Path from "effect/Path"; |
| 9 | import * as TestClock from "effect/testing/TestClock"; |
| 10 | |
| 11 | import * as ServerConfig from "../config.ts"; |
| 12 | import { pruneExpiredReplayMarkers, REPLAY_MARKER_MAX_AGE } from "./replayMarkers.ts"; |
| 13 | import * as ServerSecretStore from "./ServerSecretStore.ts"; |
| 14 | |
| 15 | // Every secret name the server stores today. The last three stand for names |
| 16 | // built from an id at runtime. |
| 17 | const REAL_SECRET_NAMES = [ |
| 18 | "server-signing-key", |
| 19 | "asset-access-signing-key", |
| 20 | "cloud-cli-oauth-token", |
| 21 | "cloud-cli-desired-link", |
| 22 | "cloud-link-ed25519-key-pair", |
| 23 | "cloud-link-ed25519-private-key", |
| 24 | "cloud-link-ed25519-public-key", |
| 25 | "cloud-mint-ed25519-public-key", |
| 26 | "cloud-endpoint-runtime-config", |
| 27 | "cloud-endpoint-confirmed-origin", |
| 28 | "cloud-linked-user-id", |
| 29 | "cloud-relay-url", |
| 30 | "cloud-relay-issuer", |
| 31 | "cloud-relay-environment-credential", |
| 32 | "cloud-publish-agent-activity", |
| 33 | "provider-env-Y29kZXg-T1BFTkFJX0FQSV9LRVk", |
| 34 | "provider-auth-0f1e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0", |
| 35 | "usage-limit-source-aHVi", |
| 36 | ]; |
| 37 | |
| 38 | it.layer(NodeServices.layer)("replayMarkers", (it) => { |
| 39 | it.effect("prunes only replay markers older than the max age", () => |
| 40 | Effect.gen(function* () { |
| 41 | const secretStore = yield* ServerSecretStore.ServerSecretStore; |
| 42 | const { secretsDir } = yield* ServerConfig.ServerConfig; |
| 43 | const fileSystem = yield* FileSystem.FileSystem; |
| 44 | const path = yield* Path.Path; |
| 45 | const now = DateTime.makeUnsafe("2026-01-01T00:00:00Z"); |
| 46 | const setAge = (fileName: string, age: Duration.Duration) => { |
| 47 | const mtime = DateTime.toDateUtc(DateTime.subtractDuration(now, age)); |
| 48 | return fileSystem.utimes(path.join(secretsDir, fileName), mtime, mtime); |
| 49 | }; |
| 50 | const writeAged = (name: string, age: Duration.Duration) => |
| 51 | secretStore |
| 52 | .create(name, Uint8Array.from([1])) |
| 53 | .pipe(Effect.andThen(setAge(`${name}.bin`, age))); |
| 54 | |
| 55 | const justExpired = Duration.sum(REPLAY_MARKER_MAX_AGE, Duration.seconds(1)); |
| 56 | const expiredMarkers = [ |
| 57 | "dpop-proof-old", |
| 58 | "cloud-mint-jti-old", |
| 59 | "cloud-mint-nonce-old", |
| 60 | "cloud-health-jti-old", |
| 61 | "cloud-health-nonce-old", |
| 62 | ]; |
| 63 | for (const name of expiredMarkers) yield* writeAged(name, justExpired); |
| 64 | yield* writeAged("dpop-proof-at-max-age", REPLAY_MARKER_MAX_AGE); |
| 65 | for (const name of REAL_SECRET_NAMES) yield* writeAged(name, Duration.days(30)); |
| 66 | const pendingSetFile = "dpop-proof-pending.bin.0000.tmp"; |
| 67 | yield* fileSystem.writeFile(path.join(secretsDir, pendingSetFile), Uint8Array.from([1])); |
| 68 | yield* setAge(pendingSetFile, Duration.days(30)); |
| 69 | yield* TestClock.setTime(DateTime.toEpochMillis(now)); |
| 70 | |
| 71 | yield* pruneExpiredReplayMarkers(); |
| 72 | |
| 73 | const remaining = yield* fileSystem.readDirectory(secretsDir); |
| 74 | assert.deepStrictEqual( |
| 75 | remaining.toSorted(), |
| 76 | [ |
| 77 | ...REAL_SECRET_NAMES.map((name) => `${name}.bin`), |
| 78 | "dpop-proof-at-max-age.bin", |
| 79 | pendingSetFile, |
| 80 | ].toSorted(), |
| 81 | ); |
| 82 | }).pipe( |
| 83 | Effect.provide( |
| 84 | ServerSecretStore.layer.pipe( |
| 85 | Layer.provideMerge( |
| 86 | ServerConfig.layerTest(process.cwd(), { prefix: "t3-replay-markers-test-" }), |
| 87 | ), |
| 88 | ), |
| 89 | ), |
| 90 | ), |
| 91 | ); |
| 92 | }); |