Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

nuxt-webhook-validators

Package Overview
Dependencies
Maintainers
1
Versions
26
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nuxt-webhook-validators - npm Package Compare versions

Comparing version
0.2.6
to
0.2.7
+1
-1
dist/module.json

@@ -7,3 +7,3 @@ {

},
"version": "0.2.6",
"version": "0.2.7",
"builder": {

@@ -10,0 +10,0 @@ "@nuxt/module-builder": "1.0.2",

@@ -36,1 +36,3 @@ import { type webcrypto } from 'node:crypto';

}>) => Promise<boolean>;
export declare const readRawBodyClone: (event: H3Event) => Promise<string | undefined>;
export declare const readBodyClone: <T = unknown>(event: H3Event) => Promise<T | undefined>;
import { subtle } from "node:crypto";
import { Buffer } from "node:buffer";
import { snakeCase } from "scule";
import { createError } from "h3";
import { createError, readRawBody } from "h3";
import { useRuntimeConfig } from "#imports";

@@ -39,3 +39,3 @@ export const HMAC_SHA256 = { name: "HMAC", hash: "SHA-256" };

throw createError({
statusCode: 500,
status: 500,
message: errorMessage

@@ -53,1 +53,13 @@ });

};
export const readRawBodyClone = async (event) => {
const hasClone = "clone" in (event.req ?? {});
const body = hasClone ? await event.req.clone().text() : await readRawBody(event);
if (!hasClone) {
event._requestBody = body;
}
return body;
};
export const readBodyClone = async (event) => {
const rawBody = await readRawBodyClone(event);
return rawBody ? JSON.parse(rawBody) : void 0;
};

@@ -1,3 +0,3 @@

import { getRequestHeaders, readRawBody } from "h3";
import { verifyPublicSignature, ED25519, ensureConfiguration } from "../helpers.js";
import { getRequestHeaders } from "h3";
import { verifyPublicSignature, ED25519, ensureConfiguration, readRawBodyClone } from "../helpers.js";
const DISCORD_SIGNATURE = "x-signature-ed25519";

@@ -8,3 +8,3 @@ const DISCORD_SIGNATURE_TIMESTAMP = "x-signature-timestamp";

const headers = getRequestHeaders(event);
const body = await readRawBody(event);
const body = await readRawBodyClone(event);
const webhookSignature = headers[DISCORD_SIGNATURE];

@@ -11,0 +11,0 @@ const webhookTimestamp = headers[DISCORD_SIGNATURE_TIMESTAMP];

@@ -1,3 +0,3 @@

import { getRequestHeaders, readRawBody } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration } from "../helpers.js";
import { getRequestHeaders } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration, readRawBodyClone } from "../helpers.js";
const DROPBOX_SIGNATURE = "X-Dropbox-Signature".toLowerCase();

@@ -7,3 +7,3 @@ export const isValidDropboxWebhook = async (event) => {

const headers = getRequestHeaders(event);
const body = await readRawBody(event);
const body = await readRawBodyClone(event);
const webhookSignature = headers[DROPBOX_SIGNATURE];

@@ -10,0 +10,0 @@ if (!body || !webhookSignature) return false;

@@ -1,3 +0,3 @@

import { getRequestHeaders, readRawBody } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration } from "../helpers.js";
import { getRequestHeaders } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration, readRawBodyClone } from "../helpers.js";
const FOURTHWALL_SIGNATURE = "X-Fourthwall-Hmac-SHA256".toLowerCase();

@@ -7,3 +7,3 @@ export const isValidFourthwallWebhook = async (event) => {

const headers = getRequestHeaders(event);
const body = await readRawBody(event);
const body = await readRawBodyClone(event);
const webhookSignature = headers[FOURTHWALL_SIGNATURE];

@@ -10,0 +10,0 @@ if (!body || !webhookSignature) return false;

@@ -1,3 +0,3 @@

import { getRequestHeaders, readRawBody } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration } from "../helpers.js";
import { getRequestHeaders } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration, readRawBodyClone } from "../helpers.js";
const GITHUB_SIGNATURE = "X-Hub-Signature-256".toLowerCase();

