Added tests and security hardening

This commit is contained in:
Michaël Lemaire 2020-05-14 11:24:38 +02:00
parent a8a6d544e3
commit f29e06254d
2 changed files with 117 additions and 19 deletions

73
server.test.ts Normal file
View file

@ -0,0 +1,73 @@
import {
expect,
mockfn,
test,
} from "https://code.thunderk.net/typescript/devtools/raw/1.0.1/testing.ts";
import { processRequest } from "./server.ts";
test("serveBundles standard", async () => {
const mock_run = mockfn(() => {
return {
output: () => Promise.resolve(new TextEncoder().encode("abc")),
status: () => Promise.resolve({ code: 0, success: true }),
} as any;
});
const response = await processRequest(
{ url: "/greatlib/1.0.0/reader/" } as any,
mock_run,
);
expect(response).toEqual({ body: new TextEncoder().encode("abc") });
expect(mock_run).toHaveBeenCalledTimes(1);
expect(mock_run).toHaveBeenCalledWith({
cmd: [
"deno",
"bundle",
"https://code.thunderk.net/typescript/greatlib/raw/1.0.0/reader.ts",
],
stdout: "piped",
});
});
test("serveBundles bad path", async () => {
const mock_run = mockfn(() => {
return {
output: () => Promise.resolve(new TextEncoder().encode("abc")),
status: () => Promise.resolve({ code: 0, success: true }),
} as any;
});
const response = await processRequest(
{ url: "/greatlib/1.0.0/reader{}/" } as any,
mock_run,
);
expect(response).toEqual(
{
status: 400,
body:
'console.error("bundler error - Invalid path https://code.thunderk.net/typescript/greatlib/raw/1.0.0/reader{}.ts");',
},
);
expect(mock_run).toHaveBeenCalledTimes(0);
});
test("serveBundles bundle fail", async () => {
const mock_run = mockfn(() => {
return {
output: () => Promise.resolve(undefined),
status: () => Promise.resolve({ code: 1, success: false }),
} as any;
});
const response = await processRequest(
{ url: "/great_lib/1.0.0-dev1/reader/" } as any,
mock_run,
);
expect(response).toEqual(
{
status: 500,
body:
'console.error("bundler error - Failed to bundle https://code.thunderk.net/typescript/great_lib/raw/1.0.0-dev1/reader.ts");',
},
);
});

View file

@ -1,31 +1,56 @@
#!/usr/bin/env -S deno run --allow-run --allow-net
// Automated bundle server
import { serve } from "https://deno.land/std/http/server.ts";
import { bool } from "https://code.thunderk.net/typescript/functional/raw/1.0.0/all.ts";
import {
Response,
serve,
ServerRequest,
} from "https://deno.land/std/http/server.ts";
async function serveBundles() {
export async function processRequest(
req: ServerRequest,
runner = Deno.run,
): Promise<Response> {
const params = req.url.split("/").filter(bool);
const lib = params[0] || "all";
const version = params[1] || "master";
const file = params.length > 2 ? params.slice(2).join("/") : "all";
const path =
`https://code.thunderk.net/typescript/${lib}/raw/${version}/${file}.ts`;
if (!path.match(/^[a-z0-9\/\.\:-_]+$/)) {
return {
status: 400,
body: `console.error("bundler error - Invalid path ${path}");`,
};
}
const process = runner({
cmd: ["deno", "bundle", path],
stdout: "piped",
});
const output = await process.output();
const status = await process.status();
if (status.success) {
return { body: output };
} else {
return {
status: 500,
body: `console.error("bundler error - Failed to bundle ${path}");`,
};
}
}
export async function serveBundles() {
const listen = { hostname: "0.0.0.0", port: 8000 };
const server = serve(listen);
console.log(`Serving bundles on ${listen.hostname}:${listen.port} ...`);
for await (const req of server) {
const params = req.url.split("/").filter(bool);
const lib = params[0] || "all";
const version = params[1] || "master";
const file = params.length > 2 ? params.slice(2).join("/") : "all";
const path =
`https://code.thunderk.net/typescript/${lib}/raw/${version}/${file}.ts`;
try {
const process = Deno.run({
cmd: ["deno", "bundle", path],
stdout: "piped",
});
req.respond({ body: await process.output() });
} catch {
req.respond({ body: `console.error("Failed to bundle ${path}");` });
}
const response = await processRequest(req);
await req.respond(response);
}
}
await serveBundles();
if (import.meta.main) {
await serveBundles();
}