@stacksjs/server 0.75.226 → 0.75.230

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
@@ -6,3 +6,4 @@ export * from './proxy';
6
6
  export * from './redirects';
7
7
  export * from './rewrites';
8
8
  export * from './security-headers';
9
+ export * from './shadowed-defaults';
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";export*from"./rewrites";export*from"./security-headers";
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"./rewrites";export*from"./security-headers";export*from"./shadowed-defaults";
@@ -0,0 +1,60 @@
1
+ import { path } from '@stacksjs/path';
2
+ /** A short, stable fingerprint of a default's contents. */
3
+ export declare function defaultFingerprint(contents: Uint8Array | string): string;
4
+ /**
5
+ * List the application files that shadow a framework security default.
6
+ *
7
+ * `appDir` is the application's `app/`, `defaultsDir` the framework's
8
+ * `defaults/app/`. A listed file the framework no longer ships is not a
9
+ * shadow and is skipped. A modified override carrying
10
+ * `stacks-reviewed-default: <fingerprint>` for the current default is
11
+ * treated as reviewed and left out. Never throws: an unreadable file is
12
+ * skipped, since a diagnostic must not stop a boot.
13
+ */
14
+ export declare function findShadowedSecurityDefaults(appDir: string, defaultsDir: string, files?: readonly string[]): ShadowedSecurityDefault[];
15
+ /** One readable warning for a list of shadowed defaults. */
16
+ export declare function describeShadowedSecurityDefaults(entries: readonly ShadowedSecurityDefault[]): string;
17
+ /**
18
+ * Warn once per process about app files shadowing security defaults.
19
+ *
20
+ * Called from the API server entries. Cheap (a stat per listed file, and a
21
+ * read only for the ones the app has) and never throws. Returns what it found,
22
+ * which is empty when silenced or already run.
23
+ */
24
+ export declare function warnShadowedSecurityDefaults(options?: WarnShadowedSecurityDefaultsOptions): ShadowedSecurityDefault[];
25
+ /**
26
+ * The framework defaults whose job is security, as paths relative to `app/`.
27
+ *
28
+ * Authentication, session and token issuance, password reset, request
29
+ * forgery, cross-origin policy, rate limiting, signed URLs and the
30
+ * authorization middleware. Helpers the auth actions import by relative path
31
+ * are listed too: a copied action resolves its helper from `app/` as well.
32
+ *
33
+ * Keep it explicit. A default not listed here can still be overridden; it just
34
+ * does not warn.
35
+ */
36
+ export declare const SECURITY_DEFAULT_FILES: readonly string[];
37
+ /**
38
+ * The comment an application adds to a modified override once it has been
39
+ * reviewed against the current default, followed by the default's fingerprint.
40
+ *
41
+ * Tied to the default's content, not to a flag: when the framework changes
42
+ * that default, the fingerprint stops matching and the warning returns, which
43
+ * is the moment a review is needed again.
44
+ */
45
+ export declare const SHADOWED_DEFAULT_REVIEW_MARKER: 'stacks-reviewed-default:';
46
+ /** Environment variable that turns the boot warning off. */
47
+ export declare const SHADOWED_DEFAULTS_ENV: 'STACKS_SHADOWED_DEFAULTS_WARN';
48
+ export declare interface ShadowedSecurityDefault {
49
+ path: string
50
+ kind: 'identical' | 'modified'
51
+ appFile: string
52
+ defaultFile: string
53
+ fingerprint: string
54
+ }
55
+ export declare interface WarnShadowedSecurityDefaultsOptions {
56
+ appDir?: string
57
+ defaultsDir?: string
58
+ warn?: (message: string) => void
59
+ force?: boolean
60
+ }
@@ -0,0 +1,2 @@
1
+ import{createHash}from"node:crypto";import{existsSync,readFileSync,statSync}from"node:fs";import{join}from"node:path";import process from"node:process";import{log}from"@stacksjs/logging/runtime";import{path}from"@stacksjs/path";import{frameworkDefaultsDir}from"./imports";export const SECURITY_DEFAULT_FILES=Object.freeze(["Middleware/Auth.ts","Middleware/Guest.ts","Middleware/Csrf.ts","Middleware/Cors.ts","Middleware/Throttle.ts","Middleware/Signed.ts","Middleware/Abilities.ts","Middleware/Can.ts","Middleware/Permission.ts","Middleware/Role.ts","Middleware/EnsureEmailIsVerified.ts","Actions/Auth/LoginAction.ts","Actions/Auth/LogoutAction.ts","Actions/Auth/LogoutAllAction.ts","Actions/Auth/RegisterAction.ts","Actions/Auth/RefreshTokenAction.ts","Actions/Auth/VerifyTwoFactorLoginAction.ts","Actions/Auth/VerifyEmailAction.ts","Actions/Auth/MagicLinkSendAction.ts","Actions/Auth/MagicLinkConsumeAction.ts","Actions/Auth/PasskeyAuthenticationAction.ts","Actions/Auth/VerifyAuthenticationAction.ts","Actions/Auth/SocialRedirectAction.ts","Actions/Auth/SocialCallbackAction.ts","Actions/Auth/SessionHandoffAction.ts","Actions/Auth/social-flow.ts","Actions/Auth/token-request.ts","Actions/Auth/CreateTokenAction.ts","Actions/Auth/RevokeTokenAction.ts","Actions/Auth/OAuthAuthorizationAction.ts","Actions/Auth/OAuthTokenAction.ts","Actions/Auth/oauth-request.ts","Actions/Password/SendPasswordResetEmailAction.ts","Actions/Password/VerifyResetTokenAction.ts","Actions/Password/PasswordResetAction.ts"]),SHADOWED_DEFAULT_REVIEW_MARKER="stacks-reviewed-default:",SHADOWED_DEFAULTS_ENV="STACKS_SHADOWED_DEFAULTS_WARN";export function defaultFingerprint(contents){return createHash("sha256").update(contents).digest("hex").slice(0,16)}function isFile(file){try{return statSync(file).isFile()}catch{return!1}}function sameBytes(a,b){if(a.byteLength!==b.byteLength)return!1;for(let i=0;i<a.byteLength;i++)if(a[i]!==b[i])return!1;return!0}export function findShadowedSecurityDefaults(appDir,defaultsDir,files=SECURITY_DEFAULT_FILES){const found=[];for(const relative of files){const appFile=join(appDir,relative),defaultFile=join(defaultsDir,relative);try{if(!isFile(appFile)||!isFile(defaultFile))continue;const own=readFileSync(appFile),framework=readFileSync(defaultFile),fingerprint=defaultFingerprint(framework);if(sameBytes(own,framework)){found.push({path:relative,kind:"identical",appFile,defaultFile,fingerprint});continue}if(own.toString("utf8").includes(`${SHADOWED_DEFAULT_REVIEW_MARKER} ${fingerprint}`))continue;found.push({path:relative,kind:"modified",appFile,defaultFile,fingerprint})}catch{}}return found}export function describeShadowedSecurityDefaults(entries){const lines=entries.map((entry)=>{if(entry.kind==="identical")return` - app/${entry.path} is an unchanged copy of the framework default. Delete it to receive security fixes.`;return` - app/${entry.path} differs from the framework default (${entry.defaultFile}). Review it against the current default; once reviewed, add the comment \`// ${SHADOWED_DEFAULT_REVIEW_MARKER} ${entry.fingerprint}\` to it.`});return[`[security] ${entries.length} app/ file${entries.length===1?"":"s"} shadow${entries.length===1?"s":""} a framework security default, so framework security fixes to ${entries.length===1?"it do":"them do"} not reach this app:`,...lines,` Set ${SHADOWED_DEFAULTS_ENV}=false to silence this warning.`].join(`
2
+ `)}let warned=!1;export function warnShadowedSecurityDefaults(options={}){if(warned&&!options.force)return[];warned=!0;if(String(process.env[SHADOWED_DEFAULTS_ENV]??"").toLowerCase()==="false")return[];try{const appDir=options.appDir??path.appPath(),defaultsDir=options.defaultsDir??frameworkDefaultsDir("app");if(!appDir||!defaultsDir||!existsSync(appDir)||!existsSync(defaultsDir))return[];const found=findShadowedSecurityDefaults(appDir,defaultsDir);if(found.length>0)(options.warn??((message)=>log.warn(message)))(describeShadowedSecurityDefaults(found));return found}catch{return[]}}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/server",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.75.226",
5
+ "version": "0.75.230",
6
6
  "description": "Local development and production-ready.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -58,21 +58,21 @@
