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) {
|
func openPublishRunFiles(runRoot string, files []publishUploadFile) ([]*os.File, error) {
|
||||||
sources := make([]*os.File, 0, len(files))
|
sources := make([]*os.File, 0, len(files))
|
||||||
for _, candidate := range files {
|
for _, candidate := range files {
|
||||||
file, err := openPublishOutputSource(filepath.Join(runRoot, filepath.FromSlash(candidate.SourceRelativePath)))
|
file, err := openPublishSourceUnderRoot(runRoot, candidate.SourceRelativePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
closePublishSources(sources)
|
closePublishSources(sources)
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -993,6 +993,56 @@ func openPublishRunFiles(runRoot string, files []publishUploadFile) ([]*os.File,
|
|||||||
return sources, nil
|
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 {
|
func verifyPublishSourceChecksum(file *os.File, relativePath, expected string) error {
|
||||||
if strings.TrimSpace(expected) == "" {
|
if strings.TrimSpace(expected) == "" {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -263,7 +263,7 @@ func TestPublishRejectsSymlinkedRunRootBeforeUploading(t *testing.T) {
|
|||||||
assertNoPublishUploads(t, env.ObjectStore.(*storage.FakeBackend), "")
|
assertNoPublishUploads(t, env.ObjectStore.(*storage.FakeBackend), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPublishUsesOrdinarySourceAncestorPathSemantics(t *testing.T) {
|
func TestPublishRejectsDeclaredSourceWithSymlinkedAncestor(t *testing.T) {
|
||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
t.Skip("symlink creation requires privileges on Windows")
|
t.Skip("symlink creation requires privileges on Windows")
|
||||||
}
|
}
|
||||||
@@ -278,9 +278,11 @@ func TestPublishUsesOrdinarySourceAncestorPathSemantics(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := (publishStage{}).Run(context.Background(), env, m); err != nil {
|
_, err := (publishStage{}).Run(context.Background(), env, m)
|
||||||
t.Fatalf("Run() error = %v", err)
|
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) {
|
func TestPublishRejectsDeclaredSourceReplacement(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user