@@ -7,3 +7,3 @@ export const isValidGitHubWebhook = async (event) => {

const headers = getRequestHeaders(event);
const body = await readRawBody(event);
const body = await readRawBodyClone(event);
const header = headers[GITHUB_SIGNATURE];

@@ -10,0 +10,0 @@ if (!body || !header) return false;

@@ -1,3 +0,3 @@

import { getRequestHeaders, readRawBody } from "h3";
import { ensureConfiguration } from "../helpers.js";
import { getRequestHeaders } from "h3";
import { ensureConfiguration, readRawBodyClone } from "../helpers.js";
const GITLAB_TOKEN = "X-Gitlab-Token".toLowerCase();

@@ -7,3 +7,3 @@ export const isValidGitLabWebhook = async (event) => {

const headers = getRequestHeaders(event);
const body = await readRawBody(event);
const body = await readRawBodyClone(event);
const header = headers[GITLAB_TOKEN];

@@ -10,0 +10,0 @@ if (!body || !header) return false;

@@ -1,3 +0,3 @@

import { getRequestHeaders, readRawBody } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration } from "../helpers.js";
import { getRequestHeaders } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration, readRawBodyClone } from "../helpers.js";
const HEROKU_HMAC = "Heroku-Webhook-Hmac-SHA256".toLowerCase();

@@ -7,3 +7,3 @@ export const isValidHerokuWebhook = async (event) => {

const headers = getRequestHeaders(event);
const body = await readRawBody(event);
const body = await readRawBodyClone(event);
const header = headers[HEROKU_HMAC];

@@ -10,0 +10,0 @@ if (!body || !header) return false;

@@ -1,3 +0,3 @@

import { getRequestHeaders, readRawBody } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration } from "../helpers.js";
import { getRequestHeaders } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration, readRawBodyClone } from "../helpers.js";
const DEFAULT_TOLERANCE = 300;

@@ -22,3 +22,3 @@ const HYGRAPH_SIGNATURE = "gcms-signature";

const headers = getRequestHeaders(event);
const body = await readRawBody(event);
const body = await readRawBodyClone(event);
const hygraphSignature = headers[HYGRAPH_SIGNATURE];

@@ -25,0 +25,0 @@ if (!body || !hygraphSignature) return false;

@@ -1,3 +0,3 @@

import { getRequestHeaders, readRawBody } from "h3";
import { verifyPublicSignature, RSASSA_PKCS1_v1_5_SHA256, stripPemHeaders } from "../helpers.js";
import { getRequestHeaders } from "h3";
import { verifyPublicSignature, RSASSA_PKCS1_v1_5_SHA256, stripPemHeaders, readRawBodyClone } from "../helpers.js";
import { useRuntimeConfig } from "#imports";

@@ -19,3 +19,3 @@ const KICK_MESSAGE_ID = "Kick-Event-Message-Id".toLowerCase();

const headers = getRequestHeaders(event);
const body = await readRawBody(event);
const body = await readRawBodyClone(event);
const messageId = headers[KICK_MESSAGE_ID];

@@ -22,0 +22,0 @@ const messageTimestamp = headers[KICK_MESSAGE_TIMESTAMP];

@@ -1,3 +0,3 @@

import { getRequestHeaders, readRawBody } from "h3";
import { verifyPublicSignature, ED25519, validateSha256, stripPemHeaders } from "../helpers.js";
import { getRequestHeaders } from "h3";
import { verifyPublicSignature, ED25519, validateSha256, stripPemHeaders, readRawBodyClone } from "../helpers.js";
import { useRuntimeConfig } from "#imports";

@@ -35,3 +35,3 @@ const MAILCHANNELS_CONTENT_DIGEST = "content-digest";

const headers = getRequestHeaders(event);
const body = await readRawBody(event);
const body = await readRawBodyClone(event);
const contentDigest = headers[MAILCHANNELS_CONTENT_DIGEST];

@@ -38,0 +38,0 @@ const messageSignature = headers[MAILCHANNELS_SIGNATURE];

@@ -1,3 +0,3 @@

import { getRequestHeaders, readRawBody } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration } from "../helpers.js";
import { getRequestHeaders } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration, readRawBodyClone } from "../helpers.js";
const META_SIGNATURE = "X-Hub-Signature-256".toLowerCase();

