@hasna/events 0.1.0 → 0.1.2
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/dist/cli/index.js +28 -5
- package/dist/commander.js +30 -6
- package/dist/index.d.ts +1 -0
- package/dist/index.js +33 -7
- package/dist/signing.d.ts +1 -0
- package/dist/signing.js +5 -2
- package/dist/storage.js +13 -4
- package/dist/transports.js +3 -1
- package/dist/types.d.ts +1 -0
- package/package.json +3 -3
package/dist/cli/index.js
CHANGED
|
@@ -53,7 +53,7 @@ function channelMatchesEvent(channel, event) {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
// src/storage.ts
|
|
56
|
-
import { mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
56
|
+
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
57
57
|
import { existsSync } from "fs";
|
|
58
58
|
import { homedir } from "os";
|
|
59
59
|
import { join } from "path";
|
|
@@ -75,7 +75,10 @@ class JsonEventsStore {
|
|
|
75
75
|
this.deliveriesPath = join(dataDir, "deliveries.json");
|
|
76
76
|
}
|
|
77
77
|
async init() {
|
|
78
|
-
await mkdir(this.dataDir, { recursive: true });
|
|
78
|
+
await mkdir(this.dataDir, { recursive: true, mode: 448 });
|
|
79
|
+
await chmod(this.dataDir, 448).catch(() => {
|
|
80
|
+
return;
|
|
81
|
+
});
|
|
79
82
|
await this.ensureArrayFile(this.channelsPath);
|
|
80
83
|
await this.ensureArrayFile(this.eventsPath);
|
|
81
84
|
await this.ensureArrayFile(this.deliveriesPath);
|
|
@@ -143,8 +146,11 @@ class JsonEventsStore {
|
|
|
143
146
|
async ensureArrayFile(path) {
|
|
144
147
|
if (!existsSync(path)) {
|
|
145
148
|
await writeFile(path, `[]
|
|
146
|
-
`, "utf-8");
|
|
149
|
+
`, { encoding: "utf-8", mode: 384 });
|
|
147
150
|
}
|
|
151
|
+
await chmod(path, 384).catch(() => {
|
|
152
|
+
return;
|
|
153
|
+
});
|
|
148
154
|
}
|
|
149
155
|
async readJson(path, fallback) {
|
|
150
156
|
try {
|
|
@@ -161,8 +167,11 @@ class JsonEventsStore {
|
|
|
161
167
|
async writeJson(path, value) {
|
|
162
168
|
const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
163
169
|
await writeFile(tempPath, `${JSON.stringify(value, null, 2)}
|
|
164
|
-
`, "utf-8");
|
|
170
|
+
`, { encoding: "utf-8", mode: 384 });
|
|
165
171
|
await rename(tempPath, path);
|
|
172
|
+
await chmod(path, 384).catch(() => {
|
|
173
|
+
return;
|
|
174
|
+
});
|
|
166
175
|
}
|
|
167
176
|
}
|
|
168
177
|
|
|
@@ -172,6 +181,7 @@ import { spawn } from "child_process";
|
|
|
172
181
|
|
|
173
182
|
// src/signing.ts
|
|
174
183
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
184
|
+
var DEFAULT_SIGNATURE_TOLERANCE_MS = 5 * 60 * 1000;
|
|
175
185
|
function buildSignatureBase(timestamp, body) {
|
|
176
186
|
return `${timestamp}.${body}`;
|
|
177
187
|
}
|
|
@@ -371,7 +381,7 @@ class EventsClient {
|
|
|
371
381
|
return this.store.removeChannel(id);
|
|
372
382
|
}
|
|
373
383
|
async emit(input, options = {}) {
|
|
374
|
-
const event = createEvent(input);
|
|
384
|
+
const event = options.redactSensitiveData === false ? createEvent(input) : redactSensitiveKeys(createEvent(input));
|
|
375
385
|
if (options.dedupe !== false) {
|
|
376
386
|
const existing = await this.store.findEventByIdentity({ id: input.id, dedupeKey: event.dedupeKey });
|
|
377
387
|
if (existing) {
|
|
@@ -486,9 +496,22 @@ function sanitizeChannelForOutput(channel) {
|
|
|
486
496
|
function sanitizeChannelsForOutput(channels) {
|
|
487
497
|
return channels.map(sanitizeChannelForOutput);
|
|
488
498
|
}
|
|
499
|
+
function redactSensitiveKeys(event, replacement = "[REDACTED]") {
|
|
500
|
+
return redactValue(event, replacement);
|
|
501
|
+
}
|
|
489
502
|
function shouldRedactKey(key) {
|
|
490
503
|
return /secret|token|password|api[_-]?key|authorization/i.test(key);
|
|
491
504
|
}
|
|
505
|
+
function redactValue(value, replacement) {
|
|
506
|
+
if (Array.isArray(value))
|
|
507
|
+
return value.map((item) => redactValue(item, replacement));
|
|
508
|
+
if (!value || typeof value !== "object")
|
|
509
|
+
return value;
|
|
510
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
511
|
+
key,
|
|
512
|
+
shouldRedactKey(key) ? replacement : redactValue(item, replacement)
|
|
513
|
+
]));
|
|
514
|
+
}
|
|
492
515
|
function setPath(input, path, replacement) {
|
|
493
516
|
const parts = path.split(".");
|
|
494
517
|
let cursor = input;
|
package/dist/commander.js
CHANGED
|
@@ -43,7 +43,7 @@ function channelMatchesEvent(channel, event) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
// src/storage.ts
|
|
46
|
-
import { mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
46
|
+
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
47
47
|
import { existsSync } from "fs";
|
|
48
48
|
import { homedir } from "os";
|
|
49
49
|
import { join } from "path";
|
|
@@ -65,7 +65,10 @@ class JsonEventsStore {
|
|
|
65
65
|
this.deliveriesPath = join(dataDir, "deliveries.json");
|
|
66
66
|
}
|
|
67
67
|
async init() {
|
|
68
|
-
await mkdir(this.dataDir, { recursive: true });
|
|
68
|
+
await mkdir(this.dataDir, { recursive: true, mode: 448 });
|
|
69
|
+
await chmod(this.dataDir, 448).catch(() => {
|
|
70
|
+
return;
|
|
71
|
+
});
|
|
69
72
|
await this.ensureArrayFile(this.channelsPath);
|
|
70
73
|
await this.ensureArrayFile(this.eventsPath);
|
|
71
74
|
await this.ensureArrayFile(this.deliveriesPath);
|
|
@@ -133,8 +136,11 @@ class JsonEventsStore {
|
|
|
133
136
|
async ensureArrayFile(path) {
|
|
134
137
|
if (!existsSync(path)) {
|
|
135
138
|
await writeFile(path, `[]
|
|
136
|
-
`, "utf-8");
|
|
139
|
+
`, { encoding: "utf-8", mode: 384 });
|
|
137
140
|
}
|
|
141
|
+
await chmod(path, 384).catch(() => {
|
|
142
|
+
return;
|
|
143
|
+
});
|
|
138
144
|
}
|
|
139
145
|
async readJson(path, fallback) {
|
|
140
146
|
try {
|
|
@@ -151,13 +157,17 @@ class JsonEventsStore {
|
|
|
151
157
|
async writeJson(path, value) {
|
|
152
158
|
const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
153
159
|
await writeFile(tempPath, `${JSON.stringify(value, null, 2)}
|
|
154
|
-
`, "utf-8");
|
|
160
|
+
`, { encoding: "utf-8", mode: 384 });
|
|
155
161
|
await rename(tempPath, path);
|
|
162
|
+
await chmod(path, 384).catch(() => {
|
|
163
|
+
return;
|
|
164
|
+
});
|
|
156
165
|
}
|
|
157
166
|
}
|
|
158
167
|
|
|
159
168
|
// src/signing.ts
|
|
160
169
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
170
|
+
var DEFAULT_SIGNATURE_TOLERANCE_MS = 5 * 60 * 1000;
|
|
161
171
|
function buildSignatureBase(timestamp, body) {
|
|
162
172
|
return `${timestamp}.${body}`;
|
|
163
173
|
}
|
|
@@ -182,7 +192,8 @@ function isTimestampWithinTolerance(timestamp, toleranceMs, now = Date.now()) {
|
|
|
182
192
|
return Math.abs(reference - parsed) <= toleranceMs;
|
|
183
193
|
}
|
|
184
194
|
function verifyWebhookSignature(secret, timestamp, body, signature, options = {}) {
|
|
185
|
-
|
|
195
|
+
const toleranceMs = options.toleranceMs ?? DEFAULT_SIGNATURE_TOLERANCE_MS;
|
|
196
|
+
if (!isTimestampWithinTolerance(timestamp, toleranceMs, options.now)) {
|
|
186
197
|
return false;
|
|
187
198
|
}
|
|
188
199
|
return verifyPayloadSignature(secret, timestamp, body, signature);
|
|
@@ -381,7 +392,7 @@ class EventsClient {
|
|
|
381
392
|
return this.store.removeChannel(id);
|
|
382
393
|
}
|
|
383
394
|
async emit(input, options = {}) {
|
|
384
|
-
const event = createEvent(input);
|
|
395
|
+
const event = options.redactSensitiveData === false ? createEvent(input) : redactSensitiveKeys(createEvent(input));
|
|
385
396
|
if (options.dedupe !== false) {
|
|
386
397
|
const existing = await this.store.findEventByIdentity({ id: input.id, dedupeKey: event.dedupeKey });
|
|
387
398
|
if (existing) {
|
|
@@ -496,9 +507,22 @@ function sanitizeChannelForOutput(channel) {
|
|
|
496
507
|
function sanitizeChannelsForOutput(channels) {
|
|
497
508
|
return channels.map(sanitizeChannelForOutput);
|
|
498
509
|
}
|
|
510
|
+
function redactSensitiveKeys(event, replacement = "[REDACTED]") {
|
|
511
|
+
return redactValue(event, replacement);
|
|
512
|
+
}
|
|
499
513
|
function shouldRedactKey(key) {
|
|
500
514
|
return /secret|token|password|api[_-]?key|authorization/i.test(key);
|
|
501
515
|
}
|
|
516
|
+
function redactValue(value, replacement) {
|
|
517
|
+
if (Array.isArray(value))
|
|
518
|
+
return value.map((item) => redactValue(item, replacement));
|
|
519
|
+
if (!value || typeof value !== "object")
|
|
520
|
+
return value;
|
|
521
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
522
|
+
key,
|
|
523
|
+
shouldRedactKey(key) ? replacement : redactValue(item, replacement)
|
|
524
|
+
]));
|
|
525
|
+
}
|
|
502
526
|
function setPath(input, path, replacement) {
|
|
503
527
|
const parts = path.split(".");
|
|
504
528
|
let cursor = input;
|
package/dist/index.d.ts
CHANGED
|
@@ -35,3 +35,4 @@ export declare class EventsClient {
|
|
|
35
35
|
export declare function redactPaths<T extends EventEnvelope>(event: T, paths: string[], replacement?: string): T;
|
|
36
36
|
export declare function sanitizeChannelForOutput(channel: ChannelConfig): ChannelConfig;
|
|
37
37
|
export declare function sanitizeChannelsForOutput(channels: ChannelConfig[]): ChannelConfig[];
|
|
38
|
+
export declare function redactSensitiveKeys<T extends EventEnvelope>(event: T, replacement?: string): T;
|
package/dist/index.js
CHANGED
|
@@ -43,7 +43,7 @@ function channelMatchesEvent(channel, event) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
// src/storage.ts
|
|
46
|
-
import { mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
46
|
+
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
47
47
|
import { existsSync } from "fs";
|
|
48
48
|
import { homedir } from "os";
|
|
49
49
|
import { join } from "path";
|
|
@@ -65,7 +65,10 @@ class JsonEventsStore {
|
|
|
65
65
|
this.deliveriesPath = join(dataDir, "deliveries.json");
|
|
66
66
|
}
|
|
67
67
|
async init() {
|
|
68
|
-
await mkdir(this.dataDir, { recursive: true });
|
|
68
|
+
await mkdir(this.dataDir, { recursive: true, mode: 448 });
|
|
69
|
+
await chmod(this.dataDir, 448).catch(() => {
|
|
70
|
+
return;
|
|
71
|
+
});
|
|
69
72
|
await this.ensureArrayFile(this.channelsPath);
|
|
70
73
|
await this.ensureArrayFile(this.eventsPath);
|
|
71
74
|
await this.ensureArrayFile(this.deliveriesPath);
|
|
@@ -133,8 +136,11 @@ class JsonEventsStore {
|
|
|
133
136
|
async ensureArrayFile(path) {
|
|
134
137
|
if (!existsSync(path)) {
|
|
135
138
|
await writeFile(path, `[]
|
|
136
|
-
`, "utf-8");
|
|
139
|
+
`, { encoding: "utf-8", mode: 384 });
|
|
137
140
|
}
|
|
141
|
+
await chmod(path, 384).catch(() => {
|
|
142
|
+
return;
|
|
143
|
+
});
|
|
138
144
|
}
|
|
139
145
|
async readJson(path, fallback) {
|
|
140
146
|
try {
|
|
@@ -151,13 +157,17 @@ class JsonEventsStore {
|
|
|
151
157
|
async writeJson(path, value) {
|
|
152
158
|
const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
153
159
|
await writeFile(tempPath, `${JSON.stringify(value, null, 2)}
|
|
154
|
-
`, "utf-8");
|
|
160
|
+
`, { encoding: "utf-8", mode: 384 });
|
|
155
161
|
await rename(tempPath, path);
|
|
162
|
+
await chmod(path, 384).catch(() => {
|
|
163
|
+
return;
|
|
164
|
+
});
|
|
156
165
|
}
|
|
157
166
|
}
|
|
158
167
|
|
|
159
168
|
// src/signing.ts
|
|
160
169
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
170
|
+
var DEFAULT_SIGNATURE_TOLERANCE_MS = 5 * 60 * 1000;
|
|
161
171
|
function buildSignatureBase(timestamp, body) {
|
|
162
172
|
return `${timestamp}.${body}`;
|
|
163
173
|
}
|
|
@@ -182,7 +192,8 @@ function isTimestampWithinTolerance(timestamp, toleranceMs, now = Date.now()) {
|
|
|
182
192
|
return Math.abs(reference - parsed) <= toleranceMs;
|
|
183
193
|
}
|
|
184
194
|
function verifyWebhookSignature(secret, timestamp, body, signature, options = {}) {
|
|
185
|
-
|
|
195
|
+
const toleranceMs = options.toleranceMs ?? DEFAULT_SIGNATURE_TOLERANCE_MS;
|
|
196
|
+
if (!isTimestampWithinTolerance(timestamp, toleranceMs, options.now)) {
|
|
186
197
|
return false;
|
|
187
198
|
}
|
|
188
199
|
return verifyPayloadSignature(secret, timestamp, body, signature);
|
|
@@ -381,7 +392,7 @@ class EventsClient {
|
|
|
381
392
|
return this.store.removeChannel(id);
|
|
382
393
|
}
|
|
383
394
|
async emit(input, options = {}) {
|
|
384
|
-
const event = createEvent(input);
|
|
395
|
+
const event = options.redactSensitiveData === false ? createEvent(input) : redactSensitiveKeys(createEvent(input));
|
|
385
396
|
if (options.dedupe !== false) {
|
|
386
397
|
const existing = await this.store.findEventByIdentity({ id: input.id, dedupeKey: event.dedupeKey });
|
|
387
398
|
if (existing) {
|
|
@@ -496,9 +507,22 @@ function sanitizeChannelForOutput(channel) {
|
|
|
496
507
|
function sanitizeChannelsForOutput(channels) {
|
|
497
508
|
return channels.map(sanitizeChannelForOutput);
|
|
498
509
|
}
|
|
510
|
+
function redactSensitiveKeys(event, replacement = "[REDACTED]") {
|
|
511
|
+
return redactValue(event, replacement);
|
|
512
|
+
}
|
|
499
513
|
function shouldRedactKey(key) {
|
|
500
514
|
return /secret|token|password|api[_-]?key|authorization/i.test(key);
|
|
501
515
|
}
|
|
516
|
+
function redactValue(value, replacement) {
|
|
517
|
+
if (Array.isArray(value))
|
|
518
|
+
return value.map((item) => redactValue(item, replacement));
|
|
519
|
+
if (!value || typeof value !== "object")
|
|
520
|
+
return value;
|
|
521
|
+
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
522
|
+
key,
|
|
523
|
+
shouldRedactKey(key) ? replacement : redactValue(item, replacement)
|
|
524
|
+
]));
|
|
525
|
+
}
|
|
502
526
|
function setPath(input, path, replacement) {
|
|
503
527
|
const parts = path.split(".");
|
|
504
528
|
let cursor = input;
|
|
@@ -530,6 +554,7 @@ export {
|
|
|
530
554
|
signPayload,
|
|
531
555
|
sanitizeChannelsForOutput,
|
|
532
556
|
sanitizeChannelForOutput,
|
|
557
|
+
redactSensitiveKeys,
|
|
533
558
|
redactPaths,
|
|
534
559
|
matchString,
|
|
535
560
|
isTimestampWithinTolerance,
|
|
@@ -546,5 +571,6 @@ export {
|
|
|
546
571
|
JsonEventsStore,
|
|
547
572
|
HASNA_EVENTS_HOME_ENV,
|
|
548
573
|
HASNA_EVENTS_DIR_ENV,
|
|
549
|
-
EventsClient
|
|
574
|
+
EventsClient,
|
|
575
|
+
DEFAULT_SIGNATURE_TOLERANCE_MS
|
|
550
576
|
};
|
package/dist/signing.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export interface SignatureVerificationOptions {
|
|
|
2
2
|
toleranceMs?: number;
|
|
3
3
|
now?: number | Date;
|
|
4
4
|
}
|
|
5
|
+
export declare const DEFAULT_SIGNATURE_TOLERANCE_MS: number;
|
|
5
6
|
export declare function buildSignatureBase(timestamp: string, body: string): string;
|
|
6
7
|
export declare function signPayload(secret: string, timestamp: string, body: string): string;
|
|
7
8
|
export declare function verifyPayloadSignature(secret: string, timestamp: string, body: string, signature: string): boolean;
|
package/dist/signing.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// src/signing.ts
|
|
3
3
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
4
|
+
var DEFAULT_SIGNATURE_TOLERANCE_MS = 5 * 60 * 1000;
|
|
4
5
|
function buildSignatureBase(timestamp, body) {
|
|
5
6
|
return `${timestamp}.${body}`;
|
|
6
7
|
}
|
|
@@ -25,7 +26,8 @@ function isTimestampWithinTolerance(timestamp, toleranceMs, now = Date.now()) {
|
|
|
25
26
|
return Math.abs(reference - parsed) <= toleranceMs;
|
|
26
27
|
}
|
|
27
28
|
function verifyWebhookSignature(secret, timestamp, body, signature, options = {}) {
|
|
28
|
-
|
|
29
|
+
const toleranceMs = options.toleranceMs ?? DEFAULT_SIGNATURE_TOLERANCE_MS;
|
|
30
|
+
if (!isTimestampWithinTolerance(timestamp, toleranceMs, options.now)) {
|
|
29
31
|
return false;
|
|
30
32
|
}
|
|
31
33
|
return verifyPayloadSignature(secret, timestamp, body, signature);
|
|
@@ -35,5 +37,6 @@ export {
|
|
|
35
37
|
verifyPayloadSignature,
|
|
36
38
|
signPayload,
|
|
37
39
|
isTimestampWithinTolerance,
|
|
38
|
-
buildSignatureBase
|
|
40
|
+
buildSignatureBase,
|
|
41
|
+
DEFAULT_SIGNATURE_TOLERANCE_MS
|
|
39
42
|
};
|
package/dist/storage.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// src/storage.ts
|
|
3
|
-
import { mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
3
|
+
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
4
4
|
import { existsSync } from "fs";
|
|
5
5
|
import { homedir } from "os";
|
|
6
6
|
import { join } from "path";
|
|
@@ -22,7 +22,10 @@ class JsonEventsStore {
|
|
|
22
22
|
this.deliveriesPath = join(dataDir, "deliveries.json");
|
|
23
23
|
}
|
|
24
24
|
async init() {
|
|
25
|
-
await mkdir(this.dataDir, { recursive: true });
|
|
25
|
+
await mkdir(this.dataDir, { recursive: true, mode: 448 });
|
|
26
|
+
await chmod(this.dataDir, 448).catch(() => {
|
|
27
|
+
return;
|
|
28
|
+
});
|
|
26
29
|
await this.ensureArrayFile(this.channelsPath);
|
|
27
30
|
await this.ensureArrayFile(this.eventsPath);
|
|
28
31
|
await this.ensureArrayFile(this.deliveriesPath);
|
|
@@ -90,8 +93,11 @@ class JsonEventsStore {
|
|
|
90
93
|
async ensureArrayFile(path) {
|
|
91
94
|
if (!existsSync(path)) {
|
|
92
95
|
await writeFile(path, `[]
|
|
93
|
-
`, "utf-8");
|
|
96
|
+
`, { encoding: "utf-8", mode: 384 });
|
|
94
97
|
}
|
|
98
|
+
await chmod(path, 384).catch(() => {
|
|
99
|
+
return;
|
|
100
|
+
});
|
|
95
101
|
}
|
|
96
102
|
async readJson(path, fallback) {
|
|
97
103
|
try {
|
|
@@ -108,8 +114,11 @@ class JsonEventsStore {
|
|
|
108
114
|
async writeJson(path, value) {
|
|
109
115
|
const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
110
116
|
await writeFile(tempPath, `${JSON.stringify(value, null, 2)}
|
|
111
|
-
`, "utf-8");
|
|
117
|
+
`, { encoding: "utf-8", mode: 384 });
|
|
112
118
|
await rename(tempPath, path);
|
|
119
|
+
await chmod(path, 384).catch(() => {
|
|
120
|
+
return;
|
|
121
|
+
});
|
|
113
122
|
}
|
|
114
123
|
}
|
|
115
124
|
export {
|
package/dist/transports.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// src/signing.ts
|
|
3
3
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
4
|
+
var DEFAULT_SIGNATURE_TOLERANCE_MS = 5 * 60 * 1000;
|
|
4
5
|
function buildSignatureBase(timestamp, body) {
|
|
5
6
|
return `${timestamp}.${body}`;
|
|
6
7
|
}
|
|
@@ -25,7 +26,8 @@ function isTimestampWithinTolerance(timestamp, toleranceMs, now = Date.now()) {
|
|
|
25
26
|
return Math.abs(reference - parsed) <= toleranceMs;
|
|
26
27
|
}
|
|
27
28
|
function verifyWebhookSignature(secret, timestamp, body, signature, options = {}) {
|
|
28
|
-
|
|
29
|
+
const toleranceMs = options.toleranceMs ?? DEFAULT_SIGNATURE_TOLERANCE_MS;
|
|
30
|
+
if (!isTimestampWithinTolerance(timestamp, toleranceMs, options.now)) {
|
|
29
31
|
return false;
|
|
30
32
|
}
|
|
31
33
|
return verifyPayloadSignature(secret, timestamp, body, signature);
|
package/dist/types.d.ts
CHANGED
|
@@ -99,6 +99,7 @@ export type EventRedactor = (event: EventEnvelope, channel: ChannelConfig) => Ev
|
|
|
99
99
|
export interface EmitOptions {
|
|
100
100
|
deliver?: boolean;
|
|
101
101
|
dedupe?: boolean;
|
|
102
|
+
redactSensitiveData?: boolean;
|
|
102
103
|
}
|
|
103
104
|
export interface ReplayOptions {
|
|
104
105
|
eventId?: string;
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/events",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Shared event envelopes, local subscriptions, and webhook delivery for Hasna open-source apps",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
8
|
"bin": {
|
|
9
|
-
"events": "
|
|
10
|
-
"hasna-events": "
|
|
9
|
+
"events": "dist/cli/index.js",
|
|
10
|
+
"hasna-events": "dist/cli/index.js"
|
|
11
11
|
},
|
|
12
12
|
"exports": {
|
|
13
13
|
".": {
|