diff --git a/assets/coverage-badge.svg b/assets/coverage-badge.svg index a1c07ea..ac4f67b 100644 --- a/assets/coverage-badge.svg +++ b/assets/coverage-badge.svg @@ -11,7 +11,7 @@ coverage coverage - 17.5% - 17.5% + 19.0% + 19.0% diff --git a/e2e-tests/firejailed_package_test.go b/e2e-tests/firejailed_package_test.go new file mode 100644 index 0000000..58ad2d0 --- /dev/null +++ b/e2e-tests/firejailed_package_test.go @@ -0,0 +1,41 @@ +// 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 . + +//go:build e2e + +package e2etests_test + +import ( + "fmt" + "testing" + + "github.com/efficientgo/e2e" +) + +func TestE2EFirejailedPackage(t *testing.T) { + dockerMultipleRun( + t, + "firejailed-package", + COMMON_SYSTEMS, + func(t *testing.T, r e2e.Runnable) { + defaultPrepare(t, r) + execShouldNoError(t, r, "alr", "build", "-p", fmt.Sprintf("%s/firejailed-pkg", REPO_NAME_FOR_E2E_TESTS)) + execShouldError(t, r, "alr", "build", "-p", fmt.Sprintf("%s/firejailed-pkg-incorrect", REPO_NAME_FOR_E2E_TESTS)) + execShouldNoError(t, r, "sh", "-c", "dpkg -c *.deb | grep -q '/usr/lib/alr/firejailed/_usr_bin_danger.sh'") + execShouldNoError(t, r, "sh", "-c", "dpkg -c *.deb | grep -q '/usr/lib/alr/firejailed/_usr_bin_danger.sh.profile'") + }, + ) +} diff --git a/internal/build/firejail.go b/internal/build/firejail.go new file mode 100644 index 0000000..5c5c766 --- /dev/null +++ b/internal/build/firejail.go @@ -0,0 +1,237 @@ +// 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 . + +package build + +import ( + "errors" + "fmt" + "io" + "log/slog" + "os" + "path/filepath" + "strings" + + "github.com/goreleaser/nfpm/v2/files" + "github.com/leonelquinteros/gotext" + + "gitea.plemya-x.ru/Plemya-x/ALR/internal/osutils" + "gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh" + "gitea.plemya-x.ru/Plemya-x/ALR/pkg/types" +) + +const ( + firejailedDir = "/usr/lib/alr/firejailed" + defaultDirMode = 0o755 + defaultScriptMode = 0o755 +) + +var ( + ErrInvalidDestination = errors.New("invalid destination path") + ErrMissingProfile = errors.New("default profile is missing") + ErrEmptyPackageName = errors.New("package name cannot be empty") +) + +var binaryDirectories = []string{ + "/usr/bin/", + "/bin/", + "/usr/local/bin/", +} + +func applyFirejailIntegration( + vars *alrsh.Package, + dirs types.Directories, + contents []*files.Content, +) ([]*files.Content, error) { + slog.Info(gotext.Get("Applying FireJail integration"), "package", vars.Name) + + if err := createFirejailedDirectory(dirs.PkgDir); err != nil { + return nil, fmt.Errorf("failed to create firejailed directory: %w", err) + } + + newContents, err := processBinaryFiles(vars, contents, dirs) + if err != nil { + return nil, fmt.Errorf("failed to process binary files: %w", err) + } + + return append(contents, newContents...), nil +} + +func createFirejailedDirectory(pkgDir string) error { + firejailedPath := filepath.Join(pkgDir, firejailedDir) + return os.MkdirAll(firejailedPath, defaultDirMode) +} + +func processBinaryFiles(pkg *alrsh.Package, contents []*files.Content, dirs types.Directories) ([]*files.Content, error) { + var newContents []*files.Content + + for _, content := range contents { + if content.Type == "dir" { + continue + } + + if !isBinaryFile(content.Destination) { + slog.Debug("content not binary file", "content", content) + continue + } + + slog.Debug("process content", "content", content) + + newContent, err := createFirejailedBinary(pkg, content, dirs) + if err != nil { + return nil, fmt.Errorf("failed to create firejailed binary for %s: %w", content.Destination, err) + } + + if newContent != nil { + newContents = append(newContents, newContent...) + } + } + + return newContents, nil +} + +func isBinaryFile(destination string) bool { + for _, binDir := range binaryDirectories { + if strings.HasPrefix(destination, binDir) { + return true + } + } + return false +} + +func createFirejailedBinary( + pkg *alrsh.Package, + content *files.Content, + dirs types.Directories, +) ([]*files.Content, error) { + origFilePath, err := generateFirejailedPath(content.Destination) + if err != nil { + return nil, err + } + + profiles := pkg.FireJailProfiles.Resolved() + sourceProfilePath, ok := profiles[content.Destination] + + if !ok { + sourceProfilePath, ok = profiles["default"] + if !ok { + return nil, errors.New("default profile is missing") + } + } + + sourceProfilePath = filepath.Join(dirs.ScriptDir, sourceProfilePath) + dest, err := createFirejailProfilePath(content.Destination) + if err != nil { + return nil, err + } + + err = createProfile(filepath.Join(dirs.PkgDir, dest), sourceProfilePath) + if err != nil { + return nil, err + } + + if err := osutils.Move(content.Source, filepath.Join(dirs.PkgDir, origFilePath)); err != nil { + return nil, fmt.Errorf("failed to move original binary: %w", err) + } + + // Create wrapper script + if err := createWrapperScript(content.Source, origFilePath, dest); err != nil { + return nil, fmt.Errorf("failed to create wrapper script: %w", err) + } + + profile, err := getContentFromPath(dest, dirs.PkgDir) + if err != nil { + return nil, err + } + + bin, err := getContentFromPath(origFilePath, dirs.PkgDir) + if err != nil { + return nil, err + } + + return []*files.Content{ + bin, + profile, + }, nil +} + +func getContentFromPath(path, base string) (*files.Content, error) { + absPath := filepath.Join(base, path) + + fi, err := os.Lstat(absPath) + if err != nil { + return nil, fmt.Errorf("failed to get file info: %w", err) + } + + return &files.Content{ + Source: absPath, + Destination: path, + FileInfo: &files.ContentFileInfo{ + MTime: fi.ModTime(), + Mode: fi.Mode(), + Size: fi.Size(), + }, + }, nil +} + +func generateSafeName(destination string) (string, error) { + cleanPath := strings.TrimPrefix(destination, ".") + if cleanPath == "" { + return "", fmt.Errorf("invalid destination path: %s", destination) + } + return strings.ReplaceAll(cleanPath, "/", "_"), nil +} + +func generateFirejailedPath(destination string) (string, error) { + safeName, err := generateSafeName(destination) + if err != nil { + return "", err + } + return filepath.Join(firejailedDir, safeName), nil +} + +func createProfile(destProfilePath, profilePath string) error { + srcFile, err := os.Open(profilePath) + if err != nil { + return err + } + defer srcFile.Close() + + destFile, err := os.Create(destProfilePath) + if err != nil { + return err + } + defer destFile.Close() + + _, err = io.Copy(destFile, srcFile) + if err != nil { + return err + } + return destFile.Sync() +} + +func createWrapperScript(scriptPath, origFilePath, profilePath string) error { + scriptContent := fmt.Sprintf("#!/bin/bash\nexec firejail --profile=%q %q \"$@\"\n", profilePath, origFilePath) + return os.WriteFile(scriptPath, []byte(scriptContent), defaultDirMode) +} + +func createFirejailProfilePath(binaryPath string) (string, error) { + name, err := generateSafeName(binaryPath) + if err != nil { + return "", err + } + return filepath.Join(firejailedDir, fmt.Sprintf("%s.profile", name)), nil +} diff --git a/internal/build/firejail_test.go b/internal/build/firejail_test.go new file mode 100644 index 0000000..dcc6bcd --- /dev/null +++ b/internal/build/firejail_test.go @@ -0,0 +1,316 @@ +// 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 . + +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) + + 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\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) + + srcBinary := filepath.Join(tmpDir, "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=") + } + }) + } +} diff --git a/internal/build/script_executor.go b/internal/build/script_executor.go index b0bb8f7..18417d5 100644 --- a/internal/build/script_executor.go +++ b/internal/build/script_executor.go @@ -248,6 +248,16 @@ func buildPkgMetadata( if err != nil { return nil, err } + + normalizeContents(contents) + + if vars.FireJailed.Resolved() { + contents, err = applyFirejailIntegration(vars, dirs, contents) + if err != nil { + return nil, err + } + } + pkgInfo.Overridables.Contents = contents if len(vars.AutoProv.Resolved()) == 1 && decoder.IsTruthy(vars.AutoProv.Resolved()[0]) { diff --git a/internal/build/utils.go b/internal/build/utils.go index 1ef2f9d..871b724 100644 --- a/internal/build/utils.go +++ b/internal/build/utils.go @@ -154,6 +154,12 @@ func buildContents(vars *alrsh.Package, dirs types.Directories, preferedContents return contents, nil } +func normalizeContents(contents []*files.Content) { + for _, content := range contents { + content.Destination = filepath.Join("/", content.Destination) + } +} + var RegexpALRPackageName = regexp.MustCompile(`^(?P[^+]+)\+alr-(?P.+)$`) func getBasePkgInfo(vars *alrsh.Package, input interface { diff --git a/internal/translations/default.pot b/internal/translations/default.pot index 42457b4..101951e 100644 --- a/internal/translations/default.pot +++ b/internal/translations/default.pot @@ -220,19 +220,23 @@ msgstr "" msgid "AutoReq is not implemented for this package format, so it's skipped" msgstr "" +#: internal/build/firejail.go:59 +msgid "Applying FireJail integration" +msgstr "" + #: internal/build/script_executor.go:145 msgid "Building package metadata" msgstr "" -#: internal/build/script_executor.go:275 +#: internal/build/script_executor.go:285 msgid "Executing prepare()" msgstr "" -#: internal/build/script_executor.go:284 +#: internal/build/script_executor.go:294 msgid "Executing build()" msgstr "" -#: internal/build/script_executor.go:313 internal/build/script_executor.go:333 +#: internal/build/script_executor.go:323 internal/build/script_executor.go:343 msgid "Executing %s()" msgstr "" diff --git a/internal/translations/po/ru/default.po b/internal/translations/po/ru/default.po index 4b47a80..4be205f 100644 --- a/internal/translations/po/ru/default.po +++ b/internal/translations/po/ru/default.po @@ -5,15 +5,15 @@ msgid "" msgstr "" "Project-Id-Version: unnamed project\n" -"PO-Revision-Date: 2025-05-16 20:47+0300\n" +"PO-Revision-Date: 2025-06-15 16:05+0300\n" "Last-Translator: Maxim Slipenko \n" "Language-Team: Russian\n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" -"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && " +"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" "X-Generator: Gtranslator 48.0\n" #: build.go:42 @@ -231,19 +231,23 @@ msgid "AutoReq is not implemented for this package format, so it's skipped" msgstr "" "AutoReq не реализовано для этого формата пакета, поэтому будет пропущено" +#: internal/build/firejail.go:59 +msgid "Applying FireJail integration" +msgstr "Применение интеграции FireJail" + #: internal/build/script_executor.go:145 msgid "Building package metadata" msgstr "Сборка метаданных пакета" -#: internal/build/script_executor.go:275 +#: internal/build/script_executor.go:285 msgid "Executing prepare()" msgstr "Выполнение prepare()" -#: internal/build/script_executor.go:284 +#: internal/build/script_executor.go:294 msgid "Executing build()" msgstr "Выполнение build()" -#: internal/build/script_executor.go:313 internal/build/script_executor.go:333 +#: internal/build/script_executor.go:323 internal/build/script_executor.go:343 msgid "Executing %s()" msgstr "Выполнение %s()" @@ -352,8 +356,8 @@ msgid "" "This command is deprecated and would be removed in the future, use \"%s\" " "instead!" msgstr "" -"Эта команда устарела и будет удалена в будущем, используйте вместо нее \"%s" -"\"!" +"Эта команда устарела и будет удалена в будущем, используйте вместо нее " +"\"%s\"!" #: internal/db/db.go:76 msgid "Database version mismatch; resetting" diff --git a/pkg/alrsh/package.go b/pkg/alrsh/package.go index c24d0b2..5674cd3 100644 --- a/pkg/alrsh/package.go +++ b/pkg/alrsh/package.go @@ -68,6 +68,9 @@ type Package struct { AutoProv OverridableField[[]string] `sh:"auto_prov" xorm:"-"` AutoReqSkipList OverridableField[[]string] `sh:"auto_req_skiplist" xorm:"-"` AutoProvSkipList OverridableField[[]string] `sh:"auto_prov_skiplist" xorm:"-"` + + FireJailed OverridableField[bool] `sh:"firejailed" xorm:"-"` + FireJailProfiles OverridableField[map[string]string] `sh:"firejail_profiles" xorm:"-"` } type Scripts struct {