58
58
  "prepublishOnly": "bun run build"
59
59
  },
60
60
  "dependencies": {
61
- "@stacksjs/auth": "0.75.226",
61
+ "@stacksjs/auth": "0.75.230",
62
62
  "@stacksjs/bun-router": "^0.1.24",
63
- "@stacksjs/config": "0.75.226",
64
- "@stacksjs/events": "0.75.226",
65
- "@stacksjs/i18n": "0.75.226",
66
- "@stacksjs/logging": "0.75.226",
67
- "@stacksjs/orm": "0.75.226",
68
- "@stacksjs/path": "0.75.226",
69
- "@stacksjs/payments": "0.75.226",
70
- "@stacksjs/router": "0.75.226",
71
- "@stacksjs/storage": "0.75.226",
63
+ "@stacksjs/config": "0.75.230",
64
+ "@stacksjs/events": "0.75.230",
65
+ "@stacksjs/i18n": "0.75.230",
66
+ "@stacksjs/logging": "0.75.230",
67
+ "@stacksjs/orm": "0.75.230",
68
+ "@stacksjs/path": "0.75.230",
69
+ "@stacksjs/payments": "0.75.230",
70
+ "@stacksjs/router": "0.75.230",
71
+ "@stacksjs/storage": "0.75.230",
72
72
  "bun-plugin-auto-imports": "^0.4.4"
73
73
  },
74
74
  "devDependencies": {
75
- "@stacksjs/validation": "0.75.226",
75
+ "@stacksjs/validation": "0.75.230",
76
76
  "better-dx": "^0.2.24"
77
77
  }
78
78
  }