@nestarc/webhook 0.6.1 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/webhook.url-validator.d.ts +9 -1
- package/dist/webhook.url-validator.d.ts.map +1 -1
- package/dist/webhook.url-validator.js +36 -21
- package/dist/webhook.url-validator.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -241,6 +241,24 @@ webhook-signature: v1,<base64-hmac-sha256>
|
|
|
241
241
|
- HTTP redirects are disabled (`redirect: 'manual'`)
|
|
242
242
|
- Use `allowPrivateUrls: true` for local development only
|
|
243
243
|
|
|
244
|
+
**Structured validation errors** — validation failures throw `WebhookUrlValidationError` (subclass of `Error`) with a machine-readable `reason`:
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
import { WebhookUrlValidationError } from '@nestarc/webhook';
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
await endpointAdmin.createEndpoint({ url, events: ['*'] });
|
|
251
|
+
} catch (err) {
|
|
252
|
+
if (err instanceof WebhookUrlValidationError) {
|
|
253
|
+
// err.reason: 'parse' | 'scheme' | 'blocked_hostname'
|
|
254
|
+
// | 'loopback' | 'private' | 'link_local' | 'invalid_target'
|
|
255
|
+
// err.url, err.resolvedIp also available
|
|
256
|
+
throw new BadRequestException({ message: err.message, reason: err.reason });
|
|
257
|
+
}
|
|
258
|
+
throw err;
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
244
262
|
### Secret handling
|
|
245
263
|
|
|
246
264
|
- Signing secrets are excluded from read queries (`listEndpoints`, `getEndpoint`)
|
package/dist/index.d.ts
CHANGED
|
@@ -24,5 +24,5 @@ export type { WebhookModuleOptions, WebhookModuleAsyncOptions, WebhookOptionsFac
|
|
|
24
24
|
export type { EndpointRecord, EndpointRecordWithSecret, CreateEndpointDto, UpdateEndpointDto, } from './interfaces/webhook-endpoint.interface';
|
|
25
25
|
export type { DeliveryStatus, DeliveryRecord, EventRecord, DeliveryResult, DeliveryLogFilters, } from './interfaces/webhook-delivery.interface';
|
|
26
26
|
export { WEBHOOK_MODULE_OPTIONS, WEBHOOK_EVENT_REPOSITORY, WEBHOOK_ENDPOINT_REPOSITORY, WEBHOOK_DELIVERY_REPOSITORY, WEBHOOK_HTTP_CLIENT, DEFAULT_BACKOFF_SCHEDULE, DEFAULT_DELIVERY_TIMEOUT, DEFAULT_MAX_RETRIES, DEFAULT_POLLING_INTERVAL, DEFAULT_POLLING_BATCH_SIZE, DEFAULT_CIRCUIT_BREAKER_THRESHOLD, DEFAULT_CIRCUIT_BREAKER_COOLDOWN_MINUTES, DEFAULT_STALE_SENDING_MINUTES, } from './webhook.constants';
|
|
27
|
-
export { validateWebhookUrl, resolveAndValidateHost } from './webhook.url-validator';
|
|
27
|
+
export { validateWebhookUrl, resolveAndValidateHost, WebhookUrlValidationError, type WebhookUrlValidationReason, } from './webhook.url-validator';
|
|
28
28
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGjD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAG/C,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAC/E,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAC/E,kFAAkF;AAClF,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAG9D,YAAY,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAC/E,YAAY,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AACrF,YAAY,EACV,yBAAyB,EACzB,eAAe,GAChB,MAAM,qCAAqC,CAAC;AAC7C,YAAY,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACrE,YAAY,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAGvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAGzE,YAAY,EACV,oBAAoB,EACpB,yBAAyB,EACzB,qBAAqB,EACrB,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,wCAAwC,CAAC;AAGhD,YAAY,EACV,cAAc,EACd,wBAAwB,EACxB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,yCAAyC,CAAC;AAEjD,YAAY,EACV,cAAc,EACd,cAAc,EACd,WAAW,EACX,cAAc,EACd,kBAAkB,GACnB,MAAM,yCAAyC,CAAC;AAGjD,OAAO,EACL,sBAAsB,EACtB,wBAAwB,EACxB,2BAA2B,EAC3B,2BAA2B,EAC3B,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,EACxB,0BAA0B,EAC1B,iCAAiC,EACjC,wCAAwC,EACxC,6BAA6B,GAC9B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGjD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAG/C,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAC/E,OAAO,EAAE,2BAA2B,EAAE,MAAM,kCAAkC,CAAC;AAC/E,kFAAkF;AAClF,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAG9D,YAAY,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAC/E,YAAY,EAAE,yBAAyB,EAAE,MAAM,qCAAqC,CAAC;AACrF,YAAY,EACV,yBAAyB,EACzB,eAAe,GAChB,MAAM,qCAAqC,CAAC;AAC7C,YAAY,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACrE,YAAY,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAGvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAGzE,YAAY,EACV,oBAAoB,EACpB,yBAAyB,EACzB,qBAAqB,EACrB,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,wCAAwC,CAAC;AAGhD,YAAY,EACV,cAAc,EACd,wBAAwB,EACxB,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,yCAAyC,CAAC;AAEjD,YAAY,EACV,cAAc,EACd,cAAc,EACd,WAAW,EACX,cAAc,EACd,kBAAkB,GACnB,MAAM,yCAAyC,CAAC;AAGjD,OAAO,EACL,sBAAsB,EACtB,wBAAwB,EACxB,2BAA2B,EAC3B,2BAA2B,EAC3B,mBAAmB,EACnB,wBAAwB,EACxB,wBAAwB,EACxB,mBAAmB,EACnB,wBAAwB,EACxB,0BAA0B,EAC1B,iCAAiC,EACjC,wCAAwC,EACxC,6BAA6B,GAC9B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,kBAAkB,EAClB,sBAAsB,EACtB,yBAAyB,EACzB,KAAK,0BAA0B,GAChC,MAAM,yBAAyB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.resolveAndValidateHost = exports.validateWebhookUrl = exports.DEFAULT_STALE_SENDING_MINUTES = exports.DEFAULT_CIRCUIT_BREAKER_COOLDOWN_MINUTES = exports.DEFAULT_CIRCUIT_BREAKER_THRESHOLD = exports.DEFAULT_POLLING_BATCH_SIZE = exports.DEFAULT_POLLING_INTERVAL = exports.DEFAULT_MAX_RETRIES = exports.DEFAULT_DELIVERY_TIMEOUT = exports.DEFAULT_BACKOFF_SCHEDULE = exports.WEBHOOK_HTTP_CLIENT = exports.WEBHOOK_DELIVERY_REPOSITORY = exports.WEBHOOK_ENDPOINT_REPOSITORY = exports.WEBHOOK_EVENT_REPOSITORY = exports.WEBHOOK_MODULE_OPTIONS = exports.PlaintextSecretVault = exports.FetchHttpClient = exports.PrismaDeliveryRepository = exports.PrismaEndpointRepository = exports.PrismaEventRepository = exports.WebhookAdminService = exports.WebhookDeliveryAdminService = exports.WebhookEndpointAdminService = exports.WebhookEvent = exports.WebhookCircuitBreaker = exports.WebhookSigner = exports.WebhookRetryPolicy = exports.WebhookDispatcher = exports.WebhookDeliveryWorker = exports.WebhookService = exports.WebhookModule = void 0;
|
|
3
|
+
exports.WebhookUrlValidationError = exports.resolveAndValidateHost = exports.validateWebhookUrl = exports.DEFAULT_STALE_SENDING_MINUTES = exports.DEFAULT_CIRCUIT_BREAKER_COOLDOWN_MINUTES = exports.DEFAULT_CIRCUIT_BREAKER_THRESHOLD = exports.DEFAULT_POLLING_BATCH_SIZE = exports.DEFAULT_POLLING_INTERVAL = exports.DEFAULT_MAX_RETRIES = exports.DEFAULT_DELIVERY_TIMEOUT = exports.DEFAULT_BACKOFF_SCHEDULE = exports.WEBHOOK_HTTP_CLIENT = exports.WEBHOOK_DELIVERY_REPOSITORY = exports.WEBHOOK_ENDPOINT_REPOSITORY = exports.WEBHOOK_EVENT_REPOSITORY = exports.WEBHOOK_MODULE_OPTIONS = exports.PlaintextSecretVault = exports.FetchHttpClient = exports.PrismaDeliveryRepository = exports.PrismaEndpointRepository = exports.PrismaEventRepository = exports.WebhookAdminService = exports.WebhookDeliveryAdminService = exports.WebhookEndpointAdminService = exports.WebhookEvent = exports.WebhookCircuitBreaker = exports.WebhookSigner = exports.WebhookRetryPolicy = exports.WebhookDispatcher = exports.WebhookDeliveryWorker = exports.WebhookService = exports.WebhookModule = void 0;
|
|
4
4
|
// Module
|
|
5
5
|
var webhook_module_1 = require("./webhook.module");
|
|
6
6
|
Object.defineProperty(exports, "WebhookModule", { enumerable: true, get: function () { return webhook_module_1.WebhookModule; } });
|
|
@@ -56,4 +56,5 @@ Object.defineProperty(exports, "DEFAULT_STALE_SENDING_MINUTES", { enumerable: tr
|
|
|
56
56
|
var webhook_url_validator_1 = require("./webhook.url-validator");
|
|
57
57
|
Object.defineProperty(exports, "validateWebhookUrl", { enumerable: true, get: function () { return webhook_url_validator_1.validateWebhookUrl; } });
|
|
58
58
|
Object.defineProperty(exports, "resolveAndValidateHost", { enumerable: true, get: function () { return webhook_url_validator_1.resolveAndValidateHost; } });
|
|
59
|
+
Object.defineProperty(exports, "WebhookUrlValidationError", { enumerable: true, get: function () { return webhook_url_validator_1.WebhookUrlValidationError; } });
|
|
59
60
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,SAAS;AACT,mDAAiD;AAAxC,+GAAA,aAAa,OAAA;AAEtB,gBAAgB;AAChB,qDAAmD;AAA1C,iHAAA,cAAc,OAAA;AACvB,qEAAkE;AAAzD,gIAAA,qBAAqB,OAAA;AAC9B,2DAAyD;AAAhD,uHAAA,iBAAiB,OAAA;AAC1B,+DAA4D;AAAnD,0HAAA,kBAAkB,OAAA;AAC3B,mDAAwE;AAA/D,+GAAA,aAAa,OAAA;AACtB,qEAAkE;AAAzD,gIAAA,qBAAqB,OAAA;AAC9B,iDAA+C;AAAtC,6GAAA,YAAY,OAAA;AAErB,iBAAiB;AACjB,mFAA+E;AAAtE,6IAAA,2BAA2B,OAAA;AACpC,mFAA+E;AAAtE,6IAAA,2BAA2B,OAAA;AACpC,kFAAkF;AAClF,iEAA8D;AAArD,4HAAA,mBAAmB,OAAA;AAY5B,mBAAmB;AACnB,8EAA2E;AAAlE,gIAAA,qBAAqB,OAAA;AAC9B,oFAAiF;AAAxE,sIAAA,wBAAwB,OAAA;AACjC,oFAAiF;AAAxE,sIAAA,wBAAwB,OAAA;AACjC,kEAA+D;AAAtD,oHAAA,eAAe,OAAA;AACxB,4EAAyE;AAAhE,8HAAA,oBAAoB,OAAA;AA8B7B,qBAAqB;AACrB,yDAc6B;AAb3B,2HAAA,sBAAsB,OAAA;AACtB,6HAAA,wBAAwB,OAAA;AACxB,gIAAA,2BAA2B,OAAA;AAC3B,gIAAA,2BAA2B,OAAA;AAC3B,wHAAA,mBAAmB,OAAA;AACnB,6HAAA,wBAAwB,OAAA;AACxB,6HAAA,wBAAwB,OAAA;AACxB,wHAAA,mBAAmB,OAAA;AACnB,6HAAA,wBAAwB,OAAA;AACxB,+HAAA,0BAA0B,OAAA;AAC1B,sIAAA,iCAAiC,OAAA;AACjC,6IAAA,wCAAwC,OAAA;AACxC,kIAAA,6BAA6B,OAAA;AAG/B,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,SAAS;AACT,mDAAiD;AAAxC,+GAAA,aAAa,OAAA;AAEtB,gBAAgB;AAChB,qDAAmD;AAA1C,iHAAA,cAAc,OAAA;AACvB,qEAAkE;AAAzD,gIAAA,qBAAqB,OAAA;AAC9B,2DAAyD;AAAhD,uHAAA,iBAAiB,OAAA;AAC1B,+DAA4D;AAAnD,0HAAA,kBAAkB,OAAA;AAC3B,mDAAwE;AAA/D,+GAAA,aAAa,OAAA;AACtB,qEAAkE;AAAzD,gIAAA,qBAAqB,OAAA;AAC9B,iDAA+C;AAAtC,6GAAA,YAAY,OAAA;AAErB,iBAAiB;AACjB,mFAA+E;AAAtE,6IAAA,2BAA2B,OAAA;AACpC,mFAA+E;AAAtE,6IAAA,2BAA2B,OAAA;AACpC,kFAAkF;AAClF,iEAA8D;AAArD,4HAAA,mBAAmB,OAAA;AAY5B,mBAAmB;AACnB,8EAA2E;AAAlE,gIAAA,qBAAqB,OAAA;AAC9B,oFAAiF;AAAxE,sIAAA,wBAAwB,OAAA;AACjC,oFAAiF;AAAxE,sIAAA,wBAAwB,OAAA;AACjC,kEAA+D;AAAtD,oHAAA,eAAe,OAAA;AACxB,4EAAyE;AAAhE,8HAAA,oBAAoB,OAAA;AA8B7B,qBAAqB;AACrB,yDAc6B;AAb3B,2HAAA,sBAAsB,OAAA;AACtB,6HAAA,wBAAwB,OAAA;AACxB,gIAAA,2BAA2B,OAAA;AAC3B,gIAAA,2BAA2B,OAAA;AAC3B,wHAAA,mBAAmB,OAAA;AACnB,6HAAA,wBAAwB,OAAA;AACxB,6HAAA,wBAAwB,OAAA;AACxB,wHAAA,mBAAmB,OAAA;AACnB,6HAAA,wBAAwB,OAAA;AACxB,+HAAA,0BAA0B,OAAA;AAC1B,sIAAA,iCAAiC,OAAA;AACjC,6IAAA,wCAAwC,OAAA;AACxC,kIAAA,6BAA6B,OAAA;AAG/B,iEAKiC;AAJ/B,2HAAA,kBAAkB,OAAA;AAClB,+HAAA,sBAAsB,OAAA;AACtB,kIAAA,yBAAyB,OAAA"}
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
export type WebhookUrlValidationReason = 'parse' | 'scheme' | 'blocked_hostname' | 'loopback' | 'private' | 'link_local' | 'invalid_target';
|
|
2
|
+
export declare class WebhookUrlValidationError extends Error {
|
|
3
|
+
readonly name = "WebhookUrlValidationError";
|
|
4
|
+
readonly reason: WebhookUrlValidationReason;
|
|
5
|
+
readonly url?: string;
|
|
6
|
+
readonly resolvedIp?: string;
|
|
7
|
+
constructor(message: string, reason: WebhookUrlValidationReason, url?: string, resolvedIp?: string);
|
|
8
|
+
}
|
|
1
9
|
export declare function validateWebhookUrl(url: string): Promise<void>;
|
|
2
|
-
export declare function resolveAndValidateHost(hostname: string): Promise<void>;
|
|
10
|
+
export declare function resolveAndValidateHost(hostname: string, url?: string): Promise<void>;
|
|
3
11
|
//# sourceMappingURL=webhook.url-validator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.url-validator.d.ts","sourceRoot":"","sources":["../src/webhook.url-validator.ts"],"names":[],"mappings":"AAUA,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"webhook.url-validator.d.ts","sourceRoot":"","sources":["../src/webhook.url-validator.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,0BAA0B,GAClC,OAAO,GACP,QAAQ,GACR,kBAAkB,GAClB,UAAU,GACV,SAAS,GACT,YAAY,GACZ,gBAAgB,CAAC;AAErB,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,IAAI,+BAA+B;IAC5C,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAC;IAC5C,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;gBAG3B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,0BAA0B,EAClC,GAAG,CAAC,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM;CAQtB;AAED,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuCnE;AA+GD,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,MAAM,EAChB,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAwBf"}
|
|
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.WebhookUrlValidationError = void 0;
|
|
36
37
|
exports.validateWebhookUrl = validateWebhookUrl;
|
|
37
38
|
exports.resolveAndValidateHost = resolveAndValidateHost;
|
|
38
39
|
const net = __importStar(require("net"));
|
|
@@ -43,57 +44,71 @@ const BLOCKED_HOSTNAMES = new Set([
|
|
|
43
44
|
'ip6-localhost',
|
|
44
45
|
'ip6-loopback',
|
|
45
46
|
]);
|
|
47
|
+
class WebhookUrlValidationError extends Error {
|
|
48
|
+
name = 'WebhookUrlValidationError';
|
|
49
|
+
reason;
|
|
50
|
+
url;
|
|
51
|
+
resolvedIp;
|
|
52
|
+
constructor(message, reason, url, resolvedIp) {
|
|
53
|
+
super(message);
|
|
54
|
+
this.reason = reason;
|
|
55
|
+
this.url = url;
|
|
56
|
+
this.resolvedIp = resolvedIp;
|
|
57
|
+
Object.setPrototypeOf(this, WebhookUrlValidationError.prototype);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.WebhookUrlValidationError = WebhookUrlValidationError;
|
|
46
61
|
async function validateWebhookUrl(url) {
|
|
47
62
|
let parsed;
|
|
48
63
|
try {
|
|
49
64
|
parsed = new URL(url);
|
|
50
65
|
}
|
|
51
66
|
catch {
|
|
52
|
-
throw new
|
|
67
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: unable to parse "${url}"`, 'parse', url);
|
|
53
68
|
}
|
|
54
69
|
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
55
|
-
throw new
|
|
70
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: scheme must be http or https, got "${parsed.protocol}"`, 'scheme', url);
|
|
56
71
|
}
|
|
57
72
|
const hostname = parsed.hostname.toLowerCase();
|
|
58
73
|
if (BLOCKED_HOSTNAMES.has(hostname)) {
|
|
59
|
-
throw new
|
|
74
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: "${hostname}" is not allowed (loopback address)`, 'blocked_hostname', url);
|
|
60
75
|
}
|
|
61
76
|
if (net.isIPv4(hostname)) {
|
|
62
|
-
validateIPv4(hostname);
|
|
77
|
+
validateIPv4(hostname, url);
|
|
63
78
|
}
|
|
64
79
|
else if (net.isIPv6(hostname) || hostname.startsWith('[')) {
|
|
65
80
|
const cleanIp = hostname.replace(/^\[|\]$/g, '');
|
|
66
|
-
validateIPv6(cleanIp);
|
|
81
|
+
validateIPv6(cleanIp, url);
|
|
67
82
|
}
|
|
68
83
|
else {
|
|
69
84
|
// Hostname — resolve DNS and validate all resolved IPs
|
|
70
|
-
await resolveAndValidateHost(hostname);
|
|
85
|
+
await resolveAndValidateHost(hostname, url);
|
|
71
86
|
}
|
|
72
87
|
}
|
|
73
|
-
function validateIPv4(ip) {
|
|
88
|
+
function validateIPv4(ip, url) {
|
|
74
89
|
const parts = ip.split('.').map(Number);
|
|
75
90
|
if (parts[0] === 127) {
|
|
76
|
-
throw new
|
|
91
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a loopback address`, 'loopback', url, ip);
|
|
77
92
|
}
|
|
78
93
|
if (parts[0] === 10) {
|
|
79
|
-
throw new
|
|
94
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a private address`, 'private', url, ip);
|
|
80
95
|
}
|
|
81
96
|
if (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31) {
|
|
82
|
-
throw new
|
|
97
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a private address`, 'private', url, ip);
|
|
83
98
|
}
|
|
84
99
|
if (parts[0] === 192 && parts[1] === 168) {
|
|
85
|
-
throw new
|
|
100
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a private address`, 'private', url, ip);
|
|
86
101
|
}
|
|
87
102
|
if (parts[0] === 169 && parts[1] === 254) {
|
|
88
|
-
throw new
|
|
103
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a link-local/metadata address`, 'link_local', url, ip);
|
|
89
104
|
}
|
|
90
105
|
if (parts[0] === 0) {
|
|
91
|
-
throw new
|
|
106
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is not a valid target`, 'invalid_target', url, ip);
|
|
92
107
|
}
|
|
93
108
|
}
|
|
94
|
-
function validateIPv6(ip) {
|
|
109
|
+
function validateIPv6(ip, url) {
|
|
95
110
|
if (ip === '::1') {
|
|
96
|
-
throw new
|
|
111
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a loopback address`, 'loopback', url, ip);
|
|
97
112
|
}
|
|
98
113
|
const lowerIp = ip.toLowerCase();
|
|
99
114
|
// IPv4-mapped IPv6 — ::ffff:x.x.x.x or ::ffff:HHHH:HHHH (hex form)
|
|
@@ -116,19 +131,19 @@ function validateIPv6(ip) {
|
|
|
116
131
|
return; // Unknown format, let it through (conservative)
|
|
117
132
|
}
|
|
118
133
|
}
|
|
119
|
-
validateIPv4(ipv4);
|
|
134
|
+
validateIPv4(ipv4, url);
|
|
120
135
|
return;
|
|
121
136
|
}
|
|
122
137
|
const firstSegment = ip.split(':')[0].toLowerCase();
|
|
123
138
|
if (firstSegment.startsWith('fc') || firstSegment.startsWith('fd')) {
|
|
124
|
-
throw new
|
|
139
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a private address`, 'private', url, ip);
|
|
125
140
|
}
|
|
126
141
|
if (firstSegment.startsWith('fe8') || firstSegment.startsWith('fe9') ||
|
|
127
142
|
firstSegment.startsWith('fea') || firstSegment.startsWith('feb')) {
|
|
128
|
-
throw new
|
|
143
|
+
throw new WebhookUrlValidationError(`Invalid webhook URL: "${ip}" is a link-local address`, 'link_local', url, ip);
|
|
129
144
|
}
|
|
130
145
|
}
|
|
131
|
-
async function resolveAndValidateHost(hostname) {
|
|
146
|
+
async function resolveAndValidateHost(hostname, url) {
|
|
132
147
|
let addresses = [];
|
|
133
148
|
try {
|
|
134
149
|
const ipv4 = await dns.promises.resolve4(hostname);
|
|
@@ -146,10 +161,10 @@ async function resolveAndValidateHost(hostname) {
|
|
|
146
161
|
}
|
|
147
162
|
for (const ip of addresses) {
|
|
148
163
|
if (net.isIPv4(ip)) {
|
|
149
|
-
validateIPv4(ip); // throws if private
|
|
164
|
+
validateIPv4(ip, url); // throws if private
|
|
150
165
|
}
|
|
151
166
|
else if (net.isIPv6(ip)) {
|
|
152
|
-
validateIPv6(ip); // throws if private
|
|
167
|
+
validateIPv6(ip, url); // throws if private
|
|
153
168
|
}
|
|
154
169
|
}
|
|
155
170
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook.url-validator.js","sourceRoot":"","sources":["../src/webhook.url-validator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"webhook.url-validator.js","sourceRoot":"","sources":["../src/webhook.url-validator.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nestarc/webhook",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Outbound webhook delivery for NestJS — HMAC signing, exponential retry, circuit breaker, delivery logs, fan-out, Standard Webhooks compatible",
|
|
5
5
|
"author": "nestarc",
|
|
6
6
|
"license": "MIT",
|