@bractjs/bractjs 0.1.0 → 0.1.6
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/README.md +13 -13
- package/package.json +4 -1
- package/src/__tests__/action-handler.test.ts +47 -0
- package/src/__tests__/action-registry.test.ts +73 -0
- package/src/__tests__/codegen.test.ts +50 -0
- package/src/__tests__/deferred.test.ts +96 -0
- package/src/__tests__/directives.test.ts +52 -0
- package/src/__tests__/env.test.ts +73 -0
- package/src/__tests__/errors.test.ts +113 -0
- package/src/__tests__/hash.test.ts +19 -0
- package/src/__tests__/integration.test.ts +1 -1
- package/src/__tests__/manifest.test.ts +60 -0
- package/src/__tests__/middleware.test.ts +216 -0
- package/src/__tests__/response.test.ts +106 -0
- package/src/__tests__/security.test.ts +348 -0
- package/src/__tests__/session.test.ts +3 -3
- package/src/build/bundler.ts +15 -5
- package/src/build/directives.ts +30 -3
- package/src/build/env-plugin.ts +1 -0
- package/src/build/hash.ts +0 -20
- package/src/client/ClientRouter.tsx +8 -4
- package/src/codegen/route-codegen.ts +33 -9
- package/src/dev/hmr-module-handler.ts +14 -4
- package/src/image/cache.ts +28 -8
- package/src/image/handler.ts +26 -11
- package/src/image/optimizer.ts +45 -13
- package/src/image/types.ts +1 -0
- package/src/middleware/cors.ts +24 -8
- package/src/server/action-handler.ts +40 -1
- package/src/server/action-registry.ts +14 -1
- package/src/server/csrf.ts +16 -0
- package/src/server/env.ts +10 -4
- package/src/server/middleware.ts +11 -7
- package/src/server/render.ts +7 -5
- package/src/server/request-handler.ts +14 -13
- package/src/server/response.ts +29 -5
- package/src/server/scanner.ts +6 -2
- package/src/server/session.ts +16 -5
- package/src/server/static.ts +23 -7
- package/templates/new-app/app/root.tsx +1 -1
- package/templates/new-app/app/routes/_index.tsx +3 -3
- package/templates/new-app/app/routes/about.tsx +1 -1
- package/templates/new-app/bractjs.config.ts +1 -1
- package/templates/new-app/package.json +1 -1
package/src/server/static.ts
CHANGED
|
@@ -1,26 +1,42 @@
|
|
|
1
|
-
import { join, resolve } from "node:path";
|
|
1
|
+
import { join, resolve, sep } from "node:path";
|
|
2
|
+
import { realpath } from "node:fs/promises";
|
|
2
3
|
|
|
3
4
|
const IMMUTABLE = "public, max-age=31536000, immutable";
|
|
4
5
|
const NO_CACHE = "no-cache";
|
|
5
6
|
|
|
7
|
+
// Resolve to a canonical path that follows symlinks. Returns null if the
|
|
8
|
+
// target doesn't exist OR escapes the given root after symlink expansion.
|
|
9
|
+
async function safeRealpath(root: string, requested: string): Promise<string | null> {
|
|
10
|
+
const candidate = resolve(join(root, requested));
|
|
11
|
+
// Cheap structural reject before touching the FS.
|
|
12
|
+
if (!candidate.startsWith(root + sep) && candidate !== root) return null;
|
|
13
|
+
let real: string;
|
|
14
|
+
try {
|
|
15
|
+
real = await realpath(candidate);
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
if (!real.startsWith(root + sep) && real !== root) return null;
|
|
20
|
+
return real;
|
|
21
|
+
}
|
|
22
|
+
|
|
6
23
|
/**
|
|
7
24
|
* Serve hashed client assets or public/ files.
|
|
8
25
|
* Returns null if the path doesn't match or the file isn't found.
|
|
9
|
-
* Guards against path traversal
|
|
26
|
+
* Guards against path traversal AND symlink escape.
|
|
10
27
|
*/
|
|
11
28
|
export async function serveStatic(
|
|
12
29
|
pathname: string,
|
|
13
30
|
buildDir: string,
|
|
14
31
|
publicDir: string,
|
|
15
32
|
): Promise<Response | null> {
|
|
16
|
-
// Security: reject traversal sequences before any path resolution
|
|
17
33
|
if (pathname.includes("..")) return null;
|
|
18
34
|
|
|
19
35
|
if (pathname.startsWith("/build/client/")) {
|
|
20
36
|
const rel = pathname.slice("/build/client/".length);
|
|
21
37
|
const root = resolve(join(buildDir, "client"));
|
|
22
|
-
const full =
|
|
23
|
-
if (!full
|
|
38
|
+
const full = await safeRealpath(root, rel);
|
|
39
|
+
if (!full) return null;
|
|
24
40
|
const file = Bun.file(full);
|
|
25
41
|
if (!(await file.exists())) return null;
|
|
26
42
|
return new Response(file, { headers: { "Cache-Control": IMMUTABLE } });
|
|
@@ -29,8 +45,8 @@ export async function serveStatic(
|
|
|
29
45
|
if (pathname.startsWith("/public/")) {
|
|
30
46
|
const rel = pathname.slice("/public/".length);
|
|
31
47
|
const root = resolve(publicDir);
|
|
32
|
-
const full =
|
|
33
|
-
if (!full
|
|
48
|
+
const full = await safeRealpath(root, rel);
|
|
49
|
+
if (!full) return null;
|
|
34
50
|
const file = Bun.file(full);
|
|
35
51
|
if (!(await file.exists())) return null;
|
|
36
52
|
return new Response(file, { headers: { "Cache-Control": NO_CACHE } });
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { useLoaderData } from "bractjs";
|
|
2
|
-
import type { LoaderArgs } from "bractjs";
|
|
3
|
-
import { Link } from "bractjs";
|
|
1
|
+
import { useLoaderData } from "@bractjs/bractjs";
|
|
2
|
+
import type { LoaderArgs } from "@bractjs/bractjs";
|
|
3
|
+
import { Link } from "@bractjs/bractjs";
|
|
4
4
|
|
|
5
5
|
interface HomeData {
|
|
6
6
|
message: string;
|