mirror of
https://github.com/go-task/task.git
synced 2026-08-29 10:08:27 +02:00
Merge commit from fork
This commit is contained in:
@@ -231,7 +231,11 @@ func (node *GitNode) CacheKey() string {
|
||||
// Unlike CacheKey() which includes the file path, this identifies the repository itself.
|
||||
// Two GitNodes with the same repo+ref but different file paths will share the same cache.
|
||||
//
|
||||
// Returns a path like: github.com/user/repo.git/main
|
||||
// The identity is hashed into a single, filesystem-safe path segment. This prevents an
|
||||
// attacker-controlled ref (e.g. "../../victim") from being used as a raw path component,
|
||||
// which would otherwise let the cache directory escape the task-git-repos root (CWE-22).
|
||||
//
|
||||
// Returns a path like: git/<sha256-hex>
|
||||
func (node *GitNode) repoCacheKey() string {
|
||||
repoPath := strings.Trim(node.url.Path, "/")
|
||||
|
||||
@@ -240,7 +244,8 @@ func (node *GitNode) repoCacheKey() string {
|
||||
ref = "_default_" // Placeholder for the remote's default branch
|
||||
}
|
||||
|
||||
return filepath.Join(node.url.Host, repoPath, ref)
|
||||
identity := strings.Join([]string{node.url.Host, repoPath, ref}, "/")
|
||||
return filepath.Join("git", checksum([]byte(identity)))
|
||||
}
|
||||
|
||||
func splitURLOnDoubleSlash(u *url.URL) (string, string) {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package taskfile
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -245,3 +248,23 @@ func TestRepoCacheKey_Consistency(t *testing.T) {
|
||||
assert.Equal(t, key1, key2)
|
||||
assert.Equal(t, key2, key3)
|
||||
}
|
||||
|
||||
func TestRepoCacheKey_BlocksTraversalRef(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
// A malicious ref containing path traversal components must not be able to make
|
||||
// the cache directory escape the task-git-repos root (CWE-22, GHSA-g8jx-8vm6-phr8).
|
||||
node, err := NewGitNode("https://github.com/foo/bar.git//file.yml?ref=../../../../victim-delete-me", "", false)
|
||||
require.NoError(t, err)
|
||||
|
||||
key := node.repoCacheKey()
|
||||
|
||||
// The key itself must not carry traversal components.
|
||||
assert.NotContains(t, key, "..", "cache key must not contain traversal components")
|
||||
|
||||
// Joined under the cache root, the resolved path must stay inside it.
|
||||
root := filepath.Join(os.TempDir(), "task-git-repos")
|
||||
resolved := filepath.Clean(filepath.Join(root, key))
|
||||
assert.True(t, strings.HasPrefix(resolved, root+string(os.PathSeparator)),
|
||||
"cache dir %q must stay within %q", resolved, root)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user