@stacksjs/defaults 0.70.277 → 0.70.279
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/app/Middleware/Csrf.ts
CHANGED
|
@@ -97,6 +97,28 @@ export function generateCsrfToken(): string {
|
|
|
97
97
|
* - 2-hour `Max-Age` — short enough to limit replay if leaked, long
|
|
98
98
|
* enough that idle tabs don't constantly re-fetch tokens
|
|
99
99
|
*/
|
|
100
|
+
/**
|
|
101
|
+
* Whether a response is already handing the browser a CSRF token.
|
|
102
|
+
*
|
|
103
|
+
* `getSetCookie` is the only way to read multiple `Set-Cookie` headers back;
|
|
104
|
+
* `get` joins them into one string, which is not something a cookie value can
|
|
105
|
+
* be parsed out of reliably. Older runtimes without it fall back to the joined
|
|
106
|
+
* form, where a substring match is still correct for the question being asked.
|
|
107
|
+
*/
|
|
108
|
+
function responseAlreadySeeds(response: Response): boolean {
|
|
109
|
+
const headers = response.headers as Headers & { getSetCookie?: () => string[] }
|
|
110
|
+
const cookies = typeof headers.getSetCookie === 'function'
|
|
111
|
+
? headers.getSetCookie()
|
|
112
|
+
: [headers.get('set-cookie') || '']
|
|
113
|
+
|
|
114
|
+
return cookies.some(cookie =>
|
|
115
|
+
cookie.startsWith(`${CSRF_COOKIE_NAME}=`)
|
|
116
|
+
|| cookie.startsWith('csrf-token=')
|
|
117
|
+
|| cookie.includes(`, ${CSRF_COOKIE_NAME}=`)
|
|
118
|
+
|| cookie.includes(', csrf-token='),
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
|
|
100
122
|
export function seedCsrfCookieIfMissing(req: Request, response: Response, minted?: string): Response {
|
|
101
123
|
const cookieHeader = req.headers.get('cookie') || ''
|
|
102
124
|
|
|
@@ -109,6 +131,15 @@ export function seedCsrfCookieIfMissing(req: Request, response: Response, minted
|
|
|
109
131
|
return response
|
|
110
132
|
}
|
|
111
133
|
|
|
134
|
+
// A token already on its way to the browser counts as present, exactly like
|
|
135
|
+
// one in the request. Something upstream can mint before the render - the
|
|
136
|
+
// stx dev server does, so a page's forms carry a token on a first visit -
|
|
137
|
+
// and appending a second here would leave the browser storing the last
|
|
138
|
+
// Set-Cookie while the page embedded the first. Two tokens fail the same way
|
|
139
|
+
// no token does, and are far harder to see.
|
|
140
|
+
if (responseAlreadySeeds(response))
|
|
141
|
+
return response
|
|
142
|
+
|
|
112
143
|
const token = minted || generateCsrfToken()
|
|
113
144
|
const isSecure = req.url.startsWith('https://')
|
|
114
145
|
const cookie = [
|
|
@@ -32,6 +32,7 @@ export default defineModel({
|
|
|
32
32
|
},
|
|
33
33
|
|
|
34
34
|
channel: {
|
|
35
|
+
type: 'string',
|
|
35
36
|
required: true,
|
|
36
37
|
fillable: true,
|
|
37
38
|
validation: {
|
|
@@ -64,6 +65,7 @@ export default defineModel({
|
|
|
64
65
|
},
|
|
65
66
|
|
|
66
67
|
status: {
|
|
68
|
+
type: 'string',
|
|
67
69
|
required: true,
|
|
68
70
|
fillable: true,
|
|
69
71
|
default: 'pending',
|
|
@@ -81,6 +83,7 @@ export default defineModel({
|
|
|
81
83
|
},
|
|
82
84
|
|
|
83
85
|
metadata: {
|
|
86
|
+
type: 'text',
|
|
84
87
|
required: false,
|
|
85
88
|
fillable: true,
|
|
86
89
|
validation: {
|
package/ide/vscode/package.json
CHANGED
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/defaults",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.70.
|
|
5
|
+
"version": "0.70.279",
|
|
6
6
|
"description": "The complete managed Stacks application scaffold, including runtime defaults, AI guidance, editor metadata, and npm-backed project support files.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"license": "MIT",
|