All checks were successful
		
		
	
	Update alr-git / changelog (push) Successful in 30s
				
			closes #107 Reviewed-on: #108 Co-authored-by: Maxim Slipenko <no-reply@maxim.slipenko.com> Co-committed-by: Maxim Slipenko <no-reply@maxim.slipenko.com>
		
			
				
	
	
		
			323 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			323 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // ALR - Any Linux Repository
 | |
| // Copyright (C) 2025 The ALR Authors
 | |
| //
 | |
| // 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 <http://www.gnu.org/licenses/>.
 | |
| 
 | |
| package build
 | |
| 
 | |
| import (
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"testing"
 | |
| 
 | |
| 	"github.com/goreleaser/nfpm/v2/files"
 | |
| 	"github.com/stretchr/testify/assert"
 | |
| 
 | |
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
 | |
| 	"gitea.plemya-x.ru/Plemya-x/ALR/pkg/types"
 | |
| )
 | |
| 
 | |
| func TestIsBinaryFile(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name        string
 | |
| 		destination string
 | |
| 		expected    bool
 | |
| 	}{
 | |
| 		{"usr/bin binary", "/usr/bin/test", true},
 | |
| 		{"bin binary", "/bin/test", true},
 | |
| 		{"usr/local/bin binary", "/usr/local/bin/test", true},
 | |
| 		{"lib file", "/usr/lib/test.so", false},
 | |
| 		{"etc file", "/etc/config", false},
 | |
| 		{"empty destination", "", false},
 | |
| 		{"root level file", "./test", false},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			result := isBinaryFile(tt.destination)
 | |
| 			assert.Equal(t, tt.expected, result)
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestGenerateSafeName(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name        string
 | |
| 		destination string
 | |
| 		expected    string
 | |
| 		expectError bool
 | |
| 	}{
 | |
| 		{"usr/bin path", "./usr/bin/test", "_usr_bin_test", false},
 | |
| 		{"bin path", "./bin/test", "_bin_test", false},
 | |
| 		{"nested path", "./usr/local/bin/app", "_usr_local_bin_app", false},
 | |
| 		{"path with spaces", "./usr/bin/my app", "_usr_bin_my app", false},
 | |
| 		{"empty after trim", ".", "", true},
 | |
| 		{"empty string", "", "", true},
 | |
| 		{"only dots", "..", ".", false},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			result, err := generateSafeName(tt.destination)
 | |
| 			if tt.expectError {
 | |
| 				assert.Error(t, err)
 | |
| 			} else {
 | |
| 				assert.NoError(t, err)
 | |
| 				assert.Equal(t, tt.expected, result)
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestCreateWrapperScript(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name            string
 | |
| 		origFilePath    string
 | |
| 		profilePath     string
 | |
| 		expectedContent string
 | |
| 	}{
 | |
| 		{
 | |
| 			"basic wrapper",
 | |
| 			"/usr/lib/alr/firejailed/_usr_bin_test",
 | |
| 			"/usr/lib/alr/firejailed/_usr_bin_test.profile",
 | |
| 			"#!/bin/bash\nexec firejail --profile=\"/usr/lib/alr/firejailed/_usr_bin_test.profile\" \"/usr/lib/alr/firejailed/_usr_bin_test\" \"$@\"\n",
 | |
| 		},
 | |
| 		{
 | |
| 			"path with spaces",
 | |
| 			"/usr/lib/alr/firejailed/_usr_bin_my_app",
 | |
| 			"/usr/lib/alr/firejailed/_usr_bin_my_app.profile",
 | |
| 			"#!/bin/bash\nexec firejail --profile=\"/usr/lib/alr/firejailed/_usr_bin_my_app.profile\" \"/usr/lib/alr/firejailed/_usr_bin_my_app\" \"$@\"\n",
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			tmpDir := t.TempDir()
 | |
| 			scriptPath := filepath.Join(tmpDir, "wrapper.sh")
 | |
| 
 | |
| 			err := createWrapperScript(scriptPath, tt.origFilePath, tt.profilePath)
 | |
| 
 | |
| 			assert.NoError(t, err)
 | |
| 			assert.FileExists(t, scriptPath)
 | |
| 
 | |
| 			content, err := os.ReadFile(scriptPath)
 | |
| 			assert.NoError(t, err)
 | |
| 			assert.Equal(t, tt.expectedContent, string(content))
 | |
| 
 | |
| 			// Check file permissions
 | |
| 			info, err := os.Stat(scriptPath)
 | |
| 			assert.NoError(t, err)
 | |
| 			assert.Equal(t, os.FileMode(defaultDirMode), info.Mode())
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestCreateFirejailedBinary(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name        string
 | |
| 		setupFunc   func(string) (*alrsh.Package, *files.Content, types.Directories)
 | |
| 		expectError bool
 | |
| 		errorMsg    string
 | |
| 	}{
 | |
| 		{
 | |
| 			"successful creation with default profile",
 | |
| 			func(tmpDir string) (*alrsh.Package, *files.Content, types.Directories) {
 | |
| 				pkgDir := filepath.Join(tmpDir, "pkg")
 | |
| 				scriptDir := filepath.Join(tmpDir, "scripts")
 | |
| 				os.MkdirAll(pkgDir, 0o755)
 | |
| 				os.MkdirAll(scriptDir, 0o755)
 | |
| 
 | |
| 				binDir := filepath.Join(pkgDir, "usr", "bin")
 | |
| 				os.MkdirAll(binDir, 0o755)
 | |
| 
 | |
| 				srcBinary := filepath.Join(binDir, "test-binary")
 | |
| 				os.WriteFile(srcBinary, []byte("#!/bin/bash\necho test"), 0o755)
 | |
| 
 | |
| 				defaultProfile := filepath.Join(scriptDir, "default.profile")
 | |
| 				os.WriteFile(defaultProfile, []byte("include /etc/firejail/default.profile\nnet none"), 0o644)
 | |
| 
 | |
| 				pkg := &alrsh.Package{
 | |
| 					Name: "test-pkg",
 | |
| 					FireJailProfiles: alrsh.OverridableFromMap(map[string]map[string]string{
 | |
| 						"": {"default": "default.profile"},
 | |
| 					}),
 | |
| 				}
 | |
| 				alrsh.ResolvePackage(pkg, []string{""})
 | |
| 
 | |
| 				content := &files.Content{
 | |
| 					Source:      srcBinary,
 | |
| 					Destination: "/usr/bin/test-binary",
 | |
| 					Type:        "file",
 | |
| 				}
 | |
| 
 | |
| 				dirs := types.Directories{PkgDir: pkgDir, ScriptDir: scriptDir}
 | |
| 				return pkg, content, dirs
 | |
| 			},
 | |
| 			false,
 | |
| 			"",
 | |
| 		},
 | |
| 		{
 | |
| 			"successful creation with specific profile",
 | |
| 			func(tmpDir string) (*alrsh.Package, *files.Content, types.Directories) {
 | |
| 				pkgDir := filepath.Join(tmpDir, "pkg")
 | |
| 				scriptDir := filepath.Join(tmpDir, "scripts")
 | |
| 				os.MkdirAll(pkgDir, 0o755)
 | |
| 				os.MkdirAll(scriptDir, 0o755)
 | |
| 
 | |
| 				binDir := filepath.Join(pkgDir, "usr", "bin")
 | |
| 				os.MkdirAll(binDir, 0o755)
 | |
| 
 | |
| 				srcBinary := filepath.Join(binDir, "special-binary")
 | |
| 				os.WriteFile(srcBinary, []byte("#!/bin/bash\necho special"), 0o755)
 | |
| 
 | |
| 				defaultProfile := filepath.Join(scriptDir, "default.profile")
 | |
| 				os.WriteFile(defaultProfile, []byte("include /etc/firejail/default.profile"), 0o644)
 | |
| 
 | |
| 				specialProfile := filepath.Join(scriptDir, "special.profile")
 | |
| 				os.WriteFile(specialProfile, []byte("include /etc/firejail/default.profile\nnet none\nprivate-tmp"), 0o644)
 | |
| 
 | |
| 				pkg := &alrsh.Package{
 | |
| 					Name: "test-pkg",
 | |
| 					FireJailProfiles: alrsh.OverridableFromMap(map[string]map[string]string{
 | |
| 						"": {"default": "default.profile", "/usr/bin/special-binary": "special.profile"},
 | |
| 					}),
 | |
| 				}
 | |
| 				alrsh.ResolvePackage(pkg, []string{""})
 | |
| 
 | |
| 				content := &files.Content{
 | |
| 					Source:      srcBinary,
 | |
| 					Destination: "/usr/bin/special-binary",
 | |
| 					Type:        "file",
 | |
| 				}
 | |
| 
 | |
| 				dirs := types.Directories{PkgDir: pkgDir, ScriptDir: scriptDir}
 | |
| 				return pkg, content, dirs
 | |
| 			},
 | |
| 			false,
 | |
| 			"",
 | |
| 		},
 | |
| 		{
 | |
| 			"missing default profile",
 | |
| 			func(tmpDir string) (*alrsh.Package, *files.Content, types.Directories) {
 | |
| 				pkgDir := filepath.Join(tmpDir, "pkg")
 | |
| 				scriptDir := filepath.Join(tmpDir, "scripts")
 | |
| 				os.MkdirAll(pkgDir, 0o755)
 | |
| 				os.MkdirAll(scriptDir, 0o755)
 | |
| 
 | |
| 				srcBinary := filepath.Join(tmpDir, "test-binary")
 | |
| 				os.WriteFile(srcBinary, []byte("#!/bin/bash\necho test"), 0o755)
 | |
| 
 | |
| 				pkg := &alrsh.Package{
 | |
| 					Name:             "test-pkg",
 | |
| 					FireJailProfiles: alrsh.OverridableFromMap(map[string]map[string]string{"": {}}),
 | |
| 				}
 | |
| 				alrsh.ResolvePackage(pkg, []string{""})
 | |
| 
 | |
| 				content := &files.Content{Source: srcBinary, Destination: "./usr/bin/test-binary", Type: "file"}
 | |
| 				dirs := types.Directories{PkgDir: pkgDir, ScriptDir: scriptDir}
 | |
| 				return pkg, content, dirs
 | |
| 			},
 | |
| 			true,
 | |
| 			"default profile is missing",
 | |
| 		},
 | |
| 		{
 | |
| 			"profile file not found",
 | |
| 			func(tmpDir string) (*alrsh.Package, *files.Content, types.Directories) {
 | |
| 				pkgDir := filepath.Join(tmpDir, "pkg")
 | |
| 				scriptDir := filepath.Join(tmpDir, "scripts")
 | |
| 				os.MkdirAll(pkgDir, 0o755)
 | |
| 				os.MkdirAll(scriptDir, 0o755)
 | |
| 
 | |
| 				srcBinary := filepath.Join(tmpDir, "test-binary")
 | |
| 				os.WriteFile(srcBinary, []byte("#!/bin/bash\necho test"), 0o755)
 | |
| 
 | |
| 				pkg := &alrsh.Package{
 | |
| 					Name:             "test-pkg",
 | |
| 					FireJailProfiles: alrsh.OverridableFromMap(map[string]map[string]string{"": {"default": "nonexistent.profile"}}),
 | |
| 				}
 | |
| 				alrsh.ResolvePackage(pkg, []string{""})
 | |
| 
 | |
| 				content := &files.Content{Source: srcBinary, Destination: "./usr/bin/test-binary", Type: "file"}
 | |
| 				dirs := types.Directories{PkgDir: pkgDir, ScriptDir: scriptDir}
 | |
| 				return pkg, content, dirs
 | |
| 			},
 | |
| 			true,
 | |
| 			"",
 | |
| 		},
 | |
| 		{
 | |
| 			"invalid destination path",
 | |
| 			func(tmpDir string) (*alrsh.Package, *files.Content, types.Directories) {
 | |
| 				pkgDir := filepath.Join(tmpDir, "pkg")
 | |
| 				scriptDir := filepath.Join(tmpDir, "scripts")
 | |
| 				os.MkdirAll(pkgDir, 0o755)
 | |
| 				os.MkdirAll(scriptDir, 0o755)
 | |
| 
 | |
| 				srcBinary := filepath.Join(tmpDir, "test-binary")
 | |
| 				os.WriteFile(srcBinary, []byte("#!/bin/bash\necho test"), 0o755)
 | |
| 
 | |
| 				defaultProfile := filepath.Join(scriptDir, "default.profile")
 | |
| 				os.WriteFile(defaultProfile, []byte("include /etc/firejail/default.profile"), 0o644)
 | |
| 
 | |
| 				pkg := &alrsh.Package{Name: "test-pkg", FireJailProfiles: alrsh.OverridableFromMap(map[string]map[string]string{"": {"default": "default.profile"}})}
 | |
| 				alrsh.ResolvePackage(pkg, []string{""})
 | |
| 
 | |
| 				content := &files.Content{Source: srcBinary, Destination: ".", Type: "file"}
 | |
| 				dirs := types.Directories{PkgDir: pkgDir, ScriptDir: scriptDir}
 | |
| 				return pkg, content, dirs
 | |
| 			},
 | |
| 			true,
 | |
| 			"",
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			tmpDir := t.TempDir()
 | |
| 			pkg, content, dirs := tt.setupFunc(tmpDir)
 | |
| 
 | |
| 			err := createFirejailedDirectory(dirs.PkgDir)
 | |
| 			assert.NoError(t, err)
 | |
| 
 | |
| 			result, err := createFirejailedBinary(pkg, content, dirs)
 | |
| 
 | |
| 			if tt.expectError {
 | |
| 				assert.Error(t, err)
 | |
| 				if tt.errorMsg != "" {
 | |
| 					assert.Contains(t, err.Error(), tt.errorMsg)
 | |
| 				}
 | |
| 				assert.Nil(t, result)
 | |
| 			} else {
 | |
| 				assert.NoError(t, err)
 | |
| 				assert.Len(t, result, 2)
 | |
| 
 | |
| 				binContent := result[0]
 | |
| 				assert.Contains(t, binContent.Destination, "usr/lib/alr/firejailed/")
 | |
| 				assert.FileExists(t, binContent.Source)
 | |
| 
 | |
| 				profileContent := result[1]
 | |
| 				assert.Contains(t, profileContent.Destination, "usr/lib/alr/firejailed/")
 | |
| 				assert.Contains(t, profileContent.Destination, ".profile")
 | |
| 				assert.FileExists(t, profileContent.Source)
 | |
| 
 | |
| 				assert.FileExists(t, content.Source)
 | |
| 				wrapperBytes, err := os.ReadFile(content.Source)
 | |
| 				assert.NoError(t, err)
 | |
| 				wrapper := string(wrapperBytes)
 | |
| 				assert.Contains(t, wrapper, "#!/bin/bash")
 | |
| 				assert.Contains(t, wrapper, "firejail --profile=")
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 |