9 "github.com/stretchr/testify/require"
12 func TestBackupArchiveRestoresBytesAndModes(t *testing.T) {
14 require.NoError(t, os.Mkdir(filepath.Join(root, "agent"), 0o750))
15 require.NoError(t, os.WriteFile(filepath.Join(root, "agent", "instructions.md"), []byte("Keep the whole chat.\n"), 0o640))
17 archive := filepath.Join(t.TempDir(), "backup.tar.gz")
18 size, err := backupArchive(t.Context(), root, archive)
19 require.NoError(t, err)
21 info, err := os.Stat(archive)
22 require.NoError(t, err)
23 require.Equal(t, size, info.Size())
24 require.Equal(t, os.FileMode(0o600), info.Mode().Perm())
26 restored := t.TempDir()
27 require.NoError(t, backupExtract(t.Context(), archive, restored))
29 data, err := os.ReadFile(filepath.Join(restored, "agent", "instructions.md"))
30 require.NoError(t, err)
31 require.Equal(t, "Keep the whole chat.\n", string(data))
33 info, err = os.Stat(filepath.Join(restored, "agent"))
34 require.NoError(t, err)
35 require.Equal(t, os.FileMode(0o750), info.Mode().Perm())
37 info, err = os.Stat(filepath.Join(restored, "agent", "instructions.md"))
38 require.NoError(t, err)
39 require.Equal(t, os.FileMode(0o640), info.Mode().Perm())
42 func TestRestoreRejectsUnsafeArchiveEntries(t *testing.T) {
43 testCases := []struct {
48 name: "absolute paths fail",
51 Typeflag: tar.TypeReg,
56 name: "parent paths fail",
59 Typeflag: tar.TypeReg,
64 name: "embedded parent paths fail",
66 Name: "agent/../../escape",
67 Typeflag: tar.TypeReg,
72 name: "symbolic links fail",
75 Typeflag: tar.TypeSymlink,
76 Linkname: "../escape",
81 name: "hard links fail",
84 Typeflag: tar.TypeLink,
90 name: "device files fail",
93 Typeflag: tar.TypeChar,
98 name: "setuid modes fail",
101 Typeflag: tar.TypeReg,
106 name: "lock replacement fails",
109 Typeflag: tar.TypeReg,
114 for _, tc := range testCases {
115 t.Run(tc.name, func(t *testing.T) {
116 archive := backupTestArchive(t, tc.header)
117 require.Error(t, backupExtract(t.Context(), archive, t.TempDir()))
122 func TestBackupFailureKeepsTheDestination(t *testing.T) {
124 require.NoError(t, os.Symlink("missing", filepath.Join(root, "link")))
126 archive := filepath.Join(t.TempDir(), "backup.tar.gz")
127 require.NoError(t, os.WriteFile(archive, []byte("keep"), 0o600))
129 _, err := backupArchive(t.Context(), root, archive)
130 require.ErrorContains(t, err, "unsupported backup entry")
132 data, err := os.ReadFile(archive)
133 require.NoError(t, err)
134 require.Equal(t, "keep", string(data))
136 entries, err := os.ReadDir(filepath.Dir(archive))
137 require.NoError(t, err)
138 require.Len(t, entries, 1)