@j3r3mcdev/oast-server 1.1.7 → 1.1.8

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 (95) hide show
  1. package/.github/workflows/ci.yml +29 -29
  2. package/.github/workflows/publish.yml +31 -31
  3. package/README.md +192 -192
  4. package/dist/core/router.d.ts +1 -0
  5. package/dist/core/router.js +3 -3
  6. package/jest.config.js +14 -14
  7. package/package.json +45 -45
  8. package/sadmin list shadows +9 -9
  9. package/src/api/controllers/__tests__/tasks.controller.test.ts +74 -74
  10. package/src/api/controllers/events.controller.ts +10 -10
  11. package/src/api/controllers/health.controller.ts +7 -7
  12. package/src/api/controllers/tasks.controller.ts +41 -41
  13. package/src/api/dto/__tests__/create-task.dto.test.ts +41 -41
  14. package/src/api/dto/__tests__/filter-tasks.dto.test.ts +35 -35
  15. package/src/api/dto/create-task.dto.ts +33 -33
  16. package/src/api/dto/filter-tasks.dto.ts +33 -33
  17. package/src/api/services/__tests__/events.service.test.ts +41 -41
  18. package/src/api/services/__tests__/tasks.service.test.ts +41 -41
  19. package/src/api/services/events.service.ts +17 -17
  20. package/src/api/services/tasks.service.ts +79 -79
  21. package/src/api/sse/events.stream.ts +90 -90
  22. package/src/bootstrap.ts +89 -89
  23. package/src/core/__tests__/core-router.test.ts +30 -30
  24. package/src/core/__tests__/core-server.test.ts +44 -44
  25. package/src/core/__tests__/event.normalizer.test.ts +56 -56
  26. package/src/core/__tests__/event.router.test.ts +89 -89
  27. package/src/core/__tests__/logger.test.ts +32 -32
  28. package/src/core/__tests__/storage-manager.test.ts +74 -74
  29. package/src/core/event.normalizer.ts +147 -147
  30. package/src/core/event.router.ts +13 -13
  31. package/src/core/http/__tests__/adapter-node.test.ts +52 -52
  32. package/src/core/http/__tests__/body-parser-multipart.test.ts +41 -41
  33. package/src/core/http/__tests__/body-parser-raw.test.ts +28 -28
  34. package/src/core/http/__tests__/body-parser-text.test.ts +28 -28
  35. package/src/core/http/__tests__/compile-path.test.ts +39 -39
  36. package/src/core/http/__tests__/middleware-pipeline.test.ts +51 -51
  37. package/src/core/http/__tests__/request.test.ts +34 -34
  38. package/src/core/http/__tests__/response.test.ts +35 -35
  39. package/src/core/http/__tests__/router-match.test.ts +171 -171
  40. package/src/core/http/adapter-node.ts +51 -51
  41. package/src/core/http/buildRequest.ts +18 -18
  42. package/src/core/http/compile-path.ts +32 -32
  43. package/src/core/http/errors.ts +37 -37
  44. package/src/core/http/http-server.ts +52 -52
  45. package/src/core/http/middleware.ts +160 -160
  46. package/src/core/http/request.ts +55 -55
  47. package/src/core/http/response.ts +93 -93
  48. package/src/core/http/router.ts +138 -138
  49. package/src/core/id-generator.ts +8 -8
  50. package/src/core/logger.ts +113 -113
  51. package/src/core/router.ts +44 -44
  52. package/src/core/server.ts +85 -85
  53. package/src/core/storage.ts +64 -64
  54. package/src/index.ts +14 -14
  55. package/src/listeners/api/__tests__/api.controller.test.ts +116 -116
  56. package/src/listeners/api/__tests__/api.extractor.test.ts +46 -46
  57. package/src/listeners/api/__tests__/api.listener.test.ts +82 -82
  58. package/src/listeners/api/__tests__/api.routes.test.ts +155 -155
  59. package/src/listeners/api/__tests__/api.sse.test.ts +105 -105
  60. package/src/listeners/api/api.controllers.ts +67 -67
  61. package/src/listeners/api/api.extractor.ts +43 -43
  62. package/src/listeners/api/api.listener.ts +50 -50
  63. package/src/listeners/api/api.routes.ts +76 -76
  64. package/src/listeners/api/api.sse.ts +38 -38
  65. package/src/listeners/dns/__tests__/dns.test.ts +118 -118
  66. package/src/listeners/dns/dns.extractor.ts +14 -14
  67. package/src/listeners/dns/dns.listener.ts +61 -61
  68. package/src/listeners/http/__tests__/http.extractor.test.ts +59 -59
  69. package/src/listeners/http/__tests__/http.listener.test.ts +133 -133
  70. package/src/listeners/http/http.extractor.ts +15 -15
  71. package/src/listeners/http/http.listener.ts +110 -110
  72. package/src/listeners/listener.interface.ts +4 -4
  73. package/src/listeners/smtp/__tests__/smtp.extractor.test.ts +69 -69
  74. package/src/listeners/smtp/__tests__/smtp.listener.test.ts +150 -150
  75. package/src/listeners/smtp/smtp.extractor.ts +18 -18
  76. package/src/listeners/smtp/smtp.listener.ts +60 -60
  77. package/src/listeners/ssrf/__tests__/ssrf.extractor.test.ts +41 -41
  78. package/src/listeners/ssrf/__tests__/ssrf.listener.test.ts +87 -87
  79. package/src/listeners/ssrf/ssrf.extractor.ts +14 -14
  80. package/src/listeners/ssrf/ssrf.listener.ts +37 -37
  81. package/src/listeners/tcp/tcp.extractor.ts +16 -16
  82. package/src/listeners/tcp/tcp.listener.ts +61 -61
  83. package/src/listeners/webhook/__tests__/webhook.extractor.test.ts +35 -35
  84. package/src/listeners/webhook/__tests__/webhook.listener.test.ts +122 -122
  85. package/src/listeners/webhook/webhook.extractor.ts +12 -12
  86. package/src/listeners/webhook/webhook.listener.ts +58 -58
  87. package/src/listeners/websocket/__tests__/websocket.extractor.test.ts +33 -33
  88. package/src/listeners/websocket/__tests__/websocket.listener.test.ts +90 -90
  89. package/src/listeners/websocket/websocket.extractor.ts +11 -11
  90. package/src/listeners/websocket/websocket.listener.ts +40 -40
  91. package/src/storage-adapters/adapters/__tests__/memory.storage.test.ts +75 -75
  92. package/src/storage-adapters/adapters/memory.storage.ts +64 -64
  93. package/src/storage-adapters/storage.interface.ts +26 -26
  94. package/src/types/event.types.ts +147 -147
  95. package/tsconfig.json +20 -20
