mirror of
https://github.com/go-task/task.git
synced 2025-12-16 19:57:43 +01:00
feat: add --trusted-hosts CLI and remote.trusted-hosts config for remote tasks (#2491)
Co-authored-by: Valentin Maerten <maerten.valentin@gmail.com>
This commit is contained in:
15
executor.go
15
executor.go
@@ -34,6 +34,7 @@ type (
|
|||||||
Insecure bool
|
Insecure bool
|
||||||
Download bool
|
Download bool
|
||||||
Offline bool
|
Offline bool
|
||||||
|
TrustedHosts []string
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
CacheExpiryDuration time.Duration
|
CacheExpiryDuration time.Duration
|
||||||
Watch bool
|
Watch bool
|
||||||
@@ -225,6 +226,20 @@ func (o *offlineOption) ApplyToExecutor(e *Executor) {
|
|||||||
e.Offline = o.offline
|
e.Offline = o.offline
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithTrustedHosts configures the [Executor] with a list of trusted hosts for remote
|
||||||
|
// Taskfiles. Hosts in this list will not prompt for user confirmation.
|
||||||
|
func WithTrustedHosts(trustedHosts []string) ExecutorOption {
|
||||||
|
return &trustedHostsOption{trustedHosts}
|
||||||
|
}
|
||||||
|
|
||||||
|
type trustedHostsOption struct {
|
||||||
|
trustedHosts []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *trustedHostsOption) ApplyToExecutor(e *Executor) {
|
||||||
|
e.TrustedHosts = o.trustedHosts
|
||||||
|
}
|
||||||
|
|
||||||
// WithTimeout sets the [Executor]'s timeout for fetching remote taskfiles. By
|
// WithTimeout sets the [Executor]'s timeout for fetching remote taskfiles. By
|
||||||
// default, the timeout is set to 10 seconds.
|
// default, the timeout is set to 10 seconds.
|
||||||
func WithTimeout(timeout time.Duration) ExecutorOption {
|
func WithTimeout(timeout time.Duration) ExecutorOption {
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ var (
|
|||||||
Experiments bool
|
Experiments bool
|
||||||
Download bool
|
Download bool
|
||||||
Offline bool
|
Offline bool
|
||||||
|
TrustedHosts []string
|
||||||
ClearCache bool
|
ClearCache bool
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
CacheExpiryDuration time.Duration
|
CacheExpiryDuration time.Duration
|
||||||
@@ -152,6 +153,7 @@ func init() {
|
|||||||
if experiments.RemoteTaskfiles.Enabled() {
|
if experiments.RemoteTaskfiles.Enabled() {
|
||||||
pflag.BoolVar(&Download, "download", false, "Downloads a cached version of a remote Taskfile.")
|
pflag.BoolVar(&Download, "download", false, "Downloads a cached version of a remote Taskfile.")
|
||||||
pflag.BoolVar(&Offline, "offline", getConfig(config, func() *bool { return config.Remote.Offline }, false), "Forces Task to only use local or cached Taskfiles.")
|
pflag.BoolVar(&Offline, "offline", getConfig(config, func() *bool { return config.Remote.Offline }, false), "Forces Task to only use local or cached Taskfiles.")
|
||||||
|
pflag.StringSliceVar(&TrustedHosts, "trusted-hosts", config.Remote.TrustedHosts, "List of trusted hosts for remote Taskfiles (comma-separated).")
|
||||||
pflag.DurationVar(&Timeout, "timeout", getConfig(config, func() *time.Duration { return config.Remote.Timeout }, time.Second*10), "Timeout for downloading remote Taskfiles.")
|
pflag.DurationVar(&Timeout, "timeout", getConfig(config, func() *time.Duration { return config.Remote.Timeout }, time.Second*10), "Timeout for downloading remote Taskfiles.")
|
||||||
pflag.BoolVar(&ClearCache, "clear-cache", false, "Clear the remote cache.")
|
pflag.BoolVar(&ClearCache, "clear-cache", false, "Clear the remote cache.")
|
||||||
pflag.DurationVar(&CacheExpiryDuration, "expiry", getConfig(config, func() *time.Duration { return config.Remote.CacheExpiry }, 0), "Expiry duration for cached remote Taskfiles.")
|
pflag.DurationVar(&CacheExpiryDuration, "expiry", getConfig(config, func() *time.Duration { return config.Remote.CacheExpiry }, 0), "Expiry duration for cached remote Taskfiles.")
|
||||||
@@ -238,6 +240,7 @@ func (o *flagsOption) ApplyToExecutor(e *task.Executor) {
|
|||||||
task.WithInsecure(Insecure),
|
task.WithInsecure(Insecure),
|
||||||
task.WithDownload(Download),
|
task.WithDownload(Download),
|
||||||
task.WithOffline(Offline),
|
task.WithOffline(Offline),
|
||||||
|
task.WithTrustedHosts(TrustedHosts),
|
||||||
task.WithTimeout(Timeout),
|
task.WithTimeout(Timeout),
|
||||||
task.WithCacheExpiryDuration(CacheExpiryDuration),
|
task.WithCacheExpiryDuration(CacheExpiryDuration),
|
||||||
task.WithWatch(Watch),
|
task.WithWatch(Watch),
|
||||||
|
|||||||
1
setup.go
1
setup.go
@@ -84,6 +84,7 @@ func (e *Executor) readTaskfile(node taskfile.Node) error {
|
|||||||
taskfile.WithInsecure(e.Insecure),
|
taskfile.WithInsecure(e.Insecure),
|
||||||
taskfile.WithDownload(e.Download),
|
taskfile.WithDownload(e.Download),
|
||||||
taskfile.WithOffline(e.Offline),
|
taskfile.WithOffline(e.Offline),
|
||||||
|
taskfile.WithTrustedHosts(e.TrustedHosts),
|
||||||
taskfile.WithTempDir(e.TempDir.Remote),
|
taskfile.WithTempDir(e.TempDir.Remote),
|
||||||
taskfile.WithCacheExpiryDuration(e.CacheExpiryDuration),
|
taskfile.WithCacheExpiryDuration(e.CacheExpiryDuration),
|
||||||
taskfile.WithDebugFunc(debugFunc),
|
taskfile.WithDebugFunc(debugFunc),
|
||||||
|
|||||||
23
task_test.go
23
task_test.go
@@ -9,6 +9,7 @@ import (
|
|||||||
rand "math/rand/v2"
|
rand "math/rand/v2"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -786,6 +787,11 @@ func TestIncludesRemote(t *testing.T) {
|
|||||||
|
|
||||||
var buff SyncBuffer
|
var buff SyncBuffer
|
||||||
|
|
||||||
|
// Extract host from server URL for trust testing
|
||||||
|
parsedURL, err := url.Parse(srv.URL)
|
||||||
|
require.NoError(t, err)
|
||||||
|
trustedHost := parsedURL.Host
|
||||||
|
|
||||||
executors := []struct {
|
executors := []struct {
|
||||||
name string
|
name string
|
||||||
executor *task.Executor
|
executor *task.Executor
|
||||||
@@ -825,6 +831,23 @@ func TestIncludesRemote(t *testing.T) {
|
|||||||
task.WithOffline(true),
|
task.WithOffline(true),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "with trusted hosts, no prompts",
|
||||||
|
executor: task.NewExecutor(
|
||||||
|
task.WithDir(dir),
|
||||||
|
task.WithStdout(&buff),
|
||||||
|
task.WithStderr(&buff),
|
||||||
|
task.WithTimeout(time.Minute),
|
||||||
|
task.WithInsecure(true),
|
||||||
|
task.WithStdout(&buff),
|
||||||
|
task.WithStderr(&buff),
|
||||||
|
task.WithVerbose(true),
|
||||||
|
|
||||||
|
// With trusted hosts
|
||||||
|
task.WithTrustedHosts([]string{trustedHost}),
|
||||||
|
task.WithDownload(true),
|
||||||
|
),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, e := range executors {
|
for _, e := range executors {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package taskfile
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -43,6 +44,7 @@ type (
|
|||||||
insecure bool
|
insecure bool
|
||||||
download bool
|
download bool
|
||||||
offline bool
|
offline bool
|
||||||
|
trustedHosts []string
|
||||||
tempDir string
|
tempDir string
|
||||||
cacheExpiryDuration time.Duration
|
cacheExpiryDuration time.Duration
|
||||||
debugFunc DebugFunc
|
debugFunc DebugFunc
|
||||||
@@ -59,6 +61,7 @@ func NewReader(opts ...ReaderOption) *Reader {
|
|||||||
insecure: false,
|
insecure: false,
|
||||||
download: false,
|
download: false,
|
||||||
offline: false,
|
offline: false,
|
||||||
|
trustedHosts: nil,
|
||||||
tempDir: os.TempDir(),
|
tempDir: os.TempDir(),
|
||||||
cacheExpiryDuration: 0,
|
cacheExpiryDuration: 0,
|
||||||
debugFunc: nil,
|
debugFunc: nil,
|
||||||
@@ -119,6 +122,20 @@ func (o *offlineOption) ApplyToReader(r *Reader) {
|
|||||||
r.offline = o.offline
|
r.offline = o.offline
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithTrustedHosts configures the [Reader] with a list of trusted hosts for remote
|
||||||
|
// Taskfiles. Hosts in this list will not prompt for user confirmation.
|
||||||
|
func WithTrustedHosts(trustedHosts []string) ReaderOption {
|
||||||
|
return &trustedHostsOption{trustedHosts: trustedHosts}
|
||||||
|
}
|
||||||
|
|
||||||
|
type trustedHostsOption struct {
|
||||||
|
trustedHosts []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *trustedHostsOption) ApplyToReader(r *Reader) {
|
||||||
|
r.trustedHosts = o.trustedHosts
|
||||||
|
}
|
||||||
|
|
||||||
// WithTempDir sets the temporary directory that will be used by the [Reader].
|
// WithTempDir sets the temporary directory that will be used by the [Reader].
|
||||||
// By default, the reader uses [os.TempDir].
|
// By default, the reader uses [os.TempDir].
|
||||||
func WithTempDir(tempDir string) ReaderOption {
|
func WithTempDir(tempDir string) ReaderOption {
|
||||||
@@ -206,6 +223,28 @@ func (r *Reader) promptf(format string, a ...any) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isTrusted checks if a URI's host matches any of the trusted hosts patterns.
|
||||||
|
func (r *Reader) isTrusted(uri string) bool {
|
||||||
|
if len(r.trustedHosts) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the URI to extract the host
|
||||||
|
parsedURL, err := url.Parse(uri)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
host := parsedURL.Host
|
||||||
|
|
||||||
|
// Check against each trusted pattern (exact match including port if provided)
|
||||||
|
for _, pattern := range r.trustedHosts {
|
||||||
|
if host == pattern {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Reader) include(ctx context.Context, node Node) error {
|
func (r *Reader) include(ctx context.Context, node Node) error {
|
||||||
// Create a new vertex for the Taskfile
|
// Create a new vertex for the Taskfile
|
||||||
vertex := &ast.TaskfileVertex{
|
vertex := &ast.TaskfileVertex{
|
||||||
@@ -459,9 +498,9 @@ func (r *Reader) readRemoteNodeContent(ctx context.Context, node RemoteNode) ([]
|
|||||||
|
|
||||||
// If there is no manual checksum pin, run the automatic checks
|
// If there is no manual checksum pin, run the automatic checks
|
||||||
if node.Checksum() == "" {
|
if node.Checksum() == "" {
|
||||||
// Prompt the user if required
|
// Prompt the user if required (unless host is trusted)
|
||||||
prompt := cache.ChecksumPrompt(checksum)
|
prompt := cache.ChecksumPrompt(checksum)
|
||||||
if prompt != "" {
|
if prompt != "" && !r.isTrusted(node.Location()) {
|
||||||
if err := func() error {
|
if err := func() error {
|
||||||
r.promptMutex.Lock()
|
r.promptMutex.Lock()
|
||||||
defer r.promptMutex.Unlock()
|
defer r.promptMutex.Unlock()
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package ast
|
|||||||
import (
|
import (
|
||||||
"cmp"
|
"cmp"
|
||||||
"maps"
|
"maps"
|
||||||
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
@@ -21,6 +22,7 @@ type Remote struct {
|
|||||||
Offline *bool `yaml:"offline"`
|
Offline *bool `yaml:"offline"`
|
||||||
Timeout *time.Duration `yaml:"timeout"`
|
Timeout *time.Duration `yaml:"timeout"`
|
||||||
CacheExpiry *time.Duration `yaml:"cache-expiry"`
|
CacheExpiry *time.Duration `yaml:"cache-expiry"`
|
||||||
|
TrustedHosts []string `yaml:"trusted-hosts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge combines the current TaskRC with another TaskRC, prioritizing non-nil fields from the other TaskRC.
|
// Merge combines the current TaskRC with another TaskRC, prioritizing non-nil fields from the other TaskRC.
|
||||||
@@ -43,6 +45,12 @@ func (t *TaskRC) Merge(other *TaskRC) {
|
|||||||
t.Remote.Timeout = cmp.Or(other.Remote.Timeout, t.Remote.Timeout)
|
t.Remote.Timeout = cmp.Or(other.Remote.Timeout, t.Remote.Timeout)
|
||||||
t.Remote.CacheExpiry = cmp.Or(other.Remote.CacheExpiry, t.Remote.CacheExpiry)
|
t.Remote.CacheExpiry = cmp.Or(other.Remote.CacheExpiry, t.Remote.CacheExpiry)
|
||||||
|
|
||||||
|
if len(other.Remote.TrustedHosts) > 0 {
|
||||||
|
merged := slices.Concat(other.Remote.TrustedHosts, t.Remote.TrustedHosts)
|
||||||
|
slices.Sort(merged)
|
||||||
|
t.Remote.TrustedHosts = slices.Compact(merged)
|
||||||
|
}
|
||||||
|
|
||||||
t.Verbose = cmp.Or(other.Verbose, t.Verbose)
|
t.Verbose = cmp.Or(other.Verbose, t.Verbose)
|
||||||
t.Concurrency = cmp.Or(other.Concurrency, t.Concurrency)
|
t.Concurrency = cmp.Or(other.Concurrency, t.Concurrency)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@@ -135,3 +136,174 @@ func TestGetConfig_All(t *testing.T) { //nolint:paralleltest // cannot run in pa
|
|||||||
},
|
},
|
||||||
}, cfg)
|
}, cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetConfig_RemoteTrustedHosts(t *testing.T) { //nolint:paralleltest // cannot run in parallel
|
||||||
|
_, _, localDir := setupDirs(t)
|
||||||
|
|
||||||
|
// Test with single host
|
||||||
|
configYAML := `
|
||||||
|
remote:
|
||||||
|
trusted-hosts:
|
||||||
|
- github.com
|
||||||
|
`
|
||||||
|
writeFile(t, localDir, ".taskrc.yml", configYAML)
|
||||||
|
|
||||||
|
cfg, err := GetConfig(localDir)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, cfg)
|
||||||
|
assert.Equal(t, []string{"github.com"}, cfg.Remote.TrustedHosts)
|
||||||
|
|
||||||
|
// Test with multiple hosts
|
||||||
|
configYAML = `
|
||||||
|
remote:
|
||||||
|
trusted-hosts:
|
||||||
|
- github.com
|
||||||
|
- gitlab.com
|
||||||
|
- example.com:8080
|
||||||
|
`
|
||||||
|
writeFile(t, localDir, ".taskrc.yml", configYAML)
|
||||||
|
|
||||||
|
cfg, err = GetConfig(localDir)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, cfg)
|
||||||
|
assert.Equal(t, []string{"github.com", "gitlab.com", "example.com:8080"}, cfg.Remote.TrustedHosts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetConfig_RemoteTrustedHostsMerge(t *testing.T) { //nolint:paralleltest // cannot run in parallel
|
||||||
|
t.Run("file-based merge precedence", func(t *testing.T) { //nolint:paralleltest // parent test cannot run in parallel
|
||||||
|
xdgConfigDir, homeDir, localDir := setupDirs(t)
|
||||||
|
|
||||||
|
// XDG config has github.com and gitlab.com
|
||||||
|
xdgConfig := `
|
||||||
|
remote:
|
||||||
|
trusted-hosts:
|
||||||
|
- github.com
|
||||||
|
- gitlab.com
|
||||||
|
timeout: "30s"
|
||||||
|
`
|
||||||
|
writeFile(t, xdgConfigDir, "taskrc.yml", xdgConfig)
|
||||||
|
|
||||||
|
// Home config has example.com (should be combined with XDG)
|
||||||
|
homeConfig := `
|
||||||
|
remote:
|
||||||
|
trusted-hosts:
|
||||||
|
- example.com
|
||||||
|
`
|
||||||
|
writeFile(t, homeDir, ".taskrc.yml", homeConfig)
|
||||||
|
|
||||||
|
cfg, err := GetConfig(localDir)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, cfg)
|
||||||
|
// Home config entries come first, then XDG
|
||||||
|
assert.Equal(t, []string{"example.com", "github.com", "gitlab.com"}, cfg.Remote.TrustedHosts)
|
||||||
|
|
||||||
|
// Test with local config too
|
||||||
|
localConfig := `
|
||||||
|
remote:
|
||||||
|
trusted-hosts:
|
||||||
|
- local.dev
|
||||||
|
`
|
||||||
|
writeFile(t, localDir, ".taskrc.yml", localConfig)
|
||||||
|
|
||||||
|
cfg, err = GetConfig(localDir)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, cfg)
|
||||||
|
// Local config entries come first
|
||||||
|
assert.Equal(t, []string{"example.com", "github.com", "gitlab.com", "local.dev"}, cfg.Remote.TrustedHosts)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("merge edge cases", func(t *testing.T) { //nolint:paralleltest // parent test cannot run in parallel
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
base *ast.TaskRC
|
||||||
|
other *ast.TaskRC
|
||||||
|
expected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "merge hosts into empty",
|
||||||
|
base: &ast.TaskRC{},
|
||||||
|
other: &ast.TaskRC{
|
||||||
|
Remote: ast.Remote{
|
||||||
|
TrustedHosts: []string{"github.com"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []string{"github.com"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "merge combines lists",
|
||||||
|
base: &ast.TaskRC{
|
||||||
|
Remote: ast.Remote{
|
||||||
|
TrustedHosts: []string{"base.com"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
other: &ast.TaskRC{
|
||||||
|
Remote: ast.Remote{
|
||||||
|
TrustedHosts: []string{"other.com"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []string{"base.com", "other.com"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "merge empty list does not override",
|
||||||
|
base: &ast.TaskRC{
|
||||||
|
Remote: ast.Remote{
|
||||||
|
TrustedHosts: []string{"base.com"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
other: &ast.TaskRC{
|
||||||
|
Remote: ast.Remote{
|
||||||
|
TrustedHosts: []string{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []string{"base.com"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "merge nil does not override",
|
||||||
|
base: &ast.TaskRC{
|
||||||
|
Remote: ast.Remote{
|
||||||
|
TrustedHosts: []string{"base.com"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
other: &ast.TaskRC{
|
||||||
|
Remote: ast.Remote{
|
||||||
|
TrustedHosts: nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: []string{"base.com"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) { //nolint:paralleltest // parent test cannot run in parallel
|
||||||
|
tt.base.Merge(tt.other)
|
||||||
|
assert.Equal(t, tt.expected, tt.base.Remote.TrustedHosts)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("all remote fields merge", func(t *testing.T) { //nolint:paralleltest // parent test cannot run in parallel
|
||||||
|
insecureTrue := true
|
||||||
|
offlineTrue := true
|
||||||
|
timeout := 30 * time.Second
|
||||||
|
cacheExpiry := 1 * time.Hour
|
||||||
|
|
||||||
|
base := &ast.TaskRC{}
|
||||||
|
other := &ast.TaskRC{
|
||||||
|
Remote: ast.Remote{
|
||||||
|
Insecure: &insecureTrue,
|
||||||
|
Offline: &offlineTrue,
|
||||||
|
Timeout: &timeout,
|
||||||
|
CacheExpiry: &cacheExpiry,
|
||||||
|
TrustedHosts: []string{"github.com", "gitlab.com"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
base.Merge(other)
|
||||||
|
|
||||||
|
assert.Equal(t, &insecureTrue, base.Remote.Insecure)
|
||||||
|
assert.Equal(t, &offlineTrue, base.Remote.Offline)
|
||||||
|
assert.Equal(t, &timeout, base.Remote.Timeout)
|
||||||
|
assert.Equal(t, &cacheExpiry, base.Remote.CacheExpiry)
|
||||||
|
assert.Equal(t, []string{"github.com", "gitlab.com"}, base.Remote.TrustedHosts)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -214,7 +214,10 @@ remote Taskfiles:
|
|||||||
Sometimes you need to run Task in an environment that does not have an
|
Sometimes you need to run Task in an environment that does not have an
|
||||||
interactive terminal, so you are not able to accept a prompt. In these cases you
|
interactive terminal, so you are not able to accept a prompt. In these cases you
|
||||||
are able to tell task to accept these prompts automatically by using the `--yes`
|
are able to tell task to accept these prompts automatically by using the `--yes`
|
||||||
flag. Before enabling this flag, you should:
|
flag or the `--trust` flag. The `--trust` flag allows you to specify trusted
|
||||||
|
hosts for remote Taskfiles, while `--yes` applies to all prompts in Task. You
|
||||||
|
can also configure trusted hosts in your [taskrc configuration](#trusted-hosts) using
|
||||||
|
`remote.trusted-hosts`. Before enabling automatic trust, you should:
|
||||||
|
|
||||||
1. Be sure that you trust the source and contents of the remote Taskfile.
|
1. Be sure that you trust the source and contents of the remote Taskfile.
|
||||||
2. Consider using a pinned version of the remote Taskfile (e.g. A link
|
2. Consider using a pinned version of the remote Taskfile (e.g. A link
|
||||||
@@ -305,6 +308,9 @@ remote:
|
|||||||
offline: false
|
offline: false
|
||||||
timeout: "30s"
|
timeout: "30s"
|
||||||
cache-expiry: "24h"
|
cache-expiry: "24h"
|
||||||
|
trusted-hosts:
|
||||||
|
- github.com
|
||||||
|
- gitlab.com
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `insecure`
|
#### `insecure`
|
||||||
@@ -353,3 +359,38 @@ remote:
|
|||||||
remote:
|
remote:
|
||||||
cache-expiry: "6h"
|
cache-expiry: "6h"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### `trusted-hosts`
|
||||||
|
|
||||||
|
- **Type**: `array of strings`
|
||||||
|
- **Default**: `[]` (empty list)
|
||||||
|
- **Description**: List of trusted hosts for remote Taskfiles. Hosts in this
|
||||||
|
list will not prompt for confirmation when downloading Taskfiles
|
||||||
|
- **CLI equivalent**: `--trusted-hosts`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
remote:
|
||||||
|
trusted-hosts:
|
||||||
|
- github.com
|
||||||
|
- gitlab.com
|
||||||
|
- raw.githubusercontent.com
|
||||||
|
- example.com:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
Hosts in the trusted hosts list will automatically be trusted without prompting for
|
||||||
|
confirmation when they are first downloaded or when their checksums change. The
|
||||||
|
host matching includes the port if specified in the URL. Use with caution and
|
||||||
|
only add hosts you fully trust.
|
||||||
|
|
||||||
|
You can also specify trusted hosts via the command line:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# Trust specific host for this execution
|
||||||
|
task --trusted-hosts github.com -t https://github.com/user/repo.git//Taskfile.yml
|
||||||
|
|
||||||
|
# Trust multiple hosts (comma-separated)
|
||||||
|
task --trusted-hosts github.com,gitlab.com -t https://github.com/user/repo.git//Taskfile.yml
|
||||||
|
|
||||||
|
# Trust a host with a specific port
|
||||||
|
task --trusted-hosts example.com:8080 -t https://example.com:8080/Taskfile.yml
|
||||||
|
```
|
||||||
|
|||||||
@@ -42,6 +42,13 @@
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Expiry duration for cached remote Taskfiles (e.g., '1h', '24h')",
|
"description": "Expiry duration for cached remote Taskfiles (e.g., '1h', '24h')",
|
||||||
"pattern": "^[0-9]+(ns|us|µs|ms|s|m|h)$"
|
"pattern": "^[0-9]+(ns|us|µs|ms|s|m|h)$"
|
||||||
|
},
|
||||||
|
"trusted-hosts": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "List of trusted hosts for remote Taskfiles (e.g., 'github.com', 'gitlab.com', 'example.com:8080').",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
|||||||
Reference in New Issue
Block a user