@j3r3mcdev/oast-server 1.1.8 → 1.1.9

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 +0 -1
  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,87 +1,87 @@
1
- import http from "http";
2
- import { SsrfListener } from "../ssrf.listener";
3
- import { SsrfExtractor } from "../ssrf.extractor";
4
- import { EventNormalizer } from "../../../core/event.normalizer";
5
- import {
6
- describe,
7
- it,
8
- expect,
9
- beforeEach,
10
- afterEach,
11
- jest,
12
- } from "@jest/globals";
13
-
14
- jest.mock("../ssrf.extractor");
15
- jest.mock("../../../core/event.normalizer");
16
-
17
- describe("SsrfListener", () => {
18
- let router: any;
19
- let port: number;
20
- let listener: SsrfListener;
21
-
22
- beforeEach((done) => {
23
- router = { dispatch: jest.fn() };
24
-
25
- port = 12000 + Math.floor(Math.random() * 2000);
26
-
27
- (SsrfExtractor.extract as jest.Mock).mockReturnValue({
28
- ip: "1.2.3.4",
29
- method: "GET",
30
- path: "/ssrf",
31
- headers: {},
32
- query: {},
33
- raw: {},
34
- });
35
-
36
- (EventNormalizer.normalizeSsrf as jest.Mock).mockReturnValue({
37
- id: "123",
38
- type: "ssrf",
39
- timestamp: 111,
40
- sourceIp: "1.2.3.4",
41
- request: {
42
- method: "GET",
43
- path: "/ssrf",
44
- headers: {},
45
- query: {},
46
- },
47
- });
48
-
49
- listener = new SsrfListener(router, port);
50
-
51
- // 🔥 On attend que le serveur soit prêt
52
- listener["server"].on("listening", done);
53
- });
54
-
55
- afterEach(() => {
56
- listener.stop();
57
- });
58
-
59
- it("appelle router.dispatch avec l'événement normalisé", async () => {
60
- await new Promise<void>((resolve) => {
61
- const req = http.request(
62
- { hostname: "localhost", port, path: "/ssrf", method: "GET" },
63
- (res) => {
64
- expect(res.statusCode).toBe(200);
65
- expect(router.dispatch).toHaveBeenCalledTimes(1);
66
- resolve();
67
- },
68
- );
69
- req.end();
70
- });
71
- });
72
-
73
- it("retourne 500 si router.dispatch échoue", async () => {
74
- router.dispatch.mockRejectedValue(new Error("fail"));
75
-
76
- await new Promise<void>((resolve) => {
77
- const req = http.request(
78
- { hostname: "localhost", port, path: "/ssrf", method: "GET" },
79
- (res) => {
80
- expect(res.statusCode).toBe(500);
81
- resolve();
82
- },
83
- );
84
- req.end();
85
- });
86
- });
87
- });
1
+ import http from "http";
2
+ import { SsrfListener } from "../ssrf.listener";
3
+ import { SsrfExtractor } from "../ssrf.extractor";
4
+ import { EventNormalizer } from "../../../core/event.normalizer";
5
+ import {
6
+ describe,
7
+ it,
8
+ expect,
9
+ beforeEach,
10
+ afterEach,
11
+ jest,
12
+ } from "@jest/globals";
13
+
14
+ jest.mock("../ssrf.extractor");
15
+ jest.mock("../../../core/event.normalizer");
16
+
17
+ describe("SsrfListener", () => {
18
+ let router: any;
19
+ let port: number;
20
+ let listener: SsrfListener;
21
+
22
+ beforeEach((done) => {
23
+ router = { dispatch: jest.fn() };
24
+
25
+ port = 12000 + Math.floor(Math.random() * 2000);
26
+
27
+ (SsrfExtractor.extract as jest.Mock).mockReturnValue({
28
+ ip: "1.2.3.4",
29
+ method: "GET",
30
+ path: "/ssrf",
31
+ headers: {},
32
+ query: {},
33
+ raw: {},
34
+ });
35
+
36
+ (EventNormalizer.normalizeSsrf as jest.Mock).mockReturnValue({
37
+ id: "123",
38
+ type: "ssrf",
39
+ timestamp: 111,
40
+ sourceIp: "1.2.3.4",
41
+ request: {
42
+ method: "GET",
43
+ path: "/ssrf",
44
+ headers: {},
45
+ query: {},
46
+ },
47
+ });
48
+
49
+ listener = new SsrfListener(router, port);
50
+
51
+ // 🔥 On attend que le serveur soit prêt
52
+ listener["server"].on("listening", done);
53
+ });
54
+
55
+ afterEach(() => {
56
+ listener.stop();
57
+ });
58
+
59
+ it("appelle router.dispatch avec l'événement normalisé", async () => {
60
+ await new Promise<void>((resolve) => {
61
+ const req = http.request(
62
+ { hostname: "localhost", port, path: "/ssrf", method: "GET" },
63
+ (res) => {
64
+ expect(res.statusCode).toBe(200);
65
+ expect(router.dispatch).toHaveBeenCalledTimes(1);
66
+ resolve();
67
+ },
68
+ );
69
+ req.end();
70
+ });
71
+ });
72
+
73
+ it("retourne 500 si router.dispatch échoue", async () => {
74
+ router.dispatch.mockRejectedValue(new Error("fail"));
75
+
76
+ await new Promise<void>((resolve) => {
77
+ const req = http.request(
78
+ { hostname: "localhost", port, path: "/ssrf", method: "GET" },
79
+ (res) => {
80
+ expect(res.statusCode).toBe(500);
81
+ resolve();
82
+ },
83
+ );
84
+ req.end();
85
+ });
86
+ });
87
+ });
@@ -1,14 +1,14 @@
1
- export class SsrfExtractor {
2
- static extract(req: any) {
3
- const headers = req.headers ?? {};
4
-
5
- return {
6
- ip: req.ip ?? headers["x-forwarded-for"] ?? "",
7
- method: req.method,
8
- path: req.path ?? req.url,
9
- headers,
10
- query: req.query ?? {},
11
- raw: req,
12
- };
13
- }
14
- }
1
+ export class SsrfExtractor {
2
+ static extract(req: any) {
3
+ const headers = req.headers ?? {};
4
+
5
+ return {
6
+ ip: req.ip ?? headers["x-forwarded-for"] ?? "",
7
+ method: req.method,
8
+ path: req.path ?? req.url,
9
+ headers,
10
+ query: req.query ?? {},
11
+ raw: req,
12
+ };
13
+ }
14
+ }
@@ -1,37 +1,37 @@
1
- import { CoreRouter } from "../../core/router";
2
- import { SsrfExtractor } from "./ssrf.extractor";
3
- import { EventNormalizer } from "../../core/event.normalizer";
4
- import { Logger } from "../../core/logger";
5
- import http from "http";
6
-
7
- export class SsrfListener {
8
- private router: CoreRouter;
9
- private server: http.Server;
10
- private logger: Logger;
11
-
12
- constructor(router: CoreRouter, port: number) {
13
- this.router = router;
14
- this.logger = new Logger({ context: "SsrfListener" });
15
-
16
- this.server = http.createServer(async (req, res) => {
17
- try {
18
- const extracted = SsrfExtractor.extract(req);
19
- const event = EventNormalizer.normalizeSsrf(extracted);
20
-
21
- await this.router.dispatch(event);
22
- res.writeHead(200);
23
- res.end("OK");
24
- } catch (err) {
25
- res.writeHead(500);
26
- res.end("ERROR");
27
- }
28
- });
29
-
30
- this.server.listen(port);
31
- }
32
-
33
- stop() {
34
- this.server.close();
35
- this.logger.info("SSRF Listener stopped");
36
- }
37
- }
1
+ import { CoreRouter } from "../../core/router";
2
+ import { SsrfExtractor } from "./ssrf.extractor";
3
+ import { EventNormalizer } from "../../core/event.normalizer";
4
+ import { Logger } from "../../core/logger";
5
+ import http from "http";
6
+
7
+ export class SsrfListener {
8
+ private router: CoreRouter;
9
+ private server: http.Server;
10
+ private logger: Logger;
11
+
12
+ constructor(router: CoreRouter, port: number) {
13
+ this.router = router;
14
+ this.logger = new Logger({ context: "SsrfListener" });
15
+
16
+ this.server = http.createServer(async (req, res) => {
17
+ try {
18
+ const extracted = SsrfExtractor.extract(req);
19
+ const event = EventNormalizer.normalizeSsrf(extracted);
20
+
21
+ await this.router.dispatch(event);
22
+ res.writeHead(200);
23
+ res.end("OK");
24
+ } catch (err) {
25
+ res.writeHead(500);
26
+ res.end("ERROR");
27
+ }
28
+ });
29
+
30
+ this.server.listen(port);
31
+ }
32
+
33
+ stop() {
34
+ this.server.close();
35
+ this.logger.info("SSRF Listener stopped");
36
+ }
37
+ }
@@ -1,16 +1,16 @@
1
- import { RawTcpEvent, NormalizedTcpEvent } from "../../types/event.types";
2
- import { IdGenerator } from "../../core/id-generator";
3
-
4
- export class TcpExtractor {
5
- static normalize(raw: RawTcpEvent): NormalizedTcpEvent {
6
- return {
7
- id: IdGenerator.generate(),
8
- type: "tcp",
9
- timestamp: Date.now(),
10
- ip: raw.ip ?? "",
11
- port: raw.port ?? 0,
12
- data: raw.data ?? "",
13
- raw,
14
- };
15
- }
16
- }
1
+ import { RawTcpEvent, NormalizedTcpEvent } from "../../types/event.types";
2
+ import { IdGenerator } from "../../core/id-generator";
3
+
4
+ export class TcpExtractor {
5
+ static normalize(raw: RawTcpEvent): NormalizedTcpEvent {
6
+ return {
7
+ id: IdGenerator.generate(),
8
+ type: "tcp",
9
+ timestamp: Date.now(),
10
+ ip: raw.ip ?? "",
11
+ port: raw.port ?? 0,
12
+ data: raw.data ?? "",
13
+ raw,
14
+ };
15
+ }
16
+ }
@@ -1,61 +1,61 @@
1
- import net from "net";
2
- import { Listener } from "../listener.interface";
3
- import { CoreRouter } from "../../core/router";
4
- import { StorageManager } from "../../core/storage";
5
- import { Logger } from "../../core/logger";
6
- import { TcpExtractor } from "./tcp.extractor";
7
- import { RawTcpEvent } from "../../types/event.types";
8
-
9
- export interface TcpListenerOptions {
10
- port: number;
11
- logger?: Logger;
12
- }
13
-
14
- export class TcpListener implements Listener {
15
- private server: net.Server;
16
- private logger: Logger;
17
-
18
- constructor(
19
- private router: CoreRouter,
20
- private storage: StorageManager,
21
- private options: TcpListenerOptions,
22
- ) {
23
- this.logger = options.logger ?? new Logger({ context: "TcpListener" });
24
- this.server = net.createServer();
25
- }
26
-
27
- async start(): Promise<void> {
28
- this.server.on("connection", (socket) => {
29
- const ip = socket.remoteAddress ?? "unknown";
30
- const port = socket.remotePort ?? 0;
31
-
32
- socket.on("data", async (buffer) => {
33
- const data = buffer.toString("utf8");
34
-
35
- const rawEvent: RawTcpEvent = {
36
- ip,
37
- port,
38
- data,
39
- raw: { ip, port, data },
40
- };
41
- const normalized = TcpExtractor.normalize(rawEvent);
42
-
43
- await this.storage.save(normalized);
44
- this.router.dispatch(normalized);
45
- });
46
-
47
- socket.on("error", (err) => {
48
- this.logger.error("TCP socket error", { error: err.message });
49
- });
50
- });
51
-
52
- this.server.listen(this.options.port, () => {
53
- this.logger.info(`TCP Listener started on port ${this.options.port}`);
54
- });
55
- }
56
-
57
- async stop(): Promise<void> {
58
- this.server.close();
59
- this.logger.info("TCP Listener stopped");
60
- }
61
- }
1
+ import net from "net";
2
+ import { Listener } from "../listener.interface";
3
+ import { CoreRouter } from "../../core/router";
4
+ import { StorageManager } from "../../core/storage";
5
+ import { Logger } from "../../core/logger";
6
+ import { TcpExtractor } from "./tcp.extractor";
7
+ import { RawTcpEvent } from "../../types/event.types";
8
+
9
+ export interface TcpListenerOptions {
10
+ port: number;
11
+ logger?: Logger;
12
+ }
13
+
14
+ export class TcpListener implements Listener {
15
+ private server: net.Server;
16
+ private logger: Logger;
17
+
18
+ constructor(
19
+ private router: CoreRouter,
20
+ private storage: StorageManager,
21
+ private options: TcpListenerOptions,
22
+ ) {
23
+ this.logger = options.logger ?? new Logger({ context: "TcpListener" });
24
+ this.server = net.createServer();
25
+ }
26
+
27
+ async start(): Promise<void> {
28
+ this.server.on("connection", (socket) => {
29
+ const ip = socket.remoteAddress ?? "unknown";
30
+ const port = socket.remotePort ?? 0;
31
+
32
+ socket.on("data", async (buffer) => {
33
+ const data = buffer.toString("utf8");
34
+
35
+ const rawEvent: RawTcpEvent = {
36
+ ip,
37
+ port,
38
+ data,
39
+ raw: { ip, port, data },
40
+ };
41
+ const normalized = TcpExtractor.normalize(rawEvent);
42
+
43
+ await this.storage.save(normalized);
44
+ this.router.dispatch(normalized);
45
+ });
46
+
47
+ socket.on("error", (err) => {
48
+ this.logger.error("TCP socket error", { error: err.message });
49
+ });
50
+ });
51
+
52
+ this.server.listen(this.options.port, () => {
53
+ this.logger.info(`TCP Listener started on port ${this.options.port}`);
54
+ });
55
+ }
56
+
57
+ async stop(): Promise<void> {
58
+ this.server.close();
59
+ this.logger.info("TCP Listener stopped");
60
+ }
61
+ }
@@ -1,35 +1,35 @@
1
- import { describe, it, expect } from "@jest/globals";
2
- import { WebhookExtractor } from "../webhook.extractor";
3
-
4
- describe("WebhookExtractor", () => {
5
- it("extrait correctement toutes les données", () => {
6
- const req: any = {
7
- ip: "1.2.3.4",
8
- headers: { "x-test": "ok" },
9
- };
10
-
11
- const body = { hello: "world" };
12
-
13
- const extracted = WebhookExtractor.extract(req, body);
14
-
15
- expect(extracted).toEqual({
16
- ip: "1.2.3.4",
17
- headers: { "x-test": "ok" },
18
- body,
19
- raw: req,
20
- });
21
- });
22
-
23
- it("retourne une structure complète même si des champs manquent", () => {
24
- const req: any = {};
25
-
26
- const extracted = WebhookExtractor.extract(req, null);
27
-
28
- expect(extracted).toEqual({
29
- ip: "",
30
- headers: {},
31
- body: null,
32
- raw: req,
33
- });
34
- });
35
- });
1
+ import { describe, it, expect } from "@jest/globals";
2
+ import { WebhookExtractor } from "../webhook.extractor";
3
+
4
+ describe("WebhookExtractor", () => {
5
+ it("extrait correctement toutes les données", () => {
6
+ const req: any = {
7
+ ip: "1.2.3.4",
8
+ headers: { "x-test": "ok" },
9
+ };
10
+
11
+ const body = { hello: "world" };
12
+
13
+ const extracted = WebhookExtractor.extract(req, body);
14
+
15
+ expect(extracted).toEqual({
16
+ ip: "1.2.3.4",
17
+ headers: { "x-test": "ok" },
18
+ body,
19
+ raw: req,
20
+ });
21
+ });
22
+
23
+ it("retourne une structure complète même si des champs manquent", () => {
24
+ const req: any = {};
25
+
26
+ const extracted = WebhookExtractor.extract(req, null);
27
+
28
+ expect(extracted).toEqual({
29
+ ip: "",
30
+ headers: {},
31
+ body: null,
32
+ raw: req,
33
+ });
34
+ });
35
+ });