fix(DB): validates db name for path traversal

This commit is contained in:
Lewis Wynne 2025-12-18 15:04:32 +00:00
parent 3e3c55d0b6
commit e806bd9046
4 changed files with 45 additions and 2 deletions

View file

@ -51,6 +51,9 @@ func ParseKey(raw string, defaults bool) (KeySpec, error) {
if strings.TrimSpace(rawDB) == "" { if strings.TrimSpace(rawDB) == "" {
return KeySpec{}, fmt.Errorf("bad key format, use KEY@DB") return KeySpec{}, fmt.Errorf("bad key format, use KEY@DB")
} }
if err := validateDBName(rawDB); err != nil {
return KeySpec{}, err
}
} }
key := strings.ToLower(rawKey) key := strings.ToLower(rawKey)

View file

@ -178,6 +178,9 @@ func (s *Store) parseDB(v string, defaults bool) (string, error) {
} }
return "", fmt.Errorf("cannot parse db: bad db format, use DB or @DB") return "", fmt.Errorf("cannot parse db: bad db format, use DB or @DB")
} }
if err := validateDBName(db); err != nil {
return "", fmt.Errorf("cannot parse db: %w", err)
}
return strings.ToLower(db), nil return strings.ToLower(db), nil
} }
@ -197,7 +200,11 @@ func (s *Store) path(args ...string) (string, error) {
if err := os.MkdirAll(override, 0o750); err != nil { if err := os.MkdirAll(override, 0o750); err != nil {
return "", err return "", err
} }
return filepath.Join(append([]string{override}, args...)...), nil target := filepath.Join(append([]string{override}, args...)...)
if err := ensureSubpath(override, target); err != nil {
return "", err
}
return target, nil
} }
scope := gap.NewVendorScope(gap.User, "pda", "stores") scope := gap.NewVendorScope(gap.User, "pda", "stores")
dir, err := scope.DataPath("") dir, err := scope.DataPath("")
@ -207,7 +214,11 @@ func (s *Store) path(args ...string) (string, error) {
if err := os.MkdirAll(dir, 0o750); err != nil { if err := os.MkdirAll(dir, 0o750); err != nil {
return "", err return "", err
} }
return filepath.Join(append([]string{dir}, args...)...), nil target := filepath.Join(append([]string{dir}, args...)...)
if err := ensureSubpath(dir, target); err != nil {
return "", err
}
return target, nil
} }
func (s *Store) suggestStores(target string) ([]string, error) { func (s *Store) suggestStores(target string) ([]string, error) {
@ -229,6 +240,33 @@ func (s *Store) suggestStores(target string) ([]string, error) {
return suggestions, nil return suggestions, nil
} }
func ensureSubpath(base, target string) error {
absBase, err := filepath.Abs(base)
if err != nil {
return err
}
absTarget, err := filepath.Abs(target)
if err != nil {
return err
}
rel, err := filepath.Rel(absBase, absTarget)
if err != nil {
return err
}
sep := string(filepath.Separator)
if rel == ".." || strings.HasPrefix(rel, ".."+sep) {
return fmt.Errorf("path escapes store root")
}
return nil
}
func validateDBName(name string) error {
if strings.ContainsAny(name, `/\`) {
return fmt.Errorf("bad db format, use DB or @DB")
}
return nil
}
func formatExpiry(expiresAt uint64) string { func formatExpiry(expiresAt uint64) string {
if expiresAt == 0 { if expiresAt == 0 {
return "never" return "never"

View file

@ -19,6 +19,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
*/ */
package main package main
import "github.com/llywelwyn/pda/cmd" import "github.com/llywelwyn/pda/cmd"

View file

@ -19,6 +19,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
*/ */
package main package main
import ( import (