@cytario/web 2.2.7 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -14,6 +14,7 @@ import { buildRedisOptions } from "./redisOptions";
14
14
  * - REDIS_PORT: Server port (default: 6379)
15
15
  * - REDIS_USERNAME: Optional username for authenticated connections (Redis 6+ / Valkey)
16
16
  * - REDIS_PASSWORD: Optional password for authenticated connections
17
+ * - REDIS_KEY_PREFIX: Optional prefix prepended to every key (e.g. "cytario-web:")
17
18
  * - REDIS_TLS: Set to "true" to wrap the connection in TLS (required in production)
18
19
  * - REDIS_CA_CERT: Optional PEM-encoded CA certificate (string) for self-signed deployments
19
20
  * - REDIS_TLS_SERVER_NAME: Optional SNI / certificate hostname override
@@ -35,6 +36,14 @@ import { buildRedisOptions } from "./redisOptions";
35
36
 
36
37
  const options = buildRedisOptions(process.env);
37
38
 
39
+ const auth = options.password ? "on" : "off";
40
+ const tls = options.tls ? "on" : "off";
41
+ const who = options.username ? ` as ${options.username}` : "";
42
+ const prefix = options.keyPrefix ? `, prefix "${options.keyPrefix}"` : "";
43
+ console.log(
44
+ `Redis/Valkey config: ${options.host}:${options.port}${who} (AUTH ${auth}, TLS ${tls}${prefix})`,
45
+ );
46
+
38
47
  export const redis = new Redis(options);
39
48
 
40
49
  redis.on("error", (err) => {
@@ -42,11 +51,5 @@ redis.on("error", (err) => {
42
51
  });
43
52
 
44
53
  redis.on("connect", () => {
45
- const authInfo = options.username
46
- ? ` (authenticated as ${options.username})`
47
- : options.password
48
- ? " (authenticated)"
49
- : "";
50
- const tlsInfo = options.tls ? " over TLS" : "";
51
- console.log(`Connected to Redis/Valkey at ${options.host}:${options.port}${authInfo}${tlsInfo}`);
54
+ console.log(`Connected to Redis/Valkey at ${options.host}:${options.port}`);
52
55
  });
@@ -7,7 +7,10 @@ import type { RedisOptions } from "ioredis";
7
7
  * - `REDIS_HOST` (default: `localhost`)
8
8
  * - `REDIS_PORT` (default: `6379`)
9
9
  * - `REDIS_USERNAME` — optional ACL username
10
- * - `REDIS_PASSWORD` — optional password
10
+ * - `REDIS_PASSWORD` — password; required outside development
11
+ * - `REDIS_KEY_PREFIX` — optional prefix prepended to every key issued by
12
+ * this client (e.g. `"cytario-web:"`). Lets operators scope the app to a
13
+ * single ACL keyspace pattern without code changes.
11
14
  * - `REDIS_TLS` — `"true"` to wrap the connection in TLS
12
15
  * - `REDIS_CA_CERT` — PEM-encoded CA certificate (string) used to verify
13
16
  * the server when the cert chain is not in the system trust store
@@ -16,9 +19,13 @@ import type { RedisOptions } from "ioredis";
16
19
  * production TLS requirement (escape hatch for trusted in-cluster
17
20
  * networks; logs a warning)
18
21
  *
19
- * Fails fast outside development if TLS is off and the opt-out flag is
20
- * not set session blobs contain OAuth tokens and STS credentials and
21
- * must not traverse plaintext links in production. See C-204.
22
+ * Fails fast outside development if:
23
+ * - TLS is off and the opt-out flag is not set, or
24
+ * - `REDIS_PASSWORD` is empty.
25
+ *
26
+ * Session blobs contain OAuth tokens and STS credentials and must never
27
+ * traverse plaintext links or be readable by anonymous clients in
28
+ * production.
22
29
  */
23
30
  export function buildRedisOptions(env: Record<string, string | undefined>): RedisOptions {
24
31
  const host = env.REDIS_HOST || "localhost";
@@ -29,15 +36,22 @@ export function buildRedisOptions(env: Record<string, string | undefined>): Redi
29
36
  const caCert = env.REDIS_CA_CERT;
30
37
  const tlsServerName = env.REDIS_TLS_SERVER_NAME;
31
38
  const allowPlaintext = env.REDIS_INSECURE_ALLOW_PLAINTEXT === "true";
39
+ const keyPrefix = env.REDIS_KEY_PREFIX;
32
40
  const nodeEnv = env.NODE_ENV;
33
41
 
34
42
  const isLocalEnv = nodeEnv === "development" || nodeEnv === "test";
35
43
 
44
+ if (!isLocalEnv && !password) {
45
+ throw new Error(
46
+ "Refusing to start: Redis/Valkey AUTH is disabled. Set REDIS_PASSWORD " +
47
+ "to a non-empty value so the session store rejects anonymous clients.",
48
+ );
49
+ }
50
+
36
51
  if (!tlsEnabled && !isLocalEnv && !allowPlaintext) {
37
52
  throw new Error(
38
53
  "Refusing to start: Redis/Valkey TLS is disabled. Set REDIS_TLS=true " +
39
- "(recommended) or REDIS_INSECURE_ALLOW_PLAINTEXT=true to opt out. " +
40
- "See C-204 / OWASP A02:2021.",
54
+ "(recommended) or REDIS_INSECURE_ALLOW_PLAINTEXT=true to opt out.",
41
55
  );
42
56
  }
43
57
 
@@ -53,6 +67,7 @@ export function buildRedisOptions(env: Record<string, string | undefined>): Redi
53
67
  port,
54
68
  ...(username && { username }),
55
69
  ...(password && { password }),
70
+ ...(keyPrefix && { keyPrefix }),
56
71
  maxRetriesPerRequest: 3,
57
72
  retryStrategy(times) {
58
73
  const delay = Math.min(times * 50, 2000);
package/app/config.ts CHANGED
@@ -22,7 +22,7 @@ interface CytarioConfig {
22
22
  host: string;
23
23
  /** Optional username for authenticated connections (Redis 6+ ACL / Valkey) */
24
24
  username?: string;
25
- /** Optional password for authenticated connections */
25
+ /** Password for authenticated connections. Optional in development only */
26
26
  password?: string;
27
27
  };
28
28
  cookie: CookieOptions;
@@ -85,7 +85,7 @@ function runStart(forwardedArgs) {
85
85
  const child = spawn(process.execPath, [serverEntry, ...forwardedArgs], {
86
86
  cwd: PACKAGE_ROOT,
87
87
  stdio: "inherit",
88
- env: { ...process.env, NODE_ENV: "production" },
88
+ env: { ...process.env, NODE_ENV: process.env.NODE_ENV ?? "production" },
89
89
  });
90
90
  child.on("exit", (code, signal) => {
91
91
  if (signal) process.kill(process.pid, signal);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cytario/web",
3
- "version": "2.2.7",
3
+ "version": "2.3.0",
4
4
  "description": "Cytario Web — scientific imaging data browser and viewer for OME-TIFF, OME-Zarr, Parquet and GeoTIFF on S3-compatible storage.",
5
5
  "license": "AGPL-3.0",
6
6
  "sideEffects": false,