@@ -20,6 +20,7 @@ import (
2020 "bytes"
2121 "context"
2222 "fmt"
23+ "io"
2324 "os"
2425 "path/filepath"
2526 "strings"
@@ -280,3 +281,114 @@ services:
280281 normalizeSpaces (actualOutput ),
281282 "\n Expected:\n %s\n Got:\n %s" , expected , actualOutput )
282283}
284+
285+ func TestConfirmRemoteIncludes (t * testing.T ) {
286+ ctrl := gomock .NewController (t )
287+ defer ctrl .Finish ()
288+ cli := mocks .NewMockCli (ctrl )
289+
290+ tests := []struct {
291+ name string
292+ opts buildOptions
293+ assumeYes bool
294+ userInput string
295+ wantErr bool
296+ errMessage string
297+ wantPrompt bool
298+ wantOutput string
299+ }{
300+ {
301+ name : "no remote includes" ,
302+ opts : buildOptions {
303+ ProjectOptions : & ProjectOptions {
304+ ConfigPaths : []string {
305+ "docker-compose.yaml" ,
306+ "./local/path/compose.yaml" ,
307+ },
308+ },
309+ },
310+ assumeYes : false ,
311+ wantErr : false ,
312+ wantPrompt : false ,
313+ },
314+ {
315+ name : "assume yes with remote includes" ,
316+ opts : buildOptions {
317+ ProjectOptions : & ProjectOptions {
318+ ConfigPaths : []string {
319+ "oci://registry.example.com/stack:latest" ,
320+ "git://github.com/user/repo.git" ,
321+ },
322+ },
323+ },
324+ assumeYes : true ,
325+ wantErr : false ,
326+ wantPrompt : false ,
327+ },
328+ {
329+ name : "user confirms remote includes" ,
330+ opts : buildOptions {
331+ ProjectOptions : & ProjectOptions {
332+ ConfigPaths : []string {
333+ "oci://registry.example.com/stack:latest" ,
334+ "git://github.com/user/repo.git" ,
335+ },
336+ },
337+ },
338+ assumeYes : false ,
339+ userInput : "y\n " ,
340+ wantErr : false ,
341+ wantPrompt : true ,
342+ wantOutput : "\n Warning: This Compose project includes files from remote sources:\n " +
343+ " - oci://registry.example.com/stack:latest\n " +
344+ " - git://github.com/user/repo.git\n " +
345+ "\n Remote includes could potentially be malicious. Make sure you trust the source.\n " +
346+ "Do you want to continue? [y/N]: " ,
347+ },
348+ {
349+ name : "user rejects remote includes" ,
350+ opts : buildOptions {
351+ ProjectOptions : & ProjectOptions {
352+ ConfigPaths : []string {
353+ "oci://registry.example.com/stack:latest" ,
354+ },
355+ },
356+ },
357+ assumeYes : false ,
358+ userInput : "n\n " ,
359+ wantErr : true ,
360+ errMessage : "operation cancelled by user" ,
361+ wantPrompt : true ,
362+ wantOutput : "\n Warning: This Compose project includes files from remote sources:\n " +
363+ " - oci://registry.example.com/stack:latest\n " +
364+ "\n Remote includes could potentially be malicious. Make sure you trust the source.\n " +
365+ "Do you want to continue? [y/N]: " ,
366+ },
367+ }
368+
369+ buf := new (bytes.Buffer )
370+ for _ , tt := range tests {
371+ t .Run (tt .name , func (t * testing.T ) {
372+ cli .EXPECT ().Out ().Return (streams .NewOut (buf )).AnyTimes ()
373+
374+ if tt .wantPrompt {
375+ inbuf := io .NopCloser (bytes .NewBufferString (tt .userInput ))
376+ cli .EXPECT ().In ().Return (streams .NewIn (inbuf )).AnyTimes ()
377+ }
378+
379+ err := confirmRemoteIncludes (cli , tt .opts , tt .assumeYes )
380+
381+ if tt .wantErr {
382+ require .Error (t , err )
383+ require .Equal (t , tt .errMessage , err .Error ())
384+ } else {
385+ require .NoError (t , err )
386+ }
387+
388+ if tt .wantOutput != "" {
389+ require .Equal (t , tt .wantOutput , buf .String ())
390+ }
391+ buf .Reset ()
392+ })
393+ }
394+ }
0 commit comments