@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.
Files changed (44) hide show
  1. package/README.md +13 -13
  2. package/package.json +4 -1
  3. package/src/__tests__/action-handler.test.ts +47 -0
  4. package/src/__tests__/action-registry.test.ts +73 -0
  5. package/src/__tests__/codegen.test.ts +50 -0
  6. package/src/__tests__/deferred.test.ts +96 -0
  7. package/src/__tests__/directives.test.ts +52 -0
  8. package/src/__tests__/env.test.ts +73 -0
  9. package/src/__tests__/errors.test.ts +113 -0
  10. package/src/__tests__/hash.test.ts +19 -0
  11. package/src/__tests__/integration.test.ts +1 -1
  12. package/src/__tests__/manifest.test.ts +60 -0
  13. package/src/__tests__/middleware.test.ts +216 -0
  14. package/src/__tests__/response.test.ts +106 -0
  15. package/src/__tests__/security.test.ts +348 -0
  16. package/src/__tests__/session.test.ts +3 -3
  17. package/src/build/bundler.ts +15 -5
  18. package/src/build/directives.ts +30 -3
  19. package/src/build/env-plugin.ts +1 -0
  20. package/src/build/hash.ts +0 -20
  21. package/src/client/ClientRouter.tsx +8 -4
  22. package/src/codegen/route-codegen.ts +33 -9
  23. package/src/dev/hmr-module-handler.ts +14 -4
  24. package/src/image/cache.ts +28 -8
  25. package/src/image/handler.ts +26 -11
  26. package/src/image/optimizer.ts +45 -13
  27. package/src/image/types.ts +1 -0
  28. package/src/middleware/cors.ts +24 -8
  29. package/src/server/action-handler.ts +40 -1
  30. package/src/server/action-registry.ts +14 -1
  31. package/src/server/csrf.ts +16 -0
  32. package/src/server/env.ts +10 -4
  33. package/src/server/middleware.ts +11 -7
  34. package/src/server/render.ts +7 -5
  35. package/src/server/request-handler.ts +14 -13
  36. package/src/server/response.ts +29 -5
  37. package/src/server/scanner.ts +6 -2
  38. package/src/server/session.ts +16 -5
  39. package/src/server/static.ts +23 -7
  40. package/templates/new-app/app/root.tsx +1 -1
  41. package/templates/new-app/app/routes/_index.tsx +3 -3
  42. package/templates/new-app/app/routes/about.tsx +1 -1
  43. package/templates/new-app/bractjs.config.ts +1 -1
  44. package/templates/new-app/package.json +1 -1
@@ -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 by resolving and prefix-checking.
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 = resolve(join(root, rel));
23
- if (!full.startsWith(root + "/") && full !== root) return null;
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 = resolve(join(root, rel));
33
- if (!full.startsWith(root + "/") && full !== root) return null;
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
1
  // This is the root layout for your BractJS app.
2
2
  // Every route renders inside this component.
3
- import { Scripts, LiveReload, Outlet } from "bractjs";
3
+ import { Scripts, LiveReload, Outlet } from "@bractjs/bractjs";
4
4
 
5
5
  export default function Root() {
6
6
  return (
@@ -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;
@@ -1,4 +1,4 @@
1
- import { Link } from "bractjs";
1
+ import { Link } from "@bractjs/bractjs";
2
2
 
3
3
  export function meta() {
4
4
  return [{ title: "About | {{APP_NAME}}" }];
@@ -1,4 +1,4 @@
1
- import type { BractJSConfig } from "bractjs";
1
+ import type { BractJSConfig } from "@bractjs/bractjs";
2
2
 
3
3
  const config: BractJSConfig = {
4
4
  port: 3000,
@@ -8,7 +8,7 @@
8
8
  "start": "bractjs start"
9
9
  },
10
10
  "dependencies": {
11
- "bractjs": "file:{{BRACT_PATH}}",
11
+ "@bractjs/bractjs": "latest",
12
12
  "react": "^19",
13
13
  "react-dom": "^19"
14
14
  },