@wopr-network/defcon 1.2.2 → 1.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.
@@ -7,7 +7,7 @@ export interface HttpServerDeps {
7
7
  mcpDeps: McpServerDeps;
8
8
  adminToken?: string;
9
9
  workerToken?: string;
10
- corsOrigin?: string;
10
+ corsOrigins?: string[];
11
11
  logger?: Logger;
12
12
  }
13
13
  export declare function createHttpServer(deps: HttpServerDeps): http.Server;
@@ -282,8 +282,8 @@ export function createHttpServer(deps) {
282
282
  const isLoopbackOrigin = /^https?:\/\/localhost(:\d+)?$/.test(origin) ||
283
283
  /^https?:\/\/127\.0\.0\.1(:\d+)?$/.test(origin) ||
284
284
  /^https?:\/\/\[::1\](:\d+)?$/.test(origin);
285
- const corsAllowed = deps.corsOrigin
286
- ? origin === deps.corsOrigin // explicit origin: exact match only
285
+ const corsAllowed = deps.corsOrigins
286
+ ? deps.corsOrigins.includes(origin) // explicit origins: set membership check
287
287
  : isLoopbackOrigin; // loopback mode: only reflect loopback origins
288
288
  if (corsAllowed) {
289
289
  res.setHeader("Vary", "Origin");
@@ -1,6 +1,6 @@
1
1
  export interface CorsOriginResult {
2
- /** Explicit allowed origin, or null meaning "loopback-only default pattern" */
3
- origin: string | null;
2
+ /** Explicit allowed origins, or null meaning "loopback-only default pattern" */
3
+ origins: string[] | null;
4
4
  }
5
5
  export declare function resolveCorsOrigin(opts: {
6
6
  host: string;
package/dist/src/cors.js CHANGED
@@ -2,20 +2,27 @@ const LOOPBACK_HOSTS = new Set(["127.0.0.1", "localhost", "::1"]);
2
2
  export function resolveCorsOrigin(opts) {
3
3
  const corsValue = opts.corsEnv?.trim() || undefined;
4
4
  const isLoopback = LOOPBACK_HOSTS.has(opts.host);
5
- // If explicit origin provided, validate and use it
5
+ // If explicit origins provided, validate each and use them
6
6
  if (corsValue) {
7
- if (!/^https?:\/\/[^/]+$/.test(corsValue)) {
8
- throw new Error(`DEFCON_CORS_ORIGIN must be a bare origin like https://app.example.com, not ${corsValue}. ` +
9
- "Remove any path component or trailing slash.");
7
+ const entries = corsValue
8
+ .split(",")
9
+ .map((s) => s.trim())
10
+ .filter(Boolean);
11
+ for (const entry of entries) {
12
+ if (!/^https?:\/\/[^/]+$/.test(entry)) {
13
+ throw new Error(`DEFCON_CORS_ORIGIN must be bare origins like https://app.example.com (comma-separated for multiple), not ${entry}. ` +
14
+ "Remove any path component or trailing slash.");
15
+ }
10
16
  }
11
- return { origin: corsValue };
17
+ return { origins: entries };
12
18
  }
13
19
  // Non-loopback without explicit origin — refuse to start
14
20
  if (!isLoopback) {
15
21
  throw new Error(`DEFCON_CORS_ORIGIN must be set when binding to non-loopback address "${opts.host}". ` +
16
22
  "Without an explicit CORS origin, any website on the network can make cross-origin requests to this server. " +
17
- 'Set DEFCON_CORS_ORIGIN to the allowed origin (e.g. "https://my-app.example.com") or use a loopback address.');
23
+ 'Set DEFCON_CORS_ORIGIN to the allowed origin (e.g. "https://my-app.example.com") or use a loopback address. ' +
24
+ "Multiple origins can be separated by commas.");
18
25
  }
19
26
  // Loopback without explicit origin — use default pattern
20
- return { origin: null };
27
+ return { origins: null };
21
28
  }
@@ -243,7 +243,7 @@ program
243
243
  mcpDeps: deps,
244
244
  adminToken,
245
245
  workerToken,
246
- corsOrigin: restCorsResult.origin ?? undefined,
246
+ corsOrigins: restCorsResult.origins ?? undefined,
247
247
  });
248
248
  if (adminToken) {
249
249
  const wsBroadcaster = new WebSocketBroadcaster({
@@ -278,16 +278,13 @@ program
278
278
  sqlite.close();
279
279
  process.exit(1);
280
280
  }
281
- const allowedOriginPattern = corsResult.origin
282
- ? corsResult.origin // exact string match
283
- : /^https?:\/\/(localhost|127\.0\.0\.1|\[::1\])(:\d+)?$/; // loopback default
281
+ const allowedOriginSet = corsResult.origins ? new Set(corsResult.origins) : null;
282
+ const loopbackPattern = /^https?:\/\/(localhost|127\.0\.0\.1|\[::1\])(:\d+)?$/;
284
283
  const httpServer = http.createServer(async (req, res) => {
285
284
  // CORS: restrict to localhost origins when bound to loopback; require DEFCON_CORS_ORIGIN when bound to non-loopback
286
285
  const origin = req.headers.origin;
287
286
  if (origin) {
288
- const originAllowed = typeof allowedOriginPattern === "string"
289
- ? origin === allowedOriginPattern
290
- : allowedOriginPattern.test(origin);
287
+ const originAllowed = allowedOriginSet ? allowedOriginSet.has(origin) : loopbackPattern.test(origin);
291
288
  if (originAllowed) {
292
289
  res.setHeader("Vary", "Origin");
293
290
  res.setHeader("Access-Control-Allow-Origin", origin);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wopr-network/defcon",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "type": "module",
5
5
  "packageManager": "pnpm@9.15.4",
6
6
  "engines": {