@dotsetlabs/tollgate 0.2.2 → 0.3.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.
Files changed (194) hide show
  1. package/README.md +138 -0
  2. package/dist/analyzers/filesystem.d.ts +5 -0
  3. package/dist/analyzers/filesystem.d.ts.map +1 -1
  4. package/dist/analyzers/filesystem.js +61 -7
  5. package/dist/analyzers/filesystem.js.map +1 -1
  6. package/dist/analyzers/loader.d.ts +5 -0
  7. package/dist/analyzers/loader.d.ts.map +1 -1
  8. package/dist/analyzers/loader.js +112 -6
  9. package/dist/analyzers/loader.js.map +1 -1
  10. package/dist/analyzers/prompt-injection.d.ts +1 -0
  11. package/dist/analyzers/prompt-injection.d.ts.map +1 -1
  12. package/dist/analyzers/prompt-injection.js +48 -2
  13. package/dist/analyzers/prompt-injection.js.map +1 -1
  14. package/dist/analyzers/shell.d.ts +8 -0
  15. package/dist/analyzers/shell.d.ts.map +1 -1
  16. package/dist/analyzers/shell.js +109 -8
  17. package/dist/analyzers/shell.js.map +1 -1
  18. package/dist/analyzers/sql.d.ts.map +1 -1
  19. package/dist/analyzers/sql.js +8 -7
  20. package/dist/analyzers/sql.js.map +1 -1
  21. package/dist/approval/interactive.d.ts +1 -1
  22. package/dist/approval/interactive.d.ts.map +1 -1
  23. package/dist/approval/interactive.js +13 -1
  24. package/dist/approval/interactive.js.map +1 -1
  25. package/dist/approval/rate-limiter.d.ts +115 -0
  26. package/dist/approval/rate-limiter.d.ts.map +1 -0
  27. package/dist/approval/rate-limiter.js +200 -0
  28. package/dist/approval/rate-limiter.js.map +1 -0
  29. package/dist/approval/url-validator.d.ts +51 -0
  30. package/dist/approval/url-validator.d.ts.map +1 -0
  31. package/dist/approval/url-validator.js +184 -0
  32. package/dist/approval/url-validator.js.map +1 -0
  33. package/dist/approval/webhook.d.ts +48 -0
  34. package/dist/approval/webhook.d.ts.map +1 -1
  35. package/dist/approval/webhook.js +89 -0
  36. package/dist/approval/webhook.js.map +1 -1
  37. package/dist/audit/integrity.d.ts +107 -0
  38. package/dist/audit/integrity.d.ts.map +1 -0
  39. package/dist/audit/integrity.js +191 -0
  40. package/dist/audit/integrity.js.map +1 -0
  41. package/dist/audit/logger.d.ts.map +1 -1
  42. package/dist/audit/logger.js +6 -5
  43. package/dist/audit/logger.js.map +1 -1
  44. package/dist/audit/redaction.js +6 -4
  45. package/dist/audit/redaction.js.map +1 -1
  46. package/dist/cli/commands/guard.d.ts +97 -0
  47. package/dist/cli/commands/guard.d.ts.map +1 -0
  48. package/dist/cli/commands/guard.js +456 -0
  49. package/dist/cli/commands/guard.js.map +1 -0
  50. package/dist/cli/commands/serve.js +1 -1
  51. package/dist/cli/commands/serve.js.map +1 -1
  52. package/dist/cli/index.js +3 -0
  53. package/dist/cli/index.js.map +1 -1
  54. package/dist/cli/input-validation.d.ts +83 -0
  55. package/dist/cli/input-validation.d.ts.map +1 -0
  56. package/dist/cli/input-validation.js +237 -0
  57. package/dist/cli/input-validation.js.map +1 -0
  58. package/dist/cli/ui.js +2 -2
  59. package/dist/cli/ui.js.map +1 -1
  60. package/dist/guard/alternatives/index.d.ts +68 -0
  61. package/dist/guard/alternatives/index.d.ts.map +1 -0
  62. package/dist/guard/alternatives/index.js +224 -0
  63. package/dist/guard/alternatives/index.js.map +1 -0
  64. package/dist/guard/alternatives/registry.d.ts +16 -0
  65. package/dist/guard/alternatives/registry.d.ts.map +1 -0
  66. package/dist/guard/alternatives/registry.js +518 -0
  67. package/dist/guard/alternatives/registry.js.map +1 -0
  68. package/dist/guard/alternatives/types.d.ts +86 -0
  69. package/dist/guard/alternatives/types.d.ts.map +1 -0
  70. package/dist/guard/alternatives/types.js +5 -0
  71. package/dist/guard/alternatives/types.js.map +1 -0
  72. package/dist/guard/approval/enhanced-terminal.d.ts +110 -0
  73. package/dist/guard/approval/enhanced-terminal.d.ts.map +1 -0
  74. package/dist/guard/approval/enhanced-terminal.js +387 -0
  75. package/dist/guard/approval/enhanced-terminal.js.map +1 -0
  76. package/dist/guard/config.d.ts +80 -0
  77. package/dist/guard/config.d.ts.map +1 -0
  78. package/dist/guard/config.js +260 -0
  79. package/dist/guard/config.js.map +1 -0
  80. package/dist/guard/context/directory.d.ts +35 -0
  81. package/dist/guard/context/directory.d.ts.map +1 -0
  82. package/dist/guard/context/directory.js +243 -0
  83. package/dist/guard/context/directory.js.map +1 -0
  84. package/dist/guard/context/environment.d.ts +31 -0
  85. package/dist/guard/context/environment.d.ts.map +1 -0
  86. package/dist/guard/context/environment.js +204 -0
  87. package/dist/guard/context/environment.js.map +1 -0
  88. package/dist/guard/context/git.d.ts +52 -0
  89. package/dist/guard/context/git.d.ts.map +1 -0
  90. package/dist/guard/context/git.js +278 -0
  91. package/dist/guard/context/git.js.map +1 -0
  92. package/dist/guard/context/index.d.ts +64 -0
  93. package/dist/guard/context/index.d.ts.map +1 -0
  94. package/dist/guard/context/index.js +227 -0
  95. package/dist/guard/context/index.js.map +1 -0
  96. package/dist/guard/context/project.d.ts +47 -0
  97. package/dist/guard/context/project.d.ts.map +1 -0
  98. package/dist/guard/context/project.js +281 -0
  99. package/dist/guard/context/project.js.map +1 -0
  100. package/dist/guard/context/types.d.ts +152 -0
  101. package/dist/guard/context/types.d.ts.map +1 -0
  102. package/dist/guard/context/types.js +7 -0
  103. package/dist/guard/context/types.js.map +1 -0
  104. package/dist/guard/engine.d.ts +107 -0
  105. package/dist/guard/engine.d.ts.map +1 -0
  106. package/dist/guard/engine.js +430 -0
  107. package/dist/guard/engine.js.map +1 -0
  108. package/dist/guard/enhanced-engine.d.ts +151 -0
  109. package/dist/guard/enhanced-engine.d.ts.map +1 -0
  110. package/dist/guard/enhanced-engine.js +622 -0
  111. package/dist/guard/enhanced-engine.js.map +1 -0
  112. package/dist/guard/hooks/index.d.ts +50 -0
  113. package/dist/guard/hooks/index.d.ts.map +1 -0
  114. package/dist/guard/hooks/index.js +325 -0
  115. package/dist/guard/hooks/index.js.map +1 -0
  116. package/dist/guard/index.d.ts +29 -0
  117. package/dist/guard/index.d.ts.map +1 -0
  118. package/dist/guard/index.js +31 -0
  119. package/dist/guard/index.js.map +1 -0
  120. package/dist/guard/learning/index.d.ts +136 -0
  121. package/dist/guard/learning/index.d.ts.map +1 -0
  122. package/dist/guard/learning/index.js +314 -0
  123. package/dist/guard/learning/index.js.map +1 -0
  124. package/dist/guard/learning/pattern-extractor.d.ts +50 -0
  125. package/dist/guard/learning/pattern-extractor.d.ts.map +1 -0
  126. package/dist/guard/learning/pattern-extractor.js +372 -0
  127. package/dist/guard/learning/pattern-extractor.js.map +1 -0
  128. package/dist/guard/learning/rule-suggester.d.ts +67 -0
  129. package/dist/guard/learning/rule-suggester.d.ts.map +1 -0
  130. package/dist/guard/learning/rule-suggester.js +345 -0
  131. package/dist/guard/learning/rule-suggester.js.map +1 -0
  132. package/dist/guard/learning/types.d.ts +211 -0
  133. package/dist/guard/learning/types.d.ts.map +1 -0
  134. package/dist/guard/learning/types.js +18 -0
  135. package/dist/guard/learning/types.js.map +1 -0
  136. package/dist/guard/preview/effects.d.ts +15 -0
  137. package/dist/guard/preview/effects.d.ts.map +1 -0
  138. package/dist/guard/preview/effects.js +413 -0
  139. package/dist/guard/preview/effects.js.map +1 -0
  140. package/dist/guard/preview/index.d.ts +49 -0
  141. package/dist/guard/preview/index.d.ts.map +1 -0
  142. package/dist/guard/preview/index.js +196 -0
  143. package/dist/guard/preview/index.js.map +1 -0
  144. package/dist/guard/preview/parser.d.ts +34 -0
  145. package/dist/guard/preview/parser.d.ts.map +1 -0
  146. package/dist/guard/preview/parser.js +292 -0
  147. package/dist/guard/preview/parser.js.map +1 -0
  148. package/dist/guard/preview/types.d.ts +140 -0
  149. package/dist/guard/preview/types.d.ts.map +1 -0
  150. package/dist/guard/preview/types.js +5 -0
  151. package/dist/guard/preview/types.js.map +1 -0
  152. package/dist/guard/reversibility/index.d.ts +88 -0
  153. package/dist/guard/reversibility/index.d.ts.map +1 -0
  154. package/dist/guard/reversibility/index.js +310 -0
  155. package/dist/guard/reversibility/index.js.map +1 -0
  156. package/dist/guard/types.d.ts +192 -0
  157. package/dist/guard/types.d.ts.map +1 -0
  158. package/dist/guard/types.js +8 -0
  159. package/dist/guard/types.js.map +1 -0
  160. package/dist/index.d.ts +10 -0
  161. package/dist/index.d.ts.map +1 -1
  162. package/dist/index.js +24 -0
  163. package/dist/index.js.map +1 -1
  164. package/dist/orchestrator/manager.d.ts.map +1 -1
  165. package/dist/orchestrator/manager.js +6 -1
  166. package/dist/orchestrator/manager.js.map +1 -1
  167. package/dist/policy/engine.d.ts.map +1 -1
  168. package/dist/policy/engine.js +11 -3
  169. package/dist/policy/engine.js.map +1 -1
  170. package/dist/policy/parser.d.ts.map +1 -1
  171. package/dist/policy/parser.js +3 -0
  172. package/dist/policy/parser.js.map +1 -1
  173. package/dist/proxy/server.d.ts.map +1 -1
  174. package/dist/proxy/server.js +8 -6
  175. package/dist/proxy/server.js.map +1 -1
  176. package/dist/session/manager.d.ts +2 -2
  177. package/dist/session/manager.d.ts.map +1 -1
  178. package/dist/session/manager.js +106 -88
  179. package/dist/session/manager.js.map +1 -1
  180. package/dist/session/signing.d.ts +88 -0
  181. package/dist/session/signing.d.ts.map +1 -0
  182. package/dist/session/signing.js +166 -0
  183. package/dist/session/signing.js.map +1 -0
  184. package/dist/session/types.d.ts +2 -0
  185. package/dist/session/types.d.ts.map +1 -1
  186. package/dist/session/types.js.map +1 -1
  187. package/dist/utils/security-logger.d.ts +146 -0
  188. package/dist/utils/security-logger.d.ts.map +1 -0
  189. package/dist/utils/security-logger.js +222 -0
  190. package/dist/utils/security-logger.js.map +1 -0
  191. package/dist/wizard.d.ts.map +1 -1
  192. package/dist/wizard.js +7 -1
  193. package/dist/wizard.js.map +1 -1
  194. package/package.json +3 -2
