@opensecurity/zonzon-core 0.1.5 → 0.1.6

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.
@@ -1,5 +1,5 @@
1
1
  import { describe, it } from "node:test";
2
- import assert from "assert";
2
+ import assert from "node:assert";
3
3
  import { DevDnsServer } from "./dns-service.js";
4
4
  import { DNS_TYPES } from "./types.js";
5
5
  function buildQuery(name, type) {
@@ -1,5 +1,5 @@
1
1
  import { describe, it } from "node:test";
2
- import assert from "assert";
2
+ import assert from "node:assert";
3
3
  import { DevDnsServer } from "./dns-service.js";
4
4
  import { DNS_TYPES, DNS_CLASSES } from "./types.js";
5
5
  describe("Multi-Question TTL Cache Rewriting", () => {
@@ -1,5 +1,5 @@
1
1
  import dgram from "dgram";
2
- import * as net from "net";
2
+ import * as net from "node:net";
3
3
  import * as tls from "node:tls";
4
4
  import * as https from "node:https";
5
5
  import { randomBytes } from "node:crypto";
@@ -1,5 +1,5 @@
1
1
  import { describe, it } from "node:test";
2
- import assert from "assert";
2
+ import assert from "node:assert";
3
3
  import { DevDnsServer } from "./dns-service.js";
4
4
  import { DNS_TYPES, DNS_RCODE } from "./types.js";
5
5
  function buildQuery(name, type) {
@@ -1,5 +1,5 @@
1
1
  import { describe, it } from "node:test";
2
- import assert from "assert";
2
+ import assert from "node:assert";
3
3
  import { DevDnsServer } from "./dns-service.js";
4
4
  import { DNS_TYPES, DNS_RCODE } from "./types.js";
5
5
  function buildQuery(name, type) {
@@ -1,101 +1,104 @@
1
1
  import { describe, it } from "node:test";
2
2
  import assert from "node:assert";
3
- import { generateKeyPairSync } from "node:crypto";
4
3
  import { DnsHandler } from "./dns-handler.js";
5
4
  import { DevDnsServer } from "./dns-service.js";
6
- function buildDnsQuery(name, type) {
7
- const encoder = new (class {
8
- buf = Buffer.alloc(256);
9
- offset = 0;
10
- writeUint16(v) { this.buf.writeUInt16BE(v, this.offset); this.offset += 2; }
11
- writeUint8(v) { this.buf.writeUInt8(v, this.offset); this.offset += 1; }
12
- writeDomainName(nm) {
13
- for (const label of nm.split(".")) {
14
- if (!label.length)
15
- continue;
16
- this.writeUint8(label.length);
17
- Buffer.from(label).copy(this.buf, this.offset);
18
- this.offset += label.length;
19
- }
20
- this.writeUint8(0);
5
+ function createMockReqRes(method, url, headers = {}) {
6
+ const req = {
7
+ method,
8
+ url,
9
+ headers,
10
+ socket: { remoteAddress: "127.0.0.1" },
11
+ destroy: () => { }
12
+ };
13
+ let statusCode = 0;
14
+ let endData = null;
15
+ const resHeaders = {};
16
+ const res = {
17
+ writeHead: (code, headers) => {
18
+ statusCode = code;
19
+ if (headers)
20
+ Object.assign(resHeaders, headers);
21
+ },
22
+ end: (data) => {
23
+ endData = data;
21
24
  }
22
- finish() { return this.buf.subarray(0, this.offset); }
23
- })();
24
- encoder.writeUint16(0xDEAD);
25
- encoder.writeUint16(0x0100);
26
- encoder.writeUint16(1);
27
- encoder.writeUint16(0);
28
- encoder.writeUint16(0);
29
- encoder.writeUint16(0);
30
- encoder.writeDomainName(name);
31
- encoder.writeUint16(type);
32
- encoder.writeUint16(1);
33
- return encoder.finish();
34
- }
35
- function parseResponseFlags(buf) {
36
- const flags = buf.readUInt16BE(2);
37
- const qr = (flags >> 15) & 0x1;
38
- const rcode = flags & 0xf;
39
- return { qr, rcode };
25
+ };
26
+ return { req, res, getStatus: () => statusCode, getEndData: () => endData, getHeaders: () => resHeaders };
40
27
  }
41
- describe("Modern DNS Protocols (DoH and DoT)", () => {
42
- let handler;
43
- const dotPort = 64853;
44
- const dohPort = 64443;
45
- const dnsPort = 64053;
46
- // Minimal self-signed cert generation for testing boundaries
47
- const { privateKey, publicKey } = generateKeyPairSync('rsa', {
48
- modulusLength: 2048,
49
- publicKeyEncoding: { type: 'spki', format: 'pem' },
50
- privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
51
- });
52
- // Note: For a strictly functional test without full x509 cert generation logic,
53
- // we mock the TLS block to bypass strict validation in the Node runtime,
54
- // or use a pre-generated fixture. The handler binds TLS correctly, but
55
- // client verification needs to be disabled.
28
+ describe("Modern DNS Protocols (DoH)", () => {
56
29
  const config = {
57
- port: dnsPort,
58
- dotPort: dotPort,
59
- dohPort: dohPort,
60
- tls: {
61
- key: privateKey,
62
- cert: privateKey // Will throw on deep verification, fine for structural boundary tests
63
- },
64
- hosts: {
65
- "secure.loop": { records: [{ type: "A", address: "10.0.0.1" }] }
66
- }
30
+ port: 53,
31
+ hosts: {}
67
32
  };
68
- it("safely binds DoT and DoH boundaries without crashing", async () => {
69
- const server = new DevDnsServer(config);
70
- handler = new DnsHandler(server, config);
71
- try {
72
- // Due to the fake cert, start() might throw TLS errors if tightly coupled.
73
- // We wrap to ensure the logic path executes safely.
74
- await handler.start().catch(() => { });
75
- }
76
- finally {
77
- await handler.stop();
78
- }
79
- assert.ok(true);
33
+ const server = new DevDnsServer(config);
34
+ const handler = new DnsHandler(server, config);
35
+ it("rejects DoH request on invalid path", async () => {
36
+ const { req, res, getStatus } = createMockReqRes("GET", "/invalid-path");
37
+ // @ts-ignore - Accessing private method for strict L7 unit testing
38
+ await handler.handleDohRequest(req, res);
39
+ assert.strictEqual(getStatus(), 404);
40
+ });
41
+ it("rejects DoH GET request missing dns query parameter", async () => {
42
+ const { req, res, getStatus } = createMockReqRes("GET", "/dns-query");
43
+ // @ts-ignore
44
+ await handler.handleDohRequest(req, res);
45
+ assert.strictEqual(getStatus(), 400);
46
+ });
47
+ it("rejects DoH GET request with invalid base64url payload", async () => {
48
+ const { req, res, getStatus } = createMockReqRes("GET", "/dns-query?dns=!!!invalid_base64!!!");
49
+ // @ts-ignore
50
+ await handler.handleDohRequest(req, res);
51
+ assert.strictEqual(getStatus(), 400);
52
+ });
53
+ it("rejects DoH POST request with invalid content-type", async () => {
54
+ const { req, res, getStatus } = createMockReqRes("POST", "/dns-query", {
55
+ "content-type": "application/json"
56
+ });
57
+ // @ts-ignore
58
+ await handler.handleDohRequest(req, res);
59
+ assert.strictEqual(getStatus(), 415);
80
60
  });
81
- it("rejects malformed DoH requests", async () => {
82
- // Validates the internal HTTP request parser for DoH
83
- // This logic is tested directly on the handler object using mocks
84
- const server = new DevDnsServer(config);
85
- handler = new DnsHandler(server, config);
86
- const mockReq = {
87
- method: "POST",
88
- url: "/dns-query",
89
- headers: { "content-type": "text/plain" }, // Invalid content type
90
- socket: { remoteAddress: "127.0.0.1" }
61
+ it("rejects unsupported HTTP methods", async () => {
62
+ const { req, res, getStatus } = createMockReqRes("PUT", "/dns-query");
63
+ // @ts-ignore
64
+ await handler.handleDohRequest(req, res);
65
+ assert.strictEqual(getStatus(), 405);
66
+ });
67
+ it("rejects payloads that are too short to be valid DNS packets", async () => {
68
+ const { req, res, getStatus } = createMockReqRes("GET", "/dns-query?dns=abcd");
69
+ // @ts-ignore
70
+ await handler.handleDohRequest(req, res);
71
+ assert.strictEqual(getStatus(), 400);
72
+ });
73
+ it("enforces memory bounds on DoH POST payloads to prevent exhaustion", async () => {
74
+ const { req, res, getStatus } = createMockReqRes("POST", "/dns-query", {
75
+ "content-type": "application/dns-message"
76
+ });
77
+ const oversizedBuffer = Buffer.alloc(65 * 1024); // Exceeds MAX_TCP_BUFFER_SIZE (64KB)
78
+ req[Symbol.asyncIterator] = async function* () {
79
+ yield oversizedBuffer;
91
80
  };
92
- let statusCode = 0;
93
- const mockRes = {
94
- writeHead: (code) => { statusCode = code; },
95
- end: () => { }
81
+ // @ts-ignore
82
+ await handler.handleDohRequest(req, res);
83
+ assert.strictEqual(getStatus(), 413);
84
+ });
85
+ it("processes a structurally valid DoH POST request", async () => {
86
+ const { req, res, getStatus, getHeaders } = createMockReqRes("POST", "/dns-query", {
87
+ "content-type": "application/dns-message"
88
+ });
89
+ // Create a minimal 12-byte valid DNS header
90
+ const validDnsHeader = Buffer.alloc(12);
91
+ validDnsHeader.writeUInt16BE(0x1234, 0); // ID
92
+ validDnsHeader.writeUInt16BE(0x0100, 2); // Flags (Standard Query)
93
+ validDnsHeader.writeUInt16BE(0x0000, 4); // QDCOUNT
94
+ req[Symbol.asyncIterator] = async function* () {
95
+ yield validDnsHeader;
96
96
  };
97
- // @ts-ignore - access private method for deterministic testing
98
- await handler.handleDohRequest(mockReq, mockRes);
99
- assert.strictEqual(statusCode, 415); // Unsupported Media Type
97
+ // @ts-ignore
98
+ await handler.handleDohRequest(req, res);
99
+ assert.strictEqual(getStatus(), 200);
100
+ const headers = getHeaders();
101
+ assert.strictEqual(headers["Content-Type"], "application/dns-message");
102
+ assert.strictEqual(headers["Cache-Control"], "max-age=0");
100
103
  });
101
104
  });
package/dist/firewall.js CHANGED
@@ -1,4 +1,4 @@
1
- import * as net from "net";
1
+ import * as net from "node:net";
2
2
  export class FirewallEngine {
3
3
  ipToInt(ip) {
4
4
  return ip.split('.').reduce((int, octet) => (int << 8) + parseInt(octet, 10), 0) >>> 0;
@@ -1,5 +1,5 @@
1
1
  import { describe, it } from "node:test";
2
- import assert from "assert";
2
+ import assert from "node:assert";
3
3
  import * as http from "http";
4
4
  describe("HTTP Body Forwarding Integration", () => {
5
5
  let upstreamServer;
@@ -1,5 +1,5 @@
1
1
  import { describe, it } from "node:test";
2
- import assert from "assert";
2
+ import assert from "node:assert";
3
3
  import { HttpProxyService } from "./http-proxy.js";
4
4
  function makeBodyForwardingConfig() {
5
5
  return {
@@ -1,7 +1,7 @@
1
- import * as http from "http";
2
- import * as https from "https";
3
- import * as net from "net";
4
- import * as dns from "dns/promises";
1
+ import * as http from "node:http";
2
+ import * as https from "node:https";
3
+ import * as net from "node:net";
4
+ import * as dns from "node:dns/promises";
5
5
  import { HttpProxyService } from "./http-proxy.js";
6
6
  import { audit } from "./audit.js";
7
7
  import { firewallEngine } from "./firewall.js";
@@ -1,5 +1,5 @@
1
- import * as net from "net";
2
- import * as dns from "dns/promises";
1
+ import * as net from "node:net";
2
+ import * as dns from "node:dns/promises";
3
3
  import { firewallEngine } from "./firewall.js";
4
4
  export class HttpProxyService {
5
5
  async validateTargetFirewall(targetUrl, fw) {
@@ -1,6 +1,6 @@
1
1
  import { describe, it } from "node:test";
2
- import assert from "assert";
3
- import * as net from "net";
2
+ import assert from "node:assert";
3
+ import * as net from "node:net";
4
4
  let PORT_BASE = 65000;
5
5
  function nextPort() { return PORT_BASE++; }
6
6
  describe("TCP Rate Limiting", () => {
package/dist/schema.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { z } from "zod";
2
- import * as net from "net";
2
+ import * as net from "node:net";
3
3
  const CrlfFreeString = z.string().max(8192).refine((val) => !/[\r\n]/.test(val), "Contains CR/LF");
4
4
  const Ipv4Schema = z.string().max(45).refine((ip) => net.isIPv4(ip), "Invalid IPv4");
5
5
  const Ipv6Schema = z.string().max(45).refine((ip) => net.isIPv6(ip), "Invalid IPv6");
@@ -1,5 +1,5 @@
1
1
  import { describe, it } from "node:test";
2
- import assert from "assert";
2
+ import assert from "node:assert";
3
3
  import { DevDnsServer } from "./dns-service.js";
4
4
  import { DNS_TYPES, DNS_RCODE } from "./types.js";
5
5
  function buildQuery(name, type) {
@@ -1,6 +1,6 @@
1
1
  import { describe, it } from "node:test";
2
- import assert from "assert";
3
- import * as net from "net";
2
+ import assert from "node:assert";
3
+ import * as net from "node:net";
4
4
  let PORT_BASE = 61500;
5
5
  function nextPort() {
6
6
  return PORT_BASE++;
@@ -1,5 +1,5 @@
1
1
  import { describe, it } from "node:test";
2
- import assert from "assert";
2
+ import assert from "node:assert";
3
3
  import { DevDnsServer } from "./dns-service.js";
4
4
  import { DNS_TYPES } from "./types.js";
5
5
  function buildQuery(name, type) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opensecurity/zonzon-core",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "core routing, dns, and firewall engine",
5
5
  "type": "module",
6
6
  "author": "Lucian BLETAN <neuraluc@gmail.com>",