add warning message when a remote configuration include an another remote config
Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
This commit is contained in:
parent
66a47169d5
commit
41e6094041
@ -29,6 +29,7 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/template"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/internal/tracing"
|
||||
ui "github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/compose/v2/pkg/prompt"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
@ -103,6 +104,11 @@ func checksForRemoteStack(ctx context.Context, dockerCli command.Cli, project *t
|
||||
if !isRemoteConfig(dockerCli, options) {
|
||||
return nil
|
||||
}
|
||||
if metrics, ok := ctx.Value(tracing.MetricsKey{}).(tracing.Metrics); ok && metrics.CountIncludesRemote > 0 {
|
||||
if err := confirmRemoteIncludes(dockerCli, options, assumeYes); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
displayLocationRemoteStack(dockerCli, project, options)
|
||||
return promptForInterpolatedVariables(ctx, dockerCli, options.ProjectOptions, assumeYes, cmdEnvs)
|
||||
}
|
||||
@ -245,3 +251,41 @@ func displayLocationRemoteStack(dockerCli command.Cli, project *types.Project, o
|
||||
_, _ = fmt.Fprintf(dockerCli.Out(), "Your compose stack %q is stored in %q\n", mainComposeFile, project.WorkingDir)
|
||||
}
|
||||
}
|
||||
|
||||
func confirmRemoteIncludes(dockerCli command.Cli, options buildOptions, assumeYes bool) error {
|
||||
if assumeYes {
|
||||
return nil
|
||||
}
|
||||
|
||||
var remoteIncludes []string
|
||||
remoteLoaders := options.ProjectOptions.remoteLoaders(dockerCli)
|
||||
for _, cf := range options.ProjectOptions.ConfigPaths {
|
||||
for _, loader := range remoteLoaders {
|
||||
if loader.Accept(cf) {
|
||||
remoteIncludes = append(remoteIncludes, cf)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(remoteIncludes) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintln(dockerCli.Out(), "\nWarning: This Compose project includes files from remote sources:")
|
||||
for _, include := range remoteIncludes {
|
||||
_, _ = fmt.Fprintf(dockerCli.Out(), " - %s\n", include)
|
||||
}
|
||||
_, _ = fmt.Fprintln(dockerCli.Out(), "\nRemote includes could potentially be malicious. Make sure you trust the source.")
|
||||
|
||||
msg := "Do you want to continue? [y/N]: "
|
||||
confirmed, err := prompt.NewPrompt(dockerCli.In(), dockerCli.Out()).Confirm(msg, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !confirmed {
|
||||
return fmt.Errorf("operation cancelled by user")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@ -280,3 +281,114 @@ services:
|
||||
normalizeSpaces(actualOutput),
|
||||
"\nExpected:\n%s\nGot:\n%s", expected, actualOutput)
|
||||
}
|
||||
|
||||
func TestConfirmRemoteIncludes(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
cli := mocks.NewMockCli(ctrl)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
opts buildOptions
|
||||
assumeYes bool
|
||||
userInput string
|
||||
wantErr bool
|
||||
errMessage string
|
||||
wantPrompt bool
|
||||
wantOutput string
|
||||
}{
|
||||
{
|
||||
name: "no remote includes",
|
||||
opts: buildOptions{
|
||||
ProjectOptions: &ProjectOptions{
|
||||
ConfigPaths: []string{
|
||||
"docker-compose.yaml",
|
||||
"./local/path/compose.yaml",
|
||||
},
|
||||
},
|
||||
},
|
||||
assumeYes: false,
|
||||
wantErr: false,
|
||||
wantPrompt: false,
|
||||
},
|
||||
{
|
||||
name: "assume yes with remote includes",
|
||||
opts: buildOptions{
|
||||
ProjectOptions: &ProjectOptions{
|
||||
ConfigPaths: []string{
|
||||
"oci://registry.example.com/stack:latest",
|
||||
"git://github.com/user/repo.git",
|
||||
},
|
||||
},
|
||||
},
|
||||
assumeYes: true,
|
||||
wantErr: false,
|
||||
wantPrompt: false,
|
||||
},
|
||||
{
|
||||
name: "user confirms remote includes",
|
||||
opts: buildOptions{
|
||||
ProjectOptions: &ProjectOptions{
|
||||
ConfigPaths: []string{
|
||||
"oci://registry.example.com/stack:latest",
|
||||
"git://github.com/user/repo.git",
|
||||
},
|
||||
},
|
||||
},
|
||||
assumeYes: false,
|
||||
userInput: "y\n",
|
||||
wantErr: false,
|
||||
wantPrompt: true,
|
||||
wantOutput: "\nWarning: This Compose project includes files from remote sources:\n" +
|
||||
" - oci://registry.example.com/stack:latest\n" +
|
||||
" - git://github.com/user/repo.git\n" +
|
||||
"\nRemote includes could potentially be malicious. Make sure you trust the source.\n" +
|
||||
"Do you want to continue? [y/N]: ",
|
||||
},
|
||||
{
|
||||
name: "user rejects remote includes",
|
||||
opts: buildOptions{
|
||||
ProjectOptions: &ProjectOptions{
|
||||
ConfigPaths: []string{
|
||||
"oci://registry.example.com/stack:latest",
|
||||
},
|
||||
},
|
||||
},
|
||||
assumeYes: false,
|
||||
userInput: "n\n",
|
||||
wantErr: true,
|
||||
errMessage: "operation cancelled by user",
|
||||
wantPrompt: true,
|
||||
wantOutput: "\nWarning: This Compose project includes files from remote sources:\n" +
|
||||
" - oci://registry.example.com/stack:latest\n" +
|
||||
"\nRemote includes could potentially be malicious. Make sure you trust the source.\n" +
|
||||
"Do you want to continue? [y/N]: ",
|
||||
},
|
||||
}
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cli.EXPECT().Out().Return(streams.NewOut(buf)).AnyTimes()
|
||||
|
||||
if tt.wantPrompt {
|
||||
inbuf := io.NopCloser(bytes.NewBufferString(tt.userInput))
|
||||
cli.EXPECT().In().Return(streams.NewIn(inbuf)).AnyTimes()
|
||||
}
|
||||
|
||||
err := confirmRemoteIncludes(cli, tt.opts, tt.assumeYes)
|
||||
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
require.Equal(t, tt.errMessage, err.Error())
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
if tt.wantOutput != "" {
|
||||
require.Equal(t, tt.wantOutput, buf.String())
|
||||
}
|
||||
buf.Reset()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -96,6 +96,6 @@ type Pipe struct {
|
||||
func (u Pipe) Confirm(message string, defaultValue bool) (bool, error) {
|
||||
_, _ = fmt.Fprint(u.stdout, message)
|
||||
var answer string
|
||||
_, _ = fmt.Scanln(&answer)
|
||||
_, _ = fmt.Fscanln(u.stdin, &answer)
|
||||
return utils.StringToBool(answer), nil
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user