Skip to content

Commit 7d6eae3

Browse files
Harden secret store error handling
- handle platform file errors more safely - resolve catalog overrides during server publish
1 parent 1cba2f6 commit 7d6eae3

4 files changed

Lines changed: 39 additions & 14 deletions

File tree

apps/server/scripts/cli.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,22 @@ import { resolveCatalogDependencies } from "../../../scripts/lib/resolve-catalog
1414
import rootPackageJson from "../../../package.json" with { type: "json" };
1515
import serverPackageJson from "../package.json" with { type: "json" };
1616

17+
interface PackageJson {
18+
name: string;
19+
repository: {
20+
type: string;
21+
url: string;
22+
directory: string;
23+
};
24+
bin: Record<string, string>;
25+
type: string;
26+
version: string;
27+
engines: Record<string, string>;
28+
files: string[];
29+
dependencies: Record<string, string>;
30+
overrides: Record<string, string>;
31+
}
32+
1733
class CliError extends Data.TaggedError("CliError")<{
1834
readonly message: string;
1935
readonly cause?: unknown;
@@ -192,22 +208,28 @@ const publishCmd = Command.make(
192208
// Resolve catalog dependencies before any file mutations. If this throws,
193209
// acquire fails and no release hook runs, so filesystem must still be untouched.
194210
const version = Option.getOrElse(config.appVersion, () => serverPackageJson.version);
195-
const pkg = {
211+
const pkg: PackageJson = {
196212
name: serverPackageJson.name,
197213
repository: serverPackageJson.repository,
198214
bin: serverPackageJson.bin,
199215
type: serverPackageJson.type,
200216
version,
201217
engines: serverPackageJson.engines,
202218
files: serverPackageJson.files,
203-
dependencies: serverPackageJson.dependencies as Record<string, unknown>,
219+
dependencies: serverPackageJson.dependencies,
220+
overrides: rootPackageJson.overrides,
204221
};
205222

206223
pkg.dependencies = resolveCatalogDependencies(
207224
pkg.dependencies,
208225
rootPackageJson.workspaces.catalog,
209226
"apps/server dependencies",
210227
);
228+
pkg.overrides = resolveCatalogDependencies(
229+
pkg.overrides,
230+
rootPackageJson.workspaces.catalog,
231+
"root overrides",
232+
);
211233

212234
const original = yield* fs.readFileString(packageJsonPath);
213235
yield* fs.writeFileString(backupPath, original);

apps/server/src/auth/Layers/ServerSecretStore.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as Crypto from "node:crypto";
22

3-
import { Effect, FileSystem, Layer, Path } from "effect";
3+
import { Effect, FileSystem, Layer, Path, Predicate } from "effect";
44
import * as PlatformError from "effect/PlatformError";
55

66
import { ServerConfig } from "../../config.ts";
@@ -28,17 +28,14 @@ export const makeServerSecretStore = Effect.gen(function* () {
2828

2929
const resolveSecretPath = (name: string) => path.join(serverConfig.secretsDir, `${name}.bin`);
3030

31-
const isMissingSecretFileError = (cause: unknown): cause is PlatformError.PlatformError =>
32-
cause instanceof PlatformError.PlatformError && cause.reason._tag === "NotFound";
33-
34-
const isAlreadyExistsSecretFileError = (cause: unknown): cause is PlatformError.PlatformError =>
35-
cause instanceof PlatformError.PlatformError && cause.reason._tag === "AlreadyExists";
31+
const isPlatformError = (u: unknown): u is PlatformError.PlatformError =>
32+
Predicate.isTagged(u, "PlatformError");
3633

3734
const get: ServerSecretStoreShape["get"] = (name) =>
3835
fileSystem.readFile(resolveSecretPath(name)).pipe(
3936
Effect.map((bytes) => Uint8Array.from(bytes)),
4037
Effect.catch((cause) =>
41-
isMissingSecretFileError(cause)
38+
cause.reason._tag === "NotFound"
4239
? Effect.succeed(null)
4340
: Effect.fail(
4441
new SecretStoreError({
@@ -108,7 +105,7 @@ export const makeServerSecretStore = Effect.gen(function* () {
108105
return create(name, generated).pipe(
109106
Effect.as(Uint8Array.from(generated)),
110107
Effect.catchTag("SecretStoreError", (error) =>
111-
isAlreadyExistsSecretFileError(error.cause)
108+
isPlatformError(error.cause) && error.cause.reason._tag === "AlreadyExists"
112109
? get(name).pipe(
113110
Effect.flatMap((created) =>
114111
created !== null
@@ -129,7 +126,7 @@ export const makeServerSecretStore = Effect.gen(function* () {
129126
const remove: ServerSecretStoreShape["remove"] = (name) =>
130127
fileSystem.remove(resolveSecretPath(name)).pipe(
131128
Effect.catch((cause) =>
132-
isMissingSecretFileError(cause)
129+
cause instanceof PlatformError.PlatformError && cause.reason._tag === "NotFound"
133130
? Effect.void
134131
: Effect.fail(
135132
new SecretStoreError({

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,13 @@
6262
"vitest": "catalog:"
6363
},
6464
"overrides": {
65+
"@effect/atom-react": "catalog:",
66+
"@effect/platform-bun": "catalog:",
67+
"@effect/platform-node": "catalog:",
6568
"@effect/platform-node-shared": "catalog:",
69+
"@effect/sql-sqlite-bun": "catalog:",
70+
"@effect/vitest": "catalog:",
71+
"effect": "catalog:",
6672
"vite": "^8.0.0"
6773
},
6874
"engines": {

scripts/lib/resolve-catalog.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
* the concrete version string found in `catalog`. Throws on missing entries.
66
*/
77
export function resolveCatalogDependencies(
8-
dependencies: Record<string, unknown>,
9-
catalog: Record<string, unknown>,
8+
dependencies: Record<string, string>,
9+
catalog: Record<string, string>,
1010
label: string,
11-
): Record<string, unknown> {
11+
): Record<string, string> {
1212
return Object.fromEntries(
1313
Object.entries(dependencies).map(([name, spec]) => {
1414
if (typeof spec !== "string" || !spec.startsWith("catalog:")) {

0 commit comments

Comments
 (0)