Confine published diagnostic files
This commit is contained in:
@@ -978,7 +978,7 @@ func collectPublishPreviousFiles(ctx context.Context, previousDir string) ([]pub
|
||||
func openPublishRunFiles(runRoot string, files []publishUploadFile) ([]*os.File, error) {
|
||||
sources := make([]*os.File, 0, len(files))
|
||||
for _, candidate := range files {
|
||||
file, err := openPublishOutputSource(filepath.Join(runRoot, filepath.FromSlash(candidate.SourceRelativePath)))
|
||||
file, err := openPublishSourceUnderRoot(runRoot, candidate.SourceRelativePath)
|
||||
if err != nil {
|
||||
closePublishSources(sources)
|
||||
return nil, err
|
||||
@@ -993,6 +993,56 @@ func openPublishRunFiles(runRoot string, files []publishUploadFile) ([]*os.File,
|
||||
return sources, nil
|
||||
}
|
||||
|
||||
func openPublishSourceUnderRoot(rootPath, relativePath string) (*os.File, error) {
|
||||
relativePath, err := pathsafe.NormalizeRelativeDestination(relativePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("validate publish source path %q: %w", relativePath, err)
|
||||
}
|
||||
|
||||
root, err := os.OpenRoot(rootPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open publish source root %q: %w", rootPath, err)
|
||||
}
|
||||
openedRoots := []*os.Root{root}
|
||||
defer func() {
|
||||
for index := len(openedRoots) - 1; index >= 0; index-- {
|
||||
_ = openedRoots[index].Close()
|
||||
}
|
||||
}()
|
||||
|
||||
parts := strings.Split(relativePath, "/")
|
||||
current := root
|
||||
for _, part := range parts[:len(parts)-1] {
|
||||
info, err := current.Lstat(part)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("inspect publish source ancestor %q: %w", part, err)
|
||||
}
|
||||
if info.Mode()&os.ModeSymlink != 0 || !info.IsDir() {
|
||||
return nil, fmt.Errorf("publish source ancestor %q is not a directory without symlinks", part)
|
||||
}
|
||||
next, err := current.OpenRoot(part)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open publish source ancestor %q: %w", part, err)
|
||||
}
|
||||
openedRoots = append(openedRoots, next)
|
||||
current = next
|
||||
}
|
||||
|
||||
leaf := parts[len(parts)-1]
|
||||
info, err := current.Lstat(leaf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("inspect publish source %q: %w", relativePath, err)
|
||||
}
|
||||
if info.Mode()&os.ModeSymlink != 0 || !info.Mode().IsRegular() {
|
||||
return nil, fmt.Errorf("publish source %q is not a regular file without symlinks", relativePath)
|
||||
}
|
||||
file, err := current.Open(leaf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("open publish source %q: %w", relativePath, err)
|
||||
}
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func verifyPublishSourceChecksum(file *os.File, relativePath, expected string) error {
|
||||
if strings.TrimSpace(expected) == "" {
|
||||
return nil
|
||||
|
||||
@@ -263,7 +263,7 @@ func TestPublishRejectsSymlinkedRunRootBeforeUploading(t *testing.T) {
|
||||
assertNoPublishUploads(t, env.ObjectStore.(*storage.FakeBackend), "")
|
||||
}
|
||||
|
||||
func TestPublishUsesOrdinarySourceAncestorPathSemantics(t *testing.T) {
|
||||
func TestPublishRejectsDeclaredSourceWithSymlinkedAncestor(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("symlink creation requires privileges on Windows")
|
||||
}
|
||||
@@ -278,9 +278,11 @@ func TestPublishUsesOrdinarySourceAncestorPathSemantics(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := (publishStage{}).Run(context.Background(), env, m); err != nil {
|
||||
t.Fatalf("Run() error = %v", err)
|
||||
_, err := (publishStage{}).Run(context.Background(), env, m)
|
||||
if err == nil || !strings.Contains(err.Error(), "ancestor") {
|
||||
t.Fatalf("Run() error = %v, want rejected symlinked ancestor", err)
|
||||
}
|
||||
assertNoPublishUploads(t, env.ObjectStore.(*storage.FakeBackend), "outside secret")
|
||||
}
|
||||
|
||||
func TestPublishRejectsDeclaredSourceReplacement(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user