@tschk/moonshine-deploy-node 0.3.2 → 0.3.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 +6 -6
- package/src/index.ts +20 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tschk/moonshine-deploy-node",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
"test": "bun test"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@tschk/moonshine-adapter-conformance": "^0.3.
|
|
19
|
-
"@tschk/moonshine-compiler": "^0.3.
|
|
20
|
-
"@tschk/moonshine-framework": "^0.3.
|
|
21
|
-
"@tschk/moonshine-react": "^0.3.
|
|
22
|
-
"@tschk/moonshine-server": "^0.3.
|
|
18
|
+
"@tschk/moonshine-adapter-conformance": "^0.3.4",
|
|
19
|
+
"@tschk/moonshine-compiler": "^0.3.4",
|
|
20
|
+
"@tschk/moonshine-framework": "^0.3.4",
|
|
21
|
+
"@tschk/moonshine-react": "^0.3.4",
|
|
22
|
+
"@tschk/moonshine-server": "^0.3.4"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/bun": "1.3.14",
|
package/src/index.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
type DeploymentAdapter,
|
|
12
12
|
type MoonshineManifest,
|
|
13
13
|
} from "@tschk/moonshine-framework";
|
|
14
|
-
import { resolveStaticPath } from "@tschk/moonshine-server";
|
|
14
|
+
import { isContained, resolveStaticPath } from "@tschk/moonshine-server";
|
|
15
15
|
import {
|
|
16
16
|
type Harness,
|
|
17
17
|
type HarnessFactory,
|
|
@@ -71,12 +71,18 @@ async function tryServeNodeStatic(
|
|
|
71
71
|
return null;
|
|
72
72
|
}
|
|
73
73
|
if (!stats.isFile()) return null;
|
|
74
|
+
try {
|
|
75
|
+
if (!isContained(staticDir, filePath)) return null;
|
|
76
|
+
} catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
74
79
|
const type = MIME[extOf(filePath)] ?? "application/octet-stream";
|
|
75
80
|
const stream = createReadStream(filePath);
|
|
76
81
|
return new Response(Readable.toWeb(stream) as unknown as ReadableStream, {
|
|
77
82
|
headers: {
|
|
78
83
|
"content-type": type,
|
|
79
84
|
"content-length": String(stats.size),
|
|
85
|
+
"x-content-type-options": "nosniff",
|
|
80
86
|
},
|
|
81
87
|
});
|
|
82
88
|
}
|
|
@@ -104,6 +110,18 @@ async function sendNodeResponse(
|
|
|
104
110
|
stream.pipe(res);
|
|
105
111
|
}
|
|
106
112
|
|
|
113
|
+
const HOST_PATTERN = /^(?:[a-zA-Z0-9._-]+|\[[0-9a-fA-F:.]+\])(?::\d{1,5})?$/;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Node hands the Host header through unvalidated, so a client-supplied value
|
|
117
|
+
* can smuggle userinfo, a path, or a query into the base URL and change the
|
|
118
|
+
* origin every downstream URL comparison is made against.
|
|
119
|
+
*/
|
|
120
|
+
function safeHost(host: string | undefined): string {
|
|
121
|
+
if (!host) return "localhost";
|
|
122
|
+
return HOST_PATTERN.test(host) ? host : "localhost";
|
|
123
|
+
}
|
|
124
|
+
|
|
107
125
|
export type NodeHandlerOptions = {
|
|
108
126
|
fetch: (request: Request) => Promise<Response>;
|
|
109
127
|
staticDir?: string;
|
|
@@ -115,7 +133,7 @@ export function createNodeHandler(
|
|
|
115
133
|
return async (req, res) => {
|
|
116
134
|
const method = req.method ?? "GET";
|
|
117
135
|
const reqUrl = req.url ?? "/";
|
|
118
|
-
const host = req.headers.host
|
|
136
|
+
const host = safeHost(req.headers.host);
|
|
119
137
|
const requestUrl = new URL(reqUrl, `http://${host}`);
|
|
120
138
|
const pathname = requestUrl.pathname;
|
|
121
139
|
|