@@ -7,3 +7,3 @@ export const isValidMetaWebhook = async (event) => {

const headers = getRequestHeaders(event);
const body = await readRawBody(event);
const body = await readRawBodyClone(event);
const signatureHeader = headers[META_SIGNATURE];

@@ -10,0 +10,0 @@ if (!signatureHeader) return false;

@@ -1,3 +0,3 @@

import { getRequestHeaders, readRawBody } from "h3";
import { ensureConfiguration, sha256 } from "../helpers.js";
import { getRequestHeaders } from "h3";
import { ensureConfiguration, readRawBodyClone, sha256 } from "../helpers.js";
const NUXTHUB_SIGNATURE = "x-nuxthub-signature";

@@ -7,3 +7,3 @@ export const isValidNuxtHubWebhook = async (event) => {

const headers = getRequestHeaders(event);
const body = await readRawBody(event);
const body = await readRawBodyClone(event);
const webhookSignature = headers[NUXTHUB_SIGNATURE];

@@ -10,0 +10,0 @@ if (!body || !webhookSignature) return false;

@@ -1,3 +0,3 @@

import { getRequestHeaders, readRawBody } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration } from "../helpers.js";
import { getRequestHeaders } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration, readRawBodyClone } from "../helpers.js";
const MAX_VALID_TIME_DIFFERENCE = 5;

@@ -22,3 +22,3 @@ const PADDLE_SIGNATURE = "paddle-signature";

const headers = getRequestHeaders(event);
const body = await readRawBody(event);
const body = await readRawBodyClone(event);
const paddleSignature = headers[PADDLE_SIGNATURE];

@@ -25,0 +25,0 @@ if (!body || !paddleSignature) return false;

@@ -1,3 +0,3 @@

import { getRequestHeaders, readBody } from "h3";
import { ensureConfiguration } from "../helpers.js";
import { getRequestHeaders } from "h3";
import { ensureConfiguration, readBodyClone } from "../helpers.js";
const baseAPI = import.meta.dev ? "https://api-m.sandbox.paypal.com/v1" : "https://api-m.paypal.com/v1";

@@ -7,3 +7,3 @@ export const isValidPaypalWebhook = async (event) => {

const headers = getRequestHeaders(event);
const body = await readBody(event);
const body = await readBodyClone(event);
if (!body || !headers) return false;

@@ -10,0 +10,0 @@ const basicAuth = btoa(`${config.clientId}:${config.secretKey}`);

@@ -1,3 +0,3 @@

import { getRequestHeaders, readRawBody } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration } from "../helpers.js";
import { getRequestHeaders } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration, readRawBodyClone } from "../helpers.js";
const POLAR_SIGNATURE_ID = "webhook-id";

@@ -10,3 +10,3 @@ const POLAR_SIGNATURE = "webhook-signature";

const headers = getRequestHeaders(event);
const body = await readRawBody(event);
const body = await readRawBodyClone(event);
const webhookId = headers[POLAR_SIGNATURE_ID];

@@ -13,0 +13,0 @@ const webhookSignature = headers[POLAR_SIGNATURE];

@@ -1,3 +0,3 @@

import { getRequestHeaders, readRawBody } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration } from "../helpers.js";
import { getRequestHeaders } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration, readRawBodyClone } from "../helpers.js";
const SHOPIFY_SIGNATURE = "X-Shopify-Hmac-Sha256".toLowerCase();

@@ -7,3 +7,3 @@ export const isValidShopifyWebhook = async (event) => {

const headers = getRequestHeaders(event);
const body = await readRawBody(event);
const body = await readRawBodyClone(event);
const webhookSignature = headers[SHOPIFY_SIGNATURE];

@@ -10,0 +10,0 @@ if (!body || !webhookSignature) return false;

@@ -1,3 +0,3 @@

import { getRequestHeaders, readRawBody } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration } from "../helpers.js";
import { getRequestHeaders } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration, readRawBodyClone } from "../helpers.js";
const SLACK_SIGNATURE = "X-Slack-Signature".toLowerCase();

@@ -9,3 +9,3 @@ const SLACK_TIMESTAMP = "X-Slack-Request-Timestamp".toLowerCase();

const headers = getRequestHeaders(event);
const body = await readRawBody(event);
const body = await readRawBodyClone(event);
const fullSignature = headers[SLACK_SIGNATURE];

@@ -12,0 +12,0 @@ const timestamp = headers[SLACK_TIMESTAMP];

@@ -1,3 +0,3 @@

import { getRequestHeaders, readRawBody } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration } from "../helpers.js";
import { getRequestHeaders } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration, readRawBodyClone } from "../helpers.js";
const DEFAULT_TOLERANCE = 300;

@@ -22,3 +22,3 @@ const STRIPE_SIGNATURE = "Stripe-Signature".toLowerCase();

