@j3r3mcdev/oast-server 1.0.0 → 1.1.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 (187) 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/api/controllers/__tests__/tasks.controller.test.d.ts +1 -0
  5. package/dist/api/controllers/events.controller.d.ts +6 -0
  6. package/dist/api/controllers/health.controller.d.ts +4 -0
  7. package/dist/api/controllers/index.d.ts +0 -0
  8. package/dist/api/controllers/tasks.controller.d.ts +40 -0
  9. package/dist/api/dto/__tests__/create-task.dto.test.d.ts +1 -0
  10. package/dist/api/dto/__tests__/filter-tasks.dto.test.d.ts +1 -0
  11. package/dist/api/dto/create-task.dto.d.ts +9 -0
  12. package/dist/api/dto/filter-tasks.dto.d.ts +8 -0
  13. package/dist/api/services/__tests__/events.service.test.d.ts +1 -0
  14. package/dist/api/services/__tests__/tasks.service.test.d.ts +1 -0
  15. package/dist/api/services/events.service.d.ts +8 -0
  16. package/dist/api/services/tasks.service.d.ts +22 -0
  17. package/dist/api/sse/events.stream.d.ts +25 -0
  18. package/dist/config/constants.d.ts +0 -0
  19. package/dist/config/env.d.ts +0 -0
  20. package/dist/core/__tests__/core-router.test.d.ts +1 -0
  21. package/dist/core/__tests__/core-server.test.d.ts +1 -0
  22. package/dist/core/__tests__/event.normalizer.test.d.ts +1 -0
  23. package/dist/core/__tests__/event.router.test.d.ts +1 -0
  24. package/dist/core/__tests__/logger.test.d.ts +1 -0
  25. package/dist/core/__tests__/storage-manager.test.d.ts +1 -0
  26. package/dist/core/event.normalizer.d.ts +10 -0
  27. package/dist/core/event.router.d.ts +7 -0
  28. package/dist/core/http/__tests__/adapter-node.test.d.ts +1 -0
  29. package/dist/core/http/__tests__/body-parser-multipart.test.d.ts +1 -0
  30. package/dist/core/http/__tests__/body-parser-raw.test.d.ts +1 -0
  31. package/dist/core/http/__tests__/body-parser-text.test.d.ts +1 -0
  32. package/dist/core/http/__tests__/compile-path.test.d.ts +1 -0
  33. package/dist/core/http/__tests__/middleware-pipeline.test.d.ts +1 -0
  34. package/dist/core/http/__tests__/request.test.d.ts +1 -0
  35. package/dist/core/http/__tests__/response.test.d.ts +1 -0
  36. package/dist/core/http/__tests__/router-match.test.d.ts +1 -0
  37. package/dist/core/http/adapter-node.d.ts +8 -0
  38. package/dist/core/http/buildRequest.d.ts +2 -0
  39. package/dist/core/http/compile-path.d.ts +13 -0
  40. package/dist/core/http/errors.d.ts +9 -0
  41. package/dist/core/http/http-server.d.ts +7 -0
  42. package/dist/core/http/index.d.ts +0 -0
  43. package/dist/core/http/main.d.ts +0 -0
  44. package/dist/core/http/middleware.d.ts +12 -0
  45. package/dist/core/http/request.d.ts +35 -0
  46. package/dist/core/http/response.d.ts +17 -0
  47. package/dist/core/http/router.d.ts +28 -0
  48. package/dist/core/http/utils.d.ts +0 -0
  49. package/dist/core/id-generator.d.ts +3 -0
  50. package/dist/core/logger.d.ts +29 -0
  51. package/dist/core/router.d.ts +16 -0
  52. package/dist/core/server.d.ts +25 -0
  53. package/dist/core/storage.d.ts +24 -0
  54. package/dist/index.d.ts +1 -0
  55. package/dist/listeners/api/__tests__/api.controller.test.d.ts +1 -0
  56. package/dist/listeners/api/__tests__/api.extractor.test.d.ts +1 -0
  57. package/dist/listeners/api/__tests__/api.listener.test.d.ts +1 -0
  58. package/dist/listeners/api/__tests__/api.routes.test.d.ts +1 -0
  59. package/dist/listeners/api/__tests__/api.sse.test.d.ts +1 -0
  60. package/dist/listeners/api/api.controllers.d.ts +9 -0
  61. package/dist/listeners/api/api.extractor.d.ts +5 -0
  62. package/dist/listeners/api/api.listener.d.ts +17 -0
  63. package/dist/listeners/api/api.routes.d.ts +5 -0
  64. package/dist/listeners/api/api.sse.d.ts +10 -0
  65. package/dist/listeners/dns/__tests__/dns.test.d.ts +1 -0
  66. package/dist/listeners/dns/dns.extractor.d.ts +8 -0
  67. package/dist/listeners/dns/dns.listener.d.ts +15 -0
  68. package/dist/listeners/http/__tests__/http.extractor.test.d.ts +1 -0
  69. package/dist/listeners/http/__tests__/http.listener.test.d.ts +1 -0
  70. package/dist/listeners/http/http.extractor.d.ts +11 -0
  71. package/dist/listeners/http/http.listener.d.ts +17 -0
  72. package/dist/listeners/listener.interface.d.ts +4 -0
  73. package/dist/listeners/smtp/__tests__/smtp.extractor.test.d.ts +1 -0
  74. package/dist/listeners/smtp/__tests__/smtp.listener.test.d.ts +1 -0
  75. package/dist/listeners/smtp/smtp.extractor.d.ts +12 -0
  76. package/dist/listeners/smtp/smtp.listener.d.ts +13 -0
  77. package/dist/listeners/ssrf/__tests__/ssrf.extractor.test.d.ts +1 -0
  78. package/dist/listeners/ssrf/__tests__/ssrf.listener.test.d.ts +1 -0
  79. package/dist/listeners/ssrf/__tests__/ssrf.listener.test.js +4 -16
  80. package/dist/listeners/ssrf/ssrf.extractor.d.ts +10 -0
  81. package/dist/listeners/ssrf/ssrf.listener.d.ts +8 -0
  82. package/dist/listeners/tcp/tcp.extractor.d.ts +4 -0
  83. package/dist/listeners/tcp/tcp.listener.d.ts +18 -0
  84. package/dist/listeners/webhook/__tests__/webhook.extractor.test.d.ts +1 -0
  85. package/dist/listeners/webhook/__tests__/webhook.listener.test.d.ts +1 -0
  86. package/dist/listeners/webhook/webhook.extractor.d.ts +8 -0
  87. package/dist/listeners/webhook/webhook.listener.d.ts +8 -0
  88. package/dist/listeners/websocket/__tests__/websocket.extractor.test.d.ts +1 -0
  89. package/dist/listeners/websocket/__tests__/websocket.listener.test.d.ts +1 -0
  90. package/dist/listeners/websocket/websocket.extractor.d.ts +10 -0
  91. package/dist/listeners/websocket/websocket.listener.d.ts +8 -0
  92. package/dist/storage-adapters/adapters/__tests__/memory.storage.test.d.ts +1 -0
  93. package/dist/storage-adapters/adapters/memory.storage.d.ts +21 -0
  94. package/dist/storage-adapters/adapters/redis.storage.d.ts +0 -0
  95. package/dist/storage-adapters/adapters/sqlite.storage.d.ts +0 -0
  96. package/dist/storage-adapters/storage.interface.d.ts +18 -0
  97. package/dist/types/event.types.d.ts +118 -0
  98. package/dist/utils/token.d.ts +0 -0
  99. package/jest.config.js +14 -11
  100. package/package.json +45 -45
  101. package/sadmin list shadows +9 -9
  102. package/src/api/controllers/__tests__/tasks.controller.test.ts +74 -74
  103. package/src/api/controllers/events.controller.ts +10 -10
  104. package/src/api/controllers/health.controller.ts +7 -7
  105. package/src/api/controllers/tasks.controller.ts +41 -41
  106. package/src/api/dto/__tests__/create-task.dto.test.ts +41 -41
  107. package/src/api/dto/__tests__/filter-tasks.dto.test.ts +35 -35
  108. package/src/api/dto/create-task.dto.ts +33 -33
  109. package/src/api/dto/filter-tasks.dto.ts +33 -33
  110. package/src/api/services/__tests__/events.service.test.ts +41 -41
  111. package/src/api/services/__tests__/tasks.service.test.ts +41 -41
  112. package/src/api/services/events.service.ts +17 -17
  113. package/src/api/services/tasks.service.ts +79 -79
  114. package/src/api/sse/events.stream.ts +90 -90
  115. package/src/core/__tests__/core-router.test.ts +30 -30
  116. package/src/core/__tests__/core-server.test.ts +44 -44
  117. package/src/core/__tests__/event.normalizer.test.ts +56 -56
  118. package/src/core/__tests__/event.router.test.ts +89 -89
  119. package/src/core/__tests__/logger.test.ts +32 -32
  120. package/src/core/__tests__/storage-manager.test.ts +74 -74
  121. package/src/core/event.normalizer.ts +147 -147
  122. package/src/core/event.router.ts +13 -13
  123. package/src/core/http/__tests__/adapter-node.test.ts +52 -52
  124. package/src/core/http/__tests__/body-parser-multipart.test.ts +41 -41
  125. package/src/core/http/__tests__/body-parser-raw.test.ts +28 -28
  126. package/src/core/http/__tests__/body-parser-text.test.ts +28 -28
  127. package/src/core/http/__tests__/compile-path.test.ts +39 -39
  128. package/src/core/http/__tests__/middleware-pipeline.test.ts +51 -51
  129. package/src/core/http/__tests__/request.test.ts +34 -34
  130. package/src/core/http/__tests__/response.test.ts +35 -35
  131. package/src/core/http/__tests__/router-match.test.ts +171 -171
  132. package/src/core/http/adapter-node.ts +51 -51
  133. package/src/core/http/buildRequest.ts +18 -18
  134. package/src/core/http/compile-path.ts +32 -32
  135. package/src/core/http/errors.ts +37 -37
  136. package/src/core/http/http-server.ts +52 -52
  137. package/src/core/http/middleware.ts +160 -160
  138. package/src/core/http/request.ts +55 -55
  139. package/src/core/http/response.ts +93 -93
  140. package/src/core/http/router.ts +138 -138
  141. package/src/core/id-generator.ts +8 -8
  142. package/src/core/logger.ts +113 -113
  143. package/src/core/router.ts +44 -44
  144. package/src/core/server.ts +85 -85
  145. package/src/core/storage.ts +64 -64
  146. package/src/index.ts +89 -89
  147. package/src/listeners/api/__tests__/api.controller.test.ts +116 -116
  148. package/src/listeners/api/__tests__/api.extractor.test.ts +46 -46
  149. package/src/listeners/api/__tests__/api.listener.test.ts +82 -82
  150. package/src/listeners/api/__tests__/api.routes.test.ts +155 -155
  151. package/src/listeners/api/__tests__/api.sse.test.ts +105 -105
  152. package/src/listeners/api/api.controllers.ts +67 -67
  153. package/src/listeners/api/api.extractor.ts +43 -43
  154. package/src/listeners/api/api.listener.ts +50 -50
  155. package/src/listeners/api/api.routes.ts +76 -76
  156. package/src/listeners/api/api.sse.ts +38 -38
  157. package/src/listeners/dns/__tests__/dns.test.ts +118 -118
  158. package/src/listeners/dns/dns.extractor.ts +14 -14
  159. package/src/listeners/dns/dns.listener.ts +61 -61
  160. package/src/listeners/http/__tests__/http.extractor.test.ts +59 -59
  161. package/src/listeners/http/__tests__/http.listener.test.ts +133 -133
  162. package/src/listeners/http/http.extractor.ts +15 -15
  163. package/src/listeners/http/http.listener.ts +110 -110
  164. package/src/listeners/listener.interface.ts +4 -4
  165. package/src/listeners/smtp/__tests__/smtp.extractor.test.ts +69 -69
  166. package/src/listeners/smtp/__tests__/smtp.listener.test.ts +150 -150
  167. package/src/listeners/smtp/smtp.extractor.ts +18 -18
  168. package/src/listeners/smtp/smtp.listener.ts +60 -60
  169. package/src/listeners/ssrf/__tests__/ssrf.extractor.test.ts +41 -41
  170. package/src/listeners/ssrf/__tests__/ssrf.listener.test.ts +87 -98
  171. package/src/listeners/ssrf/ssrf.extractor.ts +14 -14
  172. package/src/listeners/ssrf/ssrf.listener.ts +37 -37
  173. package/src/listeners/tcp/tcp.extractor.ts +16 -16
  174. package/src/listeners/tcp/tcp.listener.ts +61 -61
  175. package/src/listeners/webhook/__tests__/webhook.extractor.test.ts +35 -35
  176. package/src/listeners/webhook/__tests__/webhook.listener.test.ts +122 -122
  177. package/src/listeners/webhook/webhook.extractor.ts +12 -12
  178. package/src/listeners/webhook/webhook.listener.ts +58 -58
  179. package/src/listeners/websocket/__tests__/websocket.extractor.test.ts +33 -33
  180. package/src/listeners/websocket/__tests__/websocket.listener.test.ts +90 -90
  181. package/src/listeners/websocket/websocket.extractor.ts +11 -11
  182. package/src/listeners/websocket/websocket.listener.ts +40 -40
  183. package/src/storage-adapters/adapters/__tests__/memory.storage.test.ts +75 -75
  184. package/src/storage-adapters/adapters/memory.storage.ts +64 -64
  185. package/src/storage-adapters/storage.interface.ts +26 -26
  186. package/src/types/event.types.ts +147 -147
  187. package/tsconfig.json +21 -15
