paseo-wiki-viewer 0.1.0 → 0.1.1
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 +1 -1
- package/server/viewer.ts +18 -0
package/package.json
CHANGED
package/server/viewer.ts
CHANGED
|
@@ -328,6 +328,23 @@ function proxy(req: IncomingMessage, res: ServerResponse, port: number, url: URL
|
|
|
328
328
|
req.pipe(upstream);
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
+
// The viewer reads `_root/<base64url-dir>` asset paths as a replacement for
|
|
332
|
+
// ?root=, so a path sentinel may only name the grant's own root.
|
|
333
|
+
export function rootSentinelsMatch(pathname: string, root: string): boolean {
|
|
334
|
+
let segments: string[];
|
|
335
|
+
try {
|
|
336
|
+
segments = pathname.split("/").map((segment) => decodeURIComponent(segment));
|
|
337
|
+
} catch {
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
340
|
+
return segments.every((segment, i) => {
|
|
341
|
+
if (segment !== "_root") return true;
|
|
342
|
+
const encoded = segments[i + 1];
|
|
343
|
+
if (!encoded) return false;
|
|
344
|
+
return Buffer.from(encoded.replace(/-/g, "+").replace(/_/g, "/"), "base64").toString("utf8") === root;
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
331
348
|
function text(res: ServerResponse, status: number, body: string) {
|
|
332
349
|
res.writeHead(status, { "content-type": "text/plain; charset=utf-8" });
|
|
333
350
|
res.end(body);
|
|
@@ -366,6 +383,7 @@ export function createWikiService(readConfig: () => Promise<ViewerConfig>) {
|
|
|
366
383
|
grants.delete(grant);
|
|
367
384
|
return text(res, 403, "wiki grant is no longer valid");
|
|
368
385
|
}
|
|
386
|
+
if (!rootSentinelsMatch(url.pathname, entry.root)) return text(res, 403, "outside the granted workspace");
|
|
369
387
|
if (!activeViewer) return text(res, 503, "wiki viewer is not running");
|
|
370
388
|
entry.expiresAt = Date.now() + GRANT_TTL_MS;
|
|
371
389
|
proxy(req, res, activeViewer.port, url, entry.root, grant);
|