Skip to content

Commit 46da2cb

Browse files
committed
fix(webapp): reject webhook verification configs that can never verify
Two configs were accepted but would then fail-close every delivery. The dashboard secret-generation action minted a shared secret for any endpoint, including asymmetric (public-key) ones, overwriting the stored public key. It now rejects generation for asymmetric endpoints, matching the public API route. url-secret verification with path placement can never match on the hosted ingress URL, whose last path segment is the fixed opaque endpoint id, so deploy-sync now rejects it with a clear error instead of letting every inbound event 400.
1 parent 7a609bf commit 46da2cb

2 files changed

Lines changed: 21 additions & 0 deletions

File tree

apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.webhooks.endpoints.$endpointParam/route.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,17 @@ export const action = async ({ request, params }: ActionFunctionArgs) => {
176176
// Generate (integrator-supplied secret): mint a strong secret, store it, and return it so the
177177
// UI can reveal it ONCE for the integrator to paste into their provider.
178178
if (intent === "generate-secret") {
179+
const verifier = WebhookVerifierArtifact.safeParse(endpoint.verifierArtifact);
180+
if (
181+
verifier.success &&
182+
"config" in verifier.data &&
183+
verifier.data.config.scheme === "asymmetric"
184+
) {
185+
return {
186+
success: false as const,
187+
error: "Cannot generate a secret for an asymmetric endpoint; set its public key instead.",
188+
};
189+
}
179190
const secret = `whsec_${randomBytes(32).toString("hex")}`;
180191
await secretStore.setSecret(secretKey, { secret });
181192
await webhookPrisma.webhookEndpoint.update({

apps/webapp/app/v3/services/createBackgroundWorker.server.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,16 @@ export async function syncDeclarativeWebhooks(
708708

709709
missing.delete(wh.id);
710710

711+
if (
712+
"config" in wh.verifierArtifact &&
713+
wh.verifierArtifact.config.scheme === "url-secret" &&
714+
wh.verifierArtifact.config.placement === "path"
715+
) {
716+
throw new ServiceValidationError(
717+
`Webhook "${wh.id}" uses url-secret verification with path placement, which cannot be verified on the hosted ingress URL. Use query placement or a header-based scheme.`
718+
);
719+
}
720+
711721
// Compile `filter` into a FilterAst, once here at sync. A bad filter fails the deploy with a clear
712722
// message rather than surfacing at ingest. Re-deploying without a filter nulls the columns.
713723
let filterNode: FilterAst | undefined;

0 commit comments

Comments
 (0)