@@ -73,6 +73,20 @@ func withIsolatedConfig(t *testing.T, args []string) []string {
7373// stdin, and env vars, returning the exit code, stdout, and stderr.
7474func runMain (t * testing.T , args []string , stdin string , env map [string ]string ) (int , string , string ) {
7575 t .Helper ()
76+ // Clear ambient notification env vars before applying the test's env
77+ // map. config.Load discovers DISCORD_WEBHOOK_URL, TELEGRAM_BOT_TOKEN +
78+ // TELEGRAM_CHAT_ID, and SIGNAL_CLI_URL + SIGNAL_FROM + SIGNAL_TO from
79+ // the process env, so a developer's exported credentials would otherwise
80+ // add channels to tests and could send real notifications. t.Setenv
81+ // records the prior value and restores it automatically when the test
82+ // ends.
83+ for _ , k := range []string {
84+ "DISCORD_WEBHOOK_URL" ,
85+ "TELEGRAM_BOT_TOKEN" , "TELEGRAM_CHAT_ID" ,
86+ "SIGNAL_CLI_URL" , "SIGNAL_FROM" , "SIGNAL_TO" ,
87+ } {
88+ t .Setenv (k , "" )
89+ }
7690 for k , v := range env {
7791 t .Setenv (k , v )
7892 }
@@ -104,6 +118,52 @@ func TestRun_PlainStringToDiscord_ExitsZero(t *testing.T) {
104118 }
105119}
106120
121+ // TestRunMainClearsAmbientChannelEnv verifies that runMain clears ambient
122+ // notification environment variables before applying the test's env map, so a
123+ // developer's exported credentials cannot add channels to a test run or send
124+ // real notifications. A complete Signal configuration is set as ambient
125+ // process env (outside the env map passed to runMain); only a local Discord
126+ // endpoint is passed through the env map. The ambient Signal server must
127+ // receive zero requests while the intended Discord send succeeds.
128+ func TestRunMainClearsAmbientChannelEnv (t * testing.T ) {
129+ var signalHits int
130+ signalSrv := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , _ * http.Request ) {
131+ signalHits ++
132+ w .WriteHeader (http .StatusNoContent )
133+ }))
134+ defer signalSrv .Close ()
135+
136+ var discordGot map [string ]interface {}
137+ discordSrv := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
138+ body , _ := io .ReadAll (r .Body )
139+ _ = json .Unmarshal (body , & discordGot )
140+ w .WriteHeader (http .StatusNoContent )
141+ }))
142+ defer discordSrv .Close ()
143+
144+ // Ambient Signal configuration lives in the process env, NOT in the env
145+ // map handed to runMain. Without clearing, config.Load discovers it and
146+ // the Signal channel sends to signalSrv.
147+ t .Setenv ("SIGNAL_CLI_URL" , signalSrv .URL )
148+ t .Setenv ("SIGNAL_FROM" , "+15551234567" )
149+ t .Setenv ("SIGNAL_TO" , "+15557654321" )
150+
151+ code , _ , stderr := runMain (t ,
152+ []string {"agent-notify" , "ambient clear check" },
153+ "" ,
154+ map [string ]string {"DISCORD_WEBHOOK_URL" : discordSrv .URL },
155+ )
156+ if code != 0 {
157+ t .Fatalf ("exit = %d, stderr = %s" , code , stderr )
158+ }
159+ if discordGot == nil {
160+ t .Fatal ("Discord webhook never received the intended request" )
161+ }
162+ if signalHits != 0 {
163+ t .Fatalf ("ambient Signal server received %d request(s); runMain did not clear ambient channel env" , signalHits )
164+ }
165+ }
166+
107167func TestRun_NoChannelsConfigured_Exit2 (t * testing.T ) {
108168 for _ , k := range []string {"DISCORD_WEBHOOK_URL" , "TELEGRAM_BOT_TOKEN" , "TELEGRAM_CHAT_ID" , "SIGNAL_CLI_URL" , "SIGNAL_FROM" , "SIGNAL_TO" } {
109169 os .Unsetenv (k )
0 commit comments