diff --git a/.golangci-forward.yaml b/.golangci-forward.yaml index aa6084e56f..5fcd5272be 100644 --- a/.golangci-forward.yaml +++ b/.golangci-forward.yaml @@ -11,8 +11,3 @@ linters: - pattern: ^(filepath|path)\.Join$ msg: Use filesystem.JoinPaths() from github.com/portainer/portainer/api/filesystem to prevent path traversal attacks analyze-types: true - exclusions: - rules: - - path: _test\.go - linters: - - forbidigo diff --git a/api/archive/targz_test.go b/api/archive/targz_test.go index e375628e46..08d579bc63 100644 --- a/api/archive/targz_test.go +++ b/api/archive/targz_test.go @@ -5,7 +5,6 @@ import ( "compress/gzip" "os" "os/exec" - "path" "path/filepath" "testing" @@ -38,21 +37,21 @@ func Test_shouldCreateArchive(t *testing.T) { tmpdir := t.TempDir() content := []byte("content") - err := os.WriteFile(path.Join(tmpdir, "outer"), content, 0600) + err := os.WriteFile(filesystem.JoinPaths(tmpdir, "outer"), content, 0600) require.NoError(t, err) - err = os.MkdirAll(path.Join(tmpdir, "dir"), 0700) + err = os.MkdirAll(filesystem.JoinPaths(tmpdir, "dir"), 0700) require.NoError(t, err) - err = os.WriteFile(path.Join(tmpdir, "dir", ".dotfile"), content, 0600) + err = os.WriteFile(filesystem.JoinPaths(tmpdir, "dir", ".dotfile"), content, 0600) require.NoError(t, err) - err = os.WriteFile(path.Join(tmpdir, "dir", "inner"), content, 0600) + err = os.WriteFile(filesystem.JoinPaths(tmpdir, "dir", "inner"), content, 0600) require.NoError(t, err) gzPath, err := TarGzDir(tmpdir) require.NoError(t, err) - assert.Equal(t, filepath.Join(tmpdir, filepath.Base(tmpdir)+".tar.gz"), gzPath) + assert.Equal(t, filesystem.JoinPaths(tmpdir, filepath.Base(tmpdir)+".tar.gz"), gzPath) extractionDir := t.TempDir() cmd := exec.Command("tar", "-xzf", gzPath, "-C", extractionDir) @@ -62,7 +61,7 @@ func Test_shouldCreateArchive(t *testing.T) { extractedFiles := listFiles(extractionDir) wasExtracted := func(p string) { - fullpath := path.Join(extractionDir, p) + fullpath := filesystem.JoinPaths(extractionDir, p) assert.Contains(t, extractedFiles, fullpath) copyContent, err := os.ReadFile(fullpath) require.NoError(t, err) @@ -79,21 +78,21 @@ func Test_shouldCreateArchive2(t *testing.T) { tmpdir := t.TempDir() content := []byte("content") - err := os.WriteFile(path.Join(tmpdir, "outer"), content, 0600) + err := os.WriteFile(filesystem.JoinPaths(tmpdir, "outer"), content, 0600) require.NoError(t, err) - err = os.MkdirAll(path.Join(tmpdir, "dir"), 0700) + err = os.MkdirAll(filesystem.JoinPaths(tmpdir, "dir"), 0700) require.NoError(t, err) - err = os.WriteFile(path.Join(tmpdir, "dir", ".dotfile"), content, 0600) + err = os.WriteFile(filesystem.JoinPaths(tmpdir, "dir", ".dotfile"), content, 0600) require.NoError(t, err) - err = os.WriteFile(path.Join(tmpdir, "dir", "inner"), content, 0600) + err = os.WriteFile(filesystem.JoinPaths(tmpdir, "dir", "inner"), content, 0600) require.NoError(t, err) gzPath, err := TarGzDir(tmpdir) require.NoError(t, err) - assert.Equal(t, filepath.Join(tmpdir, filepath.Base(tmpdir)+".tar.gz"), gzPath) + assert.Equal(t, filesystem.JoinPaths(tmpdir, filepath.Base(tmpdir)+".tar.gz"), gzPath) extractionDir := t.TempDir() r, _ := os.Open(gzPath) @@ -103,7 +102,7 @@ func Test_shouldCreateArchive2(t *testing.T) { extractedFiles := listFiles(extractionDir) wasExtracted := func(p string) { - fullpath := path.Join(extractionDir, p) + fullpath := filesystem.JoinPaths(extractionDir, p) assert.Contains(t, extractedFiles, fullpath) copyContent, _ := os.ReadFile(fullpath) assert.Equal(t, content, copyContent) diff --git a/api/archive/zip_test.go b/api/archive/zip_test.go index 01a2999f70..52e5917f1d 100644 --- a/api/archive/zip_test.go +++ b/api/archive/zip_test.go @@ -1,9 +1,10 @@ package archive import ( - "path/filepath" "testing" + "github.com/portainer/portainer/api/filesystem" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -24,8 +25,8 @@ func TestUnzipFile(t *testing.T) { require.NoError(t, err) archiveDir := dir + "/sample_archive" - assert.FileExists(t, filepath.Join(archiveDir, "0.txt")) - assert.FileExists(t, filepath.Join(archiveDir, "0", "1.txt")) - assert.FileExists(t, filepath.Join(archiveDir, "0", "1", "2.txt")) + assert.FileExists(t, filesystem.JoinPaths(archiveDir, "0.txt")) + assert.FileExists(t, filesystem.JoinPaths(archiveDir, "0", "1.txt")) + assert.FileExists(t, filesystem.JoinPaths(archiveDir, "0", "1", "2.txt")) } diff --git a/api/cmd/portainer/main_test.go b/api/cmd/portainer/main_test.go index 08ff0952ae..c0ef7871ae 100644 --- a/api/cmd/portainer/main_test.go +++ b/api/cmd/portainer/main_test.go @@ -2,9 +2,10 @@ package main import ( "os" - "path" "testing" + "github.com/portainer/portainer/api/filesystem" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -20,7 +21,7 @@ func createPasswordFile(t *testing.T, secretPath, password string) string { func TestLoadEncryptionSecretKey(t *testing.T) { t.Parallel() tempDir := t.TempDir() - secretPath := path.Join(tempDir, secretFileName) + secretPath := filesystem.JoinPaths(tempDir, secretFileName) // first pointing to file that does not exist, gives nil hash (no encryption) encryptionKey := loadEncryptionSecretKey(secretPath) diff --git a/api/crypto/aes_test.go b/api/crypto/aes_test.go index 57f45afca6..dbff93ea54 100644 --- a/api/crypto/aes_test.go +++ b/api/crypto/aes_test.go @@ -6,9 +6,9 @@ import ( "io" "math/rand" "os" - "path/filepath" "testing" + "github.com/portainer/portainer/api/filesystem" "github.com/portainer/portainer/api/logs" "github.com/portainer/portainer/pkg/fips" @@ -42,9 +42,9 @@ func Test_encryptAndDecrypt_withTheSamePassword(t *testing.T) { tmpdir := t.TempDir() var ( - originFilePath = filepath.Join(tmpdir, "origin") - encryptedFilePath = filepath.Join(tmpdir, "encrypted") - decryptedFilePath = filepath.Join(tmpdir, "decrypted") + originFilePath = filesystem.JoinPaths(tmpdir, "origin") + encryptedFilePath = filesystem.JoinPaths(tmpdir, "encrypted") + decryptedFilePath = filesystem.JoinPaths(tmpdir, "decrypted") ) content := randBytes(1024*1024*100 + 523) @@ -148,9 +148,9 @@ func Test_encryptAndDecrypt_withStrongPassphrase(t *testing.T) { tmpdir := t.TempDir() var ( - originFilePath = filepath.Join(tmpdir, "origin2") - encryptedFilePath = filepath.Join(tmpdir, "encrypted2") - decryptedFilePath = filepath.Join(tmpdir, "decrypted2") + originFilePath = filesystem.JoinPaths(tmpdir, "origin2") + encryptedFilePath = filesystem.JoinPaths(tmpdir, "encrypted2") + decryptedFilePath = filesystem.JoinPaths(tmpdir, "decrypted2") ) content := randBytes(500) @@ -206,9 +206,9 @@ func Test_encryptAndDecrypt_withTheSamePasswordSmallFile(t *testing.T) { tmpdir := t.TempDir() var ( - originFilePath = filepath.Join(tmpdir, "origin2") - encryptedFilePath = filepath.Join(tmpdir, "encrypted2") - decryptedFilePath = filepath.Join(tmpdir, "decrypted2") + originFilePath = filesystem.JoinPaths(tmpdir, "origin2") + encryptedFilePath = filesystem.JoinPaths(tmpdir, "encrypted2") + decryptedFilePath = filesystem.JoinPaths(tmpdir, "decrypted2") ) content := randBytes(500) @@ -264,9 +264,9 @@ func Test_encryptAndDecrypt_withEmptyPassword(t *testing.T) { tmpdir := t.TempDir() var ( - originFilePath = filepath.Join(tmpdir, "origin") - encryptedFilePath = filepath.Join(tmpdir, "encrypted") - decryptedFilePath = filepath.Join(tmpdir, "decrypted") + originFilePath = filesystem.JoinPaths(tmpdir, "origin") + encryptedFilePath = filesystem.JoinPaths(tmpdir, "encrypted") + decryptedFilePath = filesystem.JoinPaths(tmpdir, "decrypted") ) content := randBytes(1024 * 50) @@ -322,9 +322,9 @@ func Test_decryptWithDifferentPassphrase_shouldProduceWrongResult(t *testing.T) tmpdir := t.TempDir() var ( - originFilePath = filepath.Join(tmpdir, "origin") - encryptedFilePath = filepath.Join(tmpdir, "encrypted") - decryptedFilePath = filepath.Join(tmpdir, "decrypted") + originFilePath = filesystem.JoinPaths(tmpdir, "origin") + encryptedFilePath = filesystem.JoinPaths(tmpdir, "encrypted") + decryptedFilePath = filesystem.JoinPaths(tmpdir, "decrypted") ) content := randBytes(1034) diff --git a/api/database/boltdb/db_test.go b/api/database/boltdb/db_test.go index 68232c39b6..cc465b6280 100644 --- a/api/database/boltdb/db_test.go +++ b/api/database/boltdb/db_test.go @@ -2,7 +2,6 @@ package boltdb import ( "os" - "path" "testing" "github.com/portainer/portainer/api/filesystem" @@ -97,7 +96,7 @@ func Test_NeedsEncryptionMigration(t *testing.T) { if tc.dbname == "both" { // Special case. If portainer.db and portainer.edb exist. - dbFile1 := path.Join(connection.Path, DatabaseFileName) + dbFile1 := filesystem.JoinPaths(connection.Path, DatabaseFileName) f, _ := os.Create(dbFile1) err := f.Close() @@ -108,7 +107,7 @@ func Test_NeedsEncryptionMigration(t *testing.T) { require.NoError(t, err) }() - dbFile2 := path.Join(connection.Path, EncryptedDatabaseFileName) + dbFile2 := filesystem.JoinPaths(connection.Path, EncryptedDatabaseFileName) f, _ = os.Create(dbFile2) err = f.Close() @@ -119,7 +118,7 @@ func Test_NeedsEncryptionMigration(t *testing.T) { require.NoError(t, err) }() } else if tc.dbname != "" { - dbFile := path.Join(connection.Path, tc.dbname) + dbFile := filesystem.JoinPaths(connection.Path, tc.dbname) f, _ := os.Create(dbFile) err := f.Close() diff --git a/api/datastore/migrate_data_test.go b/api/datastore/migrate_data_test.go index 232a5a4107..0b52feed1e 100644 --- a/api/datastore/migrate_data_test.go +++ b/api/datastore/migrate_data_test.go @@ -6,13 +6,13 @@ import ( "fmt" "io" "os" - "path/filepath" "testing" portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/database/boltdb" "github.com/portainer/portainer/api/database/models" "github.com/portainer/portainer/api/datastore/migrator" + "github.com/portainer/portainer/api/filesystem" "github.com/Masterminds/semver/v3" "github.com/google/go-cmp/cmp" @@ -325,7 +325,7 @@ func migrateDBTestHelper(t *testing.T, srcPath, wantPath string, overrideInstanc // Compare the result we got with the one we wanted. if diff := cmp.Diff(wantJSON, gotJSON); diff != "" { - gotPath := filepath.Join(os.TempDir(), "portainer-migrator-test-fail.json") + gotPath := filesystem.JoinPaths(os.TempDir(), "portainer-migrator-test-fail.json") err = os.WriteFile( gotPath, gotJSON, diff --git a/api/exec/compose_stack_integration_test.go b/api/exec/compose_stack_integration_test.go index bbbc7bbb73..bae30bb042 100644 --- a/api/exec/compose_stack_integration_test.go +++ b/api/exec/compose_stack_integration_test.go @@ -3,11 +3,11 @@ package exec import ( "os" "os/exec" - "path/filepath" "strings" "testing" portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/filesystem" "github.com/portainer/portainer/pkg/libstack/compose" "github.com/portainer/portainer/pkg/testhelpers" "github.com/stretchr/testify/require" @@ -25,7 +25,7 @@ const composedContainerName = "compose_wrapper_test" func setup(t *testing.T) (*portainer.Stack, *portainer.Endpoint) { dir := t.TempDir() composeFileName := "compose_wrapper_test.yml" - f, err := os.Create(filepath.Join(dir, composeFileName)) + f, err := os.Create(filesystem.JoinPaths(dir, composeFileName)) require.NoError(t, err) _, err = f.WriteString(composeFile) diff --git a/api/exec/compose_stack_test.go b/api/exec/compose_stack_test.go index 6c994fe931..b638c82f11 100644 --- a/api/exec/compose_stack_test.go +++ b/api/exec/compose_stack_test.go @@ -3,11 +3,10 @@ package exec import ( "io" "os" - "path" - "path/filepath" "testing" portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/filesystem" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -57,9 +56,9 @@ func Test_createEnvFile(t *testing.T) { result, _ := createEnvFile(tt.stack) if tt.expected != "" { - assert.Equal(t, filepath.Join(tt.stack.ProjectPath, "stack.env"), result) + assert.Equal(t, filesystem.JoinPaths(tt.stack.ProjectPath, "stack.env"), result) - f, _ := os.Open(path.Join(dir, "stack.env")) + f, _ := os.Open(filesystem.JoinPaths(dir, "stack.env")) content, _ := io.ReadAll(f) assert.Equal(t, tt.expected, string(content)) @@ -73,7 +72,7 @@ func Test_createEnvFile(t *testing.T) { func Test_createEnvFile_mergesDefultAndInplaceEnvVars(t *testing.T) { t.Parallel() dir := t.TempDir() - err := os.WriteFile(path.Join(dir, ".env"), []byte("VAR1=VAL1\nVAR2=VAL2\n"), 0600) + err := os.WriteFile(filesystem.JoinPaths(dir, ".env"), []byte("VAR1=VAL1\nVAR2=VAL2\n"), 0600) require.NoError(t, err) stack := &portainer.Stack{ @@ -84,11 +83,11 @@ func Test_createEnvFile_mergesDefultAndInplaceEnvVars(t *testing.T) { }, } result, err := createEnvFile(stack) - assert.Equal(t, filepath.Join(stack.ProjectPath, "stack.env"), result) + assert.Equal(t, filesystem.JoinPaths(stack.ProjectPath, "stack.env"), result) require.NoError(t, err) - assert.FileExists(t, path.Join(dir, "stack.env")) + assert.FileExists(t, filesystem.JoinPaths(dir, "stack.env")) - f, err := os.Open(path.Join(dir, "stack.env")) + f, err := os.Open(filesystem.JoinPaths(dir, "stack.env")) require.NoError(t, err) content, err := io.ReadAll(f) diff --git a/api/filesystem/copy_test.go b/api/filesystem/copy_test.go index 92b94ed012..93bea75861 100644 --- a/api/filesystem/copy_test.go +++ b/api/filesystem/copy_test.go @@ -2,8 +2,6 @@ package filesystem import ( "os" - "path" - "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -22,13 +20,13 @@ func Test_copyFile_shouldMakeAbackup(t *testing.T) { tmpdir := t.TempDir() content := []byte("content") - err := os.WriteFile(path.Join(tmpdir, "origin"), content, 0600) + err := os.WriteFile(JoinPaths(tmpdir, "origin"), content, 0600) require.NoError(t, err) - err = copyFile(path.Join(tmpdir, "origin"), path.Join(tmpdir, "copy")) + err = copyFile(JoinPaths(tmpdir, "origin"), JoinPaths(tmpdir, "copy")) require.NoError(t, err) - copyContent, err := os.ReadFile(path.Join(tmpdir, "copy")) + copyContent, err := os.ReadFile(JoinPaths(tmpdir, "copy")) require.NoError(t, err) assert.Equal(t, content, copyContent) } @@ -39,9 +37,9 @@ func Test_CopyDir_shouldCopyAllFilesAndDirectories(t *testing.T) { err := CopyDir("./testdata/copy_test", destination, true) require.NoError(t, err) - assert.FileExists(t, filepath.Join(destination, "copy_test", "outer")) - assert.FileExists(t, filepath.Join(destination, "copy_test", "dir", ".dotfile")) - assert.FileExists(t, filepath.Join(destination, "copy_test", "dir", "inner")) + assert.FileExists(t, JoinPaths(destination, "copy_test", "outer")) + assert.FileExists(t, JoinPaths(destination, "copy_test", "dir", ".dotfile")) + assert.FileExists(t, JoinPaths(destination, "copy_test", "dir", "inner")) } func Test_CopyDir_shouldCopyOnlyDirContents(t *testing.T) { @@ -50,9 +48,9 @@ func Test_CopyDir_shouldCopyOnlyDirContents(t *testing.T) { err := CopyDir("./testdata/copy_test", destination, false) require.NoError(t, err) - assert.FileExists(t, filepath.Join(destination, "outer")) - assert.FileExists(t, filepath.Join(destination, "dir", ".dotfile")) - assert.FileExists(t, filepath.Join(destination, "dir", "inner")) + assert.FileExists(t, JoinPaths(destination, "outer")) + assert.FileExists(t, JoinPaths(destination, "dir", ".dotfile")) + assert.FileExists(t, JoinPaths(destination, "dir", "inner")) } func Test_CopyPath_shouldSkipWhenNotExist(t *testing.T) { @@ -69,16 +67,16 @@ func Test_CopyPath_shouldCopyFile(t *testing.T) { tmpdir := t.TempDir() content := []byte("content") - err := os.WriteFile(path.Join(tmpdir, "file"), content, 0600) + err := os.WriteFile(JoinPaths(tmpdir, "file"), content, 0600) require.NoError(t, err) - err = os.MkdirAll(path.Join(tmpdir, "backup"), 0700) + err = os.MkdirAll(JoinPaths(tmpdir, "backup"), 0700) require.NoError(t, err) - err = CopyPath(path.Join(tmpdir, "file"), path.Join(tmpdir, "backup")) + err = CopyPath(JoinPaths(tmpdir, "file"), JoinPaths(tmpdir, "backup")) require.NoError(t, err) - copyContent, err := os.ReadFile(path.Join(tmpdir, "backup", "file")) + copyContent, err := os.ReadFile(JoinPaths(tmpdir, "backup", "file")) require.NoError(t, err) assert.Equal(t, content, copyContent) } @@ -89,15 +87,15 @@ func Test_CopyPath_shouldCopyDir(t *testing.T) { err := CopyPath("./testdata/copy_test", destination) require.NoError(t, err) - assert.FileExists(t, filepath.Join(destination, "copy_test", "outer")) - assert.FileExists(t, filepath.Join(destination, "copy_test", "dir", ".dotfile")) - assert.FileExists(t, filepath.Join(destination, "copy_test", "dir", "inner")) + assert.FileExists(t, JoinPaths(destination, "copy_test", "outer")) + assert.FileExists(t, JoinPaths(destination, "copy_test", "dir", ".dotfile")) + assert.FileExists(t, JoinPaths(destination, "copy_test", "dir", "inner")) } func TestCopyPathPanic(t *testing.T) { t.Parallel() dir := t.TempDir() - p := filepath.Join(dir, "myfile") + p := JoinPaths(dir, "myfile") err := os.WriteFile(p, []byte("contents"), 0644) require.NoError(t, err) diff --git a/api/filesystem/filesystem_fileexists_test.go b/api/filesystem/filesystem_fileexists_test.go index f559c8407c..1555604f95 100644 --- a/api/filesystem/filesystem_fileexists_test.go +++ b/api/filesystem/filesystem_fileexists_test.go @@ -4,7 +4,6 @@ import ( "fmt" "math/rand" "os" - "path" "testing" "github.com/stretchr/testify/assert" @@ -49,7 +48,7 @@ func testHelperFileExists_fileExists(t *testing.T, checker func(path string) (bo } func testHelperFileExists_fileNotExists(t *testing.T, checker func(path string) (bool, error)) { - filePath := path.Join(t.TempDir(), fmt.Sprintf("%s%d", t.Name(), rand.Int())) + filePath := JoinPaths(t.TempDir(), fmt.Sprintf("%s%d", t.Name(), rand.Int())) err := os.RemoveAll(filePath) require.NoError(t, err, "RemoveAll should not fail") diff --git a/api/filesystem/filesystem_move_test.go b/api/filesystem/filesystem_move_test.go index 0c440f0f75..e8a5354f6c 100644 --- a/api/filesystem/filesystem_move_test.go +++ b/api/filesystem/filesystem_move_test.go @@ -2,7 +2,6 @@ package filesystem import ( "os" - "path" "testing" "github.com/stretchr/testify/assert" @@ -61,30 +60,30 @@ func Test_movePath_succesIfOverwriteSetWhenDestinationDirExists(t *testing.T) { func Test_movePath_successWhenSourceExistsAndDestinationIsMissing(t *testing.T) { t.Parallel() tmp := t.TempDir() - sourceDir := path.Join(tmp, "source") + sourceDir := JoinPaths(tmp, "source") err := os.Mkdir(sourceDir, 0766) require.NoError(t, err) file1 := addFile(t, sourceDir, "dir", "file") file2 := addFile(t, sourceDir, "file") - destinationDir := path.Join(tmp, "destination") + destinationDir := JoinPaths(tmp, "destination") err = MoveDirectory(sourceDir, destinationDir, false) require.NoError(t, err) assert.NoFileExists(t, file1, "source dir contents should be moved") assert.NoFileExists(t, file2, "source dir contents should be moved") - assertFileContent(t, path.Join(destinationDir, "file")) - assertFileContent(t, path.Join(destinationDir, "dir", "file")) + assertFileContent(t, JoinPaths(destinationDir, "file")) + assertFileContent(t, JoinPaths(destinationDir, "dir", "file")) } func addFile(t *testing.T, fileParts ...string) (filepath string) { if len(fileParts) > 2 { - dir := path.Join(fileParts[:len(fileParts)-1]...) + dir := JoinPaths(fileParts[0], fileParts[1:len(fileParts)-1]...) err := os.MkdirAll(dir, 0766) require.NoError(t, err) } - p := path.Join(fileParts...) + p := JoinPaths(fileParts[0], fileParts[1:]...) err := os.WriteFile(p, content, 0766) require.NoError(t, err) diff --git a/api/filesystem/filesystem_test.go b/api/filesystem/filesystem_test.go index 19ecbc7ab9..e2a702fd57 100644 --- a/api/filesystem/filesystem_test.go +++ b/api/filesystem/filesystem_test.go @@ -2,14 +2,13 @@ package filesystem import ( "os" - "path" "testing" "github.com/stretchr/testify/require" ) func createService(t *testing.T) *Service { - dataStorePath := path.Join(t.TempDir(), t.Name()) + dataStorePath := JoinPaths(t.TempDir(), t.Name()) service, err := NewService(dataStorePath, "") require.NoError(t, err, "NewService should not fail") diff --git a/api/filesystem/write_test.go b/api/filesystem/write_test.go index 1877cca2a0..7a7562578b 100644 --- a/api/filesystem/write_test.go +++ b/api/filesystem/write_test.go @@ -2,7 +2,6 @@ package filesystem import ( "os" - "path" "testing" "github.com/stretchr/testify/assert" @@ -12,7 +11,7 @@ import ( func Test_WriteFile_CanStoreContentInANewFile(t *testing.T) { t.Parallel() tmpDir := t.TempDir() - tmpFilePath := path.Join(tmpDir, "dummy") + tmpFilePath := JoinPaths(tmpDir, "dummy") content := []byte("content") err := WriteToFile(tmpFilePath, content) @@ -25,7 +24,7 @@ func Test_WriteFile_CanStoreContentInANewFile(t *testing.T) { func Test_WriteFile_CanOverwriteExistingFile(t *testing.T) { t.Parallel() tmpDir := t.TempDir() - tmpFilePath := path.Join(tmpDir, "dummy") + tmpFilePath := JoinPaths(tmpDir, "dummy") err := WriteToFile(tmpFilePath, []byte("content")) require.NoError(t, err) @@ -41,7 +40,7 @@ func Test_WriteFile_CanOverwriteExistingFile(t *testing.T) { func Test_WriteFile_CanWriteANestedPath(t *testing.T) { t.Parallel() tmpDir := t.TempDir() - tmpFilePath := path.Join(tmpDir, "dir", "sub-dir", "dummy") + tmpFilePath := JoinPaths(tmpDir, "dir", "sub-dir", "dummy") content := []byte("content") err := WriteToFile(tmpFilePath, content) diff --git a/api/git/azure_integration_test.go b/api/git/azure_integration_test.go index 6b0fcec6b5..ac0b790aa8 100644 --- a/api/git/azure_integration_test.go +++ b/api/git/azure_integration_test.go @@ -3,10 +3,10 @@ package git import ( "fmt" "os" - "path/filepath" "testing" "time" + "github.com/portainer/portainer/api/filesystem" gittypes "github.com/portainer/portainer/api/git/types" _ "github.com/joho/godotenv/autoload" @@ -69,7 +69,7 @@ func TestService_ClonePublicRepository_Azure(t *testing.T) { false, ) require.NoError(t, err) - assert.FileExists(t, filepath.Join(dst, "README.md")) + assert.FileExists(t, filesystem.JoinPaths(dst, "README.md")) }) } } @@ -93,7 +93,7 @@ func TestService_ClonePrivateRepository_Azure(t *testing.T) { false, ) require.NoError(t, err) - assert.FileExists(t, filepath.Join(dst, "README.md")) + assert.FileExists(t, filesystem.JoinPaths(dst, "README.md")) } func TestService_LatestCommitID_Azure(t *testing.T) { diff --git a/api/git/git_integration_test.go b/api/git/git_integration_test.go index 75fdc950ac..af885443a4 100644 --- a/api/git/git_integration_test.go +++ b/api/git/git_integration_test.go @@ -3,10 +3,10 @@ package git import ( "net/http" "net/http/httptest" - "path/filepath" "testing" "time" + "github.com/portainer/portainer/api/filesystem" gittypes "github.com/portainer/portainer/api/git/types" "github.com/stretchr/testify/assert" @@ -38,7 +38,7 @@ func TestService_ClonePrivateRepository_GitHub(t *testing.T) { false, ) require.NoError(t, err) - assert.FileExists(t, filepath.Join(dst, "README.md")) + assert.FileExists(t, filesystem.JoinPaths(dst, "README.md")) } func TestService_LatestCommitID_GitHub(t *testing.T) { diff --git a/api/http/handler/backup/backup_test.go b/api/http/handler/backup/backup_test.go index 2cf3aad277..05b5d7e603 100644 --- a/api/http/handler/backup/backup_test.go +++ b/api/http/handler/backup/backup_test.go @@ -7,7 +7,6 @@ import ( "net/http/httptest" "os" "os/exec" - "path" "path/filepath" "strings" "testing" @@ -15,6 +14,7 @@ import ( "github.com/portainer/portainer/api/adminmonitor" "github.com/portainer/portainer/api/crypto" + "github.com/portainer/portainer/api/filesystem" "github.com/portainer/portainer/api/http/offlinegate" "github.com/portainer/portainer/api/internal/testhelpers" "github.com/portainer/portainer/pkg/fips" @@ -88,7 +88,7 @@ func Test_backupHandlerWithoutPassword_shouldCreateATarballArchive(t *testing.T) tmpdir := t.TempDir() - archivePath := filepath.Join(tmpdir, "archive.tar.gz") + archivePath := filesystem.JoinPaths(tmpdir, "archive.tar.gz") if err := os.WriteFile(archivePath, body, 0600); err != nil { t.Fatal("Failed to save downloaded .tar.gz archive: ", err) } @@ -100,12 +100,12 @@ func Test_backupHandlerWithoutPassword_shouldCreateATarballArchive(t *testing.T) createdFiles := listFiles(t, tmpdir) - contains(t, createdFiles, path.Join(tmpdir, "portainer.key")) - contains(t, createdFiles, path.Join(tmpdir, "portainer.pub")) - contains(t, createdFiles, path.Join(tmpdir, "tls", "file1")) - contains(t, createdFiles, path.Join(tmpdir, "tls", "file2")) - assert.NotContains(t, createdFiles, path.Join(tmpdir, "extra_file")) - assert.NotContains(t, createdFiles, path.Join(tmpdir, "extra_folder", "file1")) + contains(t, createdFiles, filesystem.JoinPaths(tmpdir, "portainer.key")) + contains(t, createdFiles, filesystem.JoinPaths(tmpdir, "portainer.pub")) + contains(t, createdFiles, filesystem.JoinPaths(tmpdir, "tls", "file1")) + contains(t, createdFiles, filesystem.JoinPaths(tmpdir, "tls", "file2")) + assert.NotContains(t, createdFiles, filesystem.JoinPaths(tmpdir, "extra_file")) + assert.NotContains(t, createdFiles, filesystem.JoinPaths(tmpdir, "extra_folder", "file1")) } func Test_backupHandlerWithPassword_shouldCreateEncryptedATarballArchive(t *testing.T) { @@ -138,7 +138,7 @@ func Test_backupHandlerWithPassword_shouldCreateEncryptedATarballArchive(t *test t.Fatal("Failed to decrypt archive") } - archivePath := filepath.Join(tmpdir, "archive.tag.gz") + archivePath := filesystem.JoinPaths(tmpdir, "archive.tag.gz") archive, err := os.Create(archivePath) require.NoError(t, err) @@ -157,10 +157,10 @@ func Test_backupHandlerWithPassword_shouldCreateEncryptedATarballArchive(t *test createdFiles := listFiles(t, tmpdir) - contains(t, createdFiles, path.Join(tmpdir, "portainer.key")) - contains(t, createdFiles, path.Join(tmpdir, "portainer.pub")) - contains(t, createdFiles, path.Join(tmpdir, "tls", "file1")) - contains(t, createdFiles, path.Join(tmpdir, "tls", "file2")) - assert.NotContains(t, createdFiles, path.Join(tmpdir, "extra_file")) - assert.NotContains(t, createdFiles, path.Join(tmpdir, "extra_folder", "file1")) + contains(t, createdFiles, filesystem.JoinPaths(tmpdir, "portainer.key")) + contains(t, createdFiles, filesystem.JoinPaths(tmpdir, "portainer.pub")) + contains(t, createdFiles, filesystem.JoinPaths(tmpdir, "tls", "file1")) + contains(t, createdFiles, filesystem.JoinPaths(tmpdir, "tls", "file2")) + assert.NotContains(t, createdFiles, filesystem.JoinPaths(tmpdir, "extra_file")) + assert.NotContains(t, createdFiles, filesystem.JoinPaths(tmpdir, "extra_folder", "file1")) } diff --git a/api/http/handler/customtemplates/customtemplate_git_fetch_test.go b/api/http/handler/customtemplates/customtemplate_git_fetch_test.go index fb3f2bca5a..7f72cca8a8 100644 --- a/api/http/handler/customtemplates/customtemplate_git_fetch_test.go +++ b/api/http/handler/customtemplates/customtemplate_git_fetch_test.go @@ -9,12 +9,12 @@ import ( "net/http" "net/http/httptest" "os" - "path/filepath" "testing" "time" portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/datastore" + "github.com/portainer/portainer/api/filesystem" gittypes "github.com/portainer/portainer/api/git/types" "github.com/portainer/portainer/api/http/security" "github.com/portainer/portainer/api/internal/authorization" @@ -71,7 +71,7 @@ type TestFileService struct { } func (f *TestFileService) GetFileContent(projectPath, configFilePath string) ([]byte, error) { - return os.ReadFile(filepath.Join(projectPath, configFilePath)) + return os.ReadFile(filesystem.JoinPaths(projectPath, configFilePath)) } type InvalidTestGitService struct { @@ -119,7 +119,7 @@ func prepareTestFolder(projectPath, filename string) error { return err } - return createTestFile(filepath.Join(projectPath, filename)) + return createTestFile(filesystem.JoinPaths(projectPath, filename)) } func singleAPIRequest(h *Handler, jwt string, expect string) error { @@ -172,7 +172,7 @@ func Test_customTemplateGitFetch(t *testing.T) { dir, err := os.Getwd() require.NoError(t, err, "error to get working directory") - template1 := &portainer.CustomTemplate{ID: 1, Title: "custom-template-1", ProjectPath: filepath.Join(dir, "fixtures/custom_template_1"), GitConfig: &gittypes.RepoConfig{ConfigFilePath: "test-config-path.txt"}} + template1 := &portainer.CustomTemplate{ID: 1, Title: "custom-template-1", ProjectPath: filesystem.JoinPaths(dir, "fixtures/custom_template_1"), GitConfig: &gittypes.RepoConfig{ConfigFilePath: "test-config-path.txt"}} err = store.CustomTemplateService.Create(template1) require.NoError(t, err, "error creating custom template 1") @@ -181,7 +181,7 @@ func Test_customTemplateGitFetch(t *testing.T) { require.NoError(t, err, "error creating testing folder") defer func() { - err := os.RemoveAll(filepath.Join(dir, "fixtures")) + err := os.RemoveAll(filesystem.JoinPaths(dir, "fixtures")) require.NoError(t, err) }() @@ -192,7 +192,7 @@ func Test_customTemplateGitFetch(t *testing.T) { requestBouncer := security.NewRequestBouncer(t.Context(), store, jwtService, nil) gitService := &TestGitService{ - targetFilePath: filepath.Join(template1.ProjectPath, template1.GitConfig.ConfigFilePath), + targetFilePath: filesystem.JoinPaths(template1.ProjectPath, template1.GitConfig.ConfigFilePath), } fileService := &TestFileService{} @@ -252,7 +252,7 @@ func Test_customTemplateGitFetch(t *testing.T) { t.Run("restore git repository if it is failed to download the new git repository", func(t *testing.T) { invalidGitService := &InvalidTestGitService{ - targetFilePath: filepath.Join(template1.ProjectPath, template1.GitConfig.ConfigFilePath), + targetFilePath: filesystem.JoinPaths(template1.ProjectPath, template1.GitConfig.ConfigFilePath), } h := NewHandler(requestBouncer, store, fileService, invalidGitService) diff --git a/api/http/handler/stacks/stack_file_test.go b/api/http/handler/stacks/stack_file_test.go index 89c0fe72ff..9c8b119fdc 100644 --- a/api/http/handler/stacks/stack_file_test.go +++ b/api/http/handler/stacks/stack_file_test.go @@ -4,7 +4,6 @@ import ( "net/http" "net/http/httptest" "os" - "path/filepath" "strconv" "testing" @@ -85,7 +84,7 @@ func TestStackFile_MatchingGitSettings_ReturnsFileContent(t *testing.T) { const configPath = "docker-compose.yml" const fileContent = "version: '3'\nservices:\n web:\n image: nginx\n" - require.NoError(t, os.WriteFile(filepath.Join(tempDir, configPath), []byte(fileContent), 0o644)) + require.NoError(t, os.WriteFile(filesystem.JoinPaths(tempDir, configPath), []byte(fileContent), 0o644)) stack := &portainer.Stack{ ID: 2, diff --git a/api/http/handler/stacks/stack_update_test.go b/api/http/handler/stacks/stack_update_test.go index 6f2449491d..56e5492256 100644 --- a/api/http/handler/stacks/stack_update_test.go +++ b/api/http/handler/stacks/stack_update_test.go @@ -6,7 +6,6 @@ import ( "fmt" "net/http" "net/http/httptest" - "path/filepath" "strconv" "testing" @@ -202,7 +201,7 @@ func TestStackUpdate(t *testing.T) { t.Helper() _, store := datastore.MustNewTestStore(t, false, true) - testDataPath := filepath.Join(t.TempDir()) + testDataPath := filesystem.JoinPaths(t.TempDir()) fileService, err := filesystem.NewService(testDataPath, "") require.NoError(t, err, "error init file service") @@ -328,7 +327,7 @@ func setupUpdateStackInTxTest[T testUpdateStackPayload](t *testing.T, stack *por _, store := datastore.MustNewTestStore(t, false, true) - testDataPath := filepath.Join(t.TempDir()) + testDataPath := filesystem.JoinPaths(t.TempDir()) fileService, err := filesystem.NewService(testDataPath, "") require.NoError(t, err, "error init file service") diff --git a/api/stacks/stackutils/validation_test.go b/api/stacks/stackutils/validation_test.go index 678489f2a4..2aca9357b6 100644 --- a/api/stacks/stackutils/validation_test.go +++ b/api/stacks/stackutils/validation_test.go @@ -2,10 +2,10 @@ package stackutils import ( "os" - "path/filepath" "testing" portainer "github.com/portainer/portainer/api" + "github.com/portainer/portainer/api/filesystem" "github.com/stretchr/testify/require" ) @@ -185,7 +185,7 @@ func TestValidateStackFiles_DotEnvFile(t *testing.T) { t.Parallel() tmpDir := t.TempDir() - err := os.WriteFile(filepath.Join(tmpDir, ".env"), []byte("HOST_PORT=3000\n"), 0600) + err := os.WriteFile(filesystem.JoinPaths(tmpDir, ".env"), []byte("HOST_PORT=3000\n"), 0600) require.NoError(t, err) fileContent := []byte(` @@ -216,7 +216,7 @@ func TestValidateStackFiles_EnvFileAttribute(t *testing.T) { t.Parallel() tmpDir := t.TempDir() - err := os.WriteFile(filepath.Join(tmpDir, "web.env"), []byte("HOST_PORT=3000\n"), 0600) + err := os.WriteFile(filesystem.JoinPaths(tmpDir, "web.env"), []byte("HOST_PORT=3000\n"), 0600) require.NoError(t, err) fileContent := []byte(` diff --git a/pkg/libhelm/sdk/values_test.go b/pkg/libhelm/sdk/values_test.go index a2fbe0fdd9..ec1e918093 100644 --- a/pkg/libhelm/sdk/values_test.go +++ b/pkg/libhelm/sdk/values_test.go @@ -2,9 +2,10 @@ package sdk import ( "os" - "path/filepath" "testing" + "github.com/portainer/portainer/api/filesystem" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -197,13 +198,13 @@ func TestGetHelmValuesFromFile(t *testing.T) { }) t.Run("non-existent file returns error", func(t *testing.T) { - _, err := GetHelmValuesFromFile(filepath.Join(tempDir, "nonexistent.yaml")) + _, err := GetHelmValuesFromFile(filesystem.JoinPaths(tempDir, "nonexistent.yaml")) require.Error(t, err) assert.Contains(t, err.Error(), "failed to read values file") }) t.Run("valid values file", func(t *testing.T) { - valuesPath := filepath.Join(tempDir, "values.yaml") + valuesPath := filesystem.JoinPaths(tempDir, "values.yaml") valuesContent := []byte(` replicaCount: 3 image: @@ -228,7 +229,7 @@ service: }) t.Run("invalid YAML in file returns error", func(t *testing.T) { - invalidPath := filepath.Join(tempDir, "invalid.yaml") + invalidPath := filesystem.JoinPaths(tempDir, "invalid.yaml") invalidContent := []byte(` invalid: yaml: content: [[[ `) diff --git a/pkg/libkubectl/apply_test.go b/pkg/libkubectl/apply_test.go index 44b4a0a5ef..6964388ae2 100644 --- a/pkg/libkubectl/apply_test.go +++ b/pkg/libkubectl/apply_test.go @@ -2,9 +2,10 @@ package libkubectl import ( "os" - "path/filepath" "runtime" "testing" + + "github.com/portainer/portainer/api/filesystem" ) // BenchmarkApply tests require a Kubernetes cluster. @@ -19,7 +20,7 @@ func BenchmarkApply(b *testing.B) { // Apply expects file paths, so create a temp file tmpDir := b.TempDir() - manifestFile := filepath.Join(tmpDir, "manifest.yaml") + manifestFile := filesystem.JoinPaths(tmpDir, "manifest.yaml") manifestContent := `apiVersion: v1 kind: ConfigMap metadata: diff --git a/pkg/libkubectl/delete_test.go b/pkg/libkubectl/delete_test.go index 81a5509922..c07dbf1bfb 100644 --- a/pkg/libkubectl/delete_test.go +++ b/pkg/libkubectl/delete_test.go @@ -2,9 +2,10 @@ package libkubectl import ( "os" - "path/filepath" "runtime" "testing" + + "github.com/portainer/portainer/api/filesystem" ) // BenchmarkDeleteDynamic tests require a Kubernetes cluster. @@ -81,7 +82,7 @@ func BenchmarkDelete(b *testing.B) { // Delete expects file paths, so create a temp file tmpDir := b.TempDir() - manifestFile := filepath.Join(tmpDir, "manifest.yaml") + manifestFile := filesystem.JoinPaths(tmpDir, "manifest.yaml") manifestContent := `apiVersion: v1 kind: ConfigMap metadata: diff --git a/pkg/libstack/compose/bind_mount_hash_test.go b/pkg/libstack/compose/bind_mount_hash_test.go index be4cca295a..00f25008b4 100644 --- a/pkg/libstack/compose/bind_mount_hash_test.go +++ b/pkg/libstack/compose/bind_mount_hash_test.go @@ -2,9 +2,10 @@ package compose import ( "os" - "path/filepath" "testing" + "github.com/portainer/portainer/api/filesystem" + "github.com/compose-spec/compose-go/v2/types" "github.com/stretchr/testify/require" ) @@ -12,7 +13,7 @@ import ( func TestPathHash_File(t *testing.T) { t.Parallel() dir := t.TempDir() - path := filepath.Join(dir, "file.txt") + path := filesystem.JoinPaths(dir, "file.txt") require.NoError(t, os.WriteFile(path, []byte("hello"), 0644)) @@ -35,8 +36,8 @@ func TestPathHash_File(t *testing.T) { func TestPathHash_Directory(t *testing.T) { t.Parallel() dir := t.TempDir() - require.NoError(t, os.WriteFile(filepath.Join(dir, "a.txt"), []byte("aaa"), 0644)) - require.NoError(t, os.WriteFile(filepath.Join(dir, "b.txt"), []byte("bbb"), 0644)) + require.NoError(t, os.WriteFile(filesystem.JoinPaths(dir, "a.txt"), []byte("aaa"), 0644)) + require.NoError(t, os.WriteFile(filesystem.JoinPaths(dir, "b.txt"), []byte("bbb"), 0644)) h1, err := pathHash(dir) require.NoError(t, err) @@ -47,14 +48,14 @@ func TestPathHash_Directory(t *testing.T) { require.Equal(t, h1, h2) // Rename a file -> different hash (relative path is part of the hash) - require.NoError(t, os.Rename(filepath.Join(dir, "a.txt"), filepath.Join(dir, "c.txt"))) + require.NoError(t, os.Rename(filesystem.JoinPaths(dir, "a.txt"), filesystem.JoinPaths(dir, "c.txt"))) h3, err := pathHash(dir) require.NoError(t, err) require.NotEqual(t, h1, h3, "renaming a file should change the directory hash") // Restore and change content -> different hash - require.NoError(t, os.Rename(filepath.Join(dir, "c.txt"), filepath.Join(dir, "a.txt"))) - require.NoError(t, os.WriteFile(filepath.Join(dir, "a.txt"), []byte("modified"), 0644)) + require.NoError(t, os.Rename(filesystem.JoinPaths(dir, "c.txt"), filesystem.JoinPaths(dir, "a.txt"))) + require.NoError(t, os.WriteFile(filesystem.JoinPaths(dir, "a.txt"), []byte("modified"), 0644)) h4, err := pathHash(dir) require.NoError(t, err) require.NotEqual(t, h1, h4, "changing file content should change the directory hash") @@ -63,9 +64,9 @@ func TestPathHash_Directory(t *testing.T) { func TestAddBindMountHashLabel(t *testing.T) { t.Parallel() dir := t.TempDir() - webDir := filepath.Join(dir, "web") + webDir := filesystem.JoinPaths(dir, "web") require.NoError(t, os.MkdirAll(webDir, 0755)) - require.NoError(t, os.WriteFile(filepath.Join(webDir, "nginx.conf"), []byte("server {}"), 0644)) + require.NoError(t, os.WriteFile(filesystem.JoinPaths(webDir, "nginx.conf"), []byte("server {}"), 0644)) t.Run("no bind mounts", func(t *testing.T) { svc := types.ServiceConfig{Name: "web"} @@ -107,7 +108,7 @@ func TestAddBindMountHashLabel(t *testing.T) { t.Run("valid bind mount with file source sets label", func(t *testing.T) { svc := types.ServiceConfig{ Name: "web", - Volumes: []types.ServiceVolumeConfig{{Type: "bind", Source: filepath.Join(webDir, "nginx.conf")}}, + Volumes: []types.ServiceVolumeConfig{{Type: "bind", Source: filesystem.JoinPaths(webDir, "nginx.conf")}}, } result, err := addBindMountHashLabel("web", svc) require.NoError(t, err) diff --git a/pkg/libstack/compose/compose_test.go b/pkg/libstack/compose/compose_test.go index 0fba305a55..5d068a3636 100644 --- a/pkg/libstack/compose/compose_test.go +++ b/pkg/libstack/compose/compose_test.go @@ -4,10 +4,10 @@ import ( "log" "os" "os/exec" - "path/filepath" "strings" "testing" + "github.com/portainer/portainer/api/filesystem" "github.com/portainer/portainer/pkg/libstack" "github.com/portainer/portainer/pkg/libstack/compose" "github.com/portainer/portainer/pkg/testhelpers" @@ -79,7 +79,7 @@ func Test_UpAndDown(t *testing.T) { } func createFile(dir, fileName, content string) (string, error) { - filePath := filepath.Join(dir, fileName) + filePath := filesystem.JoinPaths(dir, fileName) if err := os.WriteFile(filePath, []byte(content), 0644); err != nil { return "", err diff --git a/pkg/libstack/compose/composeplugin_test.go b/pkg/libstack/compose/composeplugin_test.go index 70759419fc..1922ba777e 100644 --- a/pkg/libstack/compose/composeplugin_test.go +++ b/pkg/libstack/compose/composeplugin_test.go @@ -12,6 +12,9 @@ import ( "sync" "testing" + "github.com/portainer/portainer/api/filesystem" + "github.com/portainer/portainer/pkg/libstack" + "github.com/compose-spec/compose-go/v2/consts" "github.com/compose-spec/compose-go/v2/types" "github.com/docker/cli/cli/command" @@ -21,9 +24,7 @@ import ( "github.com/docker/compose/v2/pkg/api" "github.com/docker/compose/v2/pkg/compose" "github.com/google/go-cmp/cmp" - "github.com/portainer/portainer/pkg/libstack" zerolog "github.com/rs/zerolog/log" - "github.com/stretchr/testify/require" ) @@ -161,7 +162,7 @@ services: } func createFile(t *testing.T, dir, fileName, content string) string { - filePath := filepath.Join(dir, fileName) + filePath := filesystem.JoinPaths(dir, fileName) err := os.WriteFile(filePath, []byte(content), 0o644) require.NoError(t, err) @@ -1372,7 +1373,7 @@ func Test_CredentialsStore_Behavior(t *testing.T) { "credsStore": "test-store", "auths": {} }` - configPath := filepath.Join(tmpDir, "config.json") + configPath := filesystem.JoinPaths(tmpDir, "config.json") err := os.WriteFile(configPath, []byte(configJSON), 0644) require.NoError(t, err)