Harden backup paths and backup handling
This commit is contained in:
147
actions_test.go
Normal file
147
actions_test.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSafeJoinRejectsUnsafePaths(t *testing.T) {
|
||||
versionDir := t.TempDir()
|
||||
|
||||
unsafePaths := []string{
|
||||
"",
|
||||
".",
|
||||
"/outside.txt",
|
||||
"\\outside.txt",
|
||||
"..",
|
||||
filepath.Join("..", "outside.txt"),
|
||||
filepath.Join("amt", "backup", "old.txt"),
|
||||
}
|
||||
|
||||
for _, p := range unsafePaths {
|
||||
if _, _, err := safeJoin(versionDir, p); err == nil {
|
||||
t.Fatalf("safeJoin(%q) returned nil error", p)
|
||||
}
|
||||
}
|
||||
|
||||
target, cleanRel, err := safeJoin(versionDir, filepath.Join("mods", "ok.jar"))
|
||||
if err != nil {
|
||||
t.Fatalf("safeJoin valid path failed: %v", err)
|
||||
}
|
||||
if cleanRel != filepath.Join("mods", "ok.jar") {
|
||||
t.Fatalf("unexpected clean path: %q", cleanRel)
|
||||
}
|
||||
if !strings.HasPrefix(target, versionDir) {
|
||||
t.Fatalf("target %q is not under %q", target, versionDir)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackupPathDoesNotOverwriteExistingBackup(t *testing.T) {
|
||||
versionDir := t.TempDir()
|
||||
src := filepath.Join(versionDir, "mods", "config.txt")
|
||||
if err := os.MkdirAll(filepath.Dir(src), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(src, []byte("first"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
backupDir := filepath.Join("amt", "backup", "run")
|
||||
if err := backupPath(versionDir, filepath.Join("mods", "config.txt"), backupDir); err != nil {
|
||||
t.Fatalf("first backup failed: %v", err)
|
||||
}
|
||||
if err := os.WriteFile(src, []byte("second"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := backupPath(versionDir, filepath.Join("mods", "config.txt"), backupDir); err != nil {
|
||||
t.Fatalf("second backup failed: %v", err)
|
||||
}
|
||||
|
||||
first, err := os.ReadFile(filepath.Join(versionDir, backupDir, "mods", "config.txt"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
second, err := os.ReadFile(filepath.Join(versionDir, backupDir, "mods", "config.txt.1"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(first) != "first" || string(second) != "second" {
|
||||
t.Fatalf("unexpected backups: first=%q second=%q", first, second)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackupPathRejectsSelfNestedBackup(t *testing.T) {
|
||||
versionDir := t.TempDir()
|
||||
if err := os.MkdirAll(filepath.Join(versionDir, "amt", "data"), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
err := backupPath(versionDir, "amt", filepath.Join("amt", "backup", "run"))
|
||||
if err == nil {
|
||||
t.Fatal("backupPath allowed backing up a directory into itself")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnzipBacksUpOverwrittenFiles(t *testing.T) {
|
||||
versionDir := t.TempDir()
|
||||
if err := os.WriteFile(filepath.Join(versionDir, "config.txt"), []byte("old"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
zipPath := filepath.Join(versionDir, "pack.zip")
|
||||
writeTestZip(t, zipPath, map[string]string{"config.txt": "new"})
|
||||
|
||||
backupDir := filepath.Join("amt", "backup", "run")
|
||||
if err := unzipFile(zipPath, versionDir, versionDir, backupDir); err != nil {
|
||||
t.Fatalf("unzipFile failed: %v", err)
|
||||
}
|
||||
|
||||
current, err := os.ReadFile(filepath.Join(versionDir, "config.txt"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
backup, err := os.ReadFile(filepath.Join(versionDir, backupDir, "config.txt"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if string(current) != "new" || string(backup) != "old" {
|
||||
t.Fatalf("unexpected files: current=%q backup=%q", current, backup)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnzipRejectsBackupTarget(t *testing.T) {
|
||||
versionDir := t.TempDir()
|
||||
zipPath := filepath.Join(versionDir, "pack.zip")
|
||||
writeTestZip(t, zipPath, map[string]string{"amt/backup/evil.txt": "bad"})
|
||||
|
||||
err := unzipFile(zipPath, versionDir, versionDir, filepath.Join("amt", "backup", "run"))
|
||||
if err == nil {
|
||||
t.Fatal("unzipFile allowed writing into backup directory")
|
||||
}
|
||||
}
|
||||
|
||||
func writeTestZip(t *testing.T, zipPath string, files map[string]string) {
|
||||
t.Helper()
|
||||
|
||||
out, err := os.Create(zipPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
w := zip.NewWriter(out)
|
||||
defer w.Close()
|
||||
|
||||
for name, body := range files {
|
||||
f, err := w.Create(name)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := f.Write([]byte(body)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user