@stacksjs/server 0.72.8 → 0.72.9

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/dist/index.d.ts CHANGED
@@ -4,3 +4,4 @@ export * from './imports';
4
4
  export * from './maintenance';
5
5
  export * from './proxy';
6
6
  export * from './redirects';
7
+ export * from './security-headers';
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export{config as server}from"./config";export*from"./controllers/base";export*from"./imports";export*from"./maintenance";export*from"./proxy";export*from"./redirects";
1
+ export{config as server}from"./config";export*from"./controllers/base";export*from"./imports";export*from"./maintenance";export*from"./proxy";export*from"./redirects";export*from"./security-headers";
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Normalise `config.server.security.embeddable` into the rules the
3
+ * per-response check reads.
4
+ *
5
+ * Resolved once at boot, mirroring `resolveApiProxyRules`: `@stacksjs/config`
6
+ * populates overrides asynchronously, so a per-request read would answer
7
+ * differently depending on how far boot had progressed.
8
+ *
9
+ * An entry ending in `/` is a prefix; anything else is an exact path.
10
+ */
11
+ export declare function resolveEmbeddableRules(input?: readonly string[]): EmbeddableRules;
12
+ /** Whether this path is one the app said another origin may frame. */
13
+ export declare function isEmbeddablePath(pathname: string, rules: EmbeddableRules): boolean;
14
+ /**
15
+ * Stamp the view security headers onto a response.
16
+ *
17
+ * Mutates `response.headers` in place and returns `undefined`, so a caller
18
+ * that has nothing else to change can leave the original response alone.
19
+ * A response with immutable headers (a `Response.redirect()`, for instance)
20
+ * cannot be mutated, so it is rebuilt and returned - the same shape
21
+ * `stacks-router.ts` already uses for this case.
22
+ *
23
+ * Never overwrites a header that is already set: an app that set its own
24
+ * `X-Frame-Options` in a template or a proxy has said something more
25
+ * specific.
26
+ */
27
+ export declare function applyViewSecurityHeaders(req: Request, response: Response, rules: EmbeddableRules): Response | undefined;
28
+ /** Test helper - reset the cached env-derived flag. */
29
+ export declare function __resetViewSecurityHeadersCache(): void;
30
+ /** Paths another origin is allowed to frame. */
31
+ export declare interface EmbeddableRules {
32
+ paths: string[]
33
+ prefixes: string[]
34
+ }
@@ -0,0 +1 @@
1
+ import process from"node:process";let _isDisabledCache;function isDisabled(){if(_isDisabledCache!==void 0)return _isDisabledCache;_isDisabledCache=process.env.STACKS_SECURITY_HEADERS_DISABLE==="true";return _isDisabledCache}export function resolveEmbeddableRules(input){const paths=[],prefixes=[];for(const raw of input??[]){const entry=String(raw).trim();if(!entry||!entry.startsWith("/"))continue;if(entry.endsWith("/")&&entry.length>1){if(!prefixes.includes(entry))prefixes.push(entry);continue}if(!paths.includes(entry))paths.push(entry)}return{paths,prefixes}}function matchesPrefix(pathname,prefix){return pathname.startsWith(prefix)||pathname===prefix.slice(0,-1)}export function isEmbeddablePath(pathname,rules){if(rules.paths.includes(pathname))return!0;return rules.prefixes.some((prefix)=>matchesPrefix(pathname,prefix))}export function applyViewSecurityHeaders(req,response,rules){if(isDisabled())return;const embeddable=isEmbeddablePath(new URL(req.url).pathname,rules),wanted=[["X-Content-Type-Options","nosniff"],["Referrer-Policy","strict-origin-when-cross-origin"]];if(!embeddable)wanted.push(["X-Frame-Options","SAMEORIGIN"]);const missing=wanted.filter(([name])=>!response.headers.has(name));if(missing.length===0)return;try{for(const[name,value]of missing)response.headers.set(name,value);return}catch{const headers=new Headers(response.headers);for(const[name,value]of missing)headers.set(name,value);return new Response(response.body,{status:response.status,statusText:response.statusText,headers})}}export function __resetViewSecurityHeadersCache(){_isDisabledCache=void 0}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/server",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.72.8",
5
+ "version": "0.72.9",
6
6
  "description": "Local development and production-ready.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -58,11 +58,11 @@
58
58
  "prepublishOnly": "bun run build"
59
59
  },
60
60
  "devDependencies": {
61
- "@stacksjs/config": "0.72.8",
61
+ "@stacksjs/config": "0.72.9",
62
62
  "better-dx": "^0.2.23",
63
- "@stacksjs/path": "0.72.8",
64
- "@stacksjs/router": "0.72.8",
65
- "@stacksjs/validation": "0.72.8"
63
+ "@stacksjs/path": "0.72.9",
64
+ "@stacksjs/router": "0.72.9",
65
+ "@stacksjs/validation": "0.72.9"
66
66
  },
67
67
  "dependencies": {
68
68
  "bun-plugin-auto-imports": "^0.4.0"