fix: harden workspace preprocessing contract
This commit is contained in:
@@ -122,7 +122,14 @@ func writeCanonicalExclusive(path string, contents []byte, mode fs.FileMode) err
|
||||
if err := ValidateCanonicalPath(path); err != nil || len(contents) > 16<<20 || mode.Perm() == 0 || mode.Perm()&0o077 != 0 {
|
||||
return ErrUnsafeFile
|
||||
}
|
||||
h, err := windows.CreateFile(windows.StringToUTF16Ptr(path), windows.GENERIC_WRITE, 0, nil, windows.CREATE_NEW, windows.FILE_ATTRIBUTE_NORMAL|windows.FILE_FLAG_OPEN_REPARSE_POINT, 0)
|
||||
parent, retainedParents, err := openWindowsParents(path)
|
||||
if err != nil {
|
||||
return ErrUnsafeFile
|
||||
}
|
||||
defer closeWindowsHandles(retainedParents)
|
||||
// Parent handles stay open with delete sharing denied until publication and
|
||||
// identity recheck complete; this is the Windows equivalent of retained dirfds.
|
||||
h, err := windows.CreateFile(windows.StringToUTF16Ptr(filepath.Join(parent, filepath.Base(path))), windows.GENERIC_WRITE, 0, nil, windows.CREATE_NEW, windows.FILE_ATTRIBUTE_NORMAL|windows.FILE_FLAG_OPEN_REPARSE_POINT, 0)
|
||||
if err != nil {
|
||||
return ErrUnsafeFile
|
||||
}
|
||||
@@ -132,6 +139,9 @@ func writeCanonicalExclusive(path string, contents []byte, mode fs.FileMode) err
|
||||
return ErrUnsafeFile
|
||||
}
|
||||
defer f.Close()
|
||||
if err := f.Chmod(mode); err != nil {
|
||||
return ErrUnsafeFile
|
||||
}
|
||||
if _, err := f.Write(contents); err != nil {
|
||||
return ErrUnsafeFile
|
||||
}
|
||||
@@ -153,26 +163,37 @@ func writeCanonicalExclusive(path string, contents []byte, mode fs.FileMode) err
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateCanonicalOutputPath(path string) error {
|
||||
if err := ValidateCanonicalPath(path); err != nil {
|
||||
return err
|
||||
}
|
||||
func openWindowsParents(path string) (string, []windows.Handle, error) {
|
||||
volume := filepath.VolumeName(path)
|
||||
root := volume + string(filepath.Separator)
|
||||
components := strings.Split(strings.TrimPrefix(path, root), string(filepath.Separator))
|
||||
if volume == "" || len(components) < 2 || components[0] == "" {
|
||||
return ErrUnsafeFile
|
||||
return "", nil, ErrUnsafeFile
|
||||
}
|
||||
current := root
|
||||
parents := make([]windows.Handle, 0, len(components)-1)
|
||||
for _, component := range components[:len(components)-1] {
|
||||
current = filepath.Join(current, component)
|
||||
h, err := openWindowsComponent(current, true)
|
||||
if err != nil {
|
||||
return ErrUnsafeFile
|
||||
closeWindowsHandles(parents)
|
||||
return "", nil, ErrUnsafeFile
|
||||
}
|
||||
windows.CloseHandle(h)
|
||||
parents = append(parents, h)
|
||||
}
|
||||
if _, err := os.Lstat(filepath.Join(current, components[len(components)-1])); err == nil || !os.IsNotExist(err) {
|
||||
return current, parents, nil
|
||||
}
|
||||
|
||||
func validateCanonicalOutputPath(path string) error {
|
||||
if err := ValidateCanonicalPath(path); err != nil {
|
||||
return err
|
||||
}
|
||||
current, parents, err := openWindowsParents(path)
|
||||
if err != nil {
|
||||
return ErrUnsafeFile
|
||||
}
|
||||
defer closeWindowsHandles(parents)
|
||||
if _, err := os.Lstat(filepath.Join(current, filepath.Base(path))); err == nil || !os.IsNotExist(err) {
|
||||
return ErrUnsafeFile
|
||||
}
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user