Skip to content

Commit

Permalink
fix: disallow localhost webhooks (#368)
Browse files Browse the repository at this point in the history
* fix: disallow localhost webhooks

* allow http but disallow localhost
  • Loading branch information
arcoraven authored Jan 9, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent 5a1c7ce commit 710bb5d
Showing 3 changed files with 51 additions and 25 deletions.
45 changes: 20 additions & 25 deletions src/server/routes/webhooks/create.ts
Original file line number Diff line number Diff line change
@@ -4,83 +4,77 @@ import { FastifyInstance } from "fastify";
import { StatusCodes } from "http-status-codes";
import { insertWebhook } from "../../../db/webhooks/createWebhook";
import { WebhooksEventTypes } from "../../../schema/webhooks";
import { isLocalhost } from "../../../utils/url";
import { standardResponseSchema } from "../../schemas/sharedApiSchemas";

const uriFormat = TypeSystem.Format("uri", (input: string) => {
// Assert valid URL.
try {
if (input.startsWith("http://localhost")) return true;

const url = new URL(input);

if (url.protocol === "http:") {
return false;
}
return true;
new URL(input);
} catch (err) {
return false;
}

return !isLocalhost(input);
});

const BodySchema = Type.Object({
url: Type.String({
description: "URL to send the webhook to",
description: "Webhook URL",
format: uriFormat,
examples: [
"http://localhost:3000/webhooks",
"https://example.com/webhooks",
],
examples: ["https://example.com/webhooks"],
}),
name: Type.Optional(
Type.String({
minLength: 5,
minLength: 3,
}),
),
eventType: Type.Enum(WebhooksEventTypes),
});

BodySchema.examples = [
{
url: "http://localhost:3000/allTxUpdate",
url: "https://example.com/allTxUpdate",
name: "All Transaction Events",
eventType: WebhooksEventTypes.ALL_TX,
},
{
url: "http://localhost:3000/queuedTx",
url: "https://example.com/queuedTx",
name: "QueuedTx",
eventType: WebhooksEventTypes.QUEUED_TX,
},
{
url: "http://localhost:3000/retiredTx",
url: "https://example.com/retiredTx",
name: "RetriedTx",
eventType: WebhooksEventTypes.RETRIED_TX,
},
{
url: "http://localhost:3000/sentTx",
url: "https://example.com/sentTx",
name: "Sent Transaction Event",
eventType: WebhooksEventTypes.SENT_TX,
},
{
url: "http://localhost:3000/minedTx",
url: "https://example.com/minedTx",
name: "Mined Transaction Event",
eventType: WebhooksEventTypes.MINED_TX,
},
{
url: "http://localhost:3000/erroredTx",
url: "https://example.com/erroredTx",
name: "Errored Transaction Event",
eventType: WebhooksEventTypes.ERRORED_TX,
},
{
url: "http://localhost:3000/cancelledTx",
url: "https://example.com/cancelledTx",
name: "Cancelled Transaction Event",
eventType: WebhooksEventTypes.CANCELLED_TX,
},
{
url: "http://localhost:3000/walletBalance",
url: "https://example.com/walletBalance",
name: "Backend Wallet Balance Event",
eventType: WebhooksEventTypes.BACKEND_WALLET_BALANCE,
},
{
url: "http://localhost:3000/auth",
url: "https://example.com/auth",
name: "Auth Check",
eventType: WebhooksEventTypes.AUTH,
},
@@ -104,8 +98,9 @@ export async function createWebhook(fastify: FastifyInstance) {
method: "POST",
url: "/webhooks/create",
schema: {
summary: "Create a new webhook",
description: "Create a new webhook",
summary: "Create a webhook",
description:
"Create a webhook to call when certain blockchain events occur.",
tags: ["Webhooks"],
operationId: "create",
body: BodySchema,
23 changes: 23 additions & 0 deletions src/tests/url.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { isLocalhost } from "../utils/url";

describe("isLocalhost function", () => {
test("should return true for localhost URL", () => {
const localhostUrl = "http://localhost:3000/path";
expect(isLocalhost(localhostUrl)).toBe(true);
});

test("should return true for 127.0.0.1 URL", () => {
const ipUrl = "http://127.0.0.1:8080/path";
expect(isLocalhost(ipUrl)).toBe(true);
});

test("should return false for external URL", () => {
const externalUrl = "http://example.com/path";
expect(isLocalhost(externalUrl)).toBe(false);
});

test("should return false for invalid URL", () => {
const invalidUrl = "not_a_url";
expect(isLocalhost(invalidUrl)).toBe(false);
});
});
8 changes: 8 additions & 0 deletions src/utils/url.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const isLocalhost = (url: string) => {
try {
const parsed = new URL(url);
return parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1";
} catch (err) {
return false;
}
};

0 comments on commit 710bb5d

Please sign in to comment.