@@ -1,76 +1,76 @@
1
- import { IncomingMessage, ServerResponse } from "http";
2
- import { Logger } from "../../core/logger";
3
- import { StorageManager } from "../../core/storage";
4
- import { ApiSse } from "./api.sse";
5
- import { ApiController } from "./api.controllers";
6
-
7
- export async function handleApiRequest(
8
- req: IncomingMessage,
9
- res: ServerResponse,
10
- storage: StorageManager,
11
- sse: ApiSse,
12
- logger: Logger,
13
- ): Promise<void> {
14
- try {
15
- const url = new URL(req.url ?? "", `http://${req.headers.host}`);
16
- const path = url.pathname;
17
- const method = req.method ?? "GET";
18
-
19
- // ---------------------------
20
- // SSE STREAM
21
- // ---------------------------
22
- if (method === "GET" && path === "/events/stream") {
23
- await sse.handle(res);
24
- return;
25
- }
26
-
27
- // ---------------------------
28
- // ROUTES REST
29
- // ---------------------------
30
-
31
- // GET /events
32
- if (method === "GET" && path === "/events") {
33
- await ApiController.listEvents(url, res, storage);
34
- return;
35
- }
36
-
37
- // GET /events/:id
38
- if (method === "GET" && path.startsWith("/events/")) {
39
- const id = path.split("/")[2];
40
- await ApiController.getEvent(id, res, storage);
41
- return;
42
- }
43
-
44
- // DELETE /events
45
- if (method === "DELETE" && path === "/events") {
46
- await ApiController.deleteAll(res, storage);
47
- return;
48
- }
49
-
50
- // DELETE /events/:id
51
- if (method === "DELETE" && path.startsWith("/events/")) {
52
- const id = path.split("/")[2];
53
- await ApiController.deleteOne(id, res, storage);
54
- return;
55
- }
56
-
57
- // GET /stats
58
- if (method === "GET" && path === "/stats") {
59
- await ApiController.stats(res, storage);
60
- return;
61
- }
62
-
63
- // 404
64
- res.writeHead(404, { "Content-Type": "application/json" });
65
- res.end(JSON.stringify({ success: false, error: "Not found" }));
66
- } catch (err: any) {
67
- logger.error("API error", { error: err?.message ?? String(err) });
68
- res.writeHead(500, { "Content-Type": "application/json" });
69
- res.end(
70
- JSON.stringify({
71
- success: false,
72
- error: err?.message ?? "Internal server error",
73
- }),
74
- );
75
- }
76
- }
1
+ import { IncomingMessage, ServerResponse } from "http";
2
+ import { Logger } from "../../core/logger";
3
+ import { StorageManager } from "../../core/storage";
4
+ import { ApiSse } from "./api.sse";
5
+ import { ApiController } from "./api.controllers";
6
+
7
+ export async function handleApiRequest(
8
+ req: IncomingMessage,
9
+ res: ServerResponse,
10
+ storage: StorageManager,
11
+ sse: ApiSse,
12
+ logger: Logger,
13
+ ): Promise<void> {
14
+ try {
15
+ const url = new URL(req.url ?? "", `http://${req.headers.host}`);
16
+ const path = url.pathname;
17
+ const method = req.method ?? "GET";
18
+
19
+ // ---------------------------
20
+ // SSE STREAM
21
+ // ---------------------------
22
+ if (method === "GET" && path === "/events/stream") {
23
+ await sse.handle(res);
24
+ return;
25
+ }
26
+
27
+ // ---------------------------
28
+ // ROUTES REST
29
+ // ---------------------------
30
+
31
+ // GET /events
32
+ if (method === "GET" && path === "/events") {
33
+ await ApiController.listEvents(url, res, storage);
34
+ return;
35
+ }
36
+
37
+ // GET /events/:id
38
+ if (method === "GET" && path.startsWith("/events/")) {
39
+ const id = path.split("/")[2];
40
+ await ApiController.getEvent(id, res, storage);
41
+ return;
42
+ }
43
+
44
+ // DELETE /events
45
+ if (method === "DELETE" && path === "/events") {
46
+ await ApiController.deleteAll(res, storage);
47
+ return;
48
+ }
49
+
50
+ // DELETE /events/:id
51
+ if (method === "DELETE" && path.startsWith("/events/")) {
52
+ const id = path.split("/")[2];
53
+ await ApiController.deleteOne(id, res, storage);
54
+ return;
55
+ }
56
+
57
+ // GET /stats
58
+ if (method === "GET" && path === "/stats") {
59
+ await ApiController.stats(res, storage);
60
+ return;
61
+ }
62
+
63
+ // 404
64
+ res.writeHead(404, { "Content-Type": "application/json" });
65
+ res.end(JSON.stringify({ success: false, error: "Not found" }));
66
+ } catch (err: any) {
67
+ logger.error("API error", { error: err?.message ?? String(err) });
68
+ res.writeHead(500, { "Content-Type": "application/json" });
69
+ res.end(
70
+ JSON.stringify({
71
+ success: false,
72
+ error: err?.message ?? "Internal server error",
73
+ }),
74
+ );
75
+ }
76
+ }
@@ -1,38 +1,38 @@
1
- import { ServerResponse } from "http";
2
- import { Logger } from "../../core/logger";
3
-
4
- export class ApiSse {
5
- private clients: Set<ServerResponse> = new Set();
6
-
7
- constructor(private logger: Logger) {}
8
-
9
- handle(res: ServerResponse): void {
10
- res.writeHead(200, {
11
- "Content-Type": "text/event-stream",
12
- "Cache-Control": "no-cache",
13
- Connection: "keep-alive",
14
- });
15
-
16
- this.clients.add(res);
17
- res.write(`event: connected\ndata: "ok"\n\n`);
18
-
19
- res.on("close", () => {
20
- this.clients.delete(res);
21
- this.logger.debug?.("SSE client disconnected");
22
- });
23
- }
24
-
25
- broadcast(event: any): void {
26
- const payload = `event: event\ndata: ${JSON.stringify(event)}\n\n`;
27
- for (const client of this.clients) {
28
- client.write(payload);
29
- }
30
- }
31
-
32
- closeAll(): void {
33
- for (const client of this.clients) {
34
- client.end();
35
- }
36
- this.clients.clear();
37
- }
38
- }
1
+ import { ServerResponse } from "http";
2
+ import { Logger } from "../../core/logger";
3
+
4
+ export class ApiSse {
5
+ private clients: Set<ServerResponse> = new Set();
6
+
7
+ constructor(private logger: Logger) {}
8
+
9
+ handle(res: ServerResponse): void {
10
+ res.writeHead(200, {
11
+ "Content-Type": "text/event-stream",
12
+ "Cache-Control": "no-cache",
13
+ Connection: "keep-alive",
14
+ });
15
+
16
+ this.clients.add(res);
17
+ res.write(`event: connected\ndata: "ok"\n\n`);
18
+
19
+ res.on("close", () => {
20
+ this.clients.delete(res);
21
+ this.logger.debug?.("SSE client disconnected");
22
+ });
23
+ }
24
+
25
+ broadcast(event: any): void {
26
+ const payload = `event: event\ndata: ${JSON.stringify(event)}\n\n`;
27
+ for (const client of this.clients) {
28
+ client.write(payload);
29
+ }
30
+ }
31
+
32
+ closeAll(): void {
33
+ for (const client of this.clients) {
34
+ client.end();
35
+ }
36
+ this.clients.clear();
37
+ }
38
+ }
@@ -1,118 +1,118 @@
1
- import { describe, it, expect, jest, beforeEach } from "@jest/globals";
2
- import { DnsListener } from "../dns.listener";
3
- import { EventNormalizer } from "../../../core/event.normalizer";
4
- import { CoreRouter } from "../../../core/router";
5
-
6
- // Mock EventNormalizer
7
- jest.mock("../../../core/event.normalizer", () => ({
8
- EventNormalizer: {
9
- normalizeDns: jest.fn(),
10
- },
11
- }));
12
-
13
- describe("DnsListener", () => {
14
- let router: CoreRouter;
15
- let server: any;
16
- let listener: DnsListener;
17
-
18
- beforeEach(() => {
19
- router = {
20
- dispatch: jest
21
- .fn<(event: any) => Promise<void>>()
22
- .mockResolvedValue(undefined),
23
- } as any;
24
-
25
- server = {
26
- on: jest.fn(),
27
- close: jest.fn(),
28
- };
29
-
30
- listener = new DnsListener(router, { server });
31
- });
32
-
33
- it("traite un paquet DNS valide et renvoie eventId", async () => {
34
- const send = jest.fn();
35
-
36
- (EventNormalizer.normalizeDns as jest.Mock).mockReturnValue({
37
- id: "dns-123",
38
- source: "dns",
39
- });
40
-
41
- await listener.start();
42
-
43
- // simulate server.on("request", callback)
44
- const callback = server.on.mock.calls[0][1];
45
-
46
- const packet = {
47
- client: { address: "1.2.3.4" },
48
- questions: [{ name: "example.com", type: "A" }],
49
- };
50
-
51
- await callback(packet, send);
52
-
53
- expect(EventNormalizer.normalizeDns).toHaveBeenCalled();
54
- expect(router.dispatch).toHaveBeenCalled();
55
- expect(send).toHaveBeenCalledWith({
56
- success: true,
57
- eventId: "dns-123",
58
- });
59
- });
60
-
61
- it("renvoie une erreur si normalizeDns échoue", async () => {
62
- const send = jest.fn();
63
-
64
- (EventNormalizer.normalizeDns as jest.Mock).mockImplementation(() => {
65
- throw new Error("bad dns");
66
- });
67
-
68
- await listener.start();
69
-
70
- const callback = server.on.mock.calls[0][1];
71
-
72
- const packet = {
73
- client: { address: "1.2.3.4" },
74
- questions: [{ name: "example.com", type: "A" }],
75
- };
76
-
77
- await callback(packet, send);
78
-
79
- expect(send).toHaveBeenCalledWith({
80
- success: false,
81
- error: "bad dns",
82
- });
83
- });
84
-
85
- it("renvoie une erreur si router.dispatch échoue", async () => {
86
- const send = jest.fn();
87
-
88
- (EventNormalizer.normalizeDns as jest.Mock).mockReturnValue({
89
- id: "dns-999",
90
- source: "dns",
91
- });
92
-
93
- (
94
- router.dispatch as jest.MockedFunction<(event: any) => Promise<void>>
95
- ).mockRejectedValueOnce(new Error("dispatch failed"));
96
-
97
- await listener.start();
98
-
99
- const callback = server.on.mock.calls[0][1];
100
-
101
- const packet = {
102
- client: { address: "1.2.3.4" },
103
- questions: [{ name: "example.com", type: "A" }],
104
- };
105
-
106
- await callback(packet, send);
107
-
108
- expect(send).toHaveBeenCalledWith({
109
- success: false,
110
- error: "dispatch failed",
111
- });
112
- });
113
-
114
- it("stop() appelle server.close si disponible", async () => {
115
- await listener.stop();
116
- expect(server.close).toHaveBeenCalled();
117
- });
118
- });
1
+ import { describe, it, expect, jest, beforeEach } from "@jest/globals";
2
+ import { DnsListener } from "../dns.listener";
3
+ import { EventNormalizer } from "../../../core/event.normalizer";
4
+ import { CoreRouter } from "../../../core/router";
5
+
6
+ // Mock EventNormalizer
7
+ jest.mock("../../../core/event.normalizer", () => ({
8
+ EventNormalizer: {
9
+ normalizeDns: jest.fn(),
10
+ },
11
+ }));
12
+
13
+ describe("DnsListener", () => {
14
+ let router: CoreRouter;
15
+ let server: any;
16
+ let listener: DnsListener;
17
+
18
+ beforeEach(() => {
19
+ router = {
20
+ dispatch: jest
21
+ .fn<(event: any) => Promise<void>>()
22
+ .mockResolvedValue(undefined),
23
+ } as any;
24
+
25
+ server = {
26
+ on: jest.fn(),
27
+ close: jest.fn(),
28
+ };
29
+
30
+ listener = new DnsListener(router, { server });
31
+ });
32
+
33
+ it("traite un paquet DNS valide et renvoie eventId", async () => {
34
+ const send = jest.fn();
35
+
36
+ (EventNormalizer.normalizeDns as jest.Mock).mockReturnValue({
37
+ id: "dns-123",
38
+ source: "dns",
39
+ });
40
+
41
+ await listener.start();
42
+
43
+ // simulate server.on("request", callback)
44
+ const callback = server.on.mock.calls[0][1];
45
+
46
+ const packet = {
47
+ client: { address: "1.2.3.4" },
48
+ questions: [{ name: "example.com", type: "A" }],
49
+ };
50
+
51
+ await callback(packet, send);
52
+
53
+ expect(EventNormalizer.normalizeDns).toHaveBeenCalled();
54
+ expect(router.dispatch).toHaveBeenCalled();
55
+ expect(send).toHaveBeenCalledWith({
56
+ success: true,
57
+ eventId: "dns-123",
58
+ });
59
+ });
60
+
61
+ it("renvoie une erreur si normalizeDns échoue", async () => {
62
+ const send = jest.fn();
63
+
64
+ (EventNormalizer.normalizeDns as jest.Mock).mockImplementation(() => {
65
+ throw new Error("bad dns");
66
+ });
67
+
68
+ await listener.start();
69
+
70
+ const callback = server.on.mock.calls[0][1];
71
+
72
+ const packet = {
73
+ client: { address: "1.2.3.4" },
74
+ questions: [{ name: "example.com", type: "A" }],
75
+ };
76
+
77
+ await callback(packet, send);
78
+
79
+ expect(send).toHaveBeenCalledWith({
80
+ success: false,
81
+ error: "bad dns",
82
+ });
83
+ });
84
+
85
+ it("renvoie une erreur si router.dispatch échoue", async () => {
86
+ const send = jest.fn();
87
+
88
+ (EventNormalizer.normalizeDns as jest.Mock).mockReturnValue({
89
+ id: "dns-999",
90
+ source: "dns",
91
+ });
92
+
93
+ (
94
+ router.dispatch as jest.MockedFunction<(event: any) => Promise<void>>
95
+ ).mockRejectedValueOnce(new Error("dispatch failed"));
96
+
97
+ await listener.start();
98
+
99
+ const callback = server.on.mock.calls[0][1];
100
+
101
+ const packet = {
102
+ client: { address: "1.2.3.4" },
103
+ questions: [{ name: "example.com", type: "A" }],
104
+ };
105
+
106
+ await callback(packet, send);
107
+
108
+ expect(send).toHaveBeenCalledWith({
109
+ success: false,
110
+ error: "dispatch failed",
111
+ });
112
+ });
113
+
114
+ it("stop() appelle server.close si disponible", async () => {
115
+ await listener.stop();
116
+ expect(server.close).toHaveBeenCalled();
117
+ });
118
+ });
@@ -1,14 +1,14 @@
1
- export class DnsExtractor {
2
- static extract(packet: any) {
3
- if (!packet?.questions?.[0]) {
4
- throw new Error("Invalid DNS packet");
5
- }
6
-
7
- return {
8
- ip: packet.client?.address ?? "",
9
- query: packet.questions[0].name ?? "",
10
- type: packet.questions[0].type ?? "",
11
- raw: packet,
12
- };
13
- }
14
- }
1
+ export class DnsExtractor {
2
+ static extract(packet: any) {
3
+ if (!packet?.questions?.[0]) {
4
+ throw new Error("Invalid DNS packet");
5
+ }
6
+
7
+ return {
8
+ ip: packet.client?.address ?? "",
9
+ query: packet.questions[0].name ?? "",
10
+ type: packet.questions[0].type ?? "",
11
+ raw: packet,
12
+ };
13
+ }
14
+ }
@@ -1,61 +1,61 @@
1
- import { Logger } from "../../core/logger";
2
- import { CoreRouter } from "../../core/router";
3
- import { EventNormalizer } from "../../core/event.normalizer";
4
-
5
- export interface DnsListenerOptions {
6
- server: any; // instance dns2 ou autre
7
- logger?: Logger;
8
- }
9
-
10
- export class DnsListener {
11
- private logger: Logger;
12
-
13
- constructor(
14
- private router: CoreRouter,
15
- private options: DnsListenerOptions,
16
- ) {
17
- this.logger = options.logger ?? new Logger({ context: "DnsListener" });
18
- }
19
-
20
- async start(): Promise<void> {
21
- this.options.server.on("request", async (req: any, send: any) => {
22
- await this.handleQuery(req, send);
23
- });
24
-
25
- this.logger.info("DNS Listener started");
26
- }
27
-
28
- async stop(): Promise<void> {
29
- if (this.options.server.close) {
30
- this.options.server.close();
31
- }
32
- this.logger.info("DNS Listener stopped");
33
- }
34
-
35
- private async handleQuery(packet: any, send: any) {
36
- try {
37
- const rawEvent = {
38
- ip: packet?.client?.address ?? "",
39
- query: packet?.questions?.[0]?.name ?? "",
40
- recordType: packet?.questions?.[0]?.type ?? "",
41
- raw: packet,
42
- };
43
-
44
- const normalized = EventNormalizer.normalizeDns(rawEvent);
45
-
46
- await this.router.dispatch(normalized);
47
-
48
- send({
49
- success: true,
50
- eventId: normalized.id,
51
- });
52
- } catch (err: any) {
53
- this.logger.error("DNS Listener error", { error: err.message });
54
-
55
- send({
56
- success: false,
57
- error: err.message,
58
- });
59
- }
60
- }
61
- }
1
+ import { Logger } from "../../core/logger";
2
+ import { CoreRouter } from "../../core/router";
3
+ import { EventNormalizer } from "../../core/event.normalizer";
4
+
5
+ export interface DnsListenerOptions {
6
+ server: any; // instance dns2 ou autre
7
+ logger?: Logger;
8
+ }
9
+
10
+ export class DnsListener {
11
+ private logger: Logger;
12
+
13
+ constructor(
14
+ private router: CoreRouter,
15
+ private options: DnsListenerOptions,
16
+ ) {
17
+ this.logger = options.logger ?? new Logger({ context: "DnsListener" });
18
+ }
19
+
20
+ async start(): Promise<void> {
21
+ this.options.server.on("request", async (req: any, send: any) => {
22
+ await this.handleQuery(req, send);
23
+ });
24
+
25
+ this.logger.info("DNS Listener started");
26
+ }
27
+
28
+ async stop(): Promise<void> {
29
+ if (this.options.server.close) {
30
+ this.options.server.close();
31
+ }
32
+ this.logger.info("DNS Listener stopped");
33
+ }
34
+
35
+ private async handleQuery(packet: any, send: any) {
36
+ try {
37
+ const rawEvent = {
38
+ ip: packet?.client?.address ?? "",
39
+ query: packet?.questions?.[0]?.name ?? "",
40
+ recordType: packet?.questions?.[0]?.type ?? "",
41
+ raw: packet,
42
+ };
43
+
44
+ const normalized = EventNormalizer.normalizeDns(rawEvent);
45
+
46
+ await this.router.dispatch(normalized);
47
+
48
+ send({
49
+ success: true,
50
+ eventId: normalized.id,
51
+ });
52
+ } catch (err: any) {
53
+ this.logger.error("DNS Listener error", { error: err.message });
54
+
55
+ send({
56
+ success: false,
57
+ error: err.message,
58
+ });
59
+ }
60
+ }
61
+ }