@@ -0,0 +1,200 @@
1
+ /**
2
+ * Rate Limiter for Tollgate Approval Requests
3
+ *
4
+ * Prevents DoS and approval fatigue attacks by limiting the rate of
5
+ * approval prompts per server/tool combination.
6
+ *
7
+ * Uses a sliding window algorithm for accurate rate limiting.
8
+ *
9
+ * @module approval/rate-limiter
10
+ */
11
+ import { approvalLogger as logger } from '../utils/logger.js';
12
+ // =============================================================================
13
+ // Rate Limiter Implementation
14
+ // =============================================================================
15
+ /**
16
+ * Sliding window rate limiter for approval requests.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const limiter = new RateLimiter({ maxPerMinute: 30, maxPerHour: 300 });
21
+ *
22
+ * const result = limiter.check('postgres:query');
23
+ * if (!result.allowed) {
24
+ * console.log(`Rate limited: ${result.reason}`);
25
+ * }
26
+ * ```
27
+ */
28
+ export class RateLimiter {
29
+ config;
30
+ requests = new Map();
31
+ stats = {
32
+ totalRequests: 0,
33
+ allowedRequests: 0,
34
+ deniedRequests: 0,
35
+ activeKeys: 0,
36
+ deniedByKey: {},
37
+ };
38
+ cleanupInterval = null;
39
+ constructor(config = {}) {
40
+ this.config = {
41
+ maxPerMinute: config.maxPerMinute ?? 30,
42
+ maxPerHour: config.maxPerHour ?? 300,
43
+ autoDeny: config.autoDeny ?? true,
44
+ windowMs: config.windowMs ?? 3600000, // 1 hour
45
+ };
46
+ // Start cleanup interval
47
+ this.cleanupInterval = setInterval(() => this.cleanup(), 60000);
48
+ }
49
+ /**
50
+ * Check if a request is allowed for the given key.
51
+ *
52
+ * @param key - Unique key for rate limiting (e.g., "server:tool")
53
+ * @returns Rate limit result
54
+ */
55
+ check(key) {
56
+ const now = Date.now();
57
+ const oneMinuteAgo = now - 60000;
58
+ const oneHourAgo = now - 3600000;
59
+ this.stats.totalRequests++;
60
+ // Get or create request history for this key
61
+ const history = this.requests.get(key) ?? [];
62
+ // Filter to requests within the window
63
+ const recentMinute = history.filter((t) => t > oneMinuteAgo);
64
+ const recentHour = history.filter((t) => t > oneHourAgo);
65
+ const remainingPerMinute = Math.max(0, this.config.maxPerMinute - recentMinute.length);
66
+ const remainingPerHour = Math.max(0, this.config.maxPerHour - recentHour.length);
67
+ // Check if rate exceeded
68
+ if (recentMinute.length >= this.config.maxPerMinute) {
69
+ this.stats.deniedRequests++;
70
+ this.stats.deniedByKey[key] = (this.stats.deniedByKey[key] ?? 0) + 1;
71
+ const oldestInMinute = Math.min(...recentMinute);
72
+ const retryAfterMs = oldestInMinute + 60000 - now;
73
+ logger.warn('Rate limit exceeded (per minute)', {
74
+ key,
75
+ count: recentMinute.length,
76
+ limit: this.config.maxPerMinute,
77
+ });
78
+ return {
79
+ allowed: false,
80
+ reason: `Rate limit exceeded: ${recentMinute.length}/${this.config.maxPerMinute} requests per minute`,
81
+ remainingPerMinute: 0,
82
+ remainingPerHour,
83
+ retryAfterMs: Math.max(0, retryAfterMs),
84
+ };
85
+ }
86
+ if (recentHour.length >= this.config.maxPerHour) {
87
+ this.stats.deniedRequests++;
88
+ this.stats.deniedByKey[key] = (this.stats.deniedByKey[key] ?? 0) + 1;
89
+ const oldestInHour = Math.min(...recentHour);
90
+ const retryAfterMs = oldestInHour + 3600000 - now;
91
+ logger.warn('Rate limit exceeded (per hour)', {
92
+ key,
93
+ count: recentHour.length,
94
+ limit: this.config.maxPerHour,
95
+ });
96
+ return {
97
+ allowed: false,
98
+ reason: `Rate limit exceeded: ${recentHour.length}/${this.config.maxPerHour} requests per hour`,
99
+ remainingPerMinute,
100
+ remainingPerHour: 0,
101
+ retryAfterMs: Math.max(0, retryAfterMs),
102
+ };
103
+ }
104
+ // Request allowed - record it
105
+ recentHour.push(now);
106
+ this.requests.set(key, recentHour);
107
+ this.stats.allowedRequests++;
108
+ this.stats.activeKeys = this.requests.size;
109
+ return {
110
+ allowed: true,
111
+ remainingPerMinute: remainingPerMinute - 1,
112
+ remainingPerHour: remainingPerHour - 1,
113
+ };
114
+ }
115
+ /**
116
+ * Record a request without checking limits.
117
+ * Useful for tracking requests that bypass rate limiting.
118
+ */
119
+ record(key) {
120
+ const now = Date.now();
121
+ const history = this.requests.get(key) ?? [];
122
+ history.push(now);
123
+ this.requests.set(key, history);
124
+ }
125
+ /**
126
+ * Reset rate limiting for a specific key.
127
+ */
128
+ reset(key) {
129
+ this.requests.delete(key);
130
+ delete this.stats.deniedByKey[key];
131
+ }
132
+ /**
133
+ * Reset all rate limiting.
134
+ */
135
+ resetAll() {
136
+ this.requests.clear();
137
+ this.stats = {
138
+ totalRequests: 0,
139
+ allowedRequests: 0,
140
+ deniedRequests: 0,
141
+ activeKeys: 0,
142
+ deniedByKey: {},
143
+ };
144
+ }
145
+ /**
146
+ * Get current statistics.
147
+ */
148
+ getStats() {
149
+ return { ...this.stats, activeKeys: this.requests.size };
150
+ }
151
+ /**
152
+ * Clean up old entries to prevent memory leaks.
153
+ */
154
+ cleanup() {
155
+ const cutoff = Date.now() - this.config.windowMs;
156
+ for (const [key, history] of this.requests.entries()) {
157
+ const recent = history.filter((t) => t > cutoff);
158
+ if (recent.length === 0) {
159
+ this.requests.delete(key);
160
+ }
161
+ else {
162
+ this.requests.set(key, recent);
163
+ }
164
+ }
165
+ this.stats.activeKeys = this.requests.size;
166
+ }
167
+ /**
168
+ * Close the rate limiter and stop cleanup interval.
169
+ */
170
+ close() {
171
+ if (this.cleanupInterval) {
172
+ clearInterval(this.cleanupInterval);
173
+ this.cleanupInterval = null;
174
+ }
175
+ }
176
+ }
177
+ // =============================================================================
178
+ // Global Rate Limiter Instance
179
+ // =============================================================================
180
+ let globalRateLimiter = null;
181
+ /**
182
+ * Get the global rate limiter instance.
183
+ * Creates one if it doesn't exist.
184
+ */
185
+ export function getRateLimiter(config) {
186
+ if (!globalRateLimiter) {
187
+ globalRateLimiter = new RateLimiter(config);
188
+ }
189
+ return globalRateLimiter;
190
+ }
191
+ /**
192
+ * Reset the global rate limiter.
193
+ */
194
+ export function resetGlobalRateLimiter() {
195
+ if (globalRateLimiter) {
196
+ globalRateLimiter.close();
197
+ globalRateLimiter = null;
198
+ }
199
+ }
200
+ //# sourceMappingURL=rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/approval/rate-limiter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Webhook URL Validator
3
+ *
4
+ * Validates webhook URLs to prevent SSRF attacks via configuration.
5
+ * Blocks localhost, private IPs, and dangerous protocols.
6
+ *
7
+ * @module approval/url-validator
8
+ */
9
+ /**
10
+ * Options for URL validation.
11
+ */
12
+ export interface UrlValidationOptions {
13
+ /** Allow localhost URLs (default: false) */
14
+ allowLocalhost?: boolean;
15
+ /** Allow private IP ranges (default: false) */
16
+ allowPrivateIps?: boolean;
17
+ /** Require HTTPS (default: true in production) */
18
+ requireHttps?: boolean;
19
+ /** Additional blocked hosts */
20
+ blockedHosts?: string[];
21
+ /** Additional allowed hosts (overrides blocks) */
22
+ allowedHosts?: string[];
23
+ }
24
+ /**
25
+ * Result of URL validation.
26
+ */
27
+ export interface UrlValidationResult {
28
+ /** Whether the URL is valid */
29
+ valid: boolean;
30
+ /** Reason if invalid */
31
+ reason?: string;
32
+ /** Parsed URL (if valid) */
33
+ url?: URL;
34
+ }
35
+ /**
36
+ * Validate a webhook URL.
37
+ *
38
+ * @param urlString - URL to validate
39
+ * @param options - Validation options
40
+ * @returns Validation result
41
+ */
42
+ export declare function validateWebhookUrl(urlString: string, options?: UrlValidationOptions): UrlValidationResult;
43
+ /**
44
+ * Validate URL and throw on invalid.
45
+ *
46
+ * @param urlString - URL to validate
47
+ * @param options - Validation options
48
+ * @throws Error if URL is invalid
49
+ */
50
+ export declare function assertValidWebhookUrl(urlString: string, options?: UrlValidationOptions): URL;
51
+ //# sourceMappingURL=url-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-validator.d.ts","sourceRoot":"","sources":["../../src/approval/url-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAQH;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACjC,4CAA4C;IAC5C,cAAc,CAAC,EAAE,OAAO,CAAC;IAEzB,+CAA+C;IAC/C,eAAe,CAAC,EAAE,OAAO,CAAC;IAE1B,kDAAkD;IAClD,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,+BAA+B;IAC/B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IAExB,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC,+BAA+B;IAC/B,KAAK,EAAE,OAAO,CAAC;IAEf,wBAAwB;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,4BAA4B;IAC5B,GAAG,CAAC,EAAE,GAAG,CAAC;CACb;AAyDD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAC9B,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,oBAAyB,GACnC,mBAAmB,CAyFrB;AAsCD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACjC,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,oBAAyB,GACnC,GAAG,CAML"}
@@ -0,0 +1,184 @@
1
+ /**
2
+ * Webhook URL Validator
3
+ *
4
+ * Validates webhook URLs to prevent SSRF attacks via configuration.
5
+ * Blocks localhost, private IPs, and dangerous protocols.
6
+ *
7
+ * @module approval/url-validator
8
+ */
9
+ import { approvalLogger as logger } from '../utils/logger.js';
10
+ // =============================================================================
11
+ // URL Validation Patterns
12
+ // =============================================================================
13
+ /**
14
+ * Localhost patterns.
15
+ */
16
+ const LOCALHOST_PATTERNS = [
17
+ 'localhost',
18
+ '127.0.0.1',
19
+ '::1',
20
+ '0.0.0.0',
21
+ '[::1]',
22
+ '0:0:0:0:0:0:0:1',
23
+ '[0:0:0:0:0:0:0:1]',
24
+ ];
25
+ /**
26
+ * Private IP ranges (RFC 1918).
27
+ */
28
+ const PRIVATE_IP_PATTERNS = [
29
+ /^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$/,
30
+ /^172\.(1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}$/,
31
+ /^192\.168\.\d{1,3}\.\d{1,3}$/,
32
+ /^169\.254\.\d{1,3}\.\d{1,3}$/, // Link-local
33
+ ];
34
+ /**
35
+ * Cloud metadata endpoints.
36
+ */
37
+ const METADATA_HOSTS = [
38
+ '169.254.169.254',
39
+ 'metadata.google.internal',
40
+ '100.100.100.200',
41
+ '169.254.170.2',
42
+ ];
43
+ /**
44
+ * Dangerous protocols.
45
+ */
46
+ const DANGEROUS_PROTOCOLS = [
47
+ 'file:',
48
+ 'ftp:',
49
+ 'gopher:',
50
+ 'ldap:',
51
+ 'dict:',
52
+ 'sftp:',
53
+ 'data:',
54
+ 'javascript:',
55
+ ];
56
+ // =============================================================================
57
+ // URL Validator
58
+ // =============================================================================
59
+ /**
60
+ * Validate a webhook URL.
61
+ *
62
+ * @param urlString - URL to validate
63
+ * @param options - Validation options
64
+ * @returns Validation result
65
+ */
66
+ export function validateWebhookUrl(urlString, options = {}) {
67
+ const { allowLocalhost = false, allowPrivateIps = false, requireHttps = process.env.NODE_ENV === 'production', blockedHosts = [], allowedHosts = [], } = options;
68
+ // Parse URL
69
+ let url;
70
+ try {
71
+ url = new URL(urlString);
72
+ }
73
+ catch {
74
+ return {
75
+ valid: false,
76
+ reason: 'Invalid URL format',
77
+ };
78
+ }
79
+ const hostname = url.hostname.toLowerCase();
80
+ // Check allowed hosts first (whitelist takes precedence)
81
+ if (allowedHosts.some((h) => h.toLowerCase() === hostname)) {
82
+ return { valid: true, url };
83
+ }
84
+ // Check protocol
85
+ if (DANGEROUS_PROTOCOLS.includes(url.protocol)) {
86
+ logger.warn('Webhook URL uses dangerous protocol', { url: urlString, protocol: url.protocol });
87
+ return {
88
+ valid: false,
89
+ reason: `Dangerous protocol: ${url.protocol}`,
90
+ };
91
+ }
92
+ // Check HTTPS requirement
93
+ if (requireHttps && url.protocol !== 'https:') {
94
+ return {
95
+ valid: false,
96
+ reason: 'Webhook URL must use HTTPS in production',
97
+ };
98
+ }
99
+ // Check localhost
100
+ if (!allowLocalhost && isLocalhost(hostname)) {
101
+ logger.warn('Webhook URL targets localhost', { url: urlString });
102
+ return {
103
+ valid: false,
104
+ reason: 'Webhook URL cannot target localhost',
105
+ };
106
+ }
107
+ // Check private IPs
108
+ if (!allowPrivateIps && isPrivateIp(hostname)) {
109
+ logger.warn('Webhook URL targets private IP', { url: urlString });
110
+ return {
111
+ valid: false,
112
+ reason: 'Webhook URL cannot target private IP ranges',
113
+ };
114
+ }
115
+ // Check metadata endpoints
116
+ if (METADATA_HOSTS.some((h) => hostname === h || hostname.includes(h))) {
117
+ logger.warn('Webhook URL targets cloud metadata', { url: urlString });
118
+ return {
119
+ valid: false,
120
+ reason: 'Webhook URL cannot target cloud metadata endpoints',
121
+ };
122
+ }
123
+ // Check custom blocked hosts
124
+ if (blockedHosts.some((h) => h.toLowerCase() === hostname)) {
125
+ return {
126
+ valid: false,
127
+ reason: `Host is blocked: ${hostname}`,
128
+ };
129
+ }
130
+ // Check for IP obfuscation (octal, hex, decimal)
131
+ if (isObfuscatedIp(hostname)) {
132
+ logger.warn('Webhook URL uses obfuscated IP', { url: urlString });
133
+ return {
134
+ valid: false,
135
+ reason: 'Webhook URL appears to use obfuscated IP notation',
136
+ };
137
+ }
138
+ return { valid: true, url };
139
+ }
140
+ /**
141
+ * Check if hostname is localhost.
142
+ */
143
+ function isLocalhost(hostname) {
144
+ return LOCALHOST_PATTERNS.some((p) => hostname === p || hostname === `[${p}]`);
145
+ }
146
+ /**
147
+ * Check if hostname is a private IP.
148
+ */
149
+ function isPrivateIp(hostname) {
150
+ return PRIVATE_IP_PATTERNS.some((p) => p.test(hostname));
151
+ }
152
+ /**
153
+ * Check for obfuscated IP notation.
154
+ */
155
+ function isObfuscatedIp(hostname) {
156
+ // Octal notation (e.g., 0177.0.0.1)
157
+ if (/^0[0-7]+\./.test(hostname)) {
158
+ return true;
159
+ }
160
+ // Hex notation (e.g., 0x7f.0x0.0x0.0x1)
161
+ if (/^0x[0-9a-f]+/i.test(hostname)) {
162
+ return true;
163
+ }
164
+ // Decimal notation (e.g., 2130706433)
165
+ if (/^\d{8,10}$/.test(hostname)) {
166
+ return true;
167
+ }
168
+ return false;
169
+ }
170
+ /**
171
+ * Validate URL and throw on invalid.
172
+ *
173
+ * @param urlString - URL to validate
174
+ * @param options - Validation options
175
+ * @throws Error if URL is invalid
176
+ */
177
+ export function assertValidWebhookUrl(urlString, options = {}) {
178
+ const result = validateWebhookUrl(urlString, options);
179
+ if (!result.valid) {
180
+ throw new Error(`Invalid webhook URL: ${result.reason}`);
181
+ }
182
+ return result.url;
183
+ }
184
+ //# sourceMappingURL=url-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url-validator.js","sourceRoot":"","sources":["../../src/approval/url-validator.ts"],"names":[],"mappings":"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"}
@@ -71,4 +71,52 @@ export declare class WebhookApprovalHandler implements ApprovalHandler {
71
71
  private computeSignature;
72
72
  close(): void;
73
73
  }
