@fabrika/proxy-core 0.7.0 → 0.8.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fabrika/proxy-core",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -37,9 +37,9 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "zod": "^3.25.76",
40
- "@fabrika/auth-core": "0.7.0",
41
- "@fabrika/jwks": "0.7.0",
40
+ "@fabrika/auth-core": "0.8.1",
41
+ "@fabrika/jwks": "0.8.1",
42
42
  "jose": "^5.9.0",
43
- "@fabrika/platform": "0.7.0"
43
+ "@fabrika/platform": "0.8.1"
44
44
  }
45
45
  }
package/src/headers.ts CHANGED
@@ -54,8 +54,17 @@ export interface CspOrigins {
54
54
  readonly frameSrc: readonly string[]
55
55
  readonly styleSrc: readonly string[]
56
56
  readonly fontSrc: readonly string[]
57
+ /**
58
+ * `worker-src 'self' blob:` is emitted only when it holds `blob:`, its one source (a challenge widget that starts its
59
+ * workers from `Blob` URLs); without it workers fall back to `script-src`. Absent from every manifest without it, so a
60
+ * proxy that does not know the directive still parses those.
61
+ */
62
+ readonly workerSrc?: readonly string[]
57
63
  }
58
64
 
65
+ /** The one source a contract's `workerSrc` holds: a worker the page's own script built from a `Blob`. */
66
+ export const WORKER_BLOB_SOURCE = 'blob:'
67
+
59
68
  const HOSTNAME = /^(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/
60
69
 
61
70
  /**
@@ -93,6 +102,7 @@ export function cspOrigins(csp: CspExtensions): CspOrigins {
93
102
  frameSrc: (csp.frameSrc ?? []).map((source) => source.origin),
94
103
  styleSrc: (csp.styleSrc ?? []).map((source) => source.origin),
95
104
  fontSrc: (csp.fontSrc ?? []).map((source) => source.origin),
105
+ ...(csp.workerSrc === undefined ? {} : { workerSrc: csp.workerSrc.map((source) => source.origin) }),
96
106
  }
97
107
  }
98
108
 
@@ -116,6 +126,7 @@ export function headerPolicy(origins: CspOrigins, operationsOrigin: string | nul
116
126
  `frame-ancestors ${frameAncestors.join(' ')}`,
117
127
  // Without it frames fall back to `default-src 'self'`; a declared origin only extends that, so `'self'` stays.
118
128
  ...(origins.frameSrc.length === 0 ? [] : [`frame-src ${["'self'", ...origins.frameSrc].join(' ')}`]),
129
+ ...(origins.workerSrc?.includes(WORKER_BLOB_SOURCE) === true ? [`worker-src 'self' ${WORKER_BLOB_SOURCE}`] : []),
119
130
  ]
120
131
  return Object.freeze([
121
132
  ['Content-Security-Policy', directives.join('; ')],
package/src/manifest.ts CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  requirementDigest,
22
22
  } from '@fabrika/auth-core'
23
23
  import { z } from 'zod'
24
- import { type CspOrigins, cspOrigins, isCrossOrigin, isPolicyOrigin, MAX_CROSS_ORIGINS, MAX_CSP_ORIGINS } from './headers'
24
+ import { type CspOrigins, cspOrigins, isCrossOrigin, isPolicyOrigin, MAX_CROSS_ORIGINS, MAX_CSP_ORIGINS, WORKER_BLOB_SOURCE } from './headers'
25
25
  import { type OAuthProtectedResource, oauthProtectedResourceSchema, oauthScopesSchema } from './oauth-resource'
26
26
 
27
27
  export * from './oauth-resource'
@@ -267,6 +267,8 @@ const cspOriginsSchema = z.object({
267
267
  frameSrc: cspDirectiveSchema.default([]),
268
268
  styleSrc: cspDirectiveSchema.default([]),
269
269
  fontSrc: cspDirectiveSchema.default([]),
270
+ // Only a challenge widget's workers need it, and its one source is `blob:`; absent everywhere else.
271
+ workerSrc: z.tuple([z.literal(WORKER_BLOB_SOURCE)]).optional(),
270
272
  }).strict() satisfies z.ZodType<CspOrigins, z.ZodTypeDef, unknown>
271
273
 
272
274
  // Strict: a key the proxy does not read (`gates`, or an installation entry's field) is refused, never ignored.