MCPcopy Create free account
hub / github.com/ReviewStage/stage-cli / handleRequest

Function handleRequest

packages/cli/src/server.ts:136–199  ·  view source on GitHub ↗
(
	req: http.IncomingMessage,
	res: http.ServerResponse,
	webDist: string,
	routes: CompiledRoute[],
)

Source from the content-addressed store, hash-verified

134}
135
136async function handleRequest(
137 req: http.IncomingMessage,
138 res: http.ServerResponse,
139 webDist: string,
140 routes: CompiledRoute[],
141): Promise<void> {
142 // Don't parse via `new URL()` — its WHATWG normalization collapses `/../foo` to `/foo`,
143 // hiding traversal attempts before our guard runs. Strip the query string ourselves.
144 const fullUrl = req.url ?? "/";
145 const queryIdx = fullUrl.indexOf("?");
146 const pathname = (queryIdx >= 0 ? fullUrl.slice(0, queryIdx) : fullUrl) || "/";
147 const method = (req.method ?? "GET").toUpperCase();
148
149 if (pathname.startsWith("/api/")) {
150 for (const route of routes) {
151 if (route.method !== method) continue;
152 const match = route.regex.exec(pathname);
153 if (!match) continue;
154 const params: RouteParams = {};
155 try {
156 route.paramNames.forEach((name, i) => {
157 params[name] = decodeURIComponent(match[i + 1] ?? "");
158 });
159 } catch {
160 res.writeHead(400, { "Content-Type": "text/plain" });
161 res.end("Bad Request");
162 return;
163 }
164 await route.handler(req, res, params);
165 return;
166 }
167 res.writeHead(404, { "Content-Type": "text/plain" });
168 res.end("Not Found");
169 return;
170 }
171
172 if (method !== "GET") {
173 res.writeHead(405, { "Content-Type": "text/plain", Allow: "GET" });
174 res.end("Method Not Allowed");
175 return;
176 }
177
178 let decoded: string;
179 try {
180 decoded = decodeURIComponent(pathname);
181 } catch {
182 res.writeHead(400, { "Content-Type": "text/plain" });
183 res.end("Bad Request");
184 return;
185 }
186
187 // path.relative + a check for `..`/absolute is a CodeQL-recognized path-injection sanitizer.
188 // Building filePath from the validated relative makes the data flow explicit.
189 const rel = path.relative(webDist, path.resolve(webDist, `.${decoded}`));
190 if (rel.startsWith("..") || path.isAbsolute(rel)) {
191 res.writeHead(403, { "Content-Type": "text/plain" });
192 res.end("Forbidden");
193 return;

Callers 1

startServerFunction · 0.85

Calls 2

sendFileFunction · 0.85
sendIndexFallbackFunction · 0.85

Tested by

no test coverage detected