@atlascrew/apparatus 0.9.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 (291) hide show
  1. package/bin/apparatus.mjs +2 -0
  2. package/certs/server.crt +17 -0
  3. package/certs/server.key +28 -0
  4. package/dist/ai/client.js +104 -0
  5. package/dist/ai/client.js.map +1 -0
  6. package/dist/ai/personas.js +104 -0
  7. package/dist/ai/personas.js.map +1 -0
  8. package/dist/ai/redteam.js +1404 -0
  9. package/dist/ai/redteam.js.map +1 -0
  10. package/dist/ai/report-store.js +309 -0
  11. package/dist/ai/report-store.js.map +1 -0
  12. package/dist/app.js +525 -0
  13. package/dist/app.js.map +1 -0
  14. package/dist/attack-sim.js +69 -0
  15. package/dist/attack-sim.js.map +1 -0
  16. package/dist/attacker-tracker.js +276 -0
  17. package/dist/attacker-tracker.js.map +1 -0
  18. package/dist/blackhole.js +95 -0
  19. package/dist/blackhole.js.map +1 -0
  20. package/dist/chaos.js +88 -0
  21. package/dist/chaos.js.map +1 -0
  22. package/dist/cluster.js +462 -0
  23. package/dist/cluster.js.map +1 -0
  24. package/dist/config.js +61 -0
  25. package/dist/config.js.map +1 -0
  26. package/dist/deception.js +205 -0
  27. package/dist/deception.js.map +1 -0
  28. package/dist/demo-mode.js +109 -0
  29. package/dist/demo-mode.js.map +1 -0
  30. package/dist/dist-dashboard/assets/index-BsMhEnGu.js +648 -0
  31. package/dist/dist-dashboard/assets/index-CNOkYC_Q.css +10 -0
  32. package/dist/dist-dashboard/assets/index-CW2grvPC.js +648 -0
  33. package/dist/dist-dashboard/assets/logo/apparatus-favicon.svg +15 -0
  34. package/dist/dist-dashboard/assets/logo/apparatus-icon-dark.svg +24 -0
  35. package/dist/dist-dashboard/assets/logo/apparatus-icon-light.svg +24 -0
  36. package/dist/dist-dashboard/assets/logo/apparatus-logo-512.png +0 -0
  37. package/dist/dist-dashboard/assets/logo/apparatus-logo-dark.svg +18 -0
  38. package/dist/dist-dashboard/assets/logo/apparatus-logo.svg +17 -0
  39. package/dist/dist-dashboard/assets/logo/apple-touch-icon.png +0 -0
  40. package/dist/dist-dashboard/assets/logo/favicon-192.png +0 -0
  41. package/dist/dist-dashboard/assets/logo/favicon-32.png +0 -0
  42. package/dist/dist-dashboard/assets/logo/favicon.ico +0 -0
  43. package/dist/dist-dashboard/assets/logo/icon-192.png +0 -0
  44. package/dist/dist-dashboard/assets/logo/icon-512.png +0 -0
  45. package/dist/dist-dashboard/assets/logo/icon-light-512.png +0 -0
  46. package/dist/dist-dashboard/assets/react-vendor-DpRMSntD.js +1 -0
  47. package/dist/dist-dashboard/assets/router-DSc5pRwN.js +59 -0
  48. package/dist/dist-dashboard/docs-index.json +1577 -0
  49. package/dist/dist-dashboard/index.html +21 -0
  50. package/dist/dlp.js +40 -0
  51. package/dist/dlp.js.map +1 -0
  52. package/dist/drills.js +770 -0
  53. package/dist/drills.js.map +1 -0
  54. package/dist/echoHandler.js +113 -0
  55. package/dist/echoHandler.js.map +1 -0
  56. package/dist/escape/index.js +225 -0
  57. package/dist/escape/index.js.map +1 -0
  58. package/dist/escape/methods/dns.js +74 -0
  59. package/dist/escape/methods/dns.js.map +1 -0
  60. package/dist/escape/methods/http.js +81 -0
  61. package/dist/escape/methods/http.js.map +1 -0
  62. package/dist/escape/methods/icmp.js +36 -0
  63. package/dist/escape/methods/icmp.js.map +1 -0
  64. package/dist/escape/methods/tcp.js +38 -0
  65. package/dist/escape/methods/tcp.js.map +1 -0
  66. package/dist/escape/methods/udp.js +27 -0
  67. package/dist/escape/methods/udp.js.map +1 -0
  68. package/dist/escape/methods/websocket.js +37 -0
  69. package/dist/escape/methods/websocket.js.map +1 -0
  70. package/dist/forensics.js +111 -0
  71. package/dist/forensics.js.map +1 -0
  72. package/dist/generator.js +67 -0
  73. package/dist/generator.js.map +1 -0
  74. package/dist/ghosting.js +414 -0
  75. package/dist/ghosting.js.map +1 -0
  76. package/dist/graphql.js +44 -0
  77. package/dist/graphql.js.map +1 -0
  78. package/dist/history.js +40 -0
  79. package/dist/history.js.map +1 -0
  80. package/dist/imposter/creds.js +16 -0
  81. package/dist/imposter/creds.js.map +1 -0
  82. package/dist/imposter/index.js +44 -0
  83. package/dist/imposter/index.js.map +1 -0
  84. package/dist/imposter/providers/aws.js +103 -0
  85. package/dist/imposter/providers/aws.js.map +1 -0
  86. package/dist/imposter/providers/gcp.js +26 -0
  87. package/dist/imposter/providers/gcp.js.map +1 -0
  88. package/dist/index.js +53 -0
  89. package/dist/index.js.map +1 -0
  90. package/dist/infra-debug.js +68 -0
  91. package/dist/infra-debug.js.map +1 -0
  92. package/dist/jwt-debug.js +272 -0
  93. package/dist/jwt-debug.js.map +1 -0
  94. package/dist/kv.js +22 -0
  95. package/dist/kv.js.map +1 -0
  96. package/dist/lib/generators.js +43 -0
  97. package/dist/lib/generators.js.map +1 -0
  98. package/dist/lib/json.js +26 -0
  99. package/dist/lib/json.js.map +1 -0
  100. package/dist/logger.js +9 -0
  101. package/dist/logger.js.map +1 -0
  102. package/dist/metrics.js +20 -0
  103. package/dist/metrics.js.map +1 -0
  104. package/dist/mtd.js +30 -0
  105. package/dist/mtd.js.map +1 -0
  106. package/dist/oidc.js +69 -0
  107. package/dist/oidc.js.map +1 -0
  108. package/dist/persistence/cluster-state.js +47 -0
  109. package/dist/persistence/cluster-state.js.map +1 -0
  110. package/dist/persistence/deception-history.js +65 -0
  111. package/dist/persistence/deception-history.js.map +1 -0
  112. package/dist/persistence/drill-runs.js +138 -0
  113. package/dist/persistence/drill-runs.js.map +1 -0
  114. package/dist/persistence/request-history.js +41 -0
  115. package/dist/persistence/request-history.js.map +1 -0
  116. package/dist/persistence/scenario-catalog.js +73 -0
  117. package/dist/persistence/scenario-catalog.js.map +1 -0
  118. package/dist/persistence/status.js +51 -0
  119. package/dist/persistence/status.js.map +1 -0
  120. package/dist/persistence/tarpit-state.js +47 -0
  121. package/dist/persistence/tarpit-state.js.map +1 -0
  122. package/dist/persistence/webhook-store.js +69 -0
  123. package/dist/persistence/webhook-store.js.map +1 -0
  124. package/dist/proxy.js +28 -0
  125. package/dist/proxy.js.map +1 -0
  126. package/dist/ratelimit.js +32 -0
  127. package/dist/ratelimit.js.map +1 -0
  128. package/dist/redteam.js +442 -0
  129. package/dist/redteam.js.map +1 -0
  130. package/dist/scenarios.js +229 -0
  131. package/dist/scenarios.js.map +1 -0
  132. package/dist/scripting.js +30 -0
  133. package/dist/scripting.js.map +1 -0
  134. package/dist/self-healing.js +42 -0
  135. package/dist/self-healing.js.map +1 -0
  136. package/dist/sentinel.js +50 -0
  137. package/dist/sentinel.js.map +1 -0
  138. package/dist/server-bad-ssl.js +47 -0
  139. package/dist/server-bad-ssl.js.map +1 -0
  140. package/dist/server-grpc.js +66 -0
  141. package/dist/server-grpc.js.map +1 -0
  142. package/dist/server-http1.js +5 -0
  143. package/dist/server-http1.js.map +1 -0
  144. package/dist/server-http2.js +27 -0
  145. package/dist/server-http2.js.map +1 -0
  146. package/dist/server-icap.js +46 -0
  147. package/dist/server-icap.js.map +1 -0
  148. package/dist/server-l4.js +30 -0
  149. package/dist/server-l4.js.map +1 -0
  150. package/dist/server-mqtt.js +29 -0
  151. package/dist/server-mqtt.js.map +1 -0
  152. package/dist/server-protocols.js +18 -0
  153. package/dist/server-protocols.js.map +1 -0
  154. package/dist/server-redis.js +112 -0
  155. package/dist/server-redis.js.map +1 -0
  156. package/dist/server-smtp.js +66 -0
  157. package/dist/server-smtp.js.map +1 -0
  158. package/dist/server-syslog.js +23 -0
  159. package/dist/server-syslog.js.map +1 -0
  160. package/dist/server-ws.js +18 -0
  161. package/dist/server-ws.js.map +1 -0
  162. package/dist/sidecar/chaos/engine.js +41 -0
  163. package/dist/sidecar/chaos/engine.js.map +1 -0
  164. package/dist/sidecar/index.js +98 -0
  165. package/dist/sidecar/index.js.map +1 -0
  166. package/dist/simulator/dependency-graph.js +102 -0
  167. package/dist/simulator/dependency-graph.js.map +1 -0
  168. package/dist/simulator/supply-chain.js +67 -0
  169. package/dist/simulator/supply-chain.js.map +1 -0
  170. package/dist/sink.js +24 -0
  171. package/dist/sink.js.map +1 -0
  172. package/dist/sse-broadcast.js +105 -0
  173. package/dist/sse-broadcast.js.map +1 -0
  174. package/dist/swagger.js +309 -0
  175. package/dist/swagger.js.map +1 -0
  176. package/dist/sysinfo.js +36 -0
  177. package/dist/sysinfo.js.map +1 -0
  178. package/dist/tarpit.js +126 -0
  179. package/dist/tarpit.js.map +1 -0
  180. package/dist/tool-executor.js +315 -0
  181. package/dist/tool-executor.js.map +1 -0
  182. package/dist/tui/api-client.js +341 -0
  183. package/dist/tui/api-client.js.map +1 -0
  184. package/dist/tui/core/action-handler.js +302 -0
  185. package/dist/tui/core/action-handler.js.map +1 -0
  186. package/dist/tui/core/index.js +18 -0
  187. package/dist/tui/core/index.js.map +1 -0
  188. package/dist/tui/core/keyboard.js +329 -0
  189. package/dist/tui/core/keyboard.js.map +1 -0
  190. package/dist/tui/core/modal.js +397 -0
  191. package/dist/tui/core/modal.js.map +1 -0
  192. package/dist/tui/core/screen-manager.js +262 -0
  193. package/dist/tui/core/screen-manager.js.map +1 -0
  194. package/dist/tui/core/store.js +254 -0
  195. package/dist/tui/core/store.js.map +1 -0
  196. package/dist/tui/core/widget.js +167 -0
  197. package/dist/tui/core/widget.js.map +1 -0
  198. package/dist/tui/dashboard.js +649 -0
  199. package/dist/tui/dashboard.js.map +1 -0
  200. package/dist/tui/index.js +118 -0
  201. package/dist/tui/index.js.map +1 -0
  202. package/dist/tui/modals/add-rule-modal.js +190 -0
  203. package/dist/tui/modals/add-rule-modal.js.map +1 -0
  204. package/dist/tui/modals/dlp-output-modal.js +102 -0
  205. package/dist/tui/modals/dlp-output-modal.js.map +1 -0
  206. package/dist/tui/modals/dns-form-modal.js +26 -0
  207. package/dist/tui/modals/dns-form-modal.js.map +1 -0
  208. package/dist/tui/modals/ghost-config-modal.js +35 -0
  209. package/dist/tui/modals/ghost-config-modal.js.map +1 -0
  210. package/dist/tui/modals/har-results-modal.js +41 -0
  211. package/dist/tui/modals/har-results-modal.js.map +1 -0
  212. package/dist/tui/modals/index.js +15 -0
  213. package/dist/tui/modals/index.js.map +1 -0
  214. package/dist/tui/modals/jwt-decode-modal.js +45 -0
  215. package/dist/tui/modals/jwt-decode-modal.js.map +1 -0
  216. package/dist/tui/modals/jwt-mint-modal.js +70 -0
  217. package/dist/tui/modals/jwt-mint-modal.js.map +1 -0
  218. package/dist/tui/modals/ping-form-modal.js +19 -0
  219. package/dist/tui/modals/ping-form-modal.js.map +1 -0
  220. package/dist/tui/modals/redteam-results-modal.js +43 -0
  221. package/dist/tui/modals/redteam-results-modal.js.map +1 -0
  222. package/dist/tui/modals/scan-form-modal.js +26 -0
  223. package/dist/tui/modals/scan-form-modal.js.map +1 -0
  224. package/dist/tui/screens/defense-screen.js +281 -0
  225. package/dist/tui/screens/defense-screen.js.map +1 -0
  226. package/dist/tui/screens/forensics-screen.js +81 -0
  227. package/dist/tui/screens/forensics-screen.js.map +1 -0
  228. package/dist/tui/screens/index.js +140 -0
  229. package/dist/tui/screens/index.js.map +1 -0
  230. package/dist/tui/screens/system-screen.js +81 -0
  231. package/dist/tui/screens/system-screen.js.map +1 -0
  232. package/dist/tui/screens/testing-screen.js +429 -0
  233. package/dist/tui/screens/testing-screen.js.map +1 -0
  234. package/dist/tui/screens/traffic-screen.js +76 -0
  235. package/dist/tui/screens/traffic-screen.js.map +1 -0
  236. package/dist/tui/sse-client.js +130 -0
  237. package/dist/tui/sse-client.js.map +1 -0
  238. package/dist/tui/state/metrics-buffer.js +195 -0
  239. package/dist/tui/state/metrics-buffer.js.map +1 -0
  240. package/dist/tui/state/metrics-buffer.test.js +102 -0
  241. package/dist/tui/state/metrics-buffer.test.js.map +1 -0
  242. package/dist/tui/theme.js +136 -0
  243. package/dist/tui/theme.js.map +1 -0
  244. package/dist/tui/types.js +6 -0
  245. package/dist/tui/types.js.map +1 -0
  246. package/dist/tui/widgets/chaos-widget.js +152 -0
  247. package/dist/tui/widgets/chaos-widget.js.map +1 -0
  248. package/dist/tui/widgets/cluster-widget.js +156 -0
  249. package/dist/tui/widgets/cluster-widget.js.map +1 -0
  250. package/dist/tui/widgets/dlp-widget.js +161 -0
  251. package/dist/tui/widgets/dlp-widget.js.map +1 -0
  252. package/dist/tui/widgets/ghost-widget.js +169 -0
  253. package/dist/tui/widgets/ghost-widget.js.map +1 -0
  254. package/dist/tui/widgets/har-widget.js +173 -0
  255. package/dist/tui/widgets/har-widget.js.map +1 -0
  256. package/dist/tui/widgets/index.js +122 -0
  257. package/dist/tui/widgets/index.js.map +1 -0
  258. package/dist/tui/widgets/jwt-widget.js +177 -0
  259. package/dist/tui/widgets/jwt-widget.js.map +1 -0
  260. package/dist/tui/widgets/kv-widget.js +261 -0
  261. package/dist/tui/widgets/kv-widget.js.map +1 -0
  262. package/dist/tui/widgets/mtd-widget.js +181 -0
  263. package/dist/tui/widgets/mtd-widget.js.map +1 -0
  264. package/dist/tui/widgets/netdiag-widget.js +155 -0
  265. package/dist/tui/widgets/netdiag-widget.js.map +1 -0
  266. package/dist/tui/widgets/oidc-widget.js +162 -0
  267. package/dist/tui/widgets/oidc-widget.js.map +1 -0
  268. package/dist/tui/widgets/pcap-widget.js +239 -0
  269. package/dist/tui/widgets/pcap-widget.js.map +1 -0
  270. package/dist/tui/widgets/redteam-widget.js +155 -0
  271. package/dist/tui/widgets/redteam-widget.js.map +1 -0
  272. package/dist/tui/widgets/rps-gauge-widget.js +124 -0
  273. package/dist/tui/widgets/rps-gauge-widget.js.map +1 -0
  274. package/dist/tui/widgets/sentinel-widget.js +171 -0
  275. package/dist/tui/widgets/sentinel-widget.js.map +1 -0
  276. package/dist/tui/widgets/sparklines-widget.js +127 -0
  277. package/dist/tui/widgets/sparklines-widget.js.map +1 -0
  278. package/dist/tui/widgets/sysinfo-widget.js +197 -0
  279. package/dist/tui/widgets/sysinfo-widget.js.map +1 -0
  280. package/dist/tui/widgets/traffic-chart-widget.js +170 -0
  281. package/dist/tui/widgets/traffic-chart-widget.js.map +1 -0
  282. package/dist/tui/widgets/webhook-widget.js +259 -0
  283. package/dist/tui/widgets/webhook-widget.js.map +1 -0
  284. package/dist/utils/ip.js +18 -0
  285. package/dist/utils/ip.js.map +1 -0
  286. package/dist/victim/index.js +71 -0
  287. package/dist/victim/index.js.map +1 -0
  288. package/dist/webhook.js +88 -0
  289. package/dist/webhook.js.map +1 -0
  290. package/package.json +90 -0
  291. package/proto/echo.proto +19 -0
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Traffic Chart Widget
3
+ *
4
+ * Displays traffic volume over time using a blessed-contrib line chart
5
+ * with multiple data series and auto-scaling Y-axis
6
+ */
7
+ import contrib from 'blessed-contrib';
8
+ import { BaseWidget } from '../core/widget.js';
9
+ import { MetricsBuffer } from '../state/metrics-buffer.js';
10
+ /**
11
+ * Traffic Chart Widget showing traffic volume trends
12
+ */
13
+ export class TrafficChartWidget extends BaseWidget {
14
+ chart;
15
+ buffers = new Map();
16
+ series;
17
+ maxDataPoints;
18
+ lastRequestCount = 0;
19
+ lastDeceptionCount = 0;
20
+ lastUpdateTime = Date.now();
21
+ element;
22
+ constructor(options) {
23
+ super({
24
+ id: options.id,
25
+ config: options.config,
26
+ subscriptions: ['requestCount', 'deceptionCount'],
27
+ });
28
+ this.series = options.series ?? [
29
+ { name: 'Requests', color: 'green' },
30
+ { name: 'Blocks', color: 'red' },
31
+ ];
32
+ this.maxDataPoints = options.maxDataPoints ?? 30;
33
+ // Initialize metric buffers
34
+ for (const s of this.series) {
35
+ this.buffers.set(s.name, new MetricsBuffer({ maxSize: this.maxDataPoints }));
36
+ }
37
+ // Create the line chart
38
+ // Note: blessed-contrib types are incomplete, using type casts through unknown
39
+ this.chart = contrib.line({
40
+ label: options.config.label ?? ' Traffic Volume ',
41
+ showNthLabel: 5,
42
+ showLegend: true,
43
+ legend: { width: 12 },
44
+ border: {
45
+ type: 'line',
46
+ fg: options.config.style?.border?.fg ?? 'cyan',
47
+ },
48
+ style: {
49
+ line: 'yellow',
50
+ text: 'green',
51
+ baseline: 'black',
52
+ },
53
+ xLabelPadding: 3,
54
+ xPadding: 5,
55
+ wholeNumbersOnly: false,
56
+ });
57
+ this.element = this.chart;
58
+ }
59
+ render(state) {
60
+ if (!this.mounted)
61
+ return;
62
+ // Calculate current metrics
63
+ const now = Date.now();
64
+ const timeDelta = (now - this.lastUpdateTime) / 1000;
65
+ // Calculate RPS
66
+ let rps = 0;
67
+ if (timeDelta > 0) {
68
+ const requestDelta = state.requestCount - this.lastRequestCount;
69
+ rps = requestDelta / timeDelta;
70
+ }
71
+ // Calculate blocks per second
72
+ let blocks = 0;
73
+ if (timeDelta > 0) {
74
+ const deceptionDelta = state.deceptionCount - this.lastDeceptionCount;
75
+ blocks = deceptionDelta / timeDelta;
76
+ }
77
+ // Update buffers
78
+ this.buffers.get('Requests')?.add(rps);
79
+ this.buffers.get('Blocks')?.add(blocks);
80
+ // Update last values
81
+ this.lastRequestCount = state.requestCount;
82
+ this.lastDeceptionCount = state.deceptionCount;
83
+ this.lastUpdateTime = now;
84
+ // Prepare chart data
85
+ const seriesData = this.series.map((s) => {
86
+ const buffer = this.buffers.get(s.name);
87
+ const values = buffer?.getValues() ?? [];
88
+ return {
89
+ title: s.name,
90
+ x: values.map((_, i) => String(i)),
91
+ y: values.length > 0 ? values : [0],
92
+ style: {
93
+ line: s.color,
94
+ },
95
+ };
96
+ });
97
+ // Calculate auto-scaled Y-axis range
98
+ const allValues = seriesData.flatMap((s) => s.y);
99
+ const maxY = Math.max(...allValues, 1);
100
+ const minY = Math.min(...allValues, 0);
101
+ // Set chart data
102
+ this.chart.setData(seriesData);
103
+ this.requestRender();
104
+ }
105
+ /**
106
+ * Get values for a specific series
107
+ */
108
+ getSeriesValues(seriesName) {
109
+ return this.buffers.get(seriesName)?.getValues() ?? [];
110
+ }
111
+ /**
112
+ * Get latest value for a series
113
+ */
114
+ getLatestValue(seriesName) {
115
+ return this.buffers.get(seriesName)?.getLatest() ?? 0;
116
+ }
117
+ /**
118
+ * Get average value for a series
119
+ */
120
+ getAverageValue(seriesName) {
121
+ return this.buffers.get(seriesName)?.getAverage() ?? 0;
122
+ }
123
+ /**
124
+ * Get maximum value for a series
125
+ */
126
+ getMaxValue(seriesName) {
127
+ return this.buffers.get(seriesName)?.getMax() ?? 0;
128
+ }
129
+ /**
130
+ * Clear all series buffers
131
+ */
132
+ clear() {
133
+ this.buffers.forEach((buffer) => buffer.clear());
134
+ this.lastRequestCount = 0;
135
+ this.lastDeceptionCount = 0;
136
+ this.lastUpdateTime = Date.now();
137
+ }
138
+ /**
139
+ * Add a new data series
140
+ */
141
+ addSeries(name, color) {
142
+ this.series.push({ name, color });
143
+ this.buffers.set(name, new MetricsBuffer({ maxSize: this.maxDataPoints }));
144
+ }
145
+ /**
146
+ * Remove a data series
147
+ */
148
+ removeSeries(name) {
149
+ const index = this.series.findIndex((s) => s.name === name);
150
+ if (index !== -1) {
151
+ this.series.splice(index, 1);
152
+ this.buffers.delete(name);
153
+ }
154
+ }
155
+ onUnmount() {
156
+ this.clear();
157
+ }
158
+ }
159
+ /**
160
+ * Create traffic chart widget
161
+ */
162
+ export function createTrafficChartWidget(id, config, options) {
163
+ return new TrafficChartWidget({
164
+ id,
165
+ config,
166
+ series: options?.series,
167
+ maxDataPoints: options?.maxDataPoints,
168
+ });
169
+ }
170
+ //# sourceMappingURL=traffic-chart-widget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"traffic-chart-widget.js","sourceRoot":"","sources":["../../../src/tui/widgets/traffic-chart-widget.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Webhook Inspector Widget
3
+ *
4
+ * View received webhooks by ID with body display and metadata.
5
+ */
6
+ import blessed from 'blessed';
7
+ import { BaseWidget, createBoxOptions } from '../core/widget.js';
8
+ import { colors, formatRelativeTime } from '../theme.js';
9
+ /**
10
+ * Webhook Inspector Widget
11
+ */
12
+ export class WebhookWidget extends BaseWidget {
13
+ element;
14
+ webhooks = [];
15
+ selectedIndex = 0;
16
+ modal = null;
17
+ constructor(id, config) {
18
+ super({
19
+ id,
20
+ config,
21
+ subscriptions: [],
22
+ focusable: true,
23
+ });
24
+ this.element = blessed.box({
25
+ ...createBoxOptions(config),
26
+ label: config.label ?? ' Webhook Inspector ',
27
+ keys: true,
28
+ vi: true,
29
+ scrollable: true,
30
+ alwaysScroll: true,
31
+ scrollbar: {
32
+ ch: '█',
33
+ track: { bg: 'gray' },
34
+ style: { bg: colors.warning },
35
+ },
36
+ style: {
37
+ ...createBoxOptions(config).style,
38
+ border: { fg: colors.warning },
39
+ },
40
+ });
41
+ }
42
+ /**
43
+ * Inject modal manager
44
+ */
45
+ setModal(modal) {
46
+ this.modal = modal;
47
+ }
48
+ onMount() {
49
+ // Fetch webhook data on mount
50
+ this.fetchWebhooks();
51
+ }
52
+ render(state) {
53
+ const lines = [];
54
+ // Header
55
+ lines.push(`{bold}Total Webhooks:{/bold} ${this.webhooks.length}`);
56
+ lines.push('');
57
+ // Webhooks list
58
+ if (this.webhooks.length === 0) {
59
+ lines.push(' {gray-fg}No webhooks received{/gray-fg}');
60
+ lines.push('');
61
+ lines.push(' {gray-fg}Send a webhook to /webhook/:id to see it here{/gray-fg}');
62
+ }
63
+ else {
64
+ lines.push('{bold}Recent Webhooks:{/bold}');
65
+ lines.push('');
66
+ for (let i = 0; i < this.webhooks.length; i++) {
67
+ const webhook = this.webhooks[i];
68
+ const selected = i === this.selectedIndex;
69
+ const marker = selected ? '{yellow-fg}>{/yellow-fg} ' : ' ';
70
+ const timestamp = formatRelativeTime(webhook.timestamp);
71
+ lines.push(`${marker}{cyan-fg}${webhook.id}{/cyan-fg} {gray-fg}(${timestamp}){/gray-fg}`);
72
+ lines.push(` {bold}${webhook.method}{/bold} ${webhook.path} from {magenta-fg}${webhook.source}{/magenta-fg}`);
73
+ // Show body preview for selected item
74
+ if (selected) {
75
+ const preview = this.getBodyPreview(webhook.body);
76
+ lines.push(` {bold}Body:{/bold} ${preview}`);
77
+ }
78
+ lines.push('');
79
+ }
80
+ lines.push('{gray-fg}↑/↓: navigate, Enter: view details, c: clear all, r: refresh{/gray-fg}');
81
+ }
82
+ this.element.setContent(lines.join('\n'));
83
+ this.requestRender();
84
+ }
85
+ handleKey(key) {
86
+ if (key === 'up' && this.selectedIndex > 0) {
87
+ this.selectedIndex--;
88
+ this.render(this.getState());
89
+ return true;
90
+ }
91
+ if (key === 'down' && this.selectedIndex < this.webhooks.length - 1) {
92
+ this.selectedIndex++;
93
+ this.render(this.getState());
94
+ return true;
95
+ }
96
+ if (key === 'enter' && this.webhooks.length > 0) {
97
+ this.viewSelectedWebhook();
98
+ return true;
99
+ }
100
+ if (key === 'c') {
101
+ this.clearWebhooks();
102
+ return true;
103
+ }
104
+ if (key === 'r') {
105
+ this.fetchWebhooks();
106
+ return true;
107
+ }
108
+ return false;
109
+ }
110
+ async fetchWebhooks() {
111
+ try {
112
+ // Mock data for now - will be replaced with real API call
113
+ this.webhooks = [
114
+ {
115
+ id: 'github-push-1',
116
+ timestamp: new Date(Date.now() - 30000).toISOString(), // 30s ago
117
+ source: 'github.com',
118
+ method: 'POST',
119
+ path: '/webhook/github',
120
+ headers: {
121
+ 'x-github-event': 'push',
122
+ 'x-github-delivery': 'abc123',
123
+ 'content-type': 'application/json',
124
+ },
125
+ body: {
126
+ ref: 'refs/heads/main',
127
+ commits: [
128
+ { message: 'feat: add new feature', author: 'John Doe' },
129
+ ],
130
+ },
131
+ queryParams: {},
132
+ },
133
+ {
134
+ id: 'stripe-payment-1',
135
+ timestamp: new Date(Date.now() - 120000).toISOString(), // 2m ago
136
+ source: 'stripe.com',
137
+ method: 'POST',
138
+ path: '/webhook/stripe',
139
+ headers: {
140
+ 'stripe-signature': 'sig_abc123',
141
+ 'content-type': 'application/json',
142
+ },
143
+ body: {
144
+ type: 'payment_intent.succeeded',
145
+ data: {
146
+ object: {
147
+ amount: 2000,
148
+ currency: 'usd',
149
+ status: 'succeeded',
150
+ },
151
+ },
152
+ },
153
+ },
154
+ {
155
+ id: 'slack-message-1',
156
+ timestamp: new Date(Date.now() - 300000).toISOString(), // 5m ago
157
+ source: 'slack.com',
158
+ method: 'POST',
159
+ path: '/webhook/slack',
160
+ headers: {
161
+ 'content-type': 'application/x-www-form-urlencoded',
162
+ },
163
+ body: {
164
+ text: 'Hello from Slack!',
165
+ user_name: 'alice',
166
+ channel_name: '#general',
167
+ },
168
+ },
169
+ ];
170
+ // Reset selection if out of bounds
171
+ if (this.selectedIndex >= this.webhooks.length) {
172
+ this.selectedIndex = Math.max(0, this.webhooks.length - 1);
173
+ }
174
+ this.render(this.getState());
175
+ }
176
+ catch (error) {
177
+ this.element.setContent(`\n {red-fg}Error loading webhooks:{/red-fg}\n ${error instanceof Error ? error.message : String(error)}`);
178
+ this.requestRender();
179
+ }
180
+ }
181
+ viewSelectedWebhook() {
182
+ if (!this.modal || this.webhooks.length === 0)
183
+ return;
184
+ const webhook = this.webhooks[this.selectedIndex];
185
+ const lines = [];
186
+ lines.push(`{bold}ID:{/bold} ${webhook.id}`);
187
+ lines.push(`{bold}Timestamp:{/bold} ${webhook.timestamp}`);
188
+ lines.push(`{bold}Source:{/bold} ${webhook.source}`);
189
+ lines.push(`{bold}Method:{/bold} ${webhook.method}`);
190
+ lines.push(`{bold}Path:{/bold} ${webhook.path}`);
191
+ lines.push('');
192
+ // Query params
193
+ if (webhook.queryParams && Object.keys(webhook.queryParams).length > 0) {
194
+ lines.push('{bold}Query Parameters:{/bold}');
195
+ lines.push(JSON.stringify(webhook.queryParams, null, 2));
196
+ lines.push('');
197
+ }
198
+ // Headers
199
+ lines.push('{bold}Headers:{/bold}');
200
+ lines.push(JSON.stringify(webhook.headers, null, 2));
201
+ lines.push('');
202
+ // Body
203
+ lines.push('{bold}Body:{/bold}');
204
+ const bodyStr = typeof webhook.body === 'string'
205
+ ? webhook.body
206
+ : JSON.stringify(webhook.body, null, 2);
207
+ lines.push(bodyStr);
208
+ this.modal.showHelp(lines.join('\n'), 'Webhook Details');
209
+ }
210
+ clearWebhooks() {
211
+ if (!this.modal)
212
+ return;
213
+ if (this.webhooks.length === 0) {
214
+ this.modal.showResult({
215
+ title: 'Info',
216
+ content: 'No webhooks to clear',
217
+ type: 'info',
218
+ });
219
+ return;
220
+ }
221
+ this.modal.showConfirm({
222
+ title: 'Confirm Clear',
223
+ message: `Clear all ${this.webhooks.length} webhooks?`,
224
+ danger: true,
225
+ }, () => {
226
+ const count = this.webhooks.length;
227
+ this.webhooks = [];
228
+ this.selectedIndex = 0;
229
+ this.modal.showResult({
230
+ title: 'Success',
231
+ content: `Cleared ${count} webhooks`,
232
+ type: 'success',
233
+ });
234
+ this.render(this.getState());
235
+ });
236
+ }
237
+ getBodyPreview(body) {
238
+ if (body === null)
239
+ return '{gray-fg}null{/gray-fg}';
240
+ if (body === undefined)
241
+ return '{gray-fg}undefined{/gray-fg}';
242
+ if (typeof body === 'string') {
243
+ const preview = body.substring(0, 60);
244
+ return `{green-fg}"${preview}${body.length > 60 ? '...' : ''}"{/green-fg}`;
245
+ }
246
+ if (typeof body === 'object') {
247
+ const keys = Object.keys(body);
248
+ return `{yellow-fg}{${keys.length} keys: ${keys.slice(0, 3).join(', ')}${keys.length > 3 ? '...' : ''}}{/yellow-fg}`;
249
+ }
250
+ return String(body).substring(0, 60);
251
+ }
252
+ }
253
+ /**
254
+ * Factory function for widget registry
255
+ */
256
+ export function createWebhookWidget(id, config) {
257
+ return new WebhookWidget(id, config);
258
+ }
259
+ //# sourceMappingURL=webhook-widget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-widget.js","sourceRoot":"","sources":["../../../src/tui/widgets/webhook-widget.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import { isIP } from "node:net";
2
+ export function normalizeIp(ip) {
3
+ if (typeof ip !== "string") {
4
+ return "unknown";
5
+ }
6
+ const value = ip.trim();
7
+ if (!value) {
8
+ return "unknown";
9
+ }
10
+ if (value.startsWith("::ffff:")) {
11
+ return value.slice("::ffff:".length);
12
+ }
13
+ return value;
14
+ }
15
+ export function isValidIpLiteral(ip) {
16
+ return isIP(ip) !== 0;
17
+ }
18
+ //# sourceMappingURL=ip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ip.js","sourceRoot":"","sources":["../../src/utils/ip.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEhC,MAAM,UAAU,WAAW,CAAC,EAAY;IACpC,IAAI,OAAO,EAAE,KAAK,QAAQ,EAAE,CAAC;QACzB,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,MAAM,KAAK,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC;IACxB,IAAI,CAAC,KAAK,EAAE,CAAC;QACT,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,IAAI,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,EAAU;IACvC,OAAO,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,71 @@
1
+ import { Router } from "express";
2
+ import { logger } from "../logger.js";
3
+ const router = Router();
4
+ // --- 1. SQL Injection Victim ---
5
+ // Mock Database
6
+ const USERS = [
7
+ { id: 1, user: "admin", pass: "supersecret", role: "admin" },
8
+ { id: 2, user: "guest", pass: "guest", role: "user" }
9
+ ];
10
+ router.get("/login", (req, res) => {
11
+ const user = req.query.user || "";
12
+ const pass = req.query.pass || "";
13
+ logger.info({ user, ip: req.ip }, "Victim: Login Attempt");
14
+ // VULNERABILITY: Naive string matching simulating SQLi
15
+ // If the user inputs "admin' --", this logic is "bypassed" by our naive parser below.
16
+ // Actually, properly simulating SQLi without a DB engine is hard.
17
+ // Let's rely on the Logic: if the input contains specific bypass patterns, we allow it.
18
+ let loggedIn = false;
19
+ let foundUser = null;
20
+ // Simulating: SELECT * FROM users WHERE user = '$user' AND pass = '$pass'
21
+ // The "Bypass" Check (Simulating the SQL Engine)
22
+ if (user.includes("' OR '1'='1") || user.includes("' OR 1=1")) {
23
+ loggedIn = true;
24
+ foundUser = USERS[0]; // Admin
25
+ logger.warn({ ip: req.ip }, "Victim: SQL Injection Successful!");
26
+ }
27
+ else {
28
+ // Normal Check
29
+ foundUser = USERS.find(u => u.user === user && u.pass === pass);
30
+ if (foundUser)
31
+ loggedIn = true;
32
+ }
33
+ if (loggedIn) {
34
+ res.json({ status: "success", msg: "Welcome back!", role: foundUser?.role, flag: "FLAG{SQLI_IS_DEAD_LONG_LIVE_SQLI}" });
35
+ }
36
+ else {
37
+ res.status(401).json({ status: "failed", error: "Invalid credentials" });
38
+ }
39
+ });
40
+ // --- 2. Remote Code Execution (RCE) Victim ---
41
+ router.get("/calc", (req, res) => {
42
+ const eq = req.query.eq;
43
+ if (!eq)
44
+ return res.status(400).send("Missing 'eq' param");
45
+ logger.info({ eq, ip: req.ip }, "Victim: Calc Request");
46
+ try {
47
+ // VULNERABILITY: Direct eval()
48
+ // Attack: /victim/calc?eq=require('child_process').execSync('ls').toString()
49
+ const result = eval(eq);
50
+ res.send(`Result: ${result}`);
51
+ }
52
+ catch (e) {
53
+ res.status(500).send(`Error: ${e.message}`);
54
+ }
55
+ });
56
+ // --- 3. Reflected XSS Victim ---
57
+ router.get("/guestbook", (req, res) => {
58
+ const msg = req.query.msg || "Hello!";
59
+ // VULNERABILITY: No sanitization
60
+ res.send(`
61
+ <h1>Victim Guestbook</h1>
62
+ <p>Message: ${msg}</p>
63
+ <hr>
64
+ <form action="/victim/guestbook">
65
+ <input name="msg" placeholder="Leave a message...">
66
+ <button>Sign</button>
67
+ </form>
68
+ `);
69
+ });
70
+ export const victimRouter = router;
71
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/victim/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAqB,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAEtC,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC;AAExB,kCAAkC;AAClC,gBAAgB;AAChB,MAAM,KAAK,GAAG;IACV,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,OAAO,EAAE;IAC5D,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE;CACxD,CAAC;AAEF,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IAC9B,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,IAAc,IAAI,EAAE,CAAC;IAC5C,MAAM,IAAI,GAAG,GAAG,CAAC,KAAK,CAAC,IAAc,IAAI,EAAE,CAAC;IAE5C,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,uBAAuB,CAAC,CAAC;IAE3D,uDAAuD;IACvD,sFAAsF;IACtF,mEAAmE;IACnE,wFAAwF;IAExF,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,SAAS,GAAG,IAAI,CAAC;IAErB,0EAA0E;IAE1E,iDAAiD;IACjD,IAAI,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5D,QAAQ,GAAG,IAAI,CAAC;QAChB,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ;QAC9B,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,mCAAmC,CAAC,CAAC;IACrE,CAAC;SAAM,CAAC;QACJ,eAAe;QACf,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;QAChE,IAAI,SAAS;YAAE,QAAQ,GAAG,IAAI,CAAC;IACnC,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACX,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,mCAAmC,EAAE,CAAC,CAAC;IAC5H,CAAC;SAAM,CAAC;QACJ,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,CAAC;IAC7E,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,gDAAgD;AAChD,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IAC7B,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,EAAY,CAAC;IAClC,IAAI,CAAC,EAAE;QAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAE3D,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,sBAAsB,CAAC,CAAC;IAExD,IAAI,CAAC;QACD,+BAA+B;QAC/B,6EAA6E;QAC7E,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;QACxB,GAAG,CAAC,IAAI,CAAC,WAAW,MAAM,EAAE,CAAC,CAAC;IAClC,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QACd,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IAChD,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,kCAAkC;AAClC,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;IAClC,MAAM,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,GAAa,IAAI,QAAQ,CAAC;IAEhD,iCAAiC;IACjC,GAAG,CAAC,IAAI,CAAC;;sBAES,GAAG;;;;;;KAMpB,CAAC,CAAC;AACP,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC"}
@@ -0,0 +1,88 @@
1
+ import { broadcastWebhook } from "./sse-broadcast.js";
2
+ import { cfg } from "./config.js";
3
+ import { logger } from "./logger.js";
4
+ import { loadWebhookStore, writeWebhookStore } from "./persistence/webhook-store.js";
5
+ import { markPersistenceHydrated, markPersistenceWrite, registerPersistenceStore } from "./persistence/status.js";
6
+ const webhookStore = {};
7
+ let webhookStoreHydrationPromise = null;
8
+ let webhookStorePersistQueue = Promise.resolve(true);
9
+ const WEBHOOK_STORE_KEY = "webhookStore";
10
+ // Max 50 webhooks per ID to prevent memory leaks
11
+ const MAX_WEBHOOKS = 50;
12
+ registerPersistenceStore(WEBHOOK_STORE_KEY, cfg.webhookStorePath);
13
+ function normalizePersistedWebhook(entry, hookId) {
14
+ if (entry.id !== hookId)
15
+ return null;
16
+ return {
17
+ id: entry.id,
18
+ timestamp: entry.timestamp,
19
+ method: entry.method,
20
+ headers: entry.headers,
21
+ body: entry.body,
22
+ query: entry.query,
23
+ ip: entry.ip,
24
+ };
25
+ }
26
+ async function hydrateWebhookStore() {
27
+ const persistedStore = await loadWebhookStore(cfg.webhookStorePath);
28
+ for (const [hookId, entries] of Object.entries(persistedStore)) {
29
+ webhookStore[hookId] = entries
30
+ .map((entry) => normalizePersistedWebhook(entry, hookId))
31
+ .filter((entry) => entry !== null)
32
+ .slice(0, MAX_WEBHOOKS);
33
+ }
34
+ markPersistenceHydrated(WEBHOOK_STORE_KEY);
35
+ }
36
+ async function ensureWebhookStoreHydrated() {
37
+ if (!webhookStoreHydrationPromise) {
38
+ webhookStoreHydrationPromise = hydrateWebhookStore();
39
+ }
40
+ await webhookStoreHydrationPromise;
41
+ }
42
+ function snapshotWebhookStore() {
43
+ const snapshot = {};
44
+ for (const [hookId, entries] of Object.entries(webhookStore)) {
45
+ snapshot[hookId] = entries.slice(0, MAX_WEBHOOKS);
46
+ }
47
+ return snapshot;
48
+ }
49
+ async function persistWebhookStoreQueued() {
50
+ webhookStorePersistQueue = webhookStorePersistQueue.then(() => writeWebhookStore(cfg.webhookStorePath, snapshotWebhookStore()), () => writeWebhookStore(cfg.webhookStorePath, snapshotWebhookStore()));
51
+ const persisted = await webhookStorePersistQueue;
52
+ markPersistenceWrite(WEBHOOK_STORE_KEY, persisted);
53
+ return persisted;
54
+ }
55
+ export async function webhookReceiveHandler(req, res) {
56
+ await ensureWebhookStoreHydrated();
57
+ const hookId = req.params.id;
58
+ if (!webhookStore[hookId]) {
59
+ webhookStore[hookId] = [];
60
+ }
61
+ const webhook = {
62
+ id: hookId,
63
+ timestamp: new Date().toISOString(),
64
+ method: req.method,
65
+ headers: req.headers,
66
+ body: req.body,
67
+ query: req.query,
68
+ ip: req.ip || req.socket.remoteAddress || "unknown"
69
+ };
70
+ webhookStore[hookId].unshift(webhook);
71
+ // Trim
72
+ if (webhookStore[hookId].length > MAX_WEBHOOKS) {
73
+ webhookStore[hookId] = webhookStore[hookId].slice(0, MAX_WEBHOOKS);
74
+ }
75
+ const persisted = await persistWebhookStoreQueued();
76
+ if (!persisted) {
77
+ logger.warn({ hookId }, "Webhook stored in memory but persistence write failed");
78
+ }
79
+ broadcastWebhook(hookId, webhook);
80
+ res.status(200).json({ status: "received", id: hookId });
81
+ }
82
+ export async function webhookListHandler(req, res) {
83
+ await ensureWebhookStoreHydrated();
84
+ const hookId = req.params.id;
85
+ const hooks = webhookStore[hookId] || [];
86
+ res.json(hooks);
87
+ }
88
+ //# sourceMappingURL=webhook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook.js","sourceRoot":"","sources":["../src/webhook.ts"],"names":[],"mappings":"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"}