package main import ( "context" "errors" "fmt" "os" "path" "path/filepath" "sort" "strings" "sync" "testing" "time" ) // fwStandin plays this machine's firewall commands: netsh, ufw, firewall-cmd, // socketfilterfw, nft and iptables, each with the state it would keep. Every // command run is recorded, so a test can say what was never run. type fwStandin struct { mu sync.Mutex ran []string fail map[string]bool // a verb that fails ("add") // Windows: rule name -> its add arguments. win map[string]string // ufw: on or off, and its rules as `ufw status` lists them. ufwOn bool ufwRules []string // firewalld: running, ports now and permanently. fwdOn bool fwdNow, fwdPerm map[string]bool macOn, macAllIn bool macApps map[string]bool nftRuleset, ipts string } func newFWStandin() *fwStandin { return &fwStandin{fail: map[string]bool{}, win: map[string]string{}, fwdNow: map[string]bool{}, fwdPerm: map[string]bool{}, macApps: map[string]bool{}} } var errExit1 = errors.New("exit status 1") func (f *fwStandin) exec(name string, args ...string) (string, error) { f.mu.Lock() defer f.mu.Unlock() line := path.Base(strings.ReplaceAll(name, `\`, "/")) + " " + strings.Join(args, " ") f.ran = append(f.ran, line) a := strings.Join(args, " ") switch path.Base(strings.ReplaceAll(name, `\`, "/")) { case "netsh.exe": if !strings.HasPrefix(a, "advfirewall firewall ") || len(args) < 5 { return "", fmt.Errorf("netsh: %s", a) } rule := strings.TrimPrefix(args[4], "name=") switch args[2] { case "show": if _, ok := f.win[rule]; ok { return "Rule Name: " + rule + "\n", nil } return "No rules match the specified criteria.\n", errExit1 case "add": if f.fail["add"] { return "The requested operation requires elevation (Run as administrator).", errExit1 } f.win[rule] = strings.Join(args[5:], " ") return "Ok.\n", nil case "delete": if _, ok := f.win[rule]; !ok { return "No rules match the specified criteria.\n", errExit1 } delete(f.win, rule) return "\nDeleted 1 rule(s).\nOk.\n", nil } case "ufw": switch { case a == "status": if !f.ufwOn { return "Status: inactive\n", nil } return "Status: active\n\nTo Action From\n-- ------ ----\n" + strings.Join(f.ufwRules, "\n") + "\n", nil case strings.HasPrefix(a, "allow "): port := args[1] c := "" if len(args) == 4 && args[2] == "comment" { c = " # " + args[3] } f.ufwRules = append(f.ufwRules, port+" ALLOW Anywhere"+c, port+" (v6) ALLOW Anywhere (v6)"+c) return "Rule added\nRule added (v6)\n", nil case strings.HasPrefix(a, "delete allow "): port := args[2] var keep []string for _, r := range f.ufwRules { if strings.Fields(r)[0] != port { keep = append(keep, r) } } if len(keep) == len(f.ufwRules) { return "Could not delete non-existent rule\n", errExit1 } f.ufwRules = keep return "Rule deleted\nRule deleted (v6)\n", nil } case "firewall-cmd": perm := len(args) > 0 && args[0] == "--permanent" set := f.fwdNow if perm { set, args = f.fwdPerm, args[1:] } if len(args) == 0 { break } switch { case args[0] == "--state": if f.fwdOn { return "running\n", nil } return "not running\n", errors.New("exit status 252") case strings.HasPrefix(args[0], "--query-port="): if set[strings.TrimPrefix(args[0], "--query-port=")] { return "yes\n", nil } return "no\n", errExit1 case strings.HasPrefix(args[0], "--add-port="): if perm && f.fail["permanent"] { return "Error: COMMAND_FAILED\n", errExit1 } set[strings.TrimPrefix(args[0], "--add-port=")] = true return "success\n", nil case strings.HasPrefix(args[0], "--remove-port="): p := strings.TrimPrefix(args[0], "--remove-port=") if !set[p] { return "Warning: NOT_ENABLED: " + p + "\nsuccess\n", nil } delete(set, p) return "success\n", nil } case "socketfilterfw": switch args[0] { case "--getglobalstate": if f.macOn { return "Firewall is enabled. (State = 1)\n", nil } return "Firewall is disabled. (State = 0)\n", nil case "--getblockall": if f.macAllIn { return "Firewall is set to block all non-essential incoming connections\nBlock all ENABLED!\n", nil } return "Block all DISABLED!\n", nil case "--listapps": // As macOS before 15 prints it (developer.apple.com/forums/thread/757909). var b strings.Builder fmt.Fprintf(&b, "ALF: total number of apps = %d \n\n", len(f.macApps)) var ps []string for p := range f.macApps { ps = append(ps, p) } sort.Strings(ps) for i, p := range ps { st := "Block" if f.macApps[p] { st = "Allow" } fmt.Fprintf(&b, "%d : %s ( %s incoming connections )\n", i+1, p, st) } return b.String(), nil case "--add": f.macApps[args[1]] = false return "The file " + args[1] + " added to firewall\n", nil case "--unblockapp": if f.fail["unblock"] { return "Error\n", errExit1 } f.macApps[args[1]] = true return "Incoming connection to the application is permitted\n", nil case "--remove": delete(f.macApps, args[1]) return "The file " + args[1] + " removed from firewall\n", nil } case "nft": return f.nftRuleset, nil case "iptables": return f.ipts, nil } return "", fmt.Errorf("the stand-in does not know %s", line) } func (f *fwStandin) commands() []string { f.mu.Lock() defer f.mu.Unlock() return append([]string(nil), f.ran...) } // fwWorld stands the firewall in for goos, with the door wanted while *want. func fwWorld(t *testing.T, goos string, root bool) (*fwStandin, *bool) { t.Helper() uiTestConfigDir(t) f := newFWStandin() want := true prevExec, prevFor, prevBy, prevHave, prevRoot, prevProg, prevWant, prevRead := doorFWExec, doorFirewallFor, doorFirewallByID, doorFWHave, doorFWRoot, doorFWProgram, doorPortWanted, doorFWReadFile t.Cleanup(func() { doorFWExec, doorFirewallFor, doorFirewallByID, doorFWHave, doorFWRoot, doorFWProgram, doorPortWanted, doorFWReadFile = prevExec, prevFor, prevBy, prevHave, prevRoot, prevProg, prevWant, prevRead }) doorFWExec = f.exec doorFirewallFor = func(port string) (doorFirewall, string) { return detectDoorFirewall(goos, port) } doorFirewallByID = func(id string) doorFirewall { return doorFirewallOf(goos, id) } doorFWHave = func(paths ...string) string { return paths[0] } doorFWRoot = func() bool { return root } doorFWProgram = func() (string, error) { return `C:\Program Files\Privatae\privatae-bastion.exe`, nil } doorPortWanted = func(string) bool { return want } doorFWReadFile = func(string) ([]byte, error) { return nil, errors.New("no such file") } return f, &want } func fwAudit(t *testing.T) string { t.Helper() d, _ := configDir() b, _ := os.ReadFile(filepath.Join(d, "pairing_audit.jsonl")) return string(b) } func countOp(t *testing.T, op string) int { return strings.Count(fwAudit(t), `"op":"`+op+`"`) } // Windows: the rule is the bastion's own by name, inbound, TCP on the door's // port only, every profile, for this program only, made by netsh from // System32. It is made once, kept while the door is in use, and removed when // the last pairing and window are gone, each step in the audit and in status. func TestTheWindowsRuleIsTheBastionsOwnAndGoesWithTheDoor(t *testing.T) { f, want := fwWorld(t, "windows", false) said := doorFirewallEnsure(":8812") rule := doorFWRuleName("8812") got, ok := f.win[rule] if !ok { t.Fatalf("no rule %q: %v", rule, f.commands()) } for _, part := range []string{"dir=in", "action=allow", "protocol=TCP", "localport=8812", `program=C:\Program Files\Privatae\privatae-bastion.exe`, "profile=any", "enable=yes"} { if !strings.Contains(got, part) { t.Fatalf("the rule lacks %s: %s", part, got) } } for _, c := range f.commands() { if !strings.HasPrefix(c, "netsh.exe advfirewall firewall ") { t.Fatalf("ran %q", c) } } if !strings.HasPrefix(netshPath(), `C:\Windows\System32`) { t.Fatalf("netsh is not run by its absolute path: %s", netshPath()) } if !strings.Contains(said, "added its own rule in Windows Firewall") { t.Fatalf("said %q", said) } // Again (the daemon's door after the window's): kept, not doubled. doorFirewallEnsure(":8812") if n := strings.Count(strings.Join(f.commands(), "\n"), " add rule "); n != 1 { t.Fatalf("%d adds", n) } s := readDoorFirewallStatus() if s == nil || len(s.Rules) != 1 || !strings.Contains(s.Rules[0], "Windows Firewall allows TCP 8812") { t.Fatalf("status: %+v", s) } // Still in use: the rule stays. doorFirewallRelease(":8812") if _, ok := f.win[rule]; !ok { t.Fatal("removed while the door is still in use") } // The last pairing and window gone: removed. *want = false doorFirewallRelease(":8812") if _, ok := f.win[rule]; ok { t.Fatal("the rule outlived the door") } if s := readDoorFirewallStatus(); s != nil { t.Fatalf("status after: %+v", s) } if countOp(t, "door_firewall_opened") != 1 || countOp(t, "door_firewall_closed") != 1 { t.Fatalf("the audit:\n%s", fwAudit(t)) } // MUTATION: release removes nothing. RESULT: red, "the rule outlived the door". // MUTATION: the add drops program=. RESULT: red, "the rule lacks program=". // MUTATION: ensure adds without asking has(). RESULT: red, "2 adds". } // Windows: the bastion moved (another path): its rule is replaced by one for // the new path; and an add refused (no administrator) is said once, with the // command that does it by hand, in status and doctor. func TestTheWindowsRuleFollowsTheProgramAndAFailureIsSaidOnce(t *testing.T) { f, _ := fwWorld(t, "windows", false) doorFirewallEnsure(":8812") doorFWProgram = func() (string, error) { return `D:\Privatae\privatae-bastion.exe`, nil } doorFirewallEnsure(":8812") if got := f.win[doorFWRuleName("8812")]; !strings.Contains(got, `program=D:\Privatae\privatae-bastion.exe`) { t.Fatalf("the rule names %s", got) } if st := loadDoorFW(); len(st.Rules) != 1 || st.Rules[0].Program != `D:\Privatae\privatae-bastion.exe` { t.Fatalf("the record: %+v", st.Rules) } g, _ := fwWorld(t, "windows", false) g.fail["add"] = true doorFirewallEnsure(":8812") doorFirewallEnsure(":8812") if n := countOp(t, "door_firewall_failed"); n != 1 { t.Fatalf("the failure audited %d times", n) } s := readDoorFirewallStatus() if s == nil || len(s.Needs) != 1 || !strings.Contains(s.Needs[0], "New-NetFirewallRule") { t.Fatalf("status: %+v", s) } cs := doorFirewallChecks(s) if len(cs) != 1 || cs[0].OK || !strings.Contains(cs[0].Problem, "TCP 8812") { t.Fatalf("doctor: %+v", cs) } // MUTATION: the program is not compared. RESULT: red, the rule names the old path. // MUTATION: the note is audited on every ensure. RESULT: red, audited 2 times. } // Linux ufw: a rule only while ufw is on, with the bastion's comment; ufw is // never turned on, off or reset; a rule of the machine's own for the port is // neither doubled nor removed. func TestTheUfwRuleIsAddedOnlyWhenUfwIsOnAndNeverTheMachinesOwn(t *testing.T) { f, want := fwWorld(t, "linux", true) // Off: nothing at all. if said := doorFirewallEnsure(":8812"); said != "" || len(loadDoorFW().Rules) != 0 { t.Fatalf("ufw off: %q %v", said, f.commands()) } f.ufwOn = true doorFirewallEnsure(":8812") st := strings.Join(f.ufwRules, "\n") if !strings.Contains(st, "8812/tcp") || !strings.Contains(st, "# "+doorFWComment) { t.Fatalf("ufw rules: %s", st) } if s := readDoorFirewallStatus(); s == nil || len(s.Rules) != 1 || !strings.Contains(s.Rules[0], "ufw allows TCP 8812") { t.Fatalf("status: %+v", s) } *want = false doorFirewallRelease(":8812") if len(f.ufwRules) != 0 { t.Fatalf("left: %v", f.ufwRules) } for _, c := range f.commands() { for _, never := range []string{"ufw enable", "ufw disable", "ufw reset", "ufw default", "ufw reload"} { if strings.HasPrefix(c, never) { t.Fatalf("ran %q", c) } } } // The machine's own rule for the port. g, want2 := fwWorld(t, "linux", true) g.ufwOn = true g.ufwRules = []string{"8812/tcp ALLOW Anywhere", "8812/tcp (v6) ALLOW Anywhere (v6)"} said := doorFirewallEnsure(":8812") if !strings.Contains(said, "already allowed") || len(g.ufwRules) != 2 { t.Fatalf("said %q, rules %v", said, g.ufwRules) } *want2 = false doorFirewallRelease(":8812") if len(g.ufwRules) != 2 { t.Fatalf("the machine's own rule was removed: %v", g.commands()) } // MUTATION: ufwRuleFor counts any ALLOW as ours. RESULT: red, the machine's own rule was removed. // MUTATION: detection ignores "Status: inactive". RESULT: red, ufw off. } // Linux firewalld: the port added now and permanently, and removed the same // way; a port already open in it is the machine's own. func TestTheFirewalldPortIsAddedAndRemovedNowAndPermanently(t *testing.T) { f, want := fwWorld(t, "linux", true) f.fwdOn = true doorFirewallEnsure(":8812") if !f.fwdNow["8812/tcp"] || !f.fwdPerm["8812/tcp"] { t.Fatalf("firewalld: %v %v", f.fwdNow, f.fwdPerm) } doorFirewallEnsure(":8812") if n := strings.Count(strings.Join(f.commands(), "\n"), "--add-port"); n != 2 { t.Fatalf("%d adds: %v", n, f.commands()) } *want = false doorFirewallRelease(":8812") if f.fwdNow["8812/tcp"] || f.fwdPerm["8812/tcp"] { t.Fatalf("left: %v %v", f.fwdNow, f.fwdPerm) } g, want2 := fwWorld(t, "linux", true) g.fwdOn = true g.fwdNow["8812/tcp"], g.fwdPerm["8812/tcp"] = true, true doorFirewallEnsure(":8812") *want2 = false doorFirewallRelease(":8812") if !g.fwdNow["8812/tcp"] || !g.fwdPerm["8812/tcp"] { t.Fatal("the machine's own open port was closed") } // MUTATION: remove only the runtime port. RESULT: red, left permanently. // MUTATION: has() reports a port it did not add as ours. RESULT: red, the machine's own port closed. } // Linux: a firewall the bastion does not manage (nftables, iptables), or ufw // it may not change, is said once, with what to allow, in status and doctor, // and nothing in it is changed. func TestAFirewallTheBastionDoesNotManageIsSaidOnceAndLeftAlone(t *testing.T) { f, want := fwWorld(t, "linux", true) f.nftRuleset = "table inet filter {\n\tchain input {\n\t\ttype filter hook input priority filter; policy drop;\n\t}\n}\n" said := doorFirewallEnsure(":8812") doorFirewallEnsure(":8812") if !strings.Contains(said, "nftables") || !strings.Contains(said, "tcp dport 8812 accept") { t.Fatalf("said %q", said) } if n := countOp(t, "door_firewall_unmanaged"); n != 1 { t.Fatalf("said %d times", n) } for _, c := range f.commands() { if strings.HasPrefix(c, "nft ") && c != "nft list ruleset" { t.Fatalf("ran %q", c) } } s := readDoorFirewallStatus() if s == nil || len(s.Needs) != 1 || !strings.Contains(s.Needs[0], "nftables") { t.Fatalf("status: %+v", s) } if cs := doorFirewallChecks(s); len(cs) != 1 || cs[0].OK { t.Fatalf("doctor: %+v", cs) } *want = false doorFirewallRelease(":8812") if s := readDoorFirewallStatus(); s != nil { t.Fatalf("still said with no door: %+v", s) } // iptables, and an account that may not change ufw. g, _ := fwWorld(t, "linux", true) g.ipts = "-P INPUT DROP\n" if said := doorFirewallEnsure(":8812"); !strings.Contains(said, "iptables -I INPUT -p tcp --dport 8812 -j ACCEPT") { t.Fatalf("iptables: %q", said) } h, _ := fwWorld(t, "linux", false) doorFWReadFile = func(string) ([]byte, error) { return []byte("ENABLED=yes\n"), nil } if said := doorFirewallEnsure(":8812"); !strings.Contains(said, "sudo ufw allow 8812/tcp") { t.Fatalf("not root: %q", said) } for _, c := range h.commands() { if strings.HasPrefix(c, "ufw ") { t.Fatalf("an account that may not change ufw ran %q", c) } } // MUTATION: nftFiltersInput always false. RESULT: red, said "". // MUTATION: the note is not cleared at release. RESULT: red, still said with no door. } // macOS: with the application firewall on, this program is allowed; with it // off nothing is done; "block all incoming" is only said. func TestTheMacFirewallAllowsTheProgramOnlyWhenItIsOn(t *testing.T) { f, want := fwWorld(t, "darwin", true) doorFirewallEnsure(":8812") if len(f.macApps) != 0 { t.Fatal("added with the firewall off") } f.macOn = true doorFirewallEnsure(":8812") prog, _ := doorFWProgram() if allowed, ok := f.macApps[prog]; !ok || !allowed { t.Fatalf("apps: %v", f.macApps) } *want = false doorFirewallRelease(":8812") if len(f.macApps) != 0 { t.Fatalf("left: %v", f.macApps) } g, _ := fwWorld(t, "darwin", true) g.macOn, g.macAllIn = true, true if said := doorFirewallEnsure(":8812"); !strings.Contains(said, "blocks all incoming") || len(g.macApps) != 0 { t.Fatalf("block all: %q %v", said, g.macApps) } // MUTATION: --unblockapp not run. RESULT: red, apps not allowed. } // uninstall removes every rule the bastion added, whatever uses the door. func TestUninstallRemovesTheDoorsOwnRules(t *testing.T) { f, _ := fwWorld(t, "windows", false) doorFirewallEnsure(":8812") doorFirewallEnsure(":9443") said, err := doorFirewallRemoveAll() if err != nil || len(said) != 2 || len(f.win) != 0 { t.Fatalf("uninstall: %v %v, left %v", said, err, f.win) } if len(loadDoorFW().Rules) != 0 { t.Fatal("the record outlived the rules") } // MUTATION: RemoveAll skips the removal. RESULT: red, rules left. } // A rule left behind (a `pair --open` killed before its release, a pairing // that went while nothing ran) is removed by the daemon's sweep once nothing // uses its port, and kept while something does. func TestTheDaemonsSweepRemovesARuleNothingUses(t *testing.T) { f, want := fwWorld(t, "windows", false) doorFirewallEnsure(":8812") ctx, cancel := context.WithCancel(context.Background()) done := make(chan struct{}) go func() { doorFirewallSweep(ctx); close(done) }() cancel() <-done if _, ok := f.win[doorFWRuleName("8812")]; !ok { t.Fatal("the sweep removed a rule in use") } *want = false doorFirewallReleaseUnused() if _, ok := f.win[doorFWRuleName("8812")]; ok { t.Fatal("the sweep left a rule nothing uses") } // MUTATION: doorFirewallReleaseUnused skips recorded rules. RESULT: red, the sweep left a rule. } // ufw deletes by rule, whatever its comment: a rule the bastion recorded and // the person later replaced with their own, or a record that names a port the // bastion never opened, is the machine's, and is never removed. func TestUfwNeverRemovesARuleThatIsNotItsOwnWhateverTheRecordSays(t *testing.T) { f, want := fwWorld(t, "linux", true) f.ufwOn = true doorFirewallEnsure(":8812") // The person deletes the bastion's rule and puts their own in its place. f.ufwRules = []string{"8812/tcp ALLOW Anywhere", "8812/tcp (v6) ALLOW Anywhere (v6)"} doorFirewallEnsure(":8812") *want = false doorFirewallRelease(":8812") if len(f.ufwRules) != 2 { t.Fatalf("the person's own rule was removed: %v", f.commands()) } // A record naming a port the bastion never opened (written by hand, or // left from another time): the sweep leaves the machine's rule there. g, want2 := fwWorld(t, "linux", true) g.ufwOn = true g.ufwRules = []string{"22/tcp ALLOW Anywhere"} if err := saveDoorFW(doorFWState{Rules: []doorFWRule{{Firewall: "ufw", Port: "22", Added: "2026-01-01T00:00:00Z"}}}); err != nil { t.Fatal(err) } *want2 = false doorFirewallReleaseUnused() for _, c := range g.commands() { if strings.HasPrefix(c, "ufw delete") { t.Fatalf("ran %q on a rule that is not the bastion's", c) } } if len(g.ufwRules) != 1 || len(loadDoorFW().Rules) != 0 { t.Fatalf("rules %v, record %+v", g.ufwRules, loadDoorFW().Rules) } // MUTATION: ufw remove deletes without reading the comment. RESULT: red, the person's own rule was removed. } // Only a rule that lets a Host in from anywhere counts as the machine's own // allow: an outbound rule, or one for one source or interface, does not, so // the bastion adds its own rather than saying the port is already allowed. func TestUfwAnOutboundOrOneSourceRuleIsNotTheWayIn(t *testing.T) { for _, r := range []string{ "8812/tcp ALLOW OUT Anywhere", "8812/tcp ALLOW 10.0.0.0/8", "8812/tcp on eth1 ALLOW Anywhere", "8812/tcp DENY Anywhere", } { f, _ := fwWorld(t, "linux", true) f.ufwOn = true f.ufwRules = []string{r} said := doorFirewallEnsure(":8812") if !strings.Contains(said, "added its own rule in ufw") { t.Fatalf("with %q: said %q", r, said) } } for _, r := range []string{ "8812/tcp ALLOW Anywhere", "8812/tcp ALLOW IN Anywhere", "8812 (v6) ALLOW Anywhere (v6)", } { if _, other := ufwRuleFor(r, "8812"); !other { t.Fatalf("%q lets a Host in from anywhere", r) } } // MUTATION: any line with ALLOW counts. RESULT: red, said "already allowed". } // firewalld: a permanent add that fails takes the runtime port back out, so // no open port is left that no record says is the bastion's. func TestFirewalldAFailedPermanentAddLeavesNoPortOpen(t *testing.T) { f, _ := fwWorld(t, "linux", true) f.fwdOn = true f.fail["permanent"] = true said := doorFirewallEnsure(":8812") if !strings.Contains(said, "could not add") { t.Fatalf("said %q", said) } if f.fwdNow["8812/tcp"] { t.Fatalf("the runtime port was left open with no record: %v", f.commands()) } // Next time it is tried again, not taken for the machine's own. f.fail["permanent"] = false if said := doorFirewallEnsure(":8812"); !strings.Contains(said, "added its own rule") { t.Fatalf("said %q", said) } // MUTATION: no undo of the runtime add. RESULT: red, the runtime port was left open. } // macOS: an --unblockapp that fails takes the --add back out, so no entry is // left that would read as the machine's own and never go. func TestTheMacAFailedUnblockLeavesNoEntry(t *testing.T) { f, _ := fwWorld(t, "darwin", true) f.macOn = true f.fail["unblock"] = true if said := doorFirewallEnsure(":8812"); !strings.Contains(said, "could not add") { t.Fatalf("said %q", said) } if len(f.macApps) != 0 { t.Fatalf("left an entry with no record: %v", f.macApps) } // MUTATION: no --remove after a failed --unblockapp. RESULT: red, left an entry. } // realDoorPortWanted is the door-in-use check fwWorld stands in. var realDoorPortWanted = doorPortWanted // sweepOnce runs the daemon's sweep's first pass. func sweepOnce() { ctx, cancel := context.WithCancel(context.Background()) cancel() doorFirewallSweep(ctx) } // A door claims the window (its file is moved aside) before the pairing is // saved: between the two, neither is there to read. The window is still in // use then, so a sweep in that gap keeps the rule, and puts it back if it had // gone; once the window has its result and no pairing uses the port, the // rule goes. func TestAWindowClaimedForAPairingStillBeingSavedKeepsTheRule(t *testing.T) { f, _ := fwWorld(t, "windows", false) doorPortWanted = realDoorPortWanted code, err := newWindowCode() if err != nil { t.Fatal(err) } now := time.Now().UTC() w := &pairWindow{ID: "w-claimed", Code: code, Door: ":8812", Opened: now.Format(time.RFC3339), Expires: now.Add(windowLifetime).Format(time.RFC3339)} if err := saveWindowFile(w); err != nil { t.Fatal(err) } doorFirewallEnsure(":8812") rule := doorFWRuleName("8812") if _, ok := f.win[rule]; !ok { t.Fatalf("no rule for the open window: %v", f.commands()) } if !claimWindow(w.ID) { t.Fatal("the window could not be claimed") } sweepOnce() if _, ok := f.win[rule]; !ok { t.Fatal("the sweep removed the rule while a pairing on the window was being saved") } // The rule gone in that gap (by hand, or a release that ran then): back. delete(f.win, rule) sweepOnce() if _, ok := f.win[rule]; !ok { t.Fatal("the sweep did not put back the rule of a window still in use") } endWindow(w.ID, windowResult{Error: "the Host refused"}) sweepOnce() if _, ok := f.win[rule]; ok { t.Fatal("the rule outlived the window") } // MUTATION: doorPortsInUse ignores a claimed window. RESULT: red, the sweep removed the rule while a pairing ... was being saved. // MUTATION: claimedWindowUnderWay counts a window with its result. RESULT: red, the rule outlived the window. } // A rule that goes while its door is in use (removed by hand, the firewall // reset) is put back by the daemon's sweep; a port a rule of the machine's // own allows is still never given a second one; nothing in use, nothing added. func TestTheSweepPutsBackARuleThatWentWhileTheDoorIsInUse(t *testing.T) { f, want := fwWorld(t, "windows", false) doorFirewallEnsure(":8812") rule := doorFWRuleName("8812") delete(f.win, rule) // removed by hand sweepOnce() if got, ok := f.win[rule]; !ok || !strings.Contains(got, "localport=8812") { t.Fatalf("not put back: %v", f.commands()) } if n := countOp(t, "door_firewall_opened"); n != 2 { t.Fatalf("opened %d times:\n%s", n, fwAudit(t)) } *want = false sweepOnce() if _, ok := f.win[rule]; ok { t.Fatal("a rule nothing uses was kept") } sweepOnce() if _, ok := f.win[rule]; ok { t.Fatal("the sweep added a rule nothing uses") } // ufw: the bastion's rule deleted while the door is in use, back with its comment. g, _ := fwWorld(t, "linux", true) g.ufwOn = true doorFirewallEnsure(":8812") g.ufwRules = nil sweepOnce() if st := strings.Join(g.ufwRules, "\n"); !strings.Contains(st, "8812/tcp") || !strings.Contains(st, "# "+doorFWComment) { t.Fatalf("ufw not put back: %v", g.commands()) } // ufw: the machine's own rule allows the port: never a second one. h, _ := fwWorld(t, "linux", true) h.ufwOn = true h.ufwRules = []string{"8812/tcp ALLOW Anywhere"} doorFirewallEnsure(":8812") sweepOnce() sweepOnce() for _, c := range h.commands() { if strings.HasPrefix(c, "ufw allow") { t.Fatalf("ran %q with the machine's own rule there", c) } } // MUTATION: the sweep only releases. RESULT: red, not put back (and in // TestAWindowClaimedForAPairingStillBeingSavedKeepsTheRule). } // macOS: an entry is for one program path; the bastion that moved removes the // entry by the path it recorded, both when its door is released and when it // makes the entry for its new path. func TestTheMacEntryIsRemovedByThePathItWasAddedFor(t *testing.T) { const a, b = "/usr/local/bin/privatae-bastion", "/opt/example/privatae-bastion" f, want := fwWorld(t, "darwin", true) f.macOn = true doorFWProgram = func() (string, error) { return a, nil } doorFirewallEnsure(":8812") if !f.macApps[a] { t.Fatalf("apps: %v", f.macApps) } doorFWProgram = func() (string, error) { return b, nil } *want = false doorFirewallRelease(":8812") if len(f.macApps) != 0 { t.Fatalf("the moved bastion left its old path allowed: %v", f.macApps) } // Moved while the door is in use: the old path's entry goes, the new one's is made. g, _ := fwWorld(t, "darwin", true) g.macOn = true doorFWProgram = func() (string, error) { return a, nil } doorFirewallEnsure(":8812") doorFWProgram = func() (string, error) { return b, nil } doorFirewallEnsure(":8812") if _, ok := g.macApps[a]; ok || !g.macApps[b] { t.Fatalf("apps: %v", g.macApps) } if st := loadDoorFW(); len(st.Rules) != 1 || st.Rules[0].Program != b { t.Fatalf("the record: %+v", st.Rules) } // MUTATION: remove uses this program's path, not the record's. RESULT: red, the moved bastion left its old path allowed. // MUTATION: the moved case removes this program's path. RESULT: red, apps: both paths. } // macOS: --listapps is read entry by entry, by the exact path, and only an // "allow incoming" entry counts: a blocked entry is the person's setting and // is said, not changed; a longer path (…/privatae-bastion.old) is not this // program's. func TestTheMacReadsEachEntryAndCountsOnlyAnAllow(t *testing.T) { const prog = "/usr/local/bin/privatae-bastion" f, _ := fwWorld(t, "darwin", true) f.macOn = true doorFWProgram = func() (string, error) { return prog, nil } f.macApps[prog] = false // the person blocked it said := doorFirewallEnsure(":8812") if !strings.Contains(said, "block incoming connections for this program") { t.Fatalf("said %q", said) } for _, c := range f.commands() { if strings.Contains(c, "--unblockapp") || strings.Contains(c, "--add") || strings.Contains(c, "--remove") { t.Fatalf("changed the person's setting: %q", c) } } if s := readDoorFirewallStatus(); s == nil || len(s.Needs) != 1 { t.Fatalf("status: %+v", s) } g, _ := fwWorld(t, "darwin", true) g.macOn = true doorFWProgram = func() (string, error) { return prog, nil } g.macApps[prog+".old"] = true if said := doorFirewallEnsure(":8812"); !strings.Contains(said, "added its own rule") || !g.macApps[prog] { t.Fatalf("a longer path counted as this program: said %q, apps %v", said, g.macApps) } // Its own entry, later set to block by the person: left as it is at release. h, want := fwWorld(t, "darwin", true) h.macOn = true doorFWProgram = func() (string, error) { return prog, nil } doorFirewallEnsure(":8812") h.macApps[prog] = false *want = false doorFirewallRelease(":8812") if allowed, ok := h.macApps[prog]; !ok || allowed { t.Fatalf("the person's block was removed: %v", h.macApps) } // MUTATION: has() counts a listed program whatever its state. RESULT: red, said "already allowed". // MUTATION: the path is matched as a prefix. RESULT: red, a longer path counted as this program. // MUTATION: remove runs --remove on a blocked entry. RESULT: red, the person's block was removed. }