X-Git-Url: https://ruderich.org/simon/gitweb/?a=blobdiff_plain;f=remote%2Fsync%2Ffiles_test.go;h=7a4ce260f487b21d956b60600c8d946e8074dd10;hb=HEAD;hp=39714594382445369936666719d625d4351cd820;hpb=9269fa3c94e700afc0be823f58ea473a2db8f3dc;p=safcm%2Fsafcm.git diff --git a/remote/sync/files_test.go b/remote/sync/files_test.go index 3971459..7a4ce26 100644 --- a/remote/sync/files_test.go +++ b/remote/sync/files_test.go @@ -1,17 +1,5 @@ -// Copyright (C) 2021 Simon Ruderich -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// SPDX-License-Identifier: GPL-3.0-or-later +// Copyright (C) 2021-2024 Simon Ruderich package sync @@ -36,7 +24,7 @@ func TestSyncFiles(t *testing.T) { if err != nil { t.Fatal(err) } - defer os.Chdir(cwd) + defer os.Chdir(cwd) //nolint:errcheck err = os.RemoveAll("testdata") if err != nil { @@ -81,26 +69,26 @@ func TestSyncFiles(t *testing.T) { safcm.MsgSyncReq{ Files: map[string]*safcm.File{ ".": { + OrigGroup: "group", Path: ".", Mode: fs.ModeDir | 0700, Uid: -1, Gid: -1, - OrigGroup: "group", }, "dir": { + OrigGroup: "group", Path: "dir", Mode: fs.ModeDir | 0755, Uid: -1, Gid: -1, - OrigGroup: "group", }, "dir/file": { + OrigGroup: "group", Path: "dir/file", Mode: 0644, Uid: -1, Gid: -1, Data: []byte("content\n"), - OrigGroup: "group", }, }, }, @@ -165,26 +153,26 @@ func TestSyncFiles(t *testing.T) { safcm.MsgSyncReq{ Files: map[string]*safcm.File{ ".": { + OrigGroup: "group", Path: ".", Mode: fs.ModeDir | 0700, Uid: -1, Gid: -1, - OrigGroup: "group", }, "dir": { + OrigGroup: "group", Path: "dir", Mode: fs.ModeDir | 0755, Uid: -1, Gid: -1, - OrigGroup: "group", }, "dir/file": { + OrigGroup: "group", Path: "dir/file", Mode: 0644, Uid: -1, Gid: -1, Data: []byte("content\n"), - OrigGroup: "group", }, }, }, @@ -220,12 +208,12 @@ func TestSyncFiles(t *testing.T) { safcm.MsgSyncReq{ Files: map[string]*safcm.File{ ".": { + OrigGroup: "group", Path: ".", Mode: fs.ModeDir | 0700, User: "user", Uid: 1, Gid: -1, - OrigGroup: "group", }, }, }, @@ -244,12 +232,12 @@ func TestSyncFiles(t *testing.T) { safcm.MsgSyncReq{ Files: map[string]*safcm.File{ ".": { + OrigGroup: "group", Path: ".", Mode: fs.ModeDir | 0700, Uid: -1, Group: "group", Gid: 1, - OrigGroup: "group", }, }, }, @@ -276,25 +264,25 @@ func TestSyncFiles(t *testing.T) { safcm.MsgSyncReq{ Files: map[string]*safcm.File{ "/": { + OrigGroup: "group", Path: "/", Mode: fs.ModeDir | 0755, Uid: 0, Gid: 0, - OrigGroup: "group", }, "/etc": { + OrigGroup: "group", Path: "/etc", Mode: fs.ModeDir | 0755, Uid: 0, Gid: 0, - OrigGroup: "group", }, "/tmp": { + OrigGroup: "group", Path: "/tmp", Mode: fs.ModeDir | 0777 | fs.ModeSticky, Uid: 0, Gid: 0, - OrigGroup: "group", }, }, }, @@ -318,32 +306,32 @@ func TestSyncFiles(t *testing.T) { safcm.MsgSyncReq{ Files: map[string]*safcm.File{ ".": { + OrigGroup: "group", Path: ".", Mode: fs.ModeDir | 0700, Uid: -1, Gid: -1, - OrigGroup: "group", TriggerCommands: []string{ "echo trigger .", }, }, "dir": { + OrigGroup: "group", Path: "dir", Mode: fs.ModeDir | 0755, Uid: -1, Gid: -1, - OrigGroup: "group", TriggerCommands: []string{ "echo trigger dir", }, }, "dir/file": { + OrigGroup: "group", Path: "dir/file", Mode: 0644, Uid: -1, Gid: -1, Data: []byte("content\n"), - OrigGroup: "group", TriggerCommands: []string{ "echo trigger dir/file", }, @@ -382,32 +370,32 @@ func TestSyncFiles(t *testing.T) { safcm.MsgSyncReq{ Files: map[string]*safcm.File{ ".": { + OrigGroup: "group", Path: ".", Mode: fs.ModeDir | 0700, Uid: -1, Gid: -1, - OrigGroup: "group", TriggerCommands: []string{ "echo trigger .", }, }, "dir": { + OrigGroup: "group", Path: "dir", Mode: fs.ModeDir | 0755, Uid: -1, Gid: -1, - OrigGroup: "group", TriggerCommands: []string{ "echo trigger dir", }, }, "dir/file": { + OrigGroup: "group", Path: "dir/file", Mode: 0644, Uid: -1, Gid: -1, Data: []byte("content\n"), - OrigGroup: "group", TriggerCommands: []string{ "echo trigger dir/file", }, @@ -472,32 +460,32 @@ func TestSyncFiles(t *testing.T) { safcm.MsgSyncReq{ Files: map[string]*safcm.File{ ".": { + OrigGroup: "group", Path: ".", Mode: fs.ModeDir | 0700, Uid: -1, Gid: -1, - OrigGroup: "group", TriggerCommands: []string{ "echo trigger .", }, }, "dir": { + OrigGroup: "group", Path: "dir", Mode: fs.ModeDir | 0755, Uid: -1, Gid: -1, - OrigGroup: "group", TriggerCommands: []string{ "echo trigger dir", }, }, "dir/file": { + OrigGroup: "group", Path: "dir/file", Mode: 0644, Uid: -1, Gid: -1, Data: []byte("content\n"), - OrigGroup: "group", TriggerCommands: []string{ "echo trigger dir/file", }, @@ -563,32 +551,32 @@ func TestSyncFiles(t *testing.T) { safcm.MsgSyncReq{ Files: map[string]*safcm.File{ ".": { + OrigGroup: "group", Path: ".", Mode: fs.ModeDir | 0700, Uid: -1, Gid: -1, - OrigGroup: "group", TriggerCommands: []string{ "echo trigger .", }, }, "dir": { + OrigGroup: "group", Path: "dir", Mode: fs.ModeDir | 0755, Uid: -1, Gid: -1, - OrigGroup: "group", TriggerCommands: []string{ "echo trigger dir", }, }, "dir/file": { + OrigGroup: "group", Path: "dir/file", Mode: 0644, Uid: -1, Gid: -1, Data: []byte("content\n"), - OrigGroup: "group", TriggerCommands: []string{ "echo trigger dir/file", }, @@ -650,32 +638,32 @@ func TestSyncFiles(t *testing.T) { safcm.MsgSyncReq{ Files: map[string]*safcm.File{ ".": { + OrigGroup: "group", Path: ".", Mode: fs.ModeDir | 0700, Uid: -1, Gid: -1, - OrigGroup: "group", TriggerCommands: []string{ "echo trigger .", }, }, "dir": { + OrigGroup: "group", Path: "dir", Mode: fs.ModeDir | 0755, Uid: -1, Gid: -1, - OrigGroup: "group", TriggerCommands: []string{ "echo trigger dir", }, }, "dir/file": { + OrigGroup: "group", Path: "dir/file", Mode: 0644, Uid: -1, Gid: -1, Data: []byte("content\n"), - OrigGroup: "group", TriggerCommands: []string{ "echo trigger dir/file", }, @@ -752,31 +740,31 @@ func TestSyncFiles(t *testing.T) { safcm.MsgSyncReq{ Files: map[string]*safcm.File{ "/": { + OrigGroup: "group", Path: "/", Mode: fs.ModeDir | 0755, Uid: 0, Gid: 0, - OrigGroup: "group", TriggerCommands: []string{ "echo trigger /", }, }, "/tmp": { + OrigGroup: "group", Path: "/tmp", Mode: fs.ModeDir | 0777 | fs.ModeSticky, Uid: 0, Gid: 0, - OrigGroup: "group", TriggerCommands: []string{ "echo trigger /tmp", }, }, tmpTestFilePath: { + OrigGroup: "group", Path: tmpTestFilePath, Mode: 0600, Uid: -1, Gid: -1, - OrigGroup: "group", TriggerCommands: []string{ "echo trigger /tmp/file", }, @@ -887,7 +875,7 @@ func TestSyncFile(t *testing.T) { if err != nil { t.Fatal(err) } - defer os.Chdir(cwd) + defer os.Chdir(cwd) //nolint:errcheck err = os.RemoveAll("testdata") if err != nil { @@ -963,7 +951,7 @@ func TestSyncFile(t *testing.T) { `4: files: "file" (group): will create`, `3: files: "file" (group): creating`, `4: files: "file" (group): creating temporary file ".file*"`, - `4: files: "file" (group): renaming "./.fileRND"`, + `4: files: "file" (group): renaming ".fileRND"`, }, nil, }, @@ -1006,6 +994,44 @@ func TestSyncFile(t *testing.T) { nil, }, + { + "file: create, missing parent (dry-run)", + safcm.MsgSyncReq{ + DryRun: true, + }, + &safcm.File{ + Path: "does-not-exist/file", + Mode: 0644, + Uid: -1, + Gid: -1, + Data: []byte("content\n"), + OrigGroup: "group", + }, + nil, + true, + []ft.File{root}, + safcm.MsgSyncResp{ + FileChanges: []safcm.FileChange{ + { + Path: "does-not-exist/file", + Created: true, + New: safcm.FileChangeInfo{ + Mode: 0644, + User: user, + Uid: uid, + Group: group, + Gid: gid, + }, + }, + }, + }, + []string{ + `4: files: "does-not-exist/file" (group): will create (parent missing)`, + `4: files: "does-not-exist/file" (group): dry-run, skipping changes`, + }, + nil, + }, + { "file: unchanged", safcm.MsgSyncReq{}, @@ -1116,7 +1142,7 @@ func TestSyncFile(t *testing.T) { `4: files: "file" (group): permission differs -rwxr-xr-x -> urwxr-xr-x`, `3: files: "file" (group): updating`, `4: files: "file" (group): creating temporary file ".file*"`, - `4: files: "file" (group): renaming "./.fileRND"`, + `4: files: "file" (group): renaming ".fileRND"`, }, nil, }, @@ -1174,7 +1200,7 @@ func TestSyncFile(t *testing.T) { `4: files: "file" (group): content differs`, `3: files: "file" (group): updating`, `4: files: "file" (group): creating temporary file ".file*"`, - `4: files: "file" (group): renaming "./.fileRND"`, + `4: files: "file" (group): renaming ".fileRND"`, }, nil, }, @@ -1486,6 +1512,43 @@ func TestSyncFile(t *testing.T) { nil, }, + { + "directory: create, missing parent (dry-run)", + safcm.MsgSyncReq{ + DryRun: true, + }, + &safcm.File{ + Path: "does-not-exist/dir", + Mode: fs.ModeDir | 0755, + Uid: -1, + Gid: -1, + OrigGroup: "group", + }, + nil, + true, + []ft.File{root}, + safcm.MsgSyncResp{ + FileChanges: []safcm.FileChange{ + { + Path: "does-not-exist/dir", + Created: true, + New: safcm.FileChangeInfo{ + Mode: fs.ModeDir | 0755, + User: user, + Uid: uid, + Group: group, + Gid: gid, + }, + }, + }, + }, + []string{ + `4: files: "does-not-exist/dir" (group): will create (parent missing)`, + `4: files: "does-not-exist/dir" (group): dry-run, skipping changes`, + }, + nil, + }, + { "directory: unchanged", safcm.MsgSyncReq{}, @@ -1734,7 +1797,7 @@ func TestSyncFile(t *testing.T) { `4: files: "path" (group): type differs L--------- -> ----------`, `3: files: "path" (group): updating`, `4: files: "path" (group): creating temporary file ".path*"`, - `4: files: "path" (group): renaming "./.pathRND"`, + `4: files: "path" (group): renaming ".pathRND"`, }, nil, }, @@ -1845,10 +1908,66 @@ func TestSyncFile(t *testing.T) { `3: files: "path" (group): updating`, `4: files: "path" (group): removing (due to type change)`, `4: files: "path" (group): creating temporary file ".path*"`, - `4: files: "path" (group): renaming "./.pathRND"`, + `4: files: "path" (group): renaming ".pathRND"`, }, nil, }, + { + "change: directory to file (non-empty)", + safcm.MsgSyncReq{}, + &safcm.File{ + Path: "path", + Mode: 0666, + Uid: -1, + Gid: -1, + OrigGroup: "group", + Data: []byte("content\n"), + }, + func() { + ft.CreateDirectory("path", 0777) + ft.CreateFile("path/file", "content\n", 0644) + }, + true, + []ft.File{ + root, + { + Path: "path", + Mode: fs.ModeDir | 0777, + }, + { + Path: "path/file", + Mode: 0644, + Data: []byte("content\n"), + }, + }, + safcm.MsgSyncResp{ + FileChanges: []safcm.FileChange{ + { + Path: "path", + Old: safcm.FileChangeInfo{ + Mode: fs.ModeDir | 0777, + User: user, + Uid: uid, + Group: group, + Gid: gid, + }, + New: safcm.FileChangeInfo{ + Mode: 0666, + User: user, + Uid: uid, + Group: group, + Gid: gid, + }, + }, + }, + }, + []string{ + `4: files: "path" (group): type differs d--------- -> ----------`, + `3: files: "path" (group): updating`, + `4: files: "path" (group): removing (due to type change)`, + }, + fmt.Errorf("will not replace non-empty directory, please remove manually"), + }, { "change: directory to symlink", @@ -1952,7 +2071,7 @@ func TestSyncFile(t *testing.T) { `4: files: "path" (group): type differs p--------- -> ----------`, `3: files: "path" (group): updating`, `4: files: "path" (group): creating temporary file ".path*"`, - `4: files: "path" (group): renaming "./.pathRND"`, + `4: files: "path" (group): renaming ".pathRND"`, }, nil, }, @@ -2116,6 +2235,94 @@ func TestSyncFile(t *testing.T) { nil, }, + // Symlink "attacks" + + { + "symlink in earlier path component", + safcm.MsgSyncReq{}, + &safcm.File{ + Path: "dir/file", + Mode: 0644, + Uid: -1, + Gid: -1, + OrigGroup: "group", + Data: []byte("content"), + }, + func() { + ft.CreateDirectory("tmp", 0755) + ft.CreateSymlink("dir", "tmp") + }, + false, + []ft.File{ + root, + { + Path: "dir", + Mode: fs.ModeSymlink | 0777, + Data: []byte("tmp"), + }, + { + Path: "tmp", + Mode: fs.ModeDir | 0755, + }, + }, + safcm.MsgSyncResp{}, + nil, + fmt.Errorf("symlink not permitted in path: \"dir\""), + }, + + // Border cases + + { + "relative path with leading dot", + safcm.MsgSyncReq{}, + &safcm.File{ + Path: "./dir/file", + Mode: 0644, + Uid: -1, + Gid: -1, + OrigGroup: "group", + Data: []byte("content"), + }, + func() { + ft.CreateDirectory("dir", 0755) + }, + true, + []ft.File{ + root, + { + Path: "dir", + Mode: fs.ModeDir | 0755, + }, + { + Path: "dir/file", + Mode: 0644, + Data: []byte("content"), + }, + }, + safcm.MsgSyncResp{ + FileChanges: []safcm.FileChange{ + { + Path: "./dir/file", + Created: true, + New: safcm.FileChangeInfo{ + Mode: 0644, + User: user, + Uid: uid, + Group: group, + Gid: gid, + }, + }, + }, + }, + []string{ + `4: files: "./dir/file" (group): will create`, + `3: files: "./dir/file" (group): creating`, + `4: files: "./dir/file" (group): creating temporary file "dir/.file*"`, + `4: files: "./dir/file" (group): renaming "dir/.fileRND"`, + }, + nil, + }, + // Diffs {