mirror of
https://github.com/anchore/syft.git
synced 2026-10-30 14:02:26 +01:00
fix(archive): use writable permissions for extracted directories
Archives may contain directory entries with read-only permissions (e.g., META-INF/ with mode 0555 in JARs). When extracted to temp directories, these restricted permissions prevented writing files into those directories, causing "permission denied" errors for subsequent file entries like META-INF/MANIFEST.MF. Always use 0755 for extracted directories since they are temporary working directories owned by the extraction process. Signed-off-by: Thomas Bechtold <thomasbechtold@jpberlin.de>
This commit is contained in:
@@ -113,7 +113,7 @@ func (z *ZipExtractor) Extract(ctx context.Context, path string, destDir string,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.MkdirAll(joinedPath, file.Mode())
|
||||
return os.MkdirAll(joinedPath, 0o755)
|
||||
}
|
||||
|
||||
if limits.MaxFileCount > 0 && result.FilesExtracted >= limits.MaxFileCount {
|
||||
@@ -180,7 +180,7 @@ func (t *TarExtractor) Extract(ctx context.Context, path string, destDir string,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.MkdirAll(joinedPath, file.Mode())
|
||||
return os.MkdirAll(joinedPath, 0o755)
|
||||
}
|
||||
|
||||
if limits.MaxFileCount > 0 && result.FilesExtracted >= limits.MaxFileCount {
|
||||
|
||||
@@ -430,3 +430,96 @@ func TestTarExtractor_Extract_AbsoluteSymlinkTargetIsSkipped(t *testing.T) {
|
||||
_, statErr := os.Lstat(filepath.Join(destDir, "shadow"))
|
||||
assert.True(t, os.IsNotExist(statErr), "expected absolute-target symlink to be skipped")
|
||||
}
|
||||
|
||||
func TestZipExtractor_Extract_ReadOnlyDirectoryPermissions(t *testing.T) {
|
||||
// JARs may contain directory entries with read-only permissions (e.g., META-INF/ with mode 0o555).
|
||||
// The extractor must still be able to write files into those directories.
|
||||
dir := t.TempDir()
|
||||
ctx := context.Background()
|
||||
|
||||
zipPath := filepath.Join(dir, "readonly-dirs.zip")
|
||||
f, err := os.Create(zipPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
w := zip.NewWriter(f)
|
||||
|
||||
// create a directory entry with read-only permissions (0o555)
|
||||
dirHeader := &zip.FileHeader{
|
||||
Name: "META-INF/",
|
||||
}
|
||||
dirHeader.SetMode(0o555)
|
||||
_, err = w.CreateHeader(dirHeader)
|
||||
require.NoError(t, err)
|
||||
|
||||
// create a file inside that directory
|
||||
fileHeader := &zip.FileHeader{
|
||||
Name: "META-INF/MANIFEST.MF",
|
||||
}
|
||||
fileHeader.SetMode(0o644)
|
||||
fw, err := w.CreateHeader(fileHeader)
|
||||
require.NoError(t, err)
|
||||
_, err = fw.Write([]byte("Manifest-Version: 1.0"))
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, w.Close())
|
||||
require.NoError(t, f.Close())
|
||||
|
||||
ext := &ZipExtractor{}
|
||||
destDir := filepath.Join(dir, "extracted")
|
||||
require.NoError(t, os.MkdirAll(destDir, 0o755))
|
||||
|
||||
result, err := ext.Extract(ctx, zipPath, destDir, ExtractionLimits{})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, result.FilesExtracted)
|
||||
|
||||
content, err := os.ReadFile(filepath.Join(destDir, "META-INF", "MANIFEST.MF"))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Manifest-Version: 1.0", string(content))
|
||||
}
|
||||
|
||||
func TestTarExtractor_Extract_ReadOnlyDirectoryPermissions(t *testing.T) {
|
||||
// Tar archives may contain directory entries with read-only permissions.
|
||||
// The extractor must still be able to write files into those directories.
|
||||
dir := t.TempDir()
|
||||
ctx := context.Background()
|
||||
|
||||
tarPath := filepath.Join(dir, "readonly-dirs.tar.gz")
|
||||
f, err := os.Create(tarPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
gw := gzip.NewWriter(f)
|
||||
tw := tar.NewWriter(gw)
|
||||
|
||||
// write a directory entry with read-only permissions
|
||||
require.NoError(t, tw.WriteHeader(&tar.Header{
|
||||
Name: "META-INF/",
|
||||
Typeflag: tar.TypeDir,
|
||||
Mode: 0o555,
|
||||
}))
|
||||
|
||||
// write a file inside that directory
|
||||
content := []byte("Manifest-Version: 1.0")
|
||||
require.NoError(t, tw.WriteHeader(&tar.Header{
|
||||
Name: "META-INF/MANIFEST.MF",
|
||||
Mode: 0o644,
|
||||
Size: int64(len(content)),
|
||||
}))
|
||||
_, err = tw.Write(content)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoError(t, tw.Close())
|
||||
require.NoError(t, gw.Close())
|
||||
require.NoError(t, f.Close())
|
||||
|
||||
ext := &TarExtractor{}
|
||||
destDir := filepath.Join(dir, "extracted")
|
||||
require.NoError(t, os.MkdirAll(destDir, 0o755))
|
||||
|
||||
result, err := ext.Extract(ctx, tarPath, destDir, ExtractionLimits{})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, result.FilesExtracted)
|
||||
|
||||
extracted, err := os.ReadFile(filepath.Join(destDir, "META-INF", "MANIFEST.MF"))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Manifest-Version: 1.0", string(extracted))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user