-
Notifications
You must be signed in to change notification settings - Fork 139
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Removed dependency on sh/tar from alpine image
This commit removes depencency on sh and tar binaries by implementing the logic in our func-util binary. Signed-off-by: Matej Vašek <[email protected]>
- Loading branch information
1 parent
079db29
commit 6ace6cc
Showing
7 changed files
with
693 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package tar | ||
|
||
import ( | ||
"archive/tar" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"io/fs" | ||
"os" | ||
"path" | ||
"path/filepath" | ||
"strings" | ||
) | ||
|
||
func Extract(input io.Reader, targetDir string) error { | ||
var err error | ||
|
||
r := tar.NewReader(input) | ||
|
||
var first bool = true | ||
for { | ||
var hdr *tar.Header | ||
hdr, err = r.Next() | ||
if err != nil { | ||
if errors.Is(err, io.EOF) { | ||
if first { | ||
// mimic tar output on empty input | ||
return fmt.Errorf("does not look like a tar") | ||
} | ||
return nil | ||
} | ||
return err | ||
} | ||
|
||
if strings.Contains(hdr.Name, "..") { | ||
return fmt.Errorf("name contains '..': %s", hdr.Name) | ||
} | ||
if path.IsAbs(hdr.Linkname) { | ||
return fmt.Errorf("absolute symlink: %s->%s", hdr.Name, hdr.Linkname) | ||
} | ||
if strings.HasPrefix(path.Clean(path.Join(path.Dir(hdr.Name), hdr.Linkname)), "..") { | ||
return fmt.Errorf("link target escapes: %s->%s", hdr.Name, hdr.Linkname) | ||
} | ||
|
||
var targetPath, rel string | ||
targetPath = filepath.Join(targetDir, filepath.FromSlash(hdr.Name)) | ||
Check failure Code scanning / CodeQL Arbitrary file write extracting an archive containing symbolic links High
Unresolved path from an archive header, which may point outside the archive root, is used in
symlink creation Error loading related location Loading |
||
rel, err = filepath.Rel(targetDir, targetPath) | ||
if err != nil { | ||
return fmt.Errorf("cannot get relative path: %w", err) | ||
} | ||
if strings.HasPrefix(rel, "..") { | ||
return fmt.Errorf("name escapes") | ||
} | ||
|
||
// remove if already exists | ||
err = os.Remove(targetPath) | ||
if err != nil && !errors.Is(err, os.ErrNotExist) { | ||
return fmt.Errorf("cannot remove: %w", err) | ||
} | ||
|
||
// ensure parent | ||
err = os.MkdirAll(filepath.Dir(targetPath), os.FileMode(hdr.Mode)&fs.ModePerm|0111) | ||
if err != nil { | ||
return fmt.Errorf("cannot ensure parent: %w", err) | ||
} | ||
|
||
first = false | ||
switch { | ||
case hdr.Typeflag == tar.TypeReg: | ||
err = writeRegularFile(targetPath, os.FileMode(hdr.Mode&0777), r) | ||
case hdr.Typeflag == tar.TypeDir: | ||
err = os.MkdirAll(targetPath, os.FileMode(hdr.Mode)&fs.ModePerm) | ||
case hdr.Typeflag == tar.TypeSymlink: | ||
err = os.Symlink(hdr.Linkname, targetPath) | ||
default: | ||
_, _ = fmt.Printf("unsupported type flag: %d\n", hdr.Typeflag) | ||
} | ||
if err != nil { | ||
return fmt.Errorf("cannot create entry: %w", err) | ||
} | ||
} | ||
} | ||
|
||
func writeRegularFile(target string, perm os.FileMode, content io.Reader) error { | ||
f, err := os.OpenFile(target, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, perm) | ||
if err != nil { | ||
return err | ||
} | ||
defer func(f *os.File) { | ||
_ = f.Close() | ||
}(f) | ||
_, err = io.Copy(f, content) | ||
if err != nil { | ||
return err | ||
} | ||
return nil | ||
Check failure Code scanning / CodeQL Arbitrary file write extracting an archive containing symbolic links High
Unresolved path from an archive header, which may point outside the archive root, is used in
symlink creation Error loading related location Loading |
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
package tar_test | ||
|
||
import ( | ||
"archive/tar" | ||
"bytes" | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
|
||
tarutil "knative.dev/func/pkg/tar" | ||
) | ||
|
||
const ( | ||
aTxt1 = "a.txt first revision" | ||
bTxt1 = "b.txt first revision" | ||
aTxt2 = "a.txt second revision" | ||
bTxt2 = "b.txt second revision" | ||
) | ||
|
||
func TestExtract(t *testing.T) { | ||
var err error | ||
d := t.TempDir() | ||
err = tarutil.Extract(tarballV1(t), d) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
bs, err := os.ReadFile(filepath.Join(d, "dir/a.txt")) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
s := string(bs) | ||
if s != aTxt1 { | ||
t.Errorf("unexpected data: %s", s) | ||
} | ||
bs, err = os.ReadFile(filepath.Join(d, "dir/b.txt")) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
s = string(bs) | ||
if s != bTxt1 { | ||
t.Errorf("unexpected data: %s", s) | ||
} | ||
|
||
err = tarutil.Extract(tarballV2(t), d) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
bs, err = os.ReadFile(filepath.Join(d, "dir/a.txt")) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
s = string(bs) | ||
if s != aTxt2 { | ||
t.Errorf("unexpected data: %s", s) | ||
} | ||
bs, err = os.ReadFile(filepath.Join(d, "dir/b.txt")) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
s = string(bs) | ||
if s != bTxt2 { | ||
t.Errorf("unexpected data: %s", s) | ||
} | ||
} | ||
|
||
func tarballV1(t *testing.T) io.Reader { | ||
t.Helper() | ||
|
||
var err error | ||
var buff bytes.Buffer | ||
|
||
w := tar.NewWriter(&buff) | ||
defer func(w *tar.Writer) { | ||
_ = w.Close() | ||
}(w) | ||
|
||
err = w.WriteHeader(&tar.Header{ | ||
Name: "dir/a.txt", | ||
Typeflag: tar.TypeReg, | ||
Mode: 0644, | ||
Size: int64(len(aTxt1)), | ||
}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
_, err = w.Write([]byte(aTxt1)) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
err = w.WriteHeader(&tar.Header{ | ||
Name: "dir/data1", | ||
Typeflag: tar.TypeReg, | ||
Mode: 0644, | ||
Size: int64(len(bTxt1)), | ||
}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
_, err = w.Write([]byte(bTxt1)) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
err = w.WriteHeader(&tar.Header{ | ||
Name: "dir/data2", | ||
Typeflag: tar.TypeReg, | ||
Mode: 0644, | ||
Size: int64(len(bTxt2)), | ||
}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
_, err = w.Write([]byte(bTxt2)) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
err = w.WriteHeader(&tar.Header{ | ||
Name: "dir/b.txt", | ||
Linkname: "data1", | ||
Typeflag: tar.TypeSymlink, | ||
}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
return &buff | ||
} | ||
|
||
func tarballV2(t *testing.T) io.Reader { | ||
t.Helper() | ||
|
||
var err error | ||
var buff bytes.Buffer | ||
|
||
w := tar.NewWriter(&buff) | ||
defer func(w *tar.Writer) { | ||
_ = w.Close() | ||
}(w) | ||
|
||
err = w.WriteHeader(&tar.Header{ | ||
Name: "dir/a.txt", | ||
Typeflag: tar.TypeReg, | ||
Mode: 0644, | ||
Size: int64(len(aTxt2)), | ||
}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
_, err = w.Write([]byte(aTxt2)) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
err = w.WriteHeader(&tar.Header{ | ||
Name: "dir/b.txt", | ||
Linkname: "data2", | ||
Typeflag: tar.TypeSymlink, | ||
}) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
return &buff | ||
} |
Oops, something went wrong.