bosia 0.1.3 → 0.1.4

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bosia",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "type": "module",
5
5
  "description": "A fast, batteries-included fullstack framework — SSR · Svelte 5 Runes · Bun · ElysiaJS. File-based routing inspired by SvelteKit. No Node.js, no Vite, no adapters.",
6
6
  "keywords": [
@@ -8,8 +8,55 @@ export class HttpError extends Error {
8
8
  }
9
9
  }
10
10
 
11
+ export interface RedirectOptions {
12
+ /** Set to `true` to allow redirects to external origins (e.g. OAuth providers). */
13
+ allowExternal?: boolean;
14
+ }
15
+
11
16
  export class Redirect {
12
- constructor(public status: number, public location: string) {}
17
+ constructor(
18
+ public status: number,
19
+ public location: string,
20
+ options?: RedirectOptions,
21
+ ) {
22
+ validateRedirectLocation(location, options);
23
+ }
24
+ }
25
+
26
+ const DANGEROUS_SCHEMES = /^(javascript|data|vbscript):/i;
27
+
28
+ function validateRedirectLocation(
29
+ location: string,
30
+ options?: RedirectOptions,
31
+ ): void {
32
+ if (options?.allowExternal) return;
33
+
34
+ const trimmed = location.trim();
35
+
36
+ // Reject dangerous schemes
37
+ if (DANGEROUS_SCHEMES.test(trimmed)) {
38
+ throw new Error(
39
+ `redirect(): dangerous scheme in URL "${location}". ` +
40
+ `Only relative paths and same-origin URLs are allowed.`,
41
+ );
42
+ }
43
+
44
+ // Reject protocol-relative URLs (//evil.com)
45
+ if (trimmed.startsWith("//")) {
46
+ throw new Error(
47
+ `redirect(): protocol-relative URLs like "${location}" are not allowed. ` +
48
+ `Use a relative path or pass { allowExternal: true } for external redirects.`,
49
+ );
50
+ }
51
+
52
+ // Allow relative paths (no scheme)
53
+ if (!/^[a-zA-Z][a-zA-Z0-9+\-.]*:/.test(trimmed)) return;
54
+
55
+ // It's an absolute URL — reject external origins
56
+ throw new Error(
57
+ `redirect(): external URL "${location}" is not allowed. ` +
58
+ `Use a relative path or pass { allowExternal: true } for external redirects.`,
59
+ );
13
60
  }
14
61
 
15
62
  /** Throw an HTTP error from a load() function. */
@@ -18,8 +65,12 @@ export function error(status: number, message: string): never {
18
65
  }
19
66
 
20
67
  /** Redirect the user from a load() function. */
21
- export function redirect(status: number, location: string): never {
22
- throw new Redirect(status, location);
68
+ export function redirect(
69
+ status: number,
70
+ location: string,
71
+ options?: RedirectOptions,
72
+ ): never {
73
+ throw new Redirect(status, location, options);
23
74
  }
24
75
 
25
76
  // ─── Form Action Helpers ─────────────────────────────────
package/src/lib/index.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  export { cn, getServerTime } from "./utils.ts";
7
7
  export { sequence } from "../core/hooks.ts";
8
8
  export { error, redirect, fail } from "../core/errors.ts";
9
- export type { HttpError, Redirect, ActionFailure } from "../core/errors.ts";
9
+ export type { HttpError, Redirect, RedirectOptions, ActionFailure } from "../core/errors.ts";
10
10
  export type {
11
11
  RequestEvent,
12
12
  LoadEvent,