davaux 0.8.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 +6 -2
- package/BASELINE.md +0 -169
- package/CLAUDE.md +0 -518
- package/ROADMAP.md +0 -198
- package/build.mjs +0 -101
- package/client/control.ts +0 -247
- package/client/hydrate.ts +0 -37
- package/client/index.ts +0 -19
- package/client/jsx-runtime.ts +0 -209
- package/client/resource.ts +0 -122
- package/client/signal.ts +0 -211
- package/client/store.ts +0 -110
- package/client/useHead.ts +0 -63
- package/pka.config.json +0 -32
- package/src/build/config.ts +0 -42
- package/src/build/index.ts +0 -6
- package/src/build/plugins.ts +0 -118
- package/src/cli.ts +0 -502
- package/src/config.ts +0 -197
- package/src/create-multisite.ts +0 -310
- package/src/create.ts +0 -194
- package/src/dev/blueprints.ts +0 -75
- package/src/dev/components.ts +0 -108
- package/src/dev/insert.ts +0 -221
- package/src/dev/remove.ts +0 -677
- package/src/dev/watch.ts +0 -3098
- package/src/errors.ts +0 -64
- package/src/generate.ts +0 -228
- package/src/index.ts +0 -67
- package/src/island.ts +0 -47
- package/src/jsx-runtime.d.ts +0 -408
- package/src/jsx-runtime.d.ts.map +0 -1
- package/src/jsx-runtime.ts +0 -536
- package/src/link.ts +0 -49
- package/src/oml/fragment.ts +0 -54
- package/src/oml/index.ts +0 -21
- package/src/oml/jsx-runtime.ts +0 -121
- package/src/oml/jsx.ts +0 -151
- package/src/oml/page.ts +0 -13
- package/src/oml/render.ts +0 -181
- package/src/oml/types.ts +0 -159
- package/src/router/handler.ts +0 -515
- package/src/router/matcher.ts +0 -52
- package/src/router/scanner.ts +0 -272
- package/src/server/index.ts +0 -49
- package/src/signal.ts +0 -39
- package/src/ssg.ts +0 -253
- package/src/test/actions.test.ts +0 -40
- package/src/test/body-limits.test.ts +0 -83
- package/src/test/errors.test.ts +0 -53
- package/src/test/fixtures/routes/[id].page.ts +0 -3
- package/src/test/fixtures/routes/_error.ts +0 -6
- package/src/test/fixtures/routes/_global.ts +0 -8
- package/src/test/fixtures/routes/_layout-template.ts +0 -7
- package/src/test/fixtures/routes/_layout.ts +0 -7
- package/src/test/fixtures/routes/_layout_scripts.ts +0 -8
- package/src/test/fixtures/routes/_middleware.ts +0 -8
- package/src/test/fixtures/routes/_redirect301_mw.ts +0 -5
- package/src/test/fixtures/routes/_redirect_mw.ts +0 -5
- package/src/test/fixtures/routes/about.page.ts +0 -6
- package/src/test/fixtures/routes/action.page.ts +0 -11
- package/src/test/fixtures/routes/api/form-all.post.ts +0 -5
- package/src/test/fixtures/routes/api/form-limited.post.ts +0 -6
- package/src/test/fixtures/routes/api/response-obj.get.ts +0 -17
- package/src/test/fixtures/routes/api/upload.post.ts +0 -14
- package/src/test/fixtures/routes/api/users.get.ts +0 -3
- package/src/test/fixtures/routes/api/xml.get.ts +0 -5
- package/src/test/fixtures/routes/auth/_middleware.ts +0 -11
- package/src/test/fixtures/routes/auth/protected.page.ts +0 -3
- package/src/test/fixtures/routes/index.page.ts +0 -3
- package/src/test/fixtures/routes/oml.page.ts +0 -7
- package/src/test/fixtures/routes/redirect.page.ts +0 -3
- package/src/test/fixtures/routes/ssg/[slug].page.ts +0 -8
- package/src/test/fixtures/routes/ssg/server.page.ts +0 -5
- package/src/test/fixtures/routes/state.page.ts +0 -4
- package/src/test/fixtures/routes/throw.page.ts +0 -5
- package/src/test/fixtures/routes/wiki/[...slug].page.ts +0 -3
- package/src/test/helpers.ts +0 -132
- package/src/test/layouts.test.ts +0 -76
- package/src/test/middleware.test.ts +0 -69
- package/src/test/multipart.test.ts +0 -91
- package/src/test/oml-routing.test.ts +0 -59
- package/src/test/oml.test.ts +0 -429
- package/src/test/redirects.test.ts +0 -32
- package/src/test/routing.test.ts +0 -118
- package/src/test/ssg.test.ts +0 -273
- package/src/test/web-response.test.ts +0 -33
- package/src/types.ts +0 -670
- package/tsconfig.client.json +0 -17
- package/tsconfig.json +0 -20
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "davaux",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"description": "SSR-first JSX framework with file-based routing and signals",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "David L Dyess II",
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
"url": "https://codeberg.org/davaux/davaux/issues"
|
|
14
14
|
},
|
|
15
15
|
"homepage": "https://codeberg.org/davaux/davaux#readme",
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"src/env.d.ts"
|
|
19
|
+
],
|
|
16
20
|
"exports": {
|
|
17
21
|
".": {
|
|
18
22
|
"import": "./dist/index.js",
|
|
@@ -100,4 +104,4 @@
|
|
|
100
104
|
"tsx": "^4.19.2",
|
|
101
105
|
"typescript": "^6.0.3"
|
|
102
106
|
}
|
|
103
|
-
}
|
|
107
|
+
}
|
package/BASELINE.md
DELETED
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
# Davaux — Security Review
|
|
2
|
-
|
|
3
|
-
Findings from a full audit of the framework and all `@davaux/*` packages. Grouped by severity. Each entry tracks the file, the issue, the fix, and current status.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## High
|
|
8
|
-
|
|
9
|
-
### H1 — Rate Limiter: Unconditional Trust of `X-Forwarded-For` ✅ fixed
|
|
10
|
-
|
|
11
|
-
**File:** `packages/rate-limit/src/index.ts`
|
|
12
|
-
|
|
13
|
-
The default key function reads `X-Forwarded-For` without any trust model. Any client can set this header to an arbitrary value — including `127.0.0.1` or a legitimate user's IP — bypassing rate limiting entirely.
|
|
14
|
-
|
|
15
|
-
**Fix:** Default to `req.socket.remoteAddress`. Add a `trustProxy?: boolean | number` option that, when enabled, extracts the correct leftmost-untrusted IP from the `X-Forwarded-For` chain. Document that `trustProxy` should only be enabled when the app runs behind a trusted reverse proxy.
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
### H2 — CSRF: `multipart/form-data` Token Not Read from Form Fields ✅ fixed
|
|
20
|
-
|
|
21
|
-
**File:** `packages/csrf/src/index.ts`
|
|
22
|
-
|
|
23
|
-
`resolveSubmitted()` only reads the CSRF token from `application/x-www-form-urlencoded` body fields. For `multipart/form-data` requests, it falls back to the `X-CSRF-Token` header only — which browsers cannot set on a standard form submit. A developer building a file-upload form who embeds the token as a hidden field will find it silently not enforced, and may remove the middleware believing it is broken.
|
|
24
|
-
|
|
25
|
-
**Fix:** Add a `multipart/form-data` branch in `resolveSubmitted()` that calls `ctx.multipart()` and reads `fields[field]`.
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## Medium
|
|
30
|
-
|
|
31
|
-
### M1 — Island Serialization: `<` and `>` Not Escaped in `data-props` ✅ fixed
|
|
32
|
-
|
|
33
|
-
**File:** `packages/davaux/src/island.ts`
|
|
34
|
-
|
|
35
|
-
```ts
|
|
36
|
-
const serialized = JSON.stringify(props).replace(/'/g, ''')
|
|
37
|
-
return `<div data-island="${id}" data-props='${serialized}'>${rendered}</div>`
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
`JSON.stringify` does not escape `<`, `>`, or `&`. A prop value containing `</div>` produces unescaped angle brackets inside the HTML attribute. Single-quote breakout is prevented, but the unescaped characters break HTML parsers and become exploitable if the context ever changes (e.g., inside a `<script>` or `<template>` element).
|
|
41
|
-
|
|
42
|
-
**Fix:** Use Unicode escapes to keep the JSON parseable while making it safe in any HTML context:
|
|
43
|
-
|
|
44
|
-
```ts
|
|
45
|
-
const serialized = JSON.stringify(props)
|
|
46
|
-
.replace(/&/g, '\\u0026')
|
|
47
|
-
.replace(/</g, '\\u003c')
|
|
48
|
-
.replace(/>/g, '\\u003e')
|
|
49
|
-
.replace(/'/g, '\\u0027')
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
---
|
|
53
|
-
|
|
54
|
-
### M2 — Swagger CDN Assets Served Without Subresource Integrity ✅ fixed
|
|
55
|
-
|
|
56
|
-
**File:** `packages/swagger/src/index.ts`
|
|
57
|
-
|
|
58
|
-
The Swagger UI CSS and JS bundle are loaded from `cdn.jsdelivr.net/npm/swagger-ui-dist@5` with no `integrity` or `crossorigin` attributes. A CDN compromise or supply-chain attack on the `swagger-ui-dist` package would serve malicious JavaScript to anyone visiting `/docs`. The `@5` semver range means any `5.x` release is automatically served.
|
|
59
|
-
|
|
60
|
-
**Fix:** Pin to a specific version and add SRI `integrity` hashes. Regenerate hashes on upgrade.
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
### M3 — Session: No Minimum Secret Length Enforcement ✅ fixed
|
|
65
|
-
|
|
66
|
-
**File:** `packages/session/src/index.ts`
|
|
67
|
-
|
|
68
|
-
Only empty strings are rejected. A single-character secret like `"x"` passes through silently. Short secrets dramatically reduce the brute-force search space for forging signed session cookies.
|
|
69
|
-
|
|
70
|
-
**Fix:** Emit a `console.warn` when any secret is shorter than 32 characters. A throw is acceptable during development (`NODE_ENV !== 'production'`).
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
### M4 — Static File Serving: No Explicit Path Traversal Guard in `runStart` and Dev Server ✅ fixed
|
|
75
|
-
|
|
76
|
-
**Files:** `packages/davaux/src/cli.ts` (`runStart`), `packages/davaux/src/dev/watch.ts` (`serveStatic`)
|
|
77
|
-
|
|
78
|
-
Both rely solely on `new URL(req.url, 'http://x').pathname` to normalize request paths before `join(dir, pathname)`. This is correct in current Node.js, but is a single point of failure — no belt-and-suspenders check exists. `runPreview` already has the correct explicit guard:
|
|
79
|
-
|
|
80
|
-
```ts
|
|
81
|
-
if (!safePath.startsWith(`${outDir}/`) && safePath !== outDir) {
|
|
82
|
-
res.writeHead(403, ...); return
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
**Fix:** Apply the same `startsWith` guard to the public-directory handler in `runStart` and to `serveStatic` in `watch.ts`.
|
|
87
|
-
|
|
88
|
-
---
|
|
89
|
-
|
|
90
|
-
## Low
|
|
91
|
-
|
|
92
|
-
### L1 — Swagger: HTML `<title>` Inserted Without Escaping ✅ fixed
|
|
93
|
-
|
|
94
|
-
**File:** `packages/swagger/src/index.ts`
|
|
95
|
-
|
|
96
|
-
`options.info?.title` is inserted directly into `<title>${title}</title>` with no HTML escaping. If the title originates from a database or environment variable, a value like `</title><script>alert(1)</script>` produces reflected XSS on the `/docs` page.
|
|
97
|
-
|
|
98
|
-
**Fix:** HTML-escape the title before insertion. `specPath` is already handled correctly via `JSON.stringify`.
|
|
99
|
-
|
|
100
|
-
---
|
|
101
|
-
|
|
102
|
-
### L2 — `CookieJar`: `path` and `domain` Options Not Sanitized ✅ fixed
|
|
103
|
-
|
|
104
|
-
**File:** `packages/davaux/src/types.ts`
|
|
105
|
-
|
|
106
|
-
`name` and `value` are correctly URI-encoded, but `path` and `domain` are inserted into the `Set-Cookie` header without sanitization. Node.js rejects CRLF characters (blocking header splitting), but a semicolon in `path` or `domain` injects additional cookie attributes:
|
|
107
|
-
|
|
108
|
-
```ts
|
|
109
|
-
ctx.cookies.set('session', value, { path: '/; SameSite=None' })
|
|
110
|
-
// → Set-Cookie: session=value; Path=/; SameSite=None
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
Values come from developer configuration rather than user input directly, but the API makes it easy to pass user-controlled values through accidentally.
|
|
114
|
-
|
|
115
|
-
**Fix:** Strip semicolons, carriage returns, and newlines from `path` and `domain` before constructing the header value.
|
|
116
|
-
|
|
117
|
-
---
|
|
118
|
-
|
|
119
|
-
## Informational
|
|
120
|
-
|
|
121
|
-
### I1 — CORS: No Error on `credentials: true` + `origin: '*'` ✅ fixed
|
|
122
|
-
|
|
123
|
-
**File:** `packages/cors/src/index.ts`
|
|
124
|
-
|
|
125
|
-
Browsers reject credentialed responses with a wildcard origin by spec, so no credentials actually leak. However, the middleware silently accepts this misconfiguration rather than throwing, leading to hard-to-debug CORS failures.
|
|
126
|
-
|
|
127
|
-
**Fix:** Throw at middleware creation time:
|
|
128
|
-
```ts
|
|
129
|
-
if (options.credentials && options.origin === '*') {
|
|
130
|
-
throw new Error('@davaux/cors: credentials:true requires a specific origin, not "*"')
|
|
131
|
-
}
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
---
|
|
135
|
-
|
|
136
|
-
### I2 — Body Cache: First-Read `maxBytes` Applies to All Subsequent Reads ℹ️ by design
|
|
137
|
-
|
|
138
|
-
**File:** `packages/davaux/src/types.ts`
|
|
139
|
-
|
|
140
|
-
The body is read and cached on the first call to `ctx.form()`, `ctx.json()`, or `ctx.multipart()`. The `maxBytes` limit from the first call is the effective limit for all subsequent calls, regardless of what later callers specify. A middleware reading the body at the global default could allow more data than a per-route limit intended — or less.
|
|
141
|
-
|
|
142
|
-
This is a documented tradeoff (the body stream can only be read once), not a bug. Worth noting in the API docs.
|
|
143
|
-
|
|
144
|
-
---
|
|
145
|
-
|
|
146
|
-
### I3 — `redirect()`: No Guard Against Open Redirects ℹ️ doc needed
|
|
147
|
-
|
|
148
|
-
**File:** `packages/davaux/src/types.ts`
|
|
149
|
-
|
|
150
|
-
`redirect(url)` passes the URL directly to the `Location` header with no validation. If a developer passes unvalidated user input (e.g., `redirect(ctx.query.get('next'))`), this is an open redirect. The framework cannot prevent this without breaking legitimate uses.
|
|
151
|
-
|
|
152
|
-
**Fix:** Add a JSDoc warning that user-supplied URLs must be validated before passing to `redirect()`.
|
|
153
|
-
|
|
154
|
-
---
|
|
155
|
-
|
|
156
|
-
## What Was Reviewed and Found Correct
|
|
157
|
-
|
|
158
|
-
- **JSX runtime `escapeHtml`** — escapes `&`, `<`, `>`, `"`, `'`. Applied to all string children and attribute values. `dangerouslySetInnerHTML` correctly bypasses escaping as intended.
|
|
159
|
-
- **Session HMAC signing** — `createHmac('sha256')`, `timingSafeEqual` for verification, `base64url` encoding, `lastIndexOf('.')` split. Correct and complete.
|
|
160
|
-
- **CSRF token generation** — `randomBytes(32).toString('hex')`, 256 bits of entropy.
|
|
161
|
-
- **CSRF timing-safe comparison** — `timingSafeEqual` used after length check.
|
|
162
|
-
- **Session cookie flags** — `httpOnly: true`, `sameSite: 'Lax'` by default.
|
|
163
|
-
- **Path traversal in `runPreview`** — explicit `startsWith` guard present and correct.
|
|
164
|
-
- **URL normalization** — `new URL(req.url, 'http://x').pathname` correctly resolves `..` sequences. `/%2e%2e/` is resolved before `join`.
|
|
165
|
-
- **CORS origin reflection** — no unintended origin reflection when origin is not in allowlist.
|
|
166
|
-
- **SSE injection** — `JSON.stringify` escapes newlines, preventing event injection via error payloads.
|
|
167
|
-
- **Scanner dynamic imports** — no user input flows into import targets.
|
|
168
|
-
- **Prototype pollution** — `Object.assign(ctx.params, result.params)` assigns string values only; `JSON.parse` does not pollute `Object.prototype` in this usage.
|
|
169
|
-
- **Multipart parser** — boundary used as a literal buffer, not a regex. Body size limit enforced before parsing.
|