const headers = getRequestHeaders(event);
const body = await readRawBody(event);
const body = await readRawBodyClone(event);
const stripeSignature = headers[STRIPE_SIGNATURE];

@@ -25,0 +25,0 @@ if (!body || !stripeSignature) return false;

@@ -1,3 +0,3 @@

import { getRequestHeaders, readRawBody } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration } from "../helpers.js";
import { getRequestHeaders } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration, readRawBodyClone } from "../helpers.js";
const SVIX_SIGNATURE_ID = "svix-id";

@@ -9,3 +9,3 @@ const SVIX_SIGNATURE = "svix-signature";

const headers = getRequestHeaders(event);
const body = await readRawBody(event);
const body = await readRawBodyClone(event);
const webhookId = headers[SVIX_SIGNATURE_ID];

@@ -12,0 +12,0 @@ const webhookSignature = headers[SVIX_SIGNATURE];

@@ -1,3 +0,3 @@

import { getRequestHeaders, readRawBody } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration } from "../helpers.js";
import { getRequestHeaders } from "h3";
import { computeSignature, HMAC_SHA256, ensureConfiguration, readRawBodyClone } from "../helpers.js";
const TWITCH_MESSAGE_ID = "Twitch-Eventsub-Message-Id".toLowerCase();

@@ -10,3 +10,3 @@ const TWITCH_MESSAGE_TIMESTAMP = "Twitch-Eventsub-Message-Timestamp".toLowerCase();

const headers = getRequestHeaders(event);
const body = await readRawBody(event);
const body = await readRawBodyClone(event);
const message_id = headers[TWITCH_MESSAGE_ID];

@@ -13,0 +13,0 @@ const message_timestamp = headers[TWITCH_MESSAGE_TIMESTAMP];

{
"name": "nuxt-webhook-validators",
"version": "0.2.6",
"version": "0.2.7",
"description": "A simple nuxt module that works on the edge to easily validate incoming webhooks from different services.",

@@ -33,3 +33,3 @@ "keywords": [

"dependencies": {
"@nuxt/kit": "^4.2.2",
"@nuxt/kit": "^4.3.0",
"defu": "^6.1.4",

@@ -40,13 +40,13 @@ "scule": "^1.3.0"

"@nuxt/devtools": "^3.1.1",
"@nuxt/eslint-config": "^1.11.0",
"@nuxt/eslint-config": "^1.13.0",
"@nuxt/module-builder": "^1.0.2",
"@nuxt/schema": "^4.2.2",
"@nuxt/test-utils": "^3.21.0",
"@types/node": "^24.10.2",
"@nuxt/schema": "^4.3.0",
"@nuxt/test-utils": "^3.23.0",
"@types/node": "^25.0.10",
"changelogen": "^0.6.2",
"eslint": "^9.39.1",
"nuxt": "^4.2.2",
"eslint": "^9.39.2",
"nuxt": "^4.3.0",
"typescript": "^5.9.3",
"vitest": "^4.0.15",
"vue-tsc": "^3.1.8"
"vitest": "^4.0.18",
"vue-tsc": "^3.2.3"
},

@@ -53,0 +53,0 @@ "scripts": {

@@ -105,7 +105,9 @@ ![webhook-validators](https://github.com/Yizack/nuxt-webhook-validators/assets/16264115/56cded71-46b2-4895-8732-484ab6df5181)

## Example
## Examples
### Basic
Validate a GitHub webhook in a server API route.
`~/server/api/webhooks/github.post.ts`
`~~/server/api/webhooks/github.post.ts`

@@ -117,3 +119,3 @@ ```js

if (!isValidWebhook) {
throw createError({ statusCode: 401, message: 'Unauthorized: webhook is not valid' })
throw createError({ status: 401, message: 'Unauthorized: webhook is not valid' })
}

@@ -127,2 +129,21 @@

## Reading the request body
Make sure to read the body after validating the webhook, as most validators rely on a caching/cloning of the raw body to compute the signature and validate the webhook.
```js
export default defineEventHandler(async (event) => {
const isValidWebhook = await isValidGitHubWebhook(event)
if (!isValidWebhook) {
throw createError({ status: 401, message: 'Unauthorized: webhook is not valid' })
}
// Make sure to read the body after validating the webhook
const body = await readBody(event)
return { isValidWebhook }
})
```
# Development

@@ -129,0 +150,0 @@