74
+ /**
75
+ * Verify a webhook signature from Tollgate.
76
+ *
77
+ * Use this function in your webhook receiver to validate that requests
78
+ * are authentic and haven't been tampered with.
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * import { verifyWebhookSignature } from '@dotsetlabs/tollgate';
83
+ *
84
+ * app.post('/webhook/tollgate', async (req, res) => {
85
+ * const signature = req.headers['x-tollgate-signature'];
86
+ * const body = JSON.stringify(req.body);
87
+ *
88
+ * const isValid = await verifyWebhookSignature(body, signature, process.env.WEBHOOK_SECRET);
89
+ *
90
+ * if (!isValid) {
91
+ * return res.status(401).json({ error: 'Invalid signature' });
92
+ * }
93
+ *
94
+ * // Process the webhook...
95
+ * });
96
+ * ```
97
+ *
98
+ * @param body - The raw request body as a string
99
+ * @param signature - The X-Tollgate-Signature header value
100
+ * @param secret - Your webhook secret
101
+ * @returns Promise<boolean> - True if signature is valid
102
+ */
103
+ export declare function verifyWebhookSignature(body: string, signature: string | null | undefined, secret: string): Promise<boolean>;
104
+ /**
105
+ * Result type for webhook signature verification.
106
+ */
107
+ export interface WebhookVerificationResult {
108
+ /** Whether the signature is valid */
109
+ valid: boolean;
110
+ /** Reason for invalid signature */
111
+ reason?: string;
112
+ }
113
+ /**
114
+ * Verify a webhook signature with detailed result.
115
+ *
116
+ * @param body - The raw request body as a string
117
+ * @param signature - The X-Tollgate-Signature header value
118
+ * @param secret - Your webhook secret
119
+ * @returns Promise<WebhookVerificationResult>
120
+ */
121
+ export declare function verifyWebhookSignatureDetailed(body: string, signature: string | null | undefined, secret: string): Promise<WebhookVerificationResult>;
74
122
  //# sourceMappingURL=webhook.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/approval/webhook.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAInF;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IAEZ,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,gDAAgD;IAChD,QAAQ,EAAE,OAAO,CAAC;IAElB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,eAAe,CAAC;IAE3B,mCAAmC;IACnC,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,sBAAuB,YAAW,eAAe;IAC5D,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,eAAe,CAAgC;gBAE3C,MAAM,EAAE,qBAAqB;IAOnC,MAAM,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA0CjE;;OAEG;YACW,kBAAkB;IAqEhC;;OAEG;YACW,gBAAgB;IAmB9B,KAAK,IAAI,IAAI;CAId"}
