package asterisk import ( "context" "encoding/json" "os" "path/filepath" "strings" "testing" ) func TestLoaderPersistsOnlyVerifiedNativeReload(t *testing.T) { dir := t.TempDir() if err := os.WriteFile(filepath.Join(dir, "pjsip.conf"), []byte("[go-sip-udp]\ntype=transport\nprotocol=udp\nbind=0.0.0.0:5060\n#tryinclude go-sip-managed.conf\n"), 0600); err != nil { t.Fatal(err) } fake := filepath.Join(dir, "asterisk") script := `#!/bin/sh case "$*" in *"pjsip show transports"*) echo 'Transport: go-sip-udp udp 0 0 0.0.0.0:5060' ;; *"pjsip show endpoints"*) echo 'Endpoint: '; echo 'Endpoint: trunk-shuqi Not in use' ;; *"pjsip show endpoint trunk-shuqi"*) echo 'Aor: trunk-shuqi-aor allow: alaw transport: go-sip-udp context: go-sip-no-inbound' ;; *"pjsip show aor trunk-shuqi-aor"*) echo 'Contact: sip:61.132.228.221:5060' ;; *) exit 9 ;; esac ` if err := os.WriteFile(fake, []byte(script), 0700); err != nil { t.Fatal(err) } bin := filepath.Join(dir, "bin") if err := os.Mkdir(bin, 0700); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(bin, "systemctl"), []byte("#!/bin/sh\n[ \"$*\" = '--user reload go-sip-asterisk.service' ]\n"), 0700); err != nil { t.Fatal(err) } t.Setenv("PATH", bin+":"+os.Getenv("PATH")) loader := Loader{ConfigDir: dir, Asterisk: fake, LibraryDir: dir} sip := testSIP(t) body, err := json.Marshal(sip) if err != nil { t.Fatal(err) } // Recover an interrupted first apply only when the approved file and // native PJSIP objects both agree with the complete snapshot. rendered, err := Render(sip) if err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(dir, "go-sip-managed.conf"), []byte(rendered), 0600); err != nil { t.Fatal(err) } loaded, err := loader.Apply(context.Background(), body) if err != nil || loaded["trunk-shuqi"] != 9 { t.Fatalf("real command path did not verify SIP revision: %v %v", loaded, err) } if _, err := loader.Apply(context.Background(), body); err != nil { t.Fatalf("same revision should observe without rewriting: %v", err) } if err := os.WriteFile(fake, []byte(strings.Replace(script, "Transport: go-sip-udp udp", "Transport: go-sip-udp tcp", 1)), 0700); err != nil { t.Fatal(err) } if _, err := loader.LoadedSIP(context.Background()); err == nil { t.Fatal("accepted native TCP transport as approved UDP") } if err := os.WriteFile(fake, []byte(script), 0700); err != nil { t.Fatal(err) } changed := testSIP(t) changed.Trunks = []byte(strings.Replace(string(changed.Trunks), "BD93205882", "BD93205883", 1)) changedBody, err := json.Marshal(changed) if err != nil { t.Fatal(err) } if _, err := loader.Apply(context.Background(), changedBody); err == nil { t.Fatal("same revision changed non-rendered caller authorization") } p := filepath.Join(dir, "go-sip-managed.conf") if err := os.WriteFile(p, []byte("[tampered]"), 0600); err != nil { t.Fatal(err) } if _, err := loader.LoadedSIP(context.Background()); err == nil || !strings.Contains(err.Error(), "no longer matches") { t.Fatalf("tampered config was accepted: %v", err) } } func TestLoaderFailsClosedWithoutApprovedStaticTransport(t *testing.T) { dir := t.TempDir() loader := Loader{ConfigDir: dir, Asterisk: "/bin/true", LibraryDir: dir} body, err := json.Marshal(testSIP(t)) if err != nil { t.Fatal(err) } if _, err := loader.Apply(context.Background(), body); err == nil { t.Fatal("applied SIP without management-owned base transport") } if _, err := os.Stat(filepath.Join(dir, "go-sip-managed.conf")); !os.IsNotExist(err) { t.Fatalf("mutated disk before static transport check: %v", err) } }