// A server's lines as Java's web server window wrote them // (packages/sim/test/browserWindow.java.json, BrowserWindowRun): each kind // the window got there, in Java's words. And a computer's firewall's log and // table as Java's firewall window wrote them // (packages/core/test/firewallWindow.java.json, FirewallWindowRun). import { readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect, it } from 'vitest'; import { DIRECTLY_CONNECTED_SOURCE_MARKER, firewallRule, type FirewallLogEntry, type ServerLogEntry, type SimpleClientLogEntry } from '@mdg-filius/core'; import type { FirewallViewRule } from '@mdg-filius/protocol'; import { firewallRow } from '../src/desktop/PersonalFirewallPanel.js'; import { firewallLine, serverLine, simpleClientLine } from '../src/desktop/serverLines.js'; import { translate } from '../src/i18n/i18n.js'; const java = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), '../../sim/test/browserWindow.java.json'), 'utf8')) as { steps: { step: string; log?: string }[] }; const log = java.steps.find((step) => step.step === 'server:stop')!.log!; const t = (key: string, values?: Readonly>) => translate('de', key, values); describe("a server's lines", () => { it("are Java's, line by line", () => { const [, address, port] = /Verbindung zu ([\d.]+):(\d+) hergestellt/.exec(log)!; const request = 'POST /ziel.html HTTP/1.1\nHost: www.filius.de\n\nname=Fil+ius&c=1&s=y&t=%C3%BC%26%3D&ok=Gut'; const answer = 'HTTP/1.1 200 OK\nContent-type: text/html\n\nZielZiel'; const entries: ServerLogEntry[] = [ { kind: 'acceptingStarted' }, { kind: 'connectionAccepted', address: address!, port: Number(port) }, { kind: 'received', text: request }, { kind: 'sent', text: answer }, { kind: 'connectionClosed', address: address!, port: Number(port) }, { kind: 'acceptingStopped' }, { kind: 'serverSocketClosed' }, ]; for (const entry of entries) expect(log, entry.kind).toContain(`${serverLine(entry, t)}\n`); expect(log.startsWith(`${serverLine({ kind: 'acceptingStarted' }, t)}\n`)).toBe(true); }); it('name a failed send and a port that is taken', () => { expect(serverLine({ kind: 'sendFailed', failure: 'timedOut' }, t)).toBe('Zeitüberschreitung während des Versands einer Nachricht'); expect(serverLine({ kind: 'sendFailed', failure: 'notConnected' }, (key) => translate('en', key))).toBe('Connection could not be established!'); expect(serverLine({ kind: 'serverSocketFailed' }, t)).toBe('Server-Socket konnte nicht geöffnet werden.'); expect(serverLine({ kind: 'connectionAccepted', address: '1.2.3.4', port: 5 }, (key, values) => translate('en', key, values))).toBe('Connection to 1.2.3.4:5 established'); }); }); // The echo windows' lines against Java's (packages/sim/test/echoWindow.java.json, EchoWindowRun). const echo = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), '../../sim/test/echoWindow.java.json'), 'utf8')) as { steps: { step: string; server: { log: string }; client: { output: string } }[]; }; describe("the echo windows' lines", () => { it("are Java's, line by line, the server's and the client's", () => { const last = echo.steps.at(-1)!; const [, port] = /Verbindung zu 192\.168\.0\.20:(\d+) hergestellt/.exec(last.server.log)!; const server: ServerLogEntry[] = [ { kind: 'acceptingStarted' }, { kind: 'connectionAccepted', address: '192.168.0.20', port: Number(port) }, { kind: 'received', text: 'Zeile 1\nZeile 2' }, { kind: 'sent', text: 'Zeile 1\nZeile 2' }, { kind: 'acceptingStopped' }, { kind: 'connectionClosed', address: '192.168.0.20', port: Number(port) }, { kind: 'serverSocketClosed' }, ]; for (const entry of server) expect(last.server.log, entry.kind).toContain(`${serverLine(entry, t)}\n`); const client: SimpleClientLogEntry[] = [ { kind: 'connected' }, { kind: 'sent', text: 'Hallo Echo' }, { kind: 'received', text: 'Hallo Echo' }, { kind: 'connectionClosed', address: '192.168.0.30', port: 55555 }, { kind: 'connectFailed', failure: 'timedOut' }, { kind: 'disconnected' }, { kind: 'connectFailed', failure: 'unresolved' }, ]; for (const entry of client) expect(last.client.output, entry.kind).toContain(`${simpleClientLine(entry, t)}\n`); // Not in this story, in Java's words all the same (sw_clientbaustein_msg4, sw_tcpsocket_msg3, msg9, msg6). expect([ simpleClientLine({ kind: 'notConnected' }, t), simpleClientLine({ kind: 'connectFailed', failure: 'failed' }, t), simpleClientLine({ kind: 'receiveFailed' }, t), simpleClientLine({ kind: 'sendFailed', failure: 'notConnected' }, t), ]).toEqual(['Fehler beim Verbindungsaufbau', 'Fehler beim Verbindungsaufbau\nAktives Öffnen des Sockets fehlgeschlagen', 'Keine Verbindung hergestellt!', 'Verbindung nicht aufgebaut!']); }); }); describe("a computer's firewall's lines", () => { const firewallJava = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), '../../core/test/firewallWindow.java.json'), 'utf8')) as { steps: { step: string; log: string }[] }; const javaLog = firewallJava.steps.find((step) => step.step === 'delete row 0')!.log.split('\n'); it("are Java's, line by line", () => { const network = { ipAddress: '10.0.0.10', subnetMask: '255.255.255.0' }; const entries: FirewallLogEntry[] = [ { kind: 'portRuleAdded', port: 55555, protocol: 'tcp' }, { kind: 'ruleMatched', ruleNumber: 1, rule: firewallRule({ port: 55555, protocol: 'tcp', action: 'accept' }), firstInterface: network, accepted: true }, { kind: 'ruleMatched', ruleNumber: 2, rule: firewallRule({ port: 53, protocol: 'udp', action: 'accept', sourceIPAddress: DIRECTLY_CONNECTED_SOURCE_MARKER }), firstInterface: network, accepted: true }, { kind: 'icmpDropped', senderIPAddress: '10.0.0.2', receiverIPAddress: '10.0.0.10', type: 8, code: 0 }, { kind: 'portRuleAdded', port: -1, protocol: 'all' }, { kind: 'portRuleRemoved' }, ]; const lines = entries.map((entry) => firewallLine(entry, t)); for (const line of lines) expect(javaLog, line).toContain(line); expect(firewallLine({ kind: 'ruleMatched', ruleNumber: 3, rule: firewallRule({ port: 80 }), firstInterface: undefined, accepted: false }, t)).toBe( 'Firewall-Regel hat gegriffen: #3 (*/* -> */*; TCP:80 => DROP) -> verwerfen', ); }); it("are the table's rows as Java's ruleToVector writes them, and name a rule the window cannot make", () => { expect( [ { protocol: 'tcp', port: 55555, sender: 'all', text: '' }, { protocol: 'udp', port: 53, sender: 'sameNetwork', text: '' }, { protocol: 'all', port: -1, sender: 'all', text: '' }, { protocol: 'tcp', port: 0, sender: 'all', text: '' }, { protocol: 'icmp', port: -1, sender: null, text: '10.0.0.0/255.0.0.0 -> */*; ?:* => DROP' }, ].map((rule) => firewallRow(rule as FirewallViewRule, t)), ).toEqual([ ['TCP', '55555', 'Alle Absender'], ['UDP', '53', 'Nur lokales Rechnernetz'], ['*', '', 'Alle Absender'], ['TCP', '0', 'Alle Absender'], ['ICMP', '', '10.0.0.0/255.0.0.0 -> */*; ?:* => DROP'], ]); }); });