1
+ {"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../../src/approval/webhook.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAInF;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IAEZ,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEjC,+DAA+D;IAC/D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,gDAAgD;IAChD,QAAQ,EAAE,OAAO,CAAC;IAElB,sCAAsC;IACtC,QAAQ,CAAC,EAAE,eAAe,CAAC;IAE3B,mCAAmC;IACnC,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,sBAAuB,YAAW,eAAe;IAC5D,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,eAAe,CAAgC;gBAE3C,MAAM,EAAE,qBAAqB;IAOnC,MAAM,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA0CjE;;OAEG;YACW,kBAAkB;IAqEhC;;OAEG;YACW,gBAAgB;IAmB9B,KAAK,IAAI,IAAI;CAId;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACpC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CA2ClB;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,qCAAqC;IACrC,KAAK,EAAE,OAAO,CAAC;IAEf,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAsB,8BAA8B,CAClD,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACpC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,yBAAyB,CAAC,CAoBpC"}
@@ -157,4 +157,93 @@ export class WebhookApprovalHandler {
157
157
  this.abortController = null;
158
158
  }
159
159
  }
160
+ // =============================================================================
161
+ // Webhook Signature Verification Helper
162
+ // =============================================================================
163
+ /**
164
+ * Verify a webhook signature from Tollgate.
165
+ *
166
+ * Use this function in your webhook receiver to validate that requests
167
+ * are authentic and haven't been tampered with.
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * import { verifyWebhookSignature } from '@dotsetlabs/tollgate';
172
+ *
173
+ * app.post('/webhook/tollgate', async (req, res) => {
174
+ * const signature = req.headers['x-tollgate-signature'];
175
+ * const body = JSON.stringify(req.body);
176
+ *
177
+ * const isValid = await verifyWebhookSignature(body, signature, process.env.WEBHOOK_SECRET);
178
+ *
179
+ * if (!isValid) {
180
+ * return res.status(401).json({ error: 'Invalid signature' });
181
+ * }
182
+ *
183
+ * // Process the webhook...
184
+ * });
185
+ * ```
186
+ *
187
+ * @param body - The raw request body as a string
188
+ * @param signature - The X-Tollgate-Signature header value
189
+ * @param secret - Your webhook secret
190
+ * @returns Promise<boolean> - True if signature is valid
191
+ */
192
+ export async function verifyWebhookSignature(body, signature, secret) {
193
+ if (!signature || !secret) {
194
+ return false;
195
+ }
196
+ // Remove 'sha256=' prefix if present
197
+ const providedSig = signature.startsWith('sha256=')
198
+ ? signature.slice(7)
199
+ : signature;
200
+ try {
201
+ // Compute expected signature
202
+ const encoder = new TextEncoder();
203
+ const key = await crypto.subtle.importKey('raw', encoder.encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
204
+ const computed = await crypto.subtle.sign('HMAC', key, encoder.encode(body));
205
+ const expectedSig = Array.from(new Uint8Array(computed))
206
+ .map((b) => b.toString(16).padStart(2, '0'))
207
+ .join('');
208
+ // Timing-safe comparison to prevent timing attacks
209
+ if (providedSig.length !== expectedSig.length) {
210
+ return false;
211
+ }
212
+ // Use subtle timing-safe comparison
213
+ // Convert to buffers for comparison
214
+ let mismatch = 0;
215
+ for (let i = 0; i < providedSig.length; i++) {
216
+ mismatch |= providedSig.charCodeAt(i) ^ expectedSig.charCodeAt(i);
217
+ }
218
+ return mismatch === 0;
219
+ }
220
+ catch (error) {
221
+ logger.error('Webhook signature verification error', { error });
222
+ return false;
223
+ }
224
+ }
225
+ /**
226
+ * Verify a webhook signature with detailed result.
227
+ *
228
+ * @param body - The raw request body as a string
229
+ * @param signature - The X-Tollgate-Signature header value
230
+ * @param secret - Your webhook secret
231
+ * @returns Promise<WebhookVerificationResult>
232
+ */
233
+ export async function verifyWebhookSignatureDetailed(body, signature, secret) {
234
+ if (!signature) {
235
+ return { valid: false, reason: 'Missing signature header' };
236
+ }
237
+ if (!secret) {
238
+ return { valid: false, reason: 'Missing webhook secret' };
239
+ }
240
+ if (!signature.startsWith('sha256=')) {
241
+ return { valid: false, reason: 'Invalid signature format (expected sha256=...)' };
242
+ }
243
+ const isValid = await verifyWebhookSignature(body, signature, secret);
244
+ if (isValid) {
245
+ return { valid: true };
246
+ }
247
+ return { valid: false, reason: 'Signature mismatch' };
248
+ }
160
249
  //# sourceMappingURL=webhook.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../src/approval/webhook.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"webhook.js","sourceRoot":"","sources":["../../src/approval/webhook.ts"],"names":[],"mappings":"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"}