internal/pkg: validate tar pathnames

TContext no longer validates FileArtifact ahead of time, validation outcome is instead determined after consuming the reader to EOF. All data must therefore be treated as untrusted input until the reader is closed.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
2026-02-11 00:40:54 +09:00
parent 0061d11f93
commit 05a828c474

View File

@@ -10,8 +10,7 @@ import (
"io/fs" "io/fs"
"net/http" "net/http"
"os" "os"
"path"
"hakurei.app/container/check"
) )
const ( const (
@@ -100,7 +99,6 @@ func (e DisallowedTypeflagError) Error() string {
// Cure cures the [Artifact], producing a directory located at work. // Cure cures the [Artifact], producing a directory located at work.
func (a *tarArtifact) Cure(t *TContext) (err error) { func (a *tarArtifact) Cure(t *TContext) (err error) {
temp := t.GetTempDir()
var tr io.ReadCloser var tr io.ReadCloser
if tr, err = t.Open(a.f); err != nil { if tr, err = t.Open(a.f); err != nil {
return return
@@ -137,14 +135,24 @@ func (a *tarArtifact) Cure(t *TContext) (err error) {
} }
type dirTargetPerm struct { type dirTargetPerm struct {
path *check.Absolute path string
mode fs.FileMode mode fs.FileMode
} }
var madeDirectories []dirTargetPerm var madeDirectories []dirTargetPerm
if err = os.MkdirAll(temp.String(), 0700); err != nil { if err = os.MkdirAll(t.GetTempDir().String(), 0700); err != nil {
return return
} }
var root *os.Root
if root, err = os.OpenRoot(t.GetTempDir().String()); err != nil {
return
}
defer func() {
closeErr := root.Close()
if err == nil {
err = closeErr
}
}()
var header *tar.Header var header *tar.Header
r := tar.NewReader(tr) r := tar.NewReader(tr)
@@ -158,9 +166,8 @@ func (a *tarArtifact) Cure(t *TContext) (err error) {
} }
} }
pathname := temp.Append(header.Name)
if typeflag >= '0' && typeflag <= '9' && typeflag != tar.TypeDir { if typeflag >= '0' && typeflag <= '9' && typeflag != tar.TypeDir {
if err = os.MkdirAll(pathname.Dir().String(), 0700); err != nil { if err = root.MkdirAll(path.Dir(header.Name), 0700); err != nil {
return return
} }
} }
@@ -168,8 +175,8 @@ func (a *tarArtifact) Cure(t *TContext) (err error) {
switch typeflag { switch typeflag {
case tar.TypeReg: case tar.TypeReg:
var f *os.File var f *os.File
if f, err = os.OpenFile( if f, err = root.OpenFile(
pathname.String(), header.Name,
os.O_CREATE|os.O_EXCL|os.O_WRONLY, os.O_CREATE|os.O_EXCL|os.O_WRONLY,
header.FileInfo().Mode()&0500, header.FileInfo().Mode()&0500,
); err != nil { ); err != nil {
@@ -184,26 +191,29 @@ func (a *tarArtifact) Cure(t *TContext) (err error) {
break break
case tar.TypeLink: case tar.TypeLink:
if err = os.Link( if err = root.Link(
temp.Append(header.Linkname).String(), header.Linkname,
pathname.String(), header.Name,
); err != nil { ); err != nil {
return return
} }
break break
case tar.TypeSymlink: case tar.TypeSymlink:
if err = os.Symlink(header.Linkname, pathname.String()); err != nil { if err = root.Symlink(
header.Linkname,
header.Name,
); err != nil {
return return
} }
break break
case tar.TypeDir: case tar.TypeDir:
madeDirectories = append(madeDirectories, dirTargetPerm{ madeDirectories = append(madeDirectories, dirTargetPerm{
path: pathname, path: header.Name,
mode: header.FileInfo().Mode(), mode: header.FileInfo().Mode(),
}) })
if err = os.MkdirAll(pathname.String(), 0700); err != nil { if err = root.MkdirAll(header.Name, 0700); err != nil {
return return
} }
break break
@@ -220,7 +230,7 @@ func (a *tarArtifact) Cure(t *TContext) (err error) {
} }
if err == nil { if err == nil {
for _, e := range madeDirectories { for _, e := range madeDirectories {
if err = os.Chmod(e.path.String(), e.mode&0500); err != nil { if err = root.Chmod(e.path, e.mode&0500); err != nil {
return return
} }
} }
@@ -228,6 +238,7 @@ func (a *tarArtifact) Cure(t *TContext) (err error) {
return return
} }
temp := t.GetTempDir()
if err = os.Chmod(temp.String(), 0700); err != nil { if err = os.Chmod(temp.String(), 0700); err != nil {
return return
} }