1
0
mirror of https://github.com/go-task/task.git synced 2025-11-23 22:24:45 +02:00

feat: checksum pinning (#2223)

This commit is contained in:
Pete Davison
2025-05-24 14:00:02 +01:00
committed by GitHub
parent 68ce8b1d84
commit 71eb8cdeea
17 changed files with 196 additions and 15 deletions

View File

@@ -24,6 +24,7 @@ type (
AdvancedImport bool
Vars *Vars
Flatten bool
Checksum string
}
// Includes is an ordered map of namespaces to includes.
Includes struct {
@@ -165,6 +166,7 @@ func (include *Include) UnmarshalYAML(node *yaml.Node) error {
Aliases []string
Excludes []string
Vars *Vars
Checksum string
}
if err := node.Decode(&includedTaskfile); err != nil {
return errors.NewTaskfileDecodeError(err, node)
@@ -178,6 +180,7 @@ func (include *Include) UnmarshalYAML(node *yaml.Node) error {
include.AdvancedImport = true
include.Vars = includedTaskfile.Vars
include.Flatten = includedTaskfile.Flatten
include.Checksum = includedTaskfile.Checksum
return nil
}
@@ -200,5 +203,7 @@ func (include *Include) DeepCopy() *Include {
AdvancedImport: include.AdvancedImport,
Vars: include.Vars.DeepCopy(),
Flatten: include.Flatten,
Aliases: deepcopy.Slice(include.Aliases),
Checksum: include.Checksum,
}
}

View File

@@ -17,6 +17,8 @@ type Node interface {
Parent() Node
Location() string
Dir() string
Checksum() string
Verify(checksum string) bool
ResolveEntrypoint(entrypoint string) (string, error)
ResolveDir(dir string) (string, error)
}

View File

@@ -7,8 +7,9 @@ type (
// designed to be embedded in other node types so that this boilerplate code
// does not need to be repeated.
baseNode struct {
parent Node
dir string
parent Node
dir string
checksum string
}
)
@@ -32,6 +33,12 @@ func WithParent(parent Node) NodeOption {
}
}
func WithChecksum(checksum string) NodeOption {
return func(node *baseNode) {
node.checksum = checksum
}
}
func (node *baseNode) Parent() Node {
return node.parent
}
@@ -39,3 +46,11 @@ func (node *baseNode) Parent() Node {
func (node *baseNode) Dir() string {
return node.dir
}
func (node *baseNode) Checksum() string {
return node.checksum
}
func (node *baseNode) Verify(checksum string) bool {
return node.checksum == "" || node.checksum == checksum
}

View File

@@ -250,6 +250,7 @@ func (r *Reader) include(ctx context.Context, node Node) error {
AdvancedImport: include.AdvancedImport,
Excludes: include.Excludes,
Vars: include.Vars,
Checksum: include.Checksum,
}
if err := cache.Err(); err != nil {
return err
@@ -267,6 +268,7 @@ func (r *Reader) include(ctx context.Context, node Node) error {
includeNode, err := NewNode(entrypoint, include.Dir, r.insecure,
WithParent(node),
WithChecksum(include.Checksum),
)
if err != nil {
if include.Optional {
@@ -362,7 +364,24 @@ func (r *Reader) readNodeContent(ctx context.Context, node Node) ([]byte, error)
if node, isRemote := node.(RemoteNode); isRemote {
return r.readRemoteNodeContent(ctx, node)
}
return node.Read()
// Read the Taskfile
b, err := node.Read()
if err != nil {
return nil, err
}
// If the given checksum doesn't match the sum pinned in the Taskfile
checksum := checksum(b)
if !node.Verify(checksum) {
return nil, &errors.TaskfileDoesNotMatchChecksum{
URI: node.Location(),
ExpectedChecksum: node.Checksum(),
ActualChecksum: checksum,
}
}
return b, nil
}
func (r *Reader) readRemoteNodeContent(ctx context.Context, node RemoteNode) ([]byte, error) {
@@ -427,17 +446,29 @@ func (r *Reader) readRemoteNodeContent(ctx context.Context, node RemoteNode) ([]
}
r.debugf("found remote file at %q\n", node.Location())
checksum := checksum(downloadedBytes)
prompt := cache.ChecksumPrompt(checksum)
// Prompt the user if required
if prompt != "" {
if err := func() error {
r.promptMutex.Lock()
defer r.promptMutex.Unlock()
return r.promptf(prompt, node.Location())
}(); err != nil {
return nil, &errors.TaskfileNotTrustedError{URI: node.Location()}
// If the given checksum doesn't match the sum pinned in the Taskfile
checksum := checksum(downloadedBytes)
if !node.Verify(checksum) {
return nil, &errors.TaskfileDoesNotMatchChecksum{
URI: node.Location(),
ExpectedChecksum: node.Checksum(),
ActualChecksum: checksum,
}
}
// If there is no manual checksum pin, run the automatic checks
if node.Checksum() == "" {
// Prompt the user if required
prompt := cache.ChecksumPrompt(checksum)
if prompt != "" {
if err := func() error {
r.promptMutex.Lock()
defer r.promptMutex.Unlock()
return r.promptf(prompt, node.Location())
}(); err != nil {
return nil, &errors.TaskfileNotTrustedError{URI: node.Location()}
}
}
}