M20: LAN-Scanner umbenannt + Netzwerk-Tools (Ping/Traceroute/DNS/Port-Scan)
Tab "Geräte" -> "LAN-Scanner", Refresh-Button "Neu scannen" + prominenter Stil. Neues Netzwerk-Tools-Menü: Ping/Traceroute/DNS-Auflösung über NetworkToolsService (eigene SSH-Verbindung, wie Backup/InterfaceTraffic- Monitor), mit Zeichen-Validierung gegen Command-Injection ueber einen boeswilligen DHCP-Hostnamen. Port-Scan laeuft direkt von diesem Mac ueber Network.framework (RouterOS hat kein eingebautes Portscan-Tool) - dabei einen echten NWConnection-Bug gefunden (verweigerte Verbindung meldet sich ueber .waiting, nicht .failed) und per Unit-Test gegen einen Loopback-Port aufgedeckt und gefixt. Zusaetzlich: Warnhinweis bei "Feste IP zuweisen" erklaert jetzt den Rueckweg. DE/EN-Umschalter zeigt Landesflaggen statt Text. 92 Tests gruen. HANDOFF.md/README.md (inkl. Mermaid-Diagramm)/Manual.md/ CHATLOG.md aktualisiert. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YDmUd93KxsYGr2kLTotWnG
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
import XCTest
|
||||
@testable import RouterOSAssistant
|
||||
|
||||
/// A LAN device's IP/hostname ultimately comes from its own DHCP request — attacker-controllable
|
||||
/// by a rogue device on the network — and gets interpolated into a RouterOS console command
|
||||
/// string for ping/traceroute/resolve. RouterOS' console treats ";" as a command separator, so an
|
||||
/// unsanitized value could inject a second command; these tests lock in the allow-list that
|
||||
/// prevents that.
|
||||
final class NetworkToolsServiceTests: XCTestCase {
|
||||
func testAcceptsValidIPv4Address() throws {
|
||||
XCTAssertEqual(try NetworkToolsService.sanitized("192.168.88.10"), "192.168.88.10")
|
||||
}
|
||||
|
||||
func testAcceptsValidIPv6Address() throws {
|
||||
XCTAssertEqual(try NetworkToolsService.sanitized("fe80::1"), "fe80::1")
|
||||
}
|
||||
|
||||
func testAcceptsValidHostname() throws {
|
||||
XCTAssertEqual(try NetworkToolsService.sanitized("my-laptop.local"), "my-laptop.local")
|
||||
}
|
||||
|
||||
func testRejectsCommandInjectionViaSemicolon() {
|
||||
XCTAssertThrowsError(try NetworkToolsService.sanitized("8.8.8.8; /system reset-configuration"))
|
||||
}
|
||||
|
||||
func testRejectsWhitespaceAndQuotes() {
|
||||
XCTAssertThrowsError(try NetworkToolsService.sanitized("8.8.8.8 count=1000000"))
|
||||
XCTAssertThrowsError(try NetworkToolsService.sanitized("\"; malicious"))
|
||||
}
|
||||
|
||||
func testRejectsEmptyValue() {
|
||||
XCTAssertThrowsError(try NetworkToolsService.sanitized(""))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import XCTest
|
||||
@testable import RouterOSAssistant
|
||||
|
||||
final class PortScannerTests: XCTestCase {
|
||||
func testCommonPortsHaveNoDuplicates() {
|
||||
let ports = PortScanner.commonPorts.map(\.port)
|
||||
XCTAssertEqual(ports.count, Set(ports).count)
|
||||
}
|
||||
|
||||
func testCommonPortsAreAllValidTCPPorts() {
|
||||
for info in PortScanner.commonPorts {
|
||||
XCTAssertTrue((1...65535).contains(info.port), "Port \(info.port) out of valid range")
|
||||
}
|
||||
}
|
||||
|
||||
/// A high, essentially-never-bound port on loopback reliably gets an immediate TCP RST
|
||||
/// (connection refused) rather than a timeout, so this is fast and deterministic — not
|
||||
/// dependent on any real network/router.
|
||||
func testScanReportsClosedForRefusedLoopbackPort() async {
|
||||
let results = await PortScanner.scan(
|
||||
host: "127.0.0.1",
|
||||
ports: [PortScanner.PortInfo(port: 59_999, serviceName: nil)],
|
||||
timeout: 2
|
||||
)
|
||||
XCTAssertEqual(results.count, 1)
|
||||
XCTAssertEqual(results.first?.status, .closed)
|
||||
}
|
||||
|
||||
func testScanSortsResultsByPortNumber() async {
|
||||
let ports = [
|
||||
PortScanner.PortInfo(port: 59_998, serviceName: nil),
|
||||
PortScanner.PortInfo(port: 59_997, serviceName: nil)
|
||||
]
|
||||
let results = await PortScanner.scan(host: "127.0.0.1", ports: ports, timeout: 2)
|
||||
XCTAssertEqual(results.map(\.port.port), [59_997, 59_998])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user