Repository: octokit/webhooks.js (ts)

Issue:
Express middleware's URL parsing should be handled for invalid URLs
**What happened?**

I caught this in one of our deployments in Kubernetes (probably some probing bot which checks for services' vulnerabilities).

```
(node:7) UnhandledPromiseRejectionWarning: TypeError [ERR_INVALID_URL]: Invalid URL: //?a=/bin/sh+-c+'which+which'&
  at onParseError (internal/url.js:279:9)
  at new URL (internal/url.js:355:5)
  at middleware (/bot/node_modules/@octokit/webhooks/dist-node/index.js:305:7)
```

**What did you expect to happen?**

Errors due to URL parsing should be handled in the following line:

https://github.com/octokit/webhooks.js/blob/e75d17a3d5b9cb0e9a52d05ec7d15f3674b61e85/src/middleware/node/middleware.ts#L18

And logged accordingly instead of throwing an exception.

Pull request description:
fix: handle invalid URL
closes #610 

Patch:
diff --git a/src/middleware/node/middleware.ts b/src/middleware/node/middleware.ts
index 319e173..9fd1535 100644
--- a/src/middleware/node/middleware.ts
+++ b/src/middleware/node/middleware.ts
@@ -19,7 +19,20 @@ export async function middleware(
   response: ServerResponse,
   next?: Function
 ) {
-  const { pathname } = new URL(request.url as string, "http://localhost");
+  let pathname: string;
+  try {
+    pathname = new URL(request.url as string, "http://localhost").pathname;
+  } catch (error) {
+    response.writeHead(422, {
+      "content-type": "application/json",
+    });
+    response.end(
+      JSON.stringify({
+        error: `Request URL could not be parsed: ${request.url}`,
+      })
+    );
+    return;
+  }
 
   const isUnknownRoute = request.method !== "POST" || pathname !== options.path;
   const isExpressMiddleware = typeof next === "function";


Test patch:
diff --git a/test/integration/node-middleware.test.ts b/test/integration/node-middleware.test.ts
index 990cf5f..6af202c 100644
--- a/test/integration/node-middleware.test.ts
+++ b/test/integration/node-middleware.test.ts
@@ -458,4 +458,46 @@ describe("createNodeMiddleware(webhooks)", () => {
 
     server.close();
   });
+
+  test("Handles invalid URL", async () => {
+    const webhooks = new Webhooks({
+      secret: "mySecret",
+    });
+
+    let middlewareWasRan: () => void;
+    const untilMiddlewareIsRan = new Promise<void>(function (resolve) {
+      middlewareWasRan = resolve;
+    });
+    const actualMiddleware = createNodeMiddleware(webhooks);
+    const mockedMiddleware = async function (
+      ...[req, ...rest]: Parameters<typeof actualMiddleware>
+    ) {
+      req.url = "//";
+      await actualMiddleware(req, ...rest);
+      middlewareWasRan();
+    };
+    const server = createServer(mockedMiddleware).listen();
+
+    // @ts-expect-error complains about { port } although it's included in returned AddressInfo interface
+    const { port } = server.address();
+
+    const response = await fetch(
+      `http://localhost:${port}/api/github/webhooks`,
+      {
+        method: "POST",
+        headers: {
+          "X-GitHub-Delivery": "123e4567-e89b-12d3-a456-426655440000",
+          "X-GitHub-Event": "push",
+          "X-Hub-Signature-256": signatureSha256,
+        },
+        body: pushEventPayload,
+      }
+    );
+
+    await untilMiddlewareIsRan;
+    expect(response.status).toEqual(422);
+    expect(await response.text()).toMatch(/Request URL could not be parsed/);
+
+    server.close();
+  });
 });