package/src/index.ts CHANGED
@@ -1,89 +1,89 @@
1
- import { Logger } from "./core/logger";
2
- import { StorageManager } from "./core/storage";
3
- import { CoreRouter } from "./core/router";
4
- import { CoreServer } from "./core/server";
5
-
6
- // Listeners
7
- import { ApiListener } from "./listeners/api/api.listener";
8
- import { HttpListener } from "./listeners/http/http.listener";
9
- import { DnsListener } from "./listeners/dns/dns.listener";
10
- import { SmtpListener } from "./listeners/smtp/smtp.listener";
11
- import { TcpListener } from "./listeners/tcp/tcp.listener";
12
-
13
- async function main() {
14
- const logger = new Logger({ context: "Bootstrap" });
15
-
16
- // Core components
17
- const storage = new StorageManager({
18
- logger: logger.withContext("Storage"),
19
- });
20
-
21
- const router = new CoreRouter(storage, {
22
- logger: logger.withContext("CoreRouter"),
23
- });
24
-
25
- // ---------------------------
26
- // LISTENERS
27
- // ---------------------------
28
-
29
- // API → (router, storage, options)
30
- const apiListener = new ApiListener(storage, {
31
- port: 3100,
32
- logger: logger.withContext("ApiListener"),
33
- });
34
-
35
- // HTTP → (router, options)
36
- const httpListener = new HttpListener(router, {
37
- port: 8080,
38
- logger: logger.withContext("HttpListener"),
39
- });
40
-
41
- // DNS → (router, options)
42
- const dnsListener = new DnsListener(router, {
43
- server: {} as any,
44
- logger: logger.withContext("DnsListener"),
45
- });
46
-
47
- const smtpListener = new SmtpListener(router, {
48
- server: {} as any,
49
- logger: logger.withContext("SmtpListener"),
50
- });
51
-
52
- // TCP → (router, storage, options)
53
- const tcpListener = new TcpListener(router, storage, {
54
- port: 9000,
55
- logger: logger.withContext("TcpListener"),
56
- });
57
-
58
- // ---------------------------
59
- // CORE SERVER
60
- // ---------------------------
61
- const server = new CoreServer({
62
- logger: logger.withContext("CoreServer"),
63
- storage,
64
- listeners: [
65
- apiListener,
66
- httpListener,
67
- dnsListener,
68
- smtpListener,
69
- tcpListener,
70
- ],
71
- });
72
-
73
- await server.start();
74
-
75
- // Graceful shutdown
76
- const shutdown = async () => {
77
- logger.warn("Received shutdown signal, stopping server...");
78
- await server.stop();
79
- process.exit(0);
80
- };
81
-
82
- process.on("SIGINT", shutdown);
83
- process.on("SIGTERM", shutdown);
84
- }
85
-
86
- main().catch((err) => {
87
- console.error("Fatal error in bootstrap:", err);
88
- process.exit(1);
89
- });
1
+ import { Logger } from "./core/logger";
2
+ import { StorageManager } from "./core/storage";
3
+ import { CoreRouter } from "./core/router";
4
+ import { CoreServer } from "./core/server";
5
+
6
+ // Listeners
7
+ import { ApiListener } from "./listeners/api/api.listener";
8
+ import { HttpListener } from "./listeners/http/http.listener";
9
+ import { DnsListener } from "./listeners/dns/dns.listener";
10
+ import { SmtpListener } from "./listeners/smtp/smtp.listener";
11
+ import { TcpListener } from "./listeners/tcp/tcp.listener";
12
+
13
+ async function main() {
14
+ const logger = new Logger({ context: "Bootstrap" });
15
+
16
+ // Core components
17
+ const storage = new StorageManager({
18
+ logger: logger.withContext("Storage"),
19
+ });
20
+
21
+ const router = new CoreRouter(storage, {
22
+ logger: logger.withContext("CoreRouter"),
23
+ });
24
+
25
+ // ---------------------------
26
+ // LISTENERS
27
+ // ---------------------------
28
+
29
+ // API → (router, storage, options)
30
+ const apiListener = new ApiListener(storage, {
31
+ port: 3100,
32
+ logger: logger.withContext("ApiListener"),
33
+ });
34
+
35
+ // HTTP → (router, options)
36
+ const httpListener = new HttpListener(router, {
37
+ port: 8080,
38
+ logger: logger.withContext("HttpListener"),
39
+ });
40
+
41
+ // DNS → (router, options)
42
+ const dnsListener = new DnsListener(router, {
43
+ server: {} as any,
44
+ logger: logger.withContext("DnsListener"),
45
+ });
46
+
47
+ const smtpListener = new SmtpListener(router, {
48
+ server: {} as any,
49
+ logger: logger.withContext("SmtpListener"),
50
+ });
51
+
52
+ // TCP → (router, storage, options)
53
+ const tcpListener = new TcpListener(router, storage, {
54
+ port: 9000,
55
+ logger: logger.withContext("TcpListener"),
56
+ });
57
+
58
+ // ---------------------------
59
+ // CORE SERVER
60
+ // ---------------------------
61
+ const server = new CoreServer({
62
+ logger: logger.withContext("CoreServer"),
63
+ storage,
64
+ listeners: [
65
+ apiListener,
66
+ httpListener,
67
+ dnsListener,
68
+ smtpListener,
69
+ tcpListener,
70
+ ],
71
+ });
72
+
73
+ await server.start();
74
+
75
+ // Graceful shutdown
76
+ const shutdown = async () => {
77
+ logger.warn("Received shutdown signal, stopping server...");
78
+ await server.stop();
79
+ process.exit(0);
80
+ };
81
+
82
+ process.on("SIGINT", shutdown);
83
+ process.on("SIGTERM", shutdown);
84
+ }
85
+
86
+ main().catch((err) => {
87
+ console.error("Fatal error in bootstrap:", err);
88
+ process.exit(1);
89
+ });
@@ -1,116 +1,116 @@
1
- import { ApiController } from "../api.controllers";
2
- import { StorageManager } from "../../../core/storage";
3
- import { ServerResponse, IncomingMessage } from "http";
4
- import { Socket } from "net";
5
- import { describe, it, expect, beforeEach, jest } from "@jest/globals";
6
-
7
- // Mock ServerResponse
8
- function mockRes(): ServerResponse {
9
- const socket = new Socket();
10
- const req = new IncomingMessage(socket);
11
- const res = new ServerResponse(req);
12
-
13
- jest.spyOn(res, "writeHead");
14
- jest.spyOn(res, "end");
15
-
16
- return res;
17
- }
18
-
19
- describe("ApiController", () => {
20
- let storage: StorageManager;
21
-
22
- beforeEach(() => {
23
- // On utilise un vrai StorageManager en mémoire
24
- storage = new StorageManager();
25
- });
26
-
27
- it("listEvents renvoie la liste des events (vide par défaut)", async () => {
28
- const res = mockRes();
29
- const url = new URL("http://localhost/events?page=2&type=http");
30
-
31
- await ApiController.listEvents(url, res, storage);
32
-
33
- expect(storage.listEvents).toBeDefined();
34
-
35
- expect(res.writeHead).toHaveBeenCalledWith(200, {
36
- "Content-Type": "application/json",
37
- });
38
-
39
- const body = (res.end as jest.Mock).mock.calls[0][0] as string;
40
- const payload = JSON.parse(body);
41
- expect(payload.success).toBe(true);
42
- expect(payload.events).toEqual([]);
43
- });
44
-
45
- it("getEvent renvoie 404 si event absent", async () => {
46
- const res = mockRes();
47
-
48
- await ApiController.getEvent("unknown", res, storage);
49
-
50
- expect(res.writeHead).toHaveBeenCalledWith(404, {
51
- "Content-Type": "application/json",
52
- });
53
-
54
- const body = (res.end as jest.Mock).mock.calls[0][0] as string;
55
- const payload = JSON.parse(body);
56
- expect(payload.success).toBe(false);
57
- });
58
-
59
- it("getEvent renvoie l'event si présent", async () => {
60
- // On mocke juste getEvent pour ce test
61
- jest.spyOn(storage, "getEvent").mockResolvedValue({ id: "abc" } as any);
62
-
63
- const res = mockRes();
64
-
65
- await ApiController.getEvent("abc", res, storage);
66
-
67
- expect(res.writeHead).toHaveBeenCalledWith(200, {
68
- "Content-Type": "application/json",
69
- });
70
-
71
- const body = (res.end as jest.Mock).mock.calls[0][0] as string;
72
- const payload = JSON.parse(body);
73
- expect(payload.success).toBe(true);
74
- expect(payload.event.id).toBe("abc");
75
- });
76
-
77
- it("deleteAll supprime tous les events", async () => {
78
- const res = mockRes();
79
-
80
- await ApiController.deleteAll(res, storage);
81
-
82
- const body = (res.end as jest.Mock).mock.calls[0][0] as string;
83
- const payload = JSON.parse(body);
84
- expect(payload.success).toBe(true);
85
- });
86
-
87
- it("deleteOne supprime un event", async () => {
88
- const res = mockRes();
89
-
90
- await storage.save({
91
- id: "xyz",
92
- type: "http",
93
- timestamp: Date.now(),
94
- sourceIp: "127.0.0.1",
95
- request: { method: "GET", path: "/", headers: {}, query: {}, body: "" },
96
- });
97
-
98
- await ApiController.deleteOne("xyz", res, storage);
99
-
100
- const raw = String((res.end as jest.Mock).mock.calls[0][0]);
101
- const payload = JSON.parse(raw);
102
-
103
- expect(payload.success).toBe(true);
104
- });
105
-
106
- it("stats renvoie les stats", async () => {
107
- const res = mockRes();
108
-
109
- await ApiController.stats(res, storage);
110
-
111
- const body = (res.end as jest.Mock).mock.calls[0][0] as string;
112
- const payload = JSON.parse(body);
113
- expect(payload.success).toBe(true);
114
- expect(payload.stats).toEqual({ total: 0, byType: {} });
115
- });
116
- });
1
+ import { ApiController } from "../api.controllers";
2
+ import { StorageManager } from "../../../core/storage";
3
+ import { ServerResponse, IncomingMessage } from "http";
4
+ import { Socket } from "net";
5
+ import { describe, it, expect, beforeEach, jest } from "@jest/globals";
6
+
7
+ // Mock ServerResponse
8
+ function mockRes(): ServerResponse {
9
+ const socket = new Socket();
10
+ const req = new IncomingMessage(socket);
11
+ const res = new ServerResponse(req);
12
+
13
+ jest.spyOn(res, "writeHead");
14
+ jest.spyOn(res, "end");
15
+
16
+ return res;
17
+ }
18
+
19
+ describe("ApiController", () => {
20
+ let storage: StorageManager;
21
+
22
+ beforeEach(() => {
23
+ // On utilise un vrai StorageManager en mémoire
24
+ storage = new StorageManager();
25
+ });
26
+
27
+ it("listEvents renvoie la liste des events (vide par défaut)", async () => {
28
+ const res = mockRes();
29
+ const url = new URL("http://localhost/events?page=2&type=http");
30
+
31
+ await ApiController.listEvents(url, res, storage);
32
+
33
+ expect(storage.listEvents).toBeDefined();
34
+
35
+ expect(res.writeHead).toHaveBeenCalledWith(200, {
36
+ "Content-Type": "application/json",
37
+ });
38
+
39
+ const body = (res.end as jest.Mock).mock.calls[0][0] as string;
40
+ const payload = JSON.parse(body);
41
+ expect(payload.success).toBe(true);
42
+ expect(payload.events).toEqual([]);
43
+ });
44
+
45
+ it("getEvent renvoie 404 si event absent", async () => {
46
+ const res = mockRes();
47
+
48
+ await ApiController.getEvent("unknown", res, storage);
49
+
50
+ expect(res.writeHead).toHaveBeenCalledWith(404, {
51
+ "Content-Type": "application/json",
52
+ });
53
+
54
+ const body = (res.end as jest.Mock).mock.calls[0][0] as string;
55
+ const payload = JSON.parse(body);
56
+ expect(payload.success).toBe(false);
57
+ });
58
+
59
+ it("getEvent renvoie l'event si présent", async () => {
60
+ // On mocke juste getEvent pour ce test
61
+ jest.spyOn(storage, "getEvent").mockResolvedValue({ id: "abc" } as any);
62
+
63
+ const res = mockRes();
64
+
65
+ await ApiController.getEvent("abc", res, storage);
66
+
67
+ expect(res.writeHead).toHaveBeenCalledWith(200, {
68
+ "Content-Type": "application/json",
69
+ });
70
+
71
+ const body = (res.end as jest.Mock).mock.calls[0][0] as string;
72
+ const payload = JSON.parse(body);
73
+ expect(payload.success).toBe(true);
74
+ expect(payload.event.id).toBe("abc");
75
+ });
76
+
77
+ it("deleteAll supprime tous les events", async () => {
78
+ const res = mockRes();
79
+
80
+ await ApiController.deleteAll(res, storage);
81
+
82
+ const body = (res.end as jest.Mock).mock.calls[0][0] as string;
83
+ const payload = JSON.parse(body);
84
+ expect(payload.success).toBe(true);
85
+ });
86
+
87
+ it("deleteOne supprime un event", async () => {
88
+ const res = mockRes();
89
+
90
+ await storage.save({
91
+ id: "xyz",
92
+ type: "http",
93
+ timestamp: Date.now(),
94
+ sourceIp: "127.0.0.1",
95
+ request: { method: "GET", path: "/", headers: {}, query: {}, body: "" },
96
+ });
97
+
98
+ await ApiController.deleteOne("xyz", res, storage);
99
+
100
+ const raw = String((res.end as jest.Mock).mock.calls[0][0]);
101
+ const payload = JSON.parse(raw);
102
+
103
+ expect(payload.success).toBe(true);
104
+ });
105
+
106
+ it("stats renvoie les stats", async () => {
107
+ const res = mockRes();
108
+
109
+ await ApiController.stats(res, storage);
110
+
111
+ const body = (res.end as jest.Mock).mock.calls[0][0] as string;
112
+ const payload = JSON.parse(body);
113
+ expect(payload.success).toBe(true);
114
+ expect(payload.stats).toEqual({ total: 0, byType: {} });
115
+ });
116
+ });
@@ -1,46 +1,46 @@
1
- import { ApiExtractor } from "../api.extractor";
2
- import { IncomingMessage } from "http";
3
- import { Socket } from "net";
4
- import { describe, it, expect } from "@jest/globals";
5
-
6
- function mockReq(url: string, method = "GET", body?: any): IncomingMessage {
7
- const socket = new Socket();
8
- const req = new IncomingMessage(socket);
9
-
10
- req.url = url;
11
- req.method = method;
12
- req.headers = { host: "localhost" };
13
-
14
- if (body !== undefined) {
15
- const json = JSON.stringify(body);
16
- process.nextTick(() => {
17
- req.emit("data", json);
18
- req.emit("end");
19
- });
20
- } else {
21
- process.nextTick(() => req.emit("end"));
22
- }
23
-
24
- return req;
25
- }
26
-
27
- describe("ApiExtractor", () => {
28
- it("extrait un RawEvent GET", async () => {
29
- const req = mockReq("/events?page=2&type=http");
30
-
31
- const event = await ApiExtractor.extract(req);
32
-
33
- expect(event.method).toBe("GET");
34
- expect(event.path).toBe("/events");
35
- expect(event.query).toEqual({ page: "2", type: "http" });
36
- });
37
-
38
- it("extrait un RawEvent POST avec body JSON", async () => {
39
- const req = mockReq("/events", "POST", { hello: "world" });
40
-
41
- const event = await ApiExtractor.extract(req);
42
-
43
- expect(event.method).toBe("POST");
44
- expect(event.body).toEqual({ hello: "world" });
45
- });
46
- });
1
+ import { ApiExtractor } from "../api.extractor";
2
+ import { IncomingMessage } from "http";
3
+ import { Socket } from "net";
4
+ import { describe, it, expect } from "@jest/globals";
5
+
6
+ function mockReq(url: string, method = "GET", body?: any): IncomingMessage {
7
+ const socket = new Socket();
8
+ const req = new IncomingMessage(socket);
9
+
10
+ req.url = url;
11
+ req.method = method;
12
+ req.headers = { host: "localhost" };
13
+
14
+ if (body !== undefined) {
15
+ const json = JSON.stringify(body);
16
+ process.nextTick(() => {
17
+ req.emit("data", json);
18
+ req.emit("end");
19
+ });
20
+ } else {
21
+ process.nextTick(() => req.emit("end"));
22
+ }
23
+
24
+ return req;
25
+ }
26
+
27
+ describe("ApiExtractor", () => {
28
+ it("extrait un RawEvent GET", async () => {
29
+ const req = mockReq("/events?page=2&type=http");
30
+
31
+ const event = await ApiExtractor.extract(req);
32
+
33
+ expect(event.method).toBe("GET");
34
+ expect(event.path).toBe("/events");
35
+ expect(event.query).toEqual({ page: "2", type: "http" });
36
+ });
37
+
38
+ it("extrait un RawEvent POST avec body JSON", async () => {
39
+ const req = mockReq("/events", "POST", { hello: "world" });
40
+
41
+ const event = await ApiExtractor.extract(req);
42
+
43
+ expect(event.method).toBe("POST");
44
+ expect(event.body).toEqual({ hello: "world" });
45
+ });
46
+ });
@@ -1,82 +1,82 @@
1
- import http from "http";
2
- import { ApiListener } from "../api.listener";
3
- import { StorageManager } from "../../../core/storage";
4
- import { Logger } from "../../../core/logger";
5
- import { describe, test, beforeEach, afterEach, expect } from "@jest/globals";
6
- import { ApiController } from "../api.controllers";
7
-
8
- describe("ApiListener", () => {
9
- let storage: StorageManager;
10
- let api: ApiListener;
11
-
12
- beforeEach(async () => {
13
- storage = new StorageManager();
14
-
15
- api = new ApiListener(storage, {
16
- port: 9999,
17
- logger: new Logger({ context: "ApiTest" }),
18
- });
19
-
20
- await api.start();
21
- });
22
-
23
- afterEach(async () => {
24
- await api.stop();
25
- });
26
-
27
- test("GET /events returns empty list", async () => {
28
- const res = await fetch("http://localhost:9999/events");
29
- const json = await res.json();
30
-
31
- expect(json.success).toBe(true);
32
- expect(json.events).toEqual([]);
33
- });
34
-
35
- test("GET /stats returns correct structure", async () => {
36
- const res = await fetch("http://localhost:9999/stats");
37
- const json = await res.json();
38
-
39
- expect(json.success).toBe(true);
40
- expect(json.stats.total).toBe(0);
41
- expect(json.stats.byType).toEqual({});
42
- });
43
-
44
- test("GET /events/:id returns 404 when not found", async () => {
45
- const res = await fetch("http://localhost:9999/events/unknown");
46
- const json = await res.json();
47
-
48
- expect(res.status).toBe(404);
49
- expect(json.success).toBe(false);
50
- });
51
-
52
- test("DELETE /events clears all events", async () => {
53
- // On ajoute un event dans le storage
54
- await storage.save({
55
- id: "1",
56
- type: "http",
57
- timestamp: Date.now(),
58
- sourceIp: "127.0.0.1",
59
- request: {
60
- method: "GET",
61
- path: "/",
62
- headers: {},
63
- query: {},
64
- body: "",
65
- },
66
- });
67
-
68
- let events = await storage.listEvents({});
69
- expect(events.length).toBe(1);
70
-
71
- // On appelle l'API réelle
72
- const res = await fetch("http://localhost:9999/events", {
73
- method: "DELETE",
74
- });
75
-
76
- const json = await res.json();
77
- expect(json.success).toBe(true);
78
-
79
- events = await storage.listEvents({});
80
- expect(events.length).toBe(0);
81
- });
82
- });
1
+ import http from "http";
2
+ import { ApiListener } from "../api.listener";
3
+ import { StorageManager } from "../../../core/storage";
4
+ import { Logger } from "../../../core/logger";
5
+ import { describe, test, beforeEach, afterEach, expect } from "@jest/globals";
6
+ import { ApiController } from "../api.controllers";
7
+
8
+ describe("ApiListener", () => {
9
+ let storage: StorageManager;
10
+ let api: ApiListener;
11
+
12
+ beforeEach(async () => {
13
+ storage = new StorageManager();
14
+
15
+ api = new ApiListener(storage, {
16
+ port: 9999,
17
+ logger: new Logger({ context: "ApiTest" }),
18
+ });
19
+
20
+ await api.start();
21
+ });
22
+
23
+ afterEach(async () => {
24
+ await api.stop();
25
+ });
26
+
27
+ test("GET /events returns empty list", async () => {
28
+ const res = await fetch("http://localhost:9999/events");
29
+ const json = await res.json();
30
+
31
+ expect(json.success).toBe(true);
32
+ expect(json.events).toEqual([]);
33
+ });
34
+
35
+ test("GET /stats returns correct structure", async () => {
36
+ const res = await fetch("http://localhost:9999/stats");
37
+ const json = await res.json();
38
+
39
+ expect(json.success).toBe(true);
40
+ expect(json.stats.total).toBe(0);
41
+ expect(json.stats.byType).toEqual({});
42
+ });
43
+
44
+ test("GET /events/:id returns 404 when not found", async () => {
45
+ const res = await fetch("http://localhost:9999/events/unknown");
46
+ const json = await res.json();
47
+
48
+ expect(res.status).toBe(404);
49
+ expect(json.success).toBe(false);
50
+ });
51
+
52
+ test("DELETE /events clears all events", async () => {
53
+ // On ajoute un event dans le storage
54
+ await storage.save({
55
+ id: "1",
56
+ type: "http",
57
+ timestamp: Date.now(),
58
+ sourceIp: "127.0.0.1",
59
+ request: {
60
+ method: "GET",
61
+ path: "/",
62
+ headers: {},
63
+ query: {},
64
+ body: "",
65
+ },
66
+ });
67
+
68
+ let events = await storage.listEvents({});
69
+ expect(events.length).toBe(1);
70
+
71
+ // On appelle l'API réelle
72
+ const res = await fetch("http://localhost:9999/events", {
73
+ method: "DELETE",
74
+ });
75
+
76
+ const json = await res.json();
77
+ expect(json.success).toBe(true);
78
+
79
+ events = await storage.listEvents({});
80
+ expect(events.length).toBe(0);
81
+ });
82
+ });