@swissjs/swite 0.3.5 → 0.4.2

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 (78) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/DIRECTIVE.md +57 -2
  3. package/__tests__/import-rewriter-bug.test.ts +100 -113
  4. package/__tests__/security-r001-r002.test.ts +190 -0
  5. package/dist/build-engine/builder.js +9 -9
  6. package/dist/cli.js +0 -0
  7. package/dist/config/config.d.ts +0 -5
  8. package/dist/config/config.d.ts.map +1 -1
  9. package/dist/dev-engine/handlers/base-handler.d.ts +6 -0
  10. package/dist/dev-engine/handlers/base-handler.d.ts.map +1 -1
  11. package/dist/dev-engine/handlers/base-handler.js +91 -0
  12. package/dist/dev-engine/handlers/ui-handler.d.ts +0 -1
  13. package/dist/dev-engine/handlers/ui-handler.d.ts.map +1 -1
  14. package/dist/dev-engine/handlers/ui-handler.js +2 -64
  15. package/dist/dev-engine/handlers/uix-handler.d.ts +0 -1
  16. package/dist/dev-engine/handlers/uix-handler.d.ts.map +1 -1
  17. package/dist/dev-engine/handlers/uix-handler.js +2 -58
  18. package/dist/dev-engine/hmr/hmr-client-template.js +111 -111
  19. package/dist/dev-engine/hmr/hmr.d.ts +10 -1
  20. package/dist/dev-engine/hmr/hmr.d.ts.map +1 -1
  21. package/dist/dev-engine/hmr/hmr.js +40 -2
  22. package/dist/dev-engine/middleware/middleware-setup.js +4 -3
  23. package/dist/dev-engine/middleware/static-files.d.ts.map +1 -1
  24. package/dist/dev-engine/middleware/static-files.js +145 -62
  25. package/dist/dev-engine/pythonDevManager.js +1 -1
  26. package/dist/dev-engine/router/file-router.d.ts.map +1 -1
  27. package/dist/dev-engine/router/file-router.js +2 -29
  28. package/dist/dev-engine/server.d.ts +7 -0
  29. package/dist/dev-engine/server.d.ts.map +1 -1
  30. package/dist/dev-engine/server.js +31 -3
  31. package/dist/kernel/package-finder.d.ts +0 -8
  32. package/dist/kernel/package-finder.d.ts.map +1 -1
  33. package/dist/kernel/package-finder.js +2 -2
  34. package/dist/kernel/package-registry.d.ts +6 -0
  35. package/dist/kernel/package-registry.d.ts.map +1 -1
  36. package/dist/kernel/package-registry.js +8 -0
  37. package/dist/kernel/workspace.d.ts.map +1 -1
  38. package/dist/kernel/workspace.js +12 -9
  39. package/docs/architecture/build-pipeline.md +97 -97
  40. package/docs/architecture/dev-server.md +87 -87
  41. package/docs/architecture/hmr.md +78 -78
  42. package/docs/architecture/import-rewriting.md +101 -101
  43. package/docs/architecture/index.md +16 -16
  44. package/docs/architecture/python-integration.md +93 -93
  45. package/docs/architecture/resolution.md +92 -92
  46. package/docs/cli/build.md +78 -78
  47. package/docs/cli/dev.md +90 -90
  48. package/docs/cli/index.md +15 -15
  49. package/docs/cli/start.md +45 -45
  50. package/docs/development/contributing.md +74 -74
  51. package/docs/development/index.md +12 -12
  52. package/docs/development/internals.md +101 -101
  53. package/docs/guide/configuration.md +89 -89
  54. package/docs/guide/index.md +13 -13
  55. package/docs/guide/project-structure.md +75 -75
  56. package/docs/guide/quickstart.md +113 -113
  57. package/docs/index.md +16 -16
  58. package/package.json +29 -16
  59. package/src/build-engine/builder.ts +9 -9
  60. package/src/config/config.ts +0 -5
  61. package/src/config/env.ts +98 -98
  62. package/src/dev-engine/handlers/base-handler.ts +109 -0
  63. package/src/dev-engine/handlers/ui-handler.ts +30 -110
  64. package/src/dev-engine/handlers/uix-handler.ts +21 -95
  65. package/src/dev-engine/hmr/hmr-client-template.ts +122 -122
  66. package/src/dev-engine/hmr/hmr.ts +46 -1
  67. package/src/dev-engine/middleware/middleware-setup.ts +354 -354
  68. package/src/dev-engine/middleware/static-files.ts +203 -121
  69. package/src/dev-engine/pythonDevManager.ts +1 -1
  70. package/src/dev-engine/router/file-router.ts +2 -45
  71. package/src/dev-engine/server.ts +33 -3
  72. package/src/kernel/package-finder.ts +2 -2
  73. package/src/kernel/package-registry.ts +9 -0
  74. package/src/kernel/workspace.ts +8 -10
  75. package/src/resolution/cdn/cdn-fallback.ts +40 -40
  76. package/src/resolution/path/path-fixup.ts +27 -27
  77. package/src/resolution/rewriting/import-rewriter.ts +237 -237
  78. package/src/resolution/symlink-registry.ts +114 -114
