@scalemule/nextjs 0.0.40 → 0.0.41

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.
@@ -1142,4 +1142,121 @@ declare function validateSafeRedirect(input: string | null | undefined, opts?: S
1142
1142
  */
1143
1143
  declare function isSafeRedirect(input: string | null | undefined, opts?: SafeRedirectOptions): boolean;
1144
1144
 
1145
- export { type AnalyticsRoutesConfig, type AnalyticsTrackingGateConfig, type AuthMiddlewareConfig, type AuthRoutesConfig, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, type HandlerContext, type HandlerOptions, KNOWN_ACCOUNTS_COOKIE_NAME, type KnownAccountEntry, type MySqlBundle, type NotificationRoutesConfig, OAUTH_STATE_COOKIE_NAME, type OAuthBundle, type PostgresBundle, type PushRoutesConfig, type RedisBundle, type S3Bundle, SESSION_COOKIE_NAME, type SafeRedirectOptions, ScaleMuleError, ServerConfig, type SessionCookieOptions, type SessionData, type SmtpBundle, USER_ID_COOKIE_NAME, apiHandler, appendKnownAccountCookie, buildClientContextHeaders, buildFlagContext, clearKnownAccountsCookie, clearOAuthState, clearSession, configureBundles, configureSecrets, createAnalyticsRoutes, createAuthMiddleware, createAuthRoutes, createNotificationRoutes, createPushRoutes, errorCodeToStatus, extractClientContext, extractClientContextFromReq, generateCSRFToken, getAppSecret, getAppSecretOrDefault, getBootstrapFlags, getBundle, getCSRFToken, getKnownAccountsCookieRaw, getKnownAccountsFromRequest, getMySqlBundle, getOAuthBundle, getPostgresBundle, getRedisBundle, getS3Bundle, getSession, getSessionFromRequest, getSmtpBundle, invalidateBundleCache, invalidateSecretCache, isSafeRedirect, normalizeKnownAccountsCookie, prefetchBundles, prefetchSecrets, removeKnownAccountFromCookie, requireAppSecret, requireBundle, requireSession, setOAuthState, unwrap, validateCSRFToken, validateCSRFTokenAsync, validateOAuthState, validateOAuthStateAsync, validateSafeRedirect, withAuth, withCSRFProtection, withCSRFToken, withSession };
1145
+ /**
1146
+ * Security-headers helper for Next.js hosts.
1147
+ *
1148
+ * Returns a shape that drops directly into the `headers()` entry in
1149
+ * `next.config.ts` (or the `headers` field of a Route Handler
1150
+ * `NextResponse`). Reasonable defaults for the top-of-OWASP headers
1151
+ * — HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy,
1152
+ * X-XSS-Protection, Permissions-Policy — with knobs for the fields
1153
+ * that typically differ by app (HSTS duration, frame ancestors,
1154
+ * camera/microphone policies).
1155
+ *
1156
+ * Framework-agnostic: no imports from `next/*` so the function works
1157
+ * in Edge / Node / middleware and is trivially unit-testable.
1158
+ */
1159
+ interface SecurityHeaderEntry {
1160
+ key: string;
1161
+ value: string;
1162
+ }
1163
+ interface BuildSecurityHeadersOptions {
1164
+ /**
1165
+ * HSTS max-age in seconds. Default `31536000` (1 year) — the value
1166
+ * required for inclusion on the HSTS preload list. Set to `0` (or
1167
+ * pass `includeHsts: false`) to opt out entirely.
1168
+ */
1169
+ hstsMaxAgeSeconds?: number;
1170
+ /** Adds the `includeSubDomains` directive to HSTS. Default `true`. */
1171
+ hstsIncludeSubDomains?: boolean;
1172
+ /**
1173
+ * Adds the `preload` directive to HSTS. Default `false` — only set
1174
+ * to `true` after you've read https://hstspreload.org and are ready
1175
+ * to commit; reversing it requires months of calendar time.
1176
+ */
1177
+ hstsPreload?: boolean;
1178
+ /** Emit the HSTS header at all. Default `true`. */
1179
+ includeHsts?: boolean;
1180
+ /**
1181
+ * Value for `X-Frame-Options`. Default `'DENY'`. Pass `'SAMEORIGIN'`
1182
+ * if your app is framed by its own subdomain. Set to `null` to
1183
+ * suppress (e.g. when you're already using `frame-ancestors` in
1184
+ * CSP). `SAMEORIGIN` accepts no host parameter; use CSP's
1185
+ * `frame-ancestors` for finer-grained framing policy.
1186
+ */
1187
+ frameOptions?: 'DENY' | 'SAMEORIGIN' | null;
1188
+ /**
1189
+ * Value for `Referrer-Policy`. Default `'strict-origin-when-cross-origin'`
1190
+ * — same as Next.js's own default, balances analytics with privacy.
1191
+ */
1192
+ referrerPolicy?: 'no-referrer' | 'no-referrer-when-downgrade' | 'origin' | 'origin-when-cross-origin' | 'same-origin' | 'strict-origin' | 'strict-origin-when-cross-origin' | 'unsafe-url';
1193
+ /**
1194
+ * Permissions-Policy directives. Pass a map of `{ feature: allowlist }`
1195
+ * where the allowlist is either an array of origins (string[]) or
1196
+ * one of the well-known tokens `'self'` / `'*'` / `'()'` (deny).
1197
+ *
1198
+ * Default: camera + microphone allowed for `'self'` only (useful
1199
+ * for the conference track); geolocation + payment denied; every
1200
+ * other common feature left unset (browsers apply their own
1201
+ * conservative defaults).
1202
+ *
1203
+ * Pass `{}` to emit an empty `Permissions-Policy: ` header (rarely
1204
+ * useful). Pass `null` to suppress the header entirely.
1205
+ */
1206
+ permissionsPolicy?: PermissionsPolicyMap | null;
1207
+ /**
1208
+ * Value for `X-Content-Type-Options`. Default `'nosniff'` — do not
1209
+ * override without a specific reason.
1210
+ */
1211
+ contentTypeOptions?: string | null;
1212
+ /**
1213
+ * Legacy `X-XSS-Protection`. Default `'0'` (disables the
1214
+ * deprecated IE/Edge filter which had known bypass issues —
1215
+ * recommended by OWASP + Next.js). Set to `'1; mode=block'` only
1216
+ * if you have a specific compliance reason.
1217
+ */
1218
+ xssProtection?: string | null;
1219
+ /**
1220
+ * Extra headers to merge into the result. Takes precedence over
1221
+ * the computed ones — useful for adding Content-Security-Policy or
1222
+ * Cross-Origin-* headers without forcing a new option for each.
1223
+ */
1224
+ extraHeaders?: SecurityHeaderEntry[];
1225
+ }
1226
+ type PermissionsPolicyValue = readonly string[] | 'self' | '*' | '()';
1227
+ type PermissionsPolicyMap = Readonly<Record<string, PermissionsPolicyValue>>;
1228
+ declare const DEFAULT_PERMISSIONS_POLICY: PermissionsPolicyMap;
1229
+ /**
1230
+ * Build a reasonable-default set of security response headers in the
1231
+ * exact shape Next.js `headers()` expects.
1232
+ *
1233
+ * @example next.config.ts
1234
+ * ```ts
1235
+ * import { buildSecurityHeaders } from '@scalemule/nextjs/server'
1236
+ *
1237
+ * export default {
1238
+ * async headers() {
1239
+ * return [
1240
+ * { source: '/:path*', headers: buildSecurityHeaders() },
1241
+ * ]
1242
+ * },
1243
+ * }
1244
+ * ```
1245
+ *
1246
+ * @example custom overrides
1247
+ * ```ts
1248
+ * buildSecurityHeaders({
1249
+ * frameOptions: 'SAMEORIGIN',
1250
+ * permissionsPolicy: {
1251
+ * camera: ['https://trusted.example.com'],
1252
+ * microphone: ['https://trusted.example.com'],
1253
+ * },
1254
+ * extraHeaders: [
1255
+ * { key: 'Content-Security-Policy', value: "default-src 'self'" },
1256
+ * ],
1257
+ * })
1258
+ * ```
1259
+ */
1260
+ declare function buildSecurityHeaders(opts?: BuildSecurityHeadersOptions): SecurityHeaderEntry[];
1261
+
1262
+ export { type AnalyticsRoutesConfig, type AnalyticsTrackingGateConfig, type AuthMiddlewareConfig, type AuthRoutesConfig, type BuildSecurityHeadersOptions, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, DEFAULT_PERMISSIONS_POLICY, type HandlerContext, type HandlerOptions, KNOWN_ACCOUNTS_COOKIE_NAME, type KnownAccountEntry, type MySqlBundle, type NotificationRoutesConfig, OAUTH_STATE_COOKIE_NAME, type OAuthBundle, type PermissionsPolicyMap, type PermissionsPolicyValue, type PostgresBundle, type PushRoutesConfig, type RedisBundle, type S3Bundle, SESSION_COOKIE_NAME, type SafeRedirectOptions, ScaleMuleError, type SecurityHeaderEntry, ServerConfig, type SessionCookieOptions, type SessionData, type SmtpBundle, USER_ID_COOKIE_NAME, apiHandler, appendKnownAccountCookie, buildClientContextHeaders, buildFlagContext, buildSecurityHeaders, clearKnownAccountsCookie, clearOAuthState, clearSession, configureBundles, configureSecrets, createAnalyticsRoutes, createAuthMiddleware, createAuthRoutes, createNotificationRoutes, createPushRoutes, errorCodeToStatus, extractClientContext, extractClientContextFromReq, generateCSRFToken, getAppSecret, getAppSecretOrDefault, getBootstrapFlags, getBundle, getCSRFToken, getKnownAccountsCookieRaw, getKnownAccountsFromRequest, getMySqlBundle, getOAuthBundle, getPostgresBundle, getRedisBundle, getS3Bundle, getSession, getSessionFromRequest, getSmtpBundle, invalidateBundleCache, invalidateSecretCache, isSafeRedirect, normalizeKnownAccountsCookie, prefetchBundles, prefetchSecrets, removeKnownAccountFromCookie, requireAppSecret, requireBundle, requireSession, setOAuthState, unwrap, validateCSRFToken, validateCSRFTokenAsync, validateOAuthState, validateOAuthStateAsync, validateSafeRedirect, withAuth, withCSRFProtection, withCSRFToken, withSession };
@@ -1142,4 +1142,121 @@ declare function validateSafeRedirect(input: string | null | undefined, opts?: S
1142
1142
  */
1143
1143
  declare function isSafeRedirect(input: string | null | undefined, opts?: SafeRedirectOptions): boolean;
1144
1144
 
1145
- export { type AnalyticsRoutesConfig, type AnalyticsTrackingGateConfig, type AuthMiddlewareConfig, type AuthRoutesConfig, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, type HandlerContext, type HandlerOptions, KNOWN_ACCOUNTS_COOKIE_NAME, type KnownAccountEntry, type MySqlBundle, type NotificationRoutesConfig, OAUTH_STATE_COOKIE_NAME, type OAuthBundle, type PostgresBundle, type PushRoutesConfig, type RedisBundle, type S3Bundle, SESSION_COOKIE_NAME, type SafeRedirectOptions, ScaleMuleError, ServerConfig, type SessionCookieOptions, type SessionData, type SmtpBundle, USER_ID_COOKIE_NAME, apiHandler, appendKnownAccountCookie, buildClientContextHeaders, buildFlagContext, clearKnownAccountsCookie, clearOAuthState, clearSession, configureBundles, configureSecrets, createAnalyticsRoutes, createAuthMiddleware, createAuthRoutes, createNotificationRoutes, createPushRoutes, errorCodeToStatus, extractClientContext, extractClientContextFromReq, generateCSRFToken, getAppSecret, getAppSecretOrDefault, getBootstrapFlags, getBundle, getCSRFToken, getKnownAccountsCookieRaw, getKnownAccountsFromRequest, getMySqlBundle, getOAuthBundle, getPostgresBundle, getRedisBundle, getS3Bundle, getSession, getSessionFromRequest, getSmtpBundle, invalidateBundleCache, invalidateSecretCache, isSafeRedirect, normalizeKnownAccountsCookie, prefetchBundles, prefetchSecrets, removeKnownAccountFromCookie, requireAppSecret, requireBundle, requireSession, setOAuthState, unwrap, validateCSRFToken, validateCSRFTokenAsync, validateOAuthState, validateOAuthStateAsync, validateSafeRedirect, withAuth, withCSRFProtection, withCSRFToken, withSession };
1145
+ /**
1146
+ * Security-headers helper for Next.js hosts.
1147
+ *
1148
+ * Returns a shape that drops directly into the `headers()` entry in
1149
+ * `next.config.ts` (or the `headers` field of a Route Handler
1150
+ * `NextResponse`). Reasonable defaults for the top-of-OWASP headers
1151
+ * — HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy,
1152
+ * X-XSS-Protection, Permissions-Policy — with knobs for the fields
1153
+ * that typically differ by app (HSTS duration, frame ancestors,
1154
+ * camera/microphone policies).
1155
+ *
1156
+ * Framework-agnostic: no imports from `next/*` so the function works
1157
+ * in Edge / Node / middleware and is trivially unit-testable.
1158
+ */
1159
+ interface SecurityHeaderEntry {
1160
+ key: string;
1161
+ value: string;
1162
+ }
1163
+ interface BuildSecurityHeadersOptions {
1164
+ /**
1165
+ * HSTS max-age in seconds. Default `31536000` (1 year) — the value
1166
+ * required for inclusion on the HSTS preload list. Set to `0` (or
1167
+ * pass `includeHsts: false`) to opt out entirely.
1168
+ */
1169
+ hstsMaxAgeSeconds?: number;
1170
+ /** Adds the `includeSubDomains` directive to HSTS. Default `true`. */
1171
+ hstsIncludeSubDomains?: boolean;
1172
+ /**
1173
+ * Adds the `preload` directive to HSTS. Default `false` — only set
1174
+ * to `true` after you've read https://hstspreload.org and are ready
1175
+ * to commit; reversing it requires months of calendar time.
1176
+ */
1177
+ hstsPreload?: boolean;
1178
+ /** Emit the HSTS header at all. Default `true`. */
1179
+ includeHsts?: boolean;
1180
+ /**
1181
+ * Value for `X-Frame-Options`. Default `'DENY'`. Pass `'SAMEORIGIN'`
1182
+ * if your app is framed by its own subdomain. Set to `null` to
1183
+ * suppress (e.g. when you're already using `frame-ancestors` in
1184
+ * CSP). `SAMEORIGIN` accepts no host parameter; use CSP's
1185
+ * `frame-ancestors` for finer-grained framing policy.
1186
+ */
1187
+ frameOptions?: 'DENY' | 'SAMEORIGIN' | null;
1188
+ /**
1189
+ * Value for `Referrer-Policy`. Default `'strict-origin-when-cross-origin'`
1190
+ * — same as Next.js's own default, balances analytics with privacy.
1191
+ */
1192
+ referrerPolicy?: 'no-referrer' | 'no-referrer-when-downgrade' | 'origin' | 'origin-when-cross-origin' | 'same-origin' | 'strict-origin' | 'strict-origin-when-cross-origin' | 'unsafe-url';
1193
+ /**
1194
+ * Permissions-Policy directives. Pass a map of `{ feature: allowlist }`
1195
+ * where the allowlist is either an array of origins (string[]) or
1196
+ * one of the well-known tokens `'self'` / `'*'` / `'()'` (deny).
1197
+ *
1198
+ * Default: camera + microphone allowed for `'self'` only (useful
1199
+ * for the conference track); geolocation + payment denied; every
1200
+ * other common feature left unset (browsers apply their own
1201
+ * conservative defaults).
1202
+ *
1203
+ * Pass `{}` to emit an empty `Permissions-Policy: ` header (rarely
1204
+ * useful). Pass `null` to suppress the header entirely.
1205
+ */
1206
+ permissionsPolicy?: PermissionsPolicyMap | null;
1207
+ /**
1208
+ * Value for `X-Content-Type-Options`. Default `'nosniff'` — do not
1209
+ * override without a specific reason.
1210
+ */
1211
+ contentTypeOptions?: string | null;
1212
+ /**
1213
+ * Legacy `X-XSS-Protection`. Default `'0'` (disables the
1214
+ * deprecated IE/Edge filter which had known bypass issues —
1215
+ * recommended by OWASP + Next.js). Set to `'1; mode=block'` only
1216
+ * if you have a specific compliance reason.
1217
+ */
1218
+ xssProtection?: string | null;
1219
+ /**
1220
+ * Extra headers to merge into the result. Takes precedence over
1221
+ * the computed ones — useful for adding Content-Security-Policy or
1222
+ * Cross-Origin-* headers without forcing a new option for each.
1223
+ */
1224
+ extraHeaders?: SecurityHeaderEntry[];
1225
+ }
1226
+ type PermissionsPolicyValue = readonly string[] | 'self' | '*' | '()';
1227
+ type PermissionsPolicyMap = Readonly<Record<string, PermissionsPolicyValue>>;
1228
+ declare const DEFAULT_PERMISSIONS_POLICY: PermissionsPolicyMap;
1229
+ /**
1230
+ * Build a reasonable-default set of security response headers in the
1231
+ * exact shape Next.js `headers()` expects.
1232
+ *
1233
+ * @example next.config.ts
1234
+ * ```ts
1235
+ * import { buildSecurityHeaders } from '@scalemule/nextjs/server'
1236
+ *
1237
+ * export default {
1238
+ * async headers() {
1239
+ * return [
1240
+ * { source: '/:path*', headers: buildSecurityHeaders() },
1241
+ * ]
1242
+ * },
1243
+ * }
1244
+ * ```
1245
+ *
1246
+ * @example custom overrides
1247
+ * ```ts
1248
+ * buildSecurityHeaders({
1249
+ * frameOptions: 'SAMEORIGIN',
1250
+ * permissionsPolicy: {
1251
+ * camera: ['https://trusted.example.com'],
1252
+ * microphone: ['https://trusted.example.com'],
1253
+ * },
1254
+ * extraHeaders: [
1255
+ * { key: 'Content-Security-Policy', value: "default-src 'self'" },
1256
+ * ],
1257
+ * })
1258
+ * ```
1259
+ */
1260
+ declare function buildSecurityHeaders(opts?: BuildSecurityHeadersOptions): SecurityHeaderEntry[];
1261
+
1262
+ export { type AnalyticsRoutesConfig, type AnalyticsTrackingGateConfig, type AuthMiddlewareConfig, type AuthRoutesConfig, type BuildSecurityHeadersOptions, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, DEFAULT_PERMISSIONS_POLICY, type HandlerContext, type HandlerOptions, KNOWN_ACCOUNTS_COOKIE_NAME, type KnownAccountEntry, type MySqlBundle, type NotificationRoutesConfig, OAUTH_STATE_COOKIE_NAME, type OAuthBundle, type PermissionsPolicyMap, type PermissionsPolicyValue, type PostgresBundle, type PushRoutesConfig, type RedisBundle, type S3Bundle, SESSION_COOKIE_NAME, type SafeRedirectOptions, ScaleMuleError, type SecurityHeaderEntry, ServerConfig, type SessionCookieOptions, type SessionData, type SmtpBundle, USER_ID_COOKIE_NAME, apiHandler, appendKnownAccountCookie, buildClientContextHeaders, buildFlagContext, buildSecurityHeaders, clearKnownAccountsCookie, clearOAuthState, clearSession, configureBundles, configureSecrets, createAnalyticsRoutes, createAuthMiddleware, createAuthRoutes, createNotificationRoutes, createPushRoutes, errorCodeToStatus, extractClientContext, extractClientContextFromReq, generateCSRFToken, getAppSecret, getAppSecretOrDefault, getBootstrapFlags, getBundle, getCSRFToken, getKnownAccountsCookieRaw, getKnownAccountsFromRequest, getMySqlBundle, getOAuthBundle, getPostgresBundle, getRedisBundle, getS3Bundle, getSession, getSessionFromRequest, getSmtpBundle, invalidateBundleCache, invalidateSecretCache, isSafeRedirect, normalizeKnownAccountsCookie, prefetchBundles, prefetchSecrets, removeKnownAccountFromCookie, requireAppSecret, requireBundle, requireSession, setOAuthState, unwrap, validateCSRFToken, validateCSRFTokenAsync, validateOAuthState, validateOAuthStateAsync, validateSafeRedirect, withAuth, withCSRFProtection, withCSRFToken, withSession };
@@ -5145,8 +5145,77 @@ function isSafeRedirect(input, opts = {}) {
5145
5145
  return resolved !== defaultPath;
5146
5146
  }
5147
5147
 
5148
+ // src/server/security-headers.ts
5149
+ var DEFAULT_PERMISSIONS_POLICY = {
5150
+ // Conference track features — allow self-origin only.
5151
+ camera: "self",
5152
+ microphone: "self",
5153
+ "display-capture": "self",
5154
+ // Deny-by-default for features the SDK never uses.
5155
+ geolocation: "()",
5156
+ payment: "()",
5157
+ "publickey-credentials-get": "self",
5158
+ usb: "()",
5159
+ serial: "()",
5160
+ bluetooth: "()"
5161
+ };
5162
+ function formatPermissionsDirective(feature, value) {
5163
+ if (value === "*") return `${feature}=*`;
5164
+ if (value === "()" || Array.isArray(value) && value.length === 0) {
5165
+ return `${feature}=()`;
5166
+ }
5167
+ if (value === "self") return `${feature}=(self)`;
5168
+ const origins = value;
5169
+ const parts = origins.map((o) => o === "self" ? "self" : `"${o}"`);
5170
+ return `${feature}=(${parts.join(" ")})`;
5171
+ }
5172
+ function buildSecurityHeaders(opts = {}) {
5173
+ const headers2 = [];
5174
+ const includeHsts = opts.includeHsts ?? true;
5175
+ const hstsMaxAge = opts.hstsMaxAgeSeconds ?? 31536e3;
5176
+ if (includeHsts && hstsMaxAge > 0) {
5177
+ const parts = [`max-age=${hstsMaxAge}`];
5178
+ if (opts.hstsIncludeSubDomains ?? true) parts.push("includeSubDomains");
5179
+ if (opts.hstsPreload ?? false) parts.push("preload");
5180
+ headers2.push({
5181
+ key: "Strict-Transport-Security",
5182
+ value: parts.join("; ")
5183
+ });
5184
+ }
5185
+ const frameOptions = opts.frameOptions === void 0 ? "DENY" : opts.frameOptions;
5186
+ if (frameOptions !== null) {
5187
+ headers2.push({ key: "X-Frame-Options", value: frameOptions });
5188
+ }
5189
+ const contentTypeOptions = opts.contentTypeOptions === void 0 ? "nosniff" : opts.contentTypeOptions;
5190
+ if (contentTypeOptions !== null) {
5191
+ headers2.push({ key: "X-Content-Type-Options", value: contentTypeOptions });
5192
+ }
5193
+ headers2.push({
5194
+ key: "Referrer-Policy",
5195
+ value: opts.referrerPolicy ?? "strict-origin-when-cross-origin"
5196
+ });
5197
+ const xss = opts.xssProtection === void 0 ? "0" : opts.xssProtection;
5198
+ if (xss !== null) {
5199
+ headers2.push({ key: "X-XSS-Protection", value: xss });
5200
+ }
5201
+ const permissions = opts.permissionsPolicy === void 0 ? DEFAULT_PERMISSIONS_POLICY : opts.permissionsPolicy;
5202
+ if (permissions !== null) {
5203
+ const directives = Object.entries(permissions).map(([feature, value]) => formatPermissionsDirective(feature, value)).join(", ");
5204
+ headers2.push({ key: "Permissions-Policy", value: directives });
5205
+ }
5206
+ if (opts.extraHeaders?.length) {
5207
+ const byKey = new Map(headers2.map((h) => [h.key.toLowerCase(), h]));
5208
+ for (const extra of opts.extraHeaders) {
5209
+ byKey.set(extra.key.toLowerCase(), extra);
5210
+ }
5211
+ return Array.from(byKey.values());
5212
+ }
5213
+ return headers2;
5214
+ }
5215
+
5148
5216
  exports.CSRF_COOKIE_NAME = CSRF_COOKIE_NAME;
5149
5217
  exports.CSRF_HEADER_NAME = CSRF_HEADER_NAME;
5218
+ exports.DEFAULT_PERMISSIONS_POLICY = DEFAULT_PERMISSIONS_POLICY;
5150
5219
  exports.KNOWN_ACCOUNTS_COOKIE_NAME = KNOWN_ACCOUNTS_COOKIE_NAME;
5151
5220
  exports.OAUTH_STATE_COOKIE_NAME = OAUTH_STATE_COOKIE_NAME;
5152
5221
  exports.SESSION_COOKIE_NAME = SESSION_COOKIE_NAME;
@@ -5157,6 +5226,7 @@ exports.apiHandler = apiHandler;
5157
5226
  exports.appendKnownAccountCookie = appendKnownAccountCookie;
5158
5227
  exports.buildClientContextHeaders = buildClientContextHeaders;
5159
5228
  exports.buildFlagContext = buildFlagContext;
5229
+ exports.buildSecurityHeaders = buildSecurityHeaders;
5160
5230
  exports.clearKnownAccountsCookie = clearKnownAccountsCookie;
5161
5231
  exports.clearOAuthState = clearOAuthState;
5162
5232
  exports.clearSession = clearSession;
@@ -5143,4 +5143,72 @@ function isSafeRedirect(input, opts = {}) {
5143
5143
  return resolved !== defaultPath;
5144
5144
  }
5145
5145
 
5146
- export { CSRF_COOKIE_NAME, CSRF_HEADER_NAME, KNOWN_ACCOUNTS_COOKIE_NAME, OAUTH_STATE_COOKIE_NAME, SESSION_COOKIE_NAME, ScaleMuleError, ScaleMuleServer, USER_ID_COOKIE_NAME, apiHandler, appendKnownAccountCookie, buildClientContextHeaders, buildFlagContext, clearKnownAccountsCookie, clearOAuthState, clearSession, configureBundles, configureSecrets, createAnalyticsRoutes, createAuthMiddleware, createAuthRoutes, createNotificationRoutes, createPushRoutes, createServerClient, createWebhookHandler, createWebhookRoutes, errorCodeToStatus, extractClientContext, extractClientContextFromReq, generateCSRFToken, getAppSecret, getAppSecretOrDefault, getBootstrapFlags, getBundle, getCSRFToken, getKnownAccountsCookieRaw, getKnownAccountsFromRequest, getMySqlBundle, getOAuthBundle, getPostgresBundle, getRedisBundle, getS3Bundle, getSession, getSessionFromRequest, getSmtpBundle, invalidateBundleCache, invalidateSecretCache, isSafeRedirect, normalizeKnownAccountsCookie, parseWebhookEvent, prefetchBundles, prefetchSecrets, registerVideoWebhook, removeKnownAccountFromCookie, requireAppSecret, requireBundle, requireSession, resolveGatewayUrl, setOAuthState, unwrap, validateCSRFToken, validateCSRFTokenAsync, validateOAuthState, validateOAuthStateAsync, validateSafeRedirect, verifyWebhookSignature, withAuth, withCSRFProtection, withCSRFToken, withSession };
5146
+ // src/server/security-headers.ts
5147
+ var DEFAULT_PERMISSIONS_POLICY = {
5148
+ // Conference track features — allow self-origin only.
5149
+ camera: "self",
5150
+ microphone: "self",
5151
+ "display-capture": "self",
5152
+ // Deny-by-default for features the SDK never uses.
5153
+ geolocation: "()",
5154
+ payment: "()",
5155
+ "publickey-credentials-get": "self",
5156
+ usb: "()",
5157
+ serial: "()",
5158
+ bluetooth: "()"
5159
+ };
5160
+ function formatPermissionsDirective(feature, value) {
5161
+ if (value === "*") return `${feature}=*`;
5162
+ if (value === "()" || Array.isArray(value) && value.length === 0) {
5163
+ return `${feature}=()`;
5164
+ }
5165
+ if (value === "self") return `${feature}=(self)`;
5166
+ const origins = value;
5167
+ const parts = origins.map((o) => o === "self" ? "self" : `"${o}"`);
5168
+ return `${feature}=(${parts.join(" ")})`;
5169
+ }
5170
+ function buildSecurityHeaders(opts = {}) {
5171
+ const headers2 = [];
5172
+ const includeHsts = opts.includeHsts ?? true;
5173
+ const hstsMaxAge = opts.hstsMaxAgeSeconds ?? 31536e3;
5174
+ if (includeHsts && hstsMaxAge > 0) {
5175
+ const parts = [`max-age=${hstsMaxAge}`];
5176
+ if (opts.hstsIncludeSubDomains ?? true) parts.push("includeSubDomains");
5177
+ if (opts.hstsPreload ?? false) parts.push("preload");
5178
+ headers2.push({
5179
+ key: "Strict-Transport-Security",
5180
+ value: parts.join("; ")
5181
+ });
5182
+ }
5183
+ const frameOptions = opts.frameOptions === void 0 ? "DENY" : opts.frameOptions;
5184
+ if (frameOptions !== null) {
5185
+ headers2.push({ key: "X-Frame-Options", value: frameOptions });
5186
+ }
5187
+ const contentTypeOptions = opts.contentTypeOptions === void 0 ? "nosniff" : opts.contentTypeOptions;
5188
+ if (contentTypeOptions !== null) {
5189
+ headers2.push({ key: "X-Content-Type-Options", value: contentTypeOptions });
5190
+ }
5191
+ headers2.push({
5192
+ key: "Referrer-Policy",
5193
+ value: opts.referrerPolicy ?? "strict-origin-when-cross-origin"
5194
+ });
5195
+ const xss = opts.xssProtection === void 0 ? "0" : opts.xssProtection;
5196
+ if (xss !== null) {
5197
+ headers2.push({ key: "X-XSS-Protection", value: xss });
5198
+ }
5199
+ const permissions = opts.permissionsPolicy === void 0 ? DEFAULT_PERMISSIONS_POLICY : opts.permissionsPolicy;
5200
+ if (permissions !== null) {
5201
+ const directives = Object.entries(permissions).map(([feature, value]) => formatPermissionsDirective(feature, value)).join(", ");
5202
+ headers2.push({ key: "Permissions-Policy", value: directives });
5203
+ }
5204
+ if (opts.extraHeaders?.length) {
5205
+ const byKey = new Map(headers2.map((h) => [h.key.toLowerCase(), h]));
5206
+ for (const extra of opts.extraHeaders) {
5207
+ byKey.set(extra.key.toLowerCase(), extra);
5208
+ }
5209
+ return Array.from(byKey.values());
5210
+ }
5211
+ return headers2;
5212
+ }
5213
+
5214
+ export { CSRF_COOKIE_NAME, CSRF_HEADER_NAME, DEFAULT_PERMISSIONS_POLICY, KNOWN_ACCOUNTS_COOKIE_NAME, OAUTH_STATE_COOKIE_NAME, SESSION_COOKIE_NAME, ScaleMuleError, ScaleMuleServer, USER_ID_COOKIE_NAME, apiHandler, appendKnownAccountCookie, buildClientContextHeaders, buildFlagContext, buildSecurityHeaders, clearKnownAccountsCookie, clearOAuthState, clearSession, configureBundles, configureSecrets, createAnalyticsRoutes, createAuthMiddleware, createAuthRoutes, createNotificationRoutes, createPushRoutes, createServerClient, createWebhookHandler, createWebhookRoutes, errorCodeToStatus, extractClientContext, extractClientContextFromReq, generateCSRFToken, getAppSecret, getAppSecretOrDefault, getBootstrapFlags, getBundle, getCSRFToken, getKnownAccountsCookieRaw, getKnownAccountsFromRequest, getMySqlBundle, getOAuthBundle, getPostgresBundle, getRedisBundle, getS3Bundle, getSession, getSessionFromRequest, getSmtpBundle, invalidateBundleCache, invalidateSecretCache, isSafeRedirect, normalizeKnownAccountsCookie, parseWebhookEvent, prefetchBundles, prefetchSecrets, registerVideoWebhook, removeKnownAccountFromCookie, requireAppSecret, requireBundle, requireSession, resolveGatewayUrl, setOAuthState, unwrap, validateCSRFToken, validateCSRFTokenAsync, validateOAuthState, validateOAuthStateAsync, validateSafeRedirect, verifyWebhookSignature, withAuth, withCSRFProtection, withCSRFToken, withSession };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scalemule/nextjs",
3
- "version": "0.0.40",
3
+ "version": "0.0.41",
4
4
  "description": "ScaleMule SDK for Next.js applications - authentication, storage, and user management",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",