Record file mode in the manifest, 0000 unless asked (closes #161)
check / check (push) Failing after 59s
check / check (push) Failing after 59s
MFFilePath gains mode (field 304): a file's permission bits, 0777 at most, or 0000, meaning none recorded. gen and freshen record real modes only with --include-permissions (ScannerOptions.IncludePermissions); the builder keeps only mode.Perm(), so setuid, setgid and sticky are never written. list -l and export show the mode in octal. check reports MODE_MISMATCH for a recorded mode other than 0000 the file lacks. fetch refuses a manifest with a mode above 0777 before requesting any file, sets each recorded mode on the files it writes, and downloads again a present file whose mode differs. The decoding-cost bound counts a file entry at 176 bytes, up from 160. Model: opus-5-5
This commit is contained in:
+38
-8
@@ -95,6 +95,9 @@ var (
|
||||
// writes another file.
|
||||
errNameClash = errors.New(
|
||||
"manifest lists a file where fetch writes another file")
|
||||
// errModeOutOfRange indicates a manifest that lists a mode with more
|
||||
// than the permission bits, such as setuid.
|
||||
errModeOutOfRange = errors.New("manifest lists a mode outside 0777")
|
||||
)
|
||||
|
||||
// DownloadProgress reports the progress of a single file download.
|
||||
@@ -292,11 +295,11 @@ func downloadManifestFiles(
|
||||
}
|
||||
|
||||
// alreadyPresent reports whether localPath under dest is a regular file
|
||||
// with the size and one of the hashes the manifest lists for entry. It
|
||||
// hashes the whole file, since a matching size alone would accept a
|
||||
// corrupted or partly written one. A file it cannot read, or reaches only
|
||||
// through a symlink, is not present: fetch downloads it, and the download
|
||||
// reports the problem.
|
||||
// with the size, the recorded mode if any, and one of the hashes the
|
||||
// manifest lists for entry. It hashes the whole file, since a matching
|
||||
// size alone would accept a corrupted or partly written one. A file it
|
||||
// cannot read, or reaches only through a symlink, is not present: fetch
|
||||
// downloads it, and the download reports the problem.
|
||||
func alreadyPresent(dest, localPath string, entry *mfer.MFFilePath) bool {
|
||||
if checkNoSymlinks(dest, localPath) != nil {
|
||||
return false
|
||||
@@ -309,6 +312,11 @@ func alreadyPresent(dest, localPath string, entry *mfer.MFFilePath) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// A recorded mode of 0 means none was recorded.
|
||||
if entry.GetMode() != 0 && info.Mode().Perm() != os.FileMode(entry.GetMode()) {
|
||||
return false
|
||||
}
|
||||
|
||||
// G304: localPath is a relative path that sanitizePath keeps inside
|
||||
// dest as text, and checkNoSymlinks just found no symlink in it.
|
||||
f, err := os.Open(path) //nolint:gosec // G304: see comment above
|
||||
@@ -414,9 +422,9 @@ func (mfa *CLIApp) fetchManifestOperation(
|
||||
|
||||
// fetchManifest downloads the manifest at manifestURL and parses it,
|
||||
// enforcing --require-signature if it is given and refusing a manifest
|
||||
// that lists a file where fetch writes another. It returns the manifest as
|
||||
// downloaded, to be saved once the files are in place, and the files it
|
||||
// lists.
|
||||
// that lists a file where fetch writes another or a mode outside 0777. It
|
||||
// returns the manifest as downloaded, to be saved once the files are in
|
||||
// place, and the files it lists.
|
||||
func fetchManifest(
|
||||
ctx context.Context, cmd *cli.Command, client retryingClient, manifestURL string,
|
||||
) ([]byte, []*mfer.MFFilePath, error) {
|
||||
@@ -460,6 +468,16 @@ func fetchManifest(
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// fetch sets each recorded mode on the file it writes, so a mode above
|
||||
// 0777, which could carry setuid, setgid or sticky bits, is refused
|
||||
// before any file is requested.
|
||||
for _, f := range files {
|
||||
if f.GetMode() > uint32(os.ModePerm) {
|
||||
return nil, nil, fmt.Errorf("%w: %s (%#o)",
|
||||
errModeOutOfRange, f.GetPath(), f.GetMode())
|
||||
}
|
||||
}
|
||||
|
||||
log.Infof("manifest contains %d files", len(files))
|
||||
|
||||
return manifestData, files, nil
|
||||
@@ -866,6 +884,18 @@ func saveResponse(
|
||||
return err
|
||||
}
|
||||
|
||||
// A recorded mode of 0 means none was recorded. Any other is set as
|
||||
// it is, whatever the umask: fetchManifest refused modes above 0777.
|
||||
if entry.GetMode() != 0 {
|
||||
err = out.Chmod(os.FileMode(entry.GetMode()))
|
||||
if err != nil {
|
||||
_ = out.Close()
|
||||
_ = os.Remove(filepath.Join(dest, tmpPath))
|
||||
|
||||
return fmt.Errorf("failed to set mode: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Set up hash computation
|
||||
h := sha256.New()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user