@@ -13,16 +13,42 @@ export class HMREngine {
13
13
  private watcher?: chokidar.FSWatcher;
14
14
  private clients = new Set<WebSocket>();
15
15
  private port: number;
16
+ /** Origins permitted to connect (e.g. "http://localhost:3000"). */
17
+ private allowedOrigins: Set<string>;
16
18
 
17
19
  constructor(
18
20
  private root: string,
19
21
  hmrPort?: number,
22
+ allowedOrigins: string[] = [],
20
23
  ) {
21
24
  this.port = hmrPort || 24678;
25
+ // Security (R-002): build an origin allowlist.
26
+ // Always allow the two canonical loopback forms so a default dev setup
27
+ // (host: "localhost", port: 3000) works without any extra config.
28
+ this.allowedOrigins = new Set([
29
+ ...allowedOrigins,
30
+ ]);
22
31
  // WebSocketServer will be created in initialize() method
23
32
  // This allows async port checking before server creation
24
33
  }
25
34
 
35
+ /**
36
+ * Return true when `origin` is on the allowlist.
37
+ * - Absent / empty origin header → REJECT (not a browser page request).
38
+ * - Exact match (scheme + host + optional port) → ALLOW.
39
+ * - The check is case-insensitive on the scheme+host portion per RFC 6454.
40
+ */
41
+ private isOriginAllowed(origin: string | undefined): boolean {
42
+ if (!origin) return false;
43
+ // Normalise: strip trailing slash, lower-case scheme+host.
44
+ const normalise = (o: string) => o.replace(/\/$/, "").toLowerCase();
45
+ const candidate = normalise(origin);
46
+ for (const allowed of this.allowedOrigins) {
47
+ if (normalise(allowed) === candidate) return true;
48
+ }
49
+ return false;
50
+ }
51
+
26
52
  async initialize(): Promise<void> {
27
53
  // Check if port is available, if not find a free one
28
54
  const isAvailable = await this.checkPortAvailable(this.port);
@@ -51,7 +77,26 @@ export class HMREngine {
51
77
  }
52
78
 
53
79
  private setupWebSocket() {
54
- this.wss.on("connection", (ws) => {
80
+ // Security (R-002): validate the Origin header on every incoming WebSocket
81
+ // upgrade to prevent cross-site WebSocket hijacking. A malicious page
82
+ // served from a different origin cannot subscribe to HMR events (which
83
+ // include absolute filesystem paths of every changed file).
84
+ //
85
+ // Connections with a missing or non-allowlisted Origin are rejected with
86
+ // a 403 close frame. Same-origin connections from the dev server's own
87
+ // host:port are always allowed via this.allowedOrigins.
88
+ this.wss.on("connection", (ws, req) => {
89
+ const origin = req.headers["origin"];
90
+ if (!this.isOriginAllowed(origin)) {
91
+ console.warn(
92
+ chalk.red(
93
+ `[HMR] Rejected connection from disallowed origin: ${origin ?? "(none)"}`,
94
+ ),
95
+ );
96
+ ws.close(1008, "Origin not allowed");
97
+ return;
98
+ }
99
+
55
100
  this.clients.add(ws);
56
101
  console.log(chalk.green("[HMR] Client connected"));
57
102