internal/pkg: optionally validate flat pathnames
All checks were successful
Test / Create distribution (push) Successful in 43s
Test / Sandbox (push) Successful in 2m34s
Test / Hakurei (push) Successful in 3m35s
Test / ShareFS (push) Successful in 3m37s
Test / Hpkg (push) Successful in 4m21s
Test / Sandbox (race detector) (push) Successful in 4m57s
Test / Hakurei (race detector) (push) Successful in 5m50s
Test / Flake checks (push) Successful in 1m42s
All checks were successful
Test / Create distribution (push) Successful in 43s
Test / Sandbox (push) Successful in 2m34s
Test / Hakurei (push) Successful in 3m35s
Test / ShareFS (push) Successful in 3m37s
Test / Hpkg (push) Successful in 4m21s
Test / Sandbox (race detector) (push) Successful in 4m57s
Test / Hakurei (race detector) (push) Successful in 5m50s
Test / Flake checks (push) Successful in 1m42s
This makes the decoder safe against untrusted input without hurting performance for a trusted stream. This should still not be called against untrusted input though. Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
@@ -8,6 +8,7 @@ import (
|
|||||||
"io/fs"
|
"io/fs"
|
||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"hakurei.app/container/check"
|
"hakurei.app/container/check"
|
||||||
@@ -52,8 +53,12 @@ func (ent *FlatEntry) Encode(w io.Writer) (n int, err error) {
|
|||||||
return w.Write(payload)
|
return w.Write(payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ErrInsecurePath is returned by [FlatEntry.Decode] if validation is requested
|
||||||
|
// and a nonlocal path is encountered in the stream.
|
||||||
|
var ErrInsecurePath = errors.New("insecure file path")
|
||||||
|
|
||||||
// Decode decodes the entry from its representation produced by Encode.
|
// Decode decodes the entry from its representation produced by Encode.
|
||||||
func (ent *FlatEntry) Decode(r io.Reader) (n int, err error) {
|
func (ent *FlatEntry) Decode(r io.Reader, validate bool) (n int, err error) {
|
||||||
var nr int
|
var nr int
|
||||||
|
|
||||||
header := make([]byte, wordSize*2)
|
header := make([]byte, wordSize*2)
|
||||||
@@ -92,6 +97,11 @@ func (ent *FlatEntry) Decode(r io.Reader) (n int, err error) {
|
|||||||
} else {
|
} else {
|
||||||
ent.Data = buf[pPathSize : pPathSize+dataSize]
|
ent.Data = buf[pPathSize : pPathSize+dataSize]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if validate && !filepath.IsLocal(ent.Path) {
|
||||||
|
err = ErrInsecurePath
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,11 +118,16 @@ type DirScanner struct {
|
|||||||
// Entry to store results in. Its address is returned by the Entry method
|
// Entry to store results in. Its address is returned by the Entry method
|
||||||
// and is updated on every call to Scan.
|
// and is updated on every call to Scan.
|
||||||
ent FlatEntry
|
ent FlatEntry
|
||||||
|
|
||||||
|
// Validate pathnames during decoding.
|
||||||
|
validate bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDirScanner returns the address of a new instance of [DirScanner] reading
|
// NewDirScanner returns the address of a new instance of [DirScanner] reading
|
||||||
// from r. The caller must no longer read from r after this function returns.
|
// from r. The caller must no longer read from r after this function returns.
|
||||||
func NewDirScanner(r io.Reader) *DirScanner { return &DirScanner{r: r} }
|
func NewDirScanner(r io.Reader, validate bool) *DirScanner {
|
||||||
|
return &DirScanner{r: r, validate: validate}
|
||||||
|
}
|
||||||
|
|
||||||
// Err returns the first non-EOF I/O error.
|
// Err returns the first non-EOF I/O error.
|
||||||
func (s *DirScanner) Err() error {
|
func (s *DirScanner) Err() error {
|
||||||
@@ -132,7 +147,7 @@ func (s *DirScanner) Scan() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var n int
|
var n int
|
||||||
n, s.err = s.ent.Decode(s.r)
|
n, s.err = s.ent.Decode(s.r, s.validate)
|
||||||
if errors.Is(s.err, io.EOF) {
|
if errors.Is(s.err, io.EOF) {
|
||||||
return n != 0
|
return n != 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ func TestFlatten(t *testing.T) {
|
|||||||
t.Fatalf("Flatten: error = %v", err)
|
t.Fatalf("Flatten: error = %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s := pkg.NewDirScanner(bytes.NewReader(buf.Bytes()))
|
s := pkg.NewDirScanner(bytes.NewReader(buf.Bytes()), true)
|
||||||
var got []pkg.FlatEntry
|
var got []pkg.FlatEntry
|
||||||
for s.Scan() {
|
for s.Scan() {
|
||||||
got = append(got, *s.Entry())
|
got = append(got, *s.Entry())
|
||||||
|
|||||||
Reference in New Issue
Block a user