diff --git a/appimage.go b/appimage.go index beb0785..7494be5 100644 --- a/appimage.go +++ b/appimage.go @@ -8,43 +8,42 @@ import ( "bufio" "crypto/md5" "debug/elf" - "fmt" "errors" + "fmt" "io" + "os" "path" "path/filepath" - "os" "strings" - ini "gopkg.in/ini.v1" - helpers "github.com/mgord9518/aisap/helpers" - profiles "github.com/mgord9518/aisap/profiles" + squashfs "github.com/CalebQ42/squashfs" + xdg "github.com/adrg/xdg" + helpers "github.com/mgord9518/aisap/helpers" permissions "github.com/mgord9518/aisap/permissions" - squashfs "github.com/CalebQ42/squashfs" - xdg "github.com/adrg/xdg" + profiles "github.com/mgord9518/aisap/profiles" + ini "gopkg.in/ini.v1" ) type AppImage struct { - Desktop *ini.File // INI of internal desktop entry - Perms *permissions.AppImagePerms // Permissions - Path string // Location of AppImage - dataDir string // The AppImage's `HOME` directory - rootDir string // Can be used to give the AppImage fake system files - tempDir string // The AppImage's `/tmp` directory - mountDir string // The location the AppImage is mounted at - md5 string // MD5 of AppImage's URI - Name string // AppImage name from the desktop entry - Version string - UpdateInfo string - Offset int // Offset of SquashFS image - imageType int // Type of AppImage (1=ISO 9660 ELF, 2=squashfs ELF, -2=shImg shell) + Desktop *ini.File // INI of internal desktop entry + Path string // Location of AppImage + dataDir string // The AppImage's `HOME` directory + rootDir string // Can be used to give the AppImage fake system files + tempDir string // The AppImage's `/tmp` directory + mountDir string // The location the AppImage is mounted at + md5 string // MD5 of AppImage's URI + Name string // AppImage name from the desktop entry + Version string + UpdateInfo string + Offset int // Offset of SquashFS image + imageType int // Type of AppImage (1=ISO 9660 ELF, 2=squashfs ELF, -2=shImg shell) architecture []string // List of CPU architectures supported by the bundle - reader *squashfs.Reader - file *os.File + reader *squashfs.Reader + file *os.File // These will both be removed when the Zig-implemented C bindings // become usable - CurrentArg int // Should only ever be used for the C bindings + CurrentArg int // Should only ever be used for the C bindings WrapArgsList []string // Should only ever be used for the C bindings } @@ -69,35 +68,47 @@ func NewAppImage(src string) (*AppImage, error) { ai.md5 = fmt.Sprintf("%x", b) ai.imageType, err = helpers.GetAppImageType(ai.Path) - if err != nil { return nil, err } + if err != nil { + return nil, err + } ai.rootDir = "/" ai.dataDir = ai.Path + ".home" ai.Offset, err = helpers.GetOffset(src) - if err != nil { return nil, err } + if err != nil { + return nil, err + } if ai.imageType == -2 || ai.imageType == 2 { ai.file, err = os.Open(ai.Path) - if err != nil { return nil, err } + if err != nil { + return nil, err + } info, _ := ai.file.Stat() off64 := int64(ai.Offset) r := io.NewSectionReader(ai.file, off64, info.Size()-off64) ai.reader, err = squashfs.NewReader(r) - if err != nil { return nil, err } + if err != nil { + return nil, err + } } // Prefer local entry if it exists (located at $XDG_DATA_HOME/aisap/[ai.Name]) desktopReader, err := ai.getEntry() - if err != nil { return ai, err } + if err != nil { + return ai, err + } ai.Desktop, err = ini.LoadSources(ini.LoadOptions{ IgnoreInlineComment: true, }, desktopReader) - if err != nil { return ai, err } + if err != nil { + return ai, err + } - ai.Name = ai.Desktop.Section("Desktop Entry").Key("Name").Value() + ai.Name = ai.Desktop.Section("Desktop Entry").Key("Name").Value() ai.Version = ai.Desktop.Section("Desktop Entry").Key("X-AppImage-Version").Value() ai.UpdateInfo, _ = helpers.ReadUpdateInfo(ai.Path) @@ -106,6 +117,17 @@ func NewAppImage(src string) (*AppImage, error) { ai.Version = "1.0" } + return ai, nil +} + +// Retrieve permissions from the AppImage in the following order: +// +// 1: User-configured settings in ~/.local/share/aisap/profiles/[ai.Name] +// 2: aisap internal permissions library +// 3: Permissions defined in the AppImage's desktop file +func (ai AppImage) Permissions() (*permissions.AppImagePerms, error) { + var perms *permissions.AppImagePerms + var err error // If PREFER_AISAP_PROFILE is set, attempt to use it over the AppImage's // suggested permissions. If no profile exists in aisap, fall back on saved @@ -114,23 +136,25 @@ func NewAppImage(src string) (*AppImage, error) { // Typically this should be unset unless testing a custom profile against // aisap's if _, present := os.LookupEnv("PREFER_AISAP_PROFILE"); present { - ai.Perms, err = profiles.FromName(ai.Name) + perms, err = profiles.FromName(ai.Name) + if err != nil { - ai.Perms, err = permissions.FromSystem(ai.Name) + perms, err = permissions.FromSystem(ai.Name) } } else { - ai.Perms, err = permissions.FromSystem(ai.Name) + perms, err = permissions.FromSystem(ai.Name) + if err != nil { - ai.Perms, err = profiles.FromName(ai.Name) + perms, err = profiles.FromName(ai.Name) } } // Fall back to permissions inside AppImage if all else fails if err != nil { - ai.Perms, _ = permissions.FromIni(ai.Desktop) + return permissions.FromIni(ai.Desktop) } - return ai, nil + return perms, nil } // Returns `true` if the AppImage in question is both executable and has @@ -149,7 +173,7 @@ func (ai *AppImage) Trusted() bool { return false } - return info.Mode() & 0100 != 0 + return info.Mode()&0100 != 0 } return false @@ -169,7 +193,7 @@ func (ai *AppImage) SetTrusted(trusted bool) error { return err } - os.Chmod(ai.Path, info.Mode() | 0100) + os.Chmod(ai.Path, info.Mode()|0100) if helpers.FileExists(filePath) { return errors.New("entry already exists in aisap config dir") @@ -190,7 +214,9 @@ func (ai *AppImage) Thumbnail() (io.Reader, error) { // Try to extract from zip, continue to SquashFS if it fails if ai.imageType == -2 { r, err := helpers.ExtractResourceReader(ai.Path, "icon/256.png") - if err == nil { return r, nil } + if err == nil { + return r, nil + } } return ai.ExtractFileReader(".DirIcon") @@ -252,26 +278,34 @@ func (ai *AppImage) ExtractFile(path string, dest string, resolveSymlinks bool) // True if file is symlink and `resolveSymlinks` is false if info != nil && !resolveSymlinks && - info.Mode()&os.ModeSymlink == os.ModeSymlink { + info.Mode()&os.ModeSymlink == os.ModeSymlink { target, _ := os.Readlink(path) err = os.Symlink(target, dest) } else { inF, err := ai.ExtractFileReader(path) defer inF.Close() - if err != nil { return err } + if err != nil { + return err + } info, err := os.Stat(path) perms := info.Mode().Perm() outF, err := os.Create(dest) defer outF.Close() - if err != nil { return err } + if err != nil { + return err + } err = os.Chmod(dest, perms) - if err != nil { return err } + if err != nil { + return err + } _, err = io.Copy(outF, inF) - if err != nil { return err } + if err != nil { + return err + } } return err @@ -297,10 +331,14 @@ func (ai *AppImage) ExtractFileReader(path string) (io.ReadCloser, error) { func (ai *AppImage) Icon() (io.ReadCloser, string, error) { if ai.imageType == -2 { r, err := helpers.ExtractResourceReader(ai.Path, "icon/default.svg") - if err == nil { return r, "icon/default.svg", nil } + if err == nil { + return r, "icon/default.svg", nil + } - r, err = helpers.ExtractResourceReader(ai.Path, "icon/default.png") - if err == nil { return r, "icon/default.png", nil } + r, err = helpers.ExtractResourceReader(ai.Path, "icon/default.png") + if err == nil { + return r, "icon/default.png", nil + } } if ai.Desktop == nil { @@ -325,7 +363,7 @@ func (ai *AppImage) Icon() (io.ReadCloser, string, error) { ".svg", } - for _, ext := range(extensions) { + for _, ext := range extensions { r, err := ai.ExtractFileReader(iconf + ext) if err == nil { @@ -338,7 +376,7 @@ func (ai *AppImage) Icon() (io.ReadCloser, string, error) { // Extract the desktop file from the AppImage func (ai *AppImage) getEntry() (io.Reader, error) { - var r io.Reader + var r io.Reader var err error if ai.imageType == -2 { @@ -360,13 +398,12 @@ func (ai *AppImage) getEntry() (io.Reader, error) { r := entry.(*squashfs.File) if r.IsSymlink() { - r = r .GetSymlinkFile() + r = r.GetSymlinkFile() } return r, err } - return r, err } @@ -382,32 +419,34 @@ func (ai *AppImage) getArchitectures() ([]string, error) { // If undefined in the desktop entry, assume arch via ELF AppImage runtime if ai.Type() >= 0 { e, err := elf.NewFile(ai.file) - if err != nil {return s, err} + if err != nil { + return s, err + } switch e.Machine { - case elf.EM_386: - return []string{"i386"}, nil - case elf.EM_X86_64: - return []string{"x86_64"}, nil - case elf.EM_ARM: - return []string{"armhf"}, nil - case elf.EM_AARCH64: - return []string{"aarch64"}, nil + case elf.EM_386: + return []string{"i386"}, nil + case elf.EM_X86_64: + return []string{"x86_64"}, nil + case elf.EM_ARM: + return []string{"armhf"}, nil + case elf.EM_AARCH64: + return []string{"aarch64"}, nil } } // Assume arch via shImg runtime if ai.Type() < -1 { - scanner := bufio.NewScanner(ai.file) + scanner := bufio.NewScanner(ai.file) arches := []string{} counter := 0 for scanner.Scan() { counter++ if strings.HasPrefix(scanner.Text(), "arch='") { - str := scanner.Text() - str = strings.ReplaceAll(str, "arch='", "") - str = strings.ReplaceAll(str, "'", "") + str := scanner.Text() + str = strings.ReplaceAll(str, "arch='", "") + str = strings.ReplaceAll(str, "'", "") arches = helpers.SplitKey(str) return arches, nil } diff --git a/cmd/aisap/flags.go b/cmd/aisap/flags.go index 2255d71..de1da5b 100644 --- a/cmd/aisap/flags.go +++ b/cmd/aisap/flags.go @@ -4,33 +4,33 @@ import ( "fmt" "os" - flag "github.com/spf13/pflag" - clr "github.com/gookit/color" + clr "github.com/gookit/color" aisap "github.com/mgord9518/aisap" + flag "github.com/spf13/pflag" ) type arrayFlags []string var ( // Normal flags - help = flag.BoolP("help", "h", false, "display this help menu") + help = flag.BoolP("help", "h", false, "display this help menu") listPerms = flag.BoolP("list-perms", "l", false, "print all permissions to be granted to the app") - verbose = flag.BoolP("verbose", "v", false, "make output more verbose") + verbose = flag.BoolP("verbose", "v", false, "make output more verbose") // Long-only flags - color = flag.Bool ("color", true, "whether to show color (default true)") - example = flag.Bool ("example", false, "print out examples") - level = flag.Int ("level", -1, "change the permissions level") - rootDir = flag.String("root-dir", "", "use a different filesystem root for system files") - dataDir = flag.String("data-dir", "", "change the AppImage's sandbox home location") - noDataDir = flag.Bool ("no-data-dir", false, "force AppImage's HOME to be a tmpfs (default false)") - extractIcon = flag.String("extract-icon", "", "extract the AppImage's icon") - extractThumbnail = flag.String("extract-thumbnail", "", "extract the AppImage's thumbnail preview") - profile = flag.String("profile", "", "use a profile from a desktop entry") - fallbackProfile = flag.String("fallback-profile", "", "set profile to fallback on if one isn't found") - version = flag.Bool ("version", false, "show the version and quit") - trustOnce = flag.Bool ("trust-once", false, "trust the AppImage for one run") - trust = flag.Bool ("trust", false, "set whether the AppImage is trusted or not") + color = flag.Bool("color", true, "whether to show color (default true)") + example = flag.Bool("example", false, "print out examples") + level = flag.Int("level", -1, "change the permissions level") + rootDir = flag.String("root-dir", "", "use a different filesystem root for system files") + dataDir = flag.String("data-dir", "", "change the AppImage's sandbox home location") + noDataDir = flag.Bool("no-data-dir", false, "force AppImage's HOME to be a tmpfs (default false)") + extractIcon = flag.String("extract-icon", "", "extract the AppImage's icon") + extractThumbnail = flag.String("extract-thumbnail", "", "extract the AppImage's thumbnail preview") + profile = flag.String("profile", "", "use a profile from a desktop entry") + fallbackProfile = flag.String("fallback-profile", "", "set profile to fallback on if one isn't found") + version = flag.Bool("version", false, "show the version and quit") + trustOnce = flag.Bool("trust-once", false, "trust the AppImage for one run") + trust = flag.Bool("trust", false, "set whether the AppImage is trusted or not") addFile arrayFlags addDevice arrayFlags @@ -47,12 +47,12 @@ func init() { var present bool handleCtrlC() - flag.Var(&addFile, "add-file", "give the sandbox access to a filesystem object") + flag.Var(&addFile, "add-file", "give the sandbox access to a filesystem object") flag.Var(&addDevice, "add-device", "add a device to the sandbox (eg dri)") flag.Var(&addSocket, "add-socket", "allow the sandbox to access another socket (eg x11)") - flag.Var(&rmFile, "rm-file", "revoke a file from the sandbox") - flag.Var(&rmDevice, "rm-device", "remove access to a device") - flag.Var(&rmSocket, "rm-socket", "disable a socket") + flag.Var(&rmFile, "rm-file", "revoke a file from the sandbox") + flag.Var(&rmDevice, "rm-device", "remove access to a device") + flag.Var(&rmSocket, "rm-socket", "disable a socket") // Prefer AppImage-provided variable `ARGV0` if present if argv0, present = os.LookupEnv("ARGV0"); !present { @@ -160,10 +160,10 @@ func (i *arrayFlags) Type() string { func flagUsed(name string) bool { found := false - flag.Visit(func(f *flag.Flag) { - if f.Name == name { - found = true - } - }) + flag.Visit(func(f *flag.Flag) { + if f.Name == name { + found = true + } + }) return found } diff --git a/cmd/aisap/main.go b/cmd/aisap/main.go index 6544da2..378391d 100644 --- a/cmd/aisap/main.go +++ b/cmd/aisap/main.go @@ -32,17 +32,17 @@ import ( "os/signal" "syscall" - aisap "github.com/mgord9518/aisap" + clr "github.com/gookit/color" + aisap "github.com/mgord9518/aisap" permissions "github.com/mgord9518/aisap/permissions" - cli "github.com/mgord9518/cli" - check "github.com/mgord9518/aisap/spooky" - flag "github.com/spf13/pflag" - ini "gopkg.in/ini.v1" - clr "github.com/gookit/color" + check "github.com/mgord9518/aisap/spooky" + cli "github.com/mgord9518/cli" + flag "github.com/spf13/pflag" + ini "gopkg.in/ini.v1" ) var ( - ai *aisap.AppImage + ai *aisap.AppImage argv0 string invalidBundle = errors.New("failed to open bundle:") @@ -51,8 +51,8 @@ var ( invalidPerms = errors.New("failed to get permissions from profile:") invalidPermLevel = errors.New("failed to set permissions level (this shouldn't happen!):") invalidFallbackProfile = errors.New("failed to set fallback profile:") - invalidSocketSet = errors.New("failed to set socket:") - cantRun = errors.New("failed to run application:") + invalidSocketSet = errors.New("failed to set socket:") + cantRun = errors.New("failed to run application:") ) // Process flags @@ -69,6 +69,12 @@ func main() { return } + perms, err := ai.Permissions() + if err != nil { + cli.Fatal(invalidPerms, err) + return + } + if *extractIcon != "" { if *verbose { cli.Notify("extracting icon to", *extractIcon) @@ -127,41 +133,38 @@ func main() { return } - ai.Perms, err = permissions.FromReader(f) + perms, err = permissions.FromReader(f) if err != nil { cli.Fatal(invalidPerms, err) return } - if err != nil { - cli.Fatal(invalidPerms, err) - return - } } // Add (and remove) permissions as passed from flags. eg: `--add-file` // Note: If *not* using XDG standard names (eg: `xdg-desktop`) you MUST // Provide the full filepath when using `AddFiles` - ai.Perms.RemoveFiles(rmFile...) - ai.Perms.RemoveDevices(rmDevice...) - ai.Perms.RemoveSockets(rmSocket...) - ai.Perms.AddFiles(addFile...) - ai.Perms.AddDevices(addDevice...) - err = ai.Perms.AddSockets(addSocket...) - - // Fail if socket is invalid - if err != nil { + perms.RemoveFiles(rmFile...) + perms.RemoveDevices(rmDevice...) + perms.RemoveSockets(rmSocket...) + perms.AddFiles(addFile...) + perms.AddDevices(addDevice...) + err = perms.AddSockets(addSocket...) + + // Fail if socket is invalid + if err != nil { // TODO: re-add socket list // clr.Println("notice:") // cli.List("valid sockets are", permissions.ValidSockets(), 18) // fmt.Println() - cli.Fatal(invalidSocketSet, err) - return - } + + cli.Fatal(invalidSocketSet, err) + return + } // If the `--level` flag is used, set the AppImage to that level if *level > -1 && *level <= 3 { - err := ai.Perms.SetLevel(*level) + err := perms.SetLevel(*level) if err != nil { cli.Fatal(invalidPermLevel, err) return @@ -171,7 +174,7 @@ func main() { noProfile := false // Fallback on `--fallback-profile` if set, otherwise just set base level to 3 - if ai.Perms.Level < 0 || ai.Perms.Level > 3 { + if perms.Level < 0 || perms.Level > 3 { if *fallbackProfile != "" { f, err := ini.LoadSources(ini.LoadOptions{ IgnoreInlineComment: true, @@ -182,9 +185,9 @@ func main() { return } - ai.Perms, err = permissions.FromIni(f) + perms, err = permissions.FromIni(f) } else { - ai.Perms.Level = 3 + perms.Level = 3 } noProfile = true @@ -211,11 +214,11 @@ func main() { fmt.Println() } - cli.ListPerms(ai.Perms) + cli.ListPerms(perms) fmt.Println() - if ai.Perms.Level == 0 { + if perms.Level == 0 { cli.Warning("this app requests to be unsandboxed!") cli.Warning("use the CLI flag --level [1..3] to sandbox it anyway") return @@ -226,7 +229,7 @@ func main() { } // Warns if the AppImage contains potential escape vectors or suspicious files - for _, v := range(ai.Perms.Files) { + for _, v := range perms.Files { if check.IsSpooky(v) { cli.Warning("this app requests files/ directories that could be used to escape sandboxing") @@ -239,7 +242,7 @@ func main() { "x11", } - for _, sock := range ai.Perms.Sockets { + for _, sock := range perms.Sockets { for _, spookySock := range spookySockets { if sock == spookySock { cli.Warning("sockets used by this app could be used to escape the sandbox") @@ -251,10 +254,10 @@ func main() { } err = ai.Mount() - if err != nil { - cli.Fatal(err, err) - return - } + if err != nil { + cli.Fatal(err, err) + return + } if *rootDir != "" { ai.SetRootDir(*rootDir) @@ -265,7 +268,7 @@ func main() { } if *noDataDir { - ai.Perms.DataDir = false + perms.DataDir = false } if flagUsed("trust") { @@ -278,15 +281,14 @@ func main() { } if *verbose { - wrapArg, _ := ai.WrapArgs([]string{}) - cli.Notify("running with sandbox base level", ai.Perms.Level) + wrapArg, _ := ai.WrapArgs(perms, []string{}) + cli.Notify("running with sandbox base level", perms.Level) cli.Notify("bwrap flags:", wrapArg) } - err = ai.Run(flag.Args()[1:]) - + err = ai.Sandbox(perms, flag.Args()[1:]) if err != nil { - fmt.Fprintln(os.Stdout, "exited non-zero status:", err) + fmt.Fprintln(os.Stdout, "sandbox error:", err) return } } diff --git a/cmd/profilegen/go.mod b/cmd/profilegen/go.mod deleted file mode 100644 index 6dd0193..0000000 --- a/cmd/profilegen/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/mgord9518/aisap/cmd/profilegen - -go 1.19 diff --git a/cmd/profilegen/main.go b/cmd/profilegen/main.go deleted file mode 100644 index 7f44aff..0000000 --- a/cmd/profilegen/main.go +++ /dev/null @@ -1,29 +0,0 @@ -// Simple CLI tool to generate JSON formatted profiles from aisap -// Eventually the main database may be switched to JSON, but for now it's -// just being provided for export - -// Sends profile database directly to stdout - -package main - -import ( - "fmt" - "encoding/json" - - profiles "github.com/mgord9518/aisap/profiles" - permissions "github.com/mgord9518/aisap/permissions" -) - -type ProfileDatabase struct { - Profiles map[string]permissions.AppImagePerms `json:"profiles"` -} - -// Process flags -func main() { - database := &ProfileDatabase{} - database.Profiles = profiles.Profiles() - - jsonData, _ := json.Marshal(database) - - fmt.Println(string(jsonData)) -} diff --git a/cmd/tablegen/main.go b/cmd/tablegen/main.go index 00ed62a..f8a9aca 100644 --- a/cmd/tablegen/main.go +++ b/cmd/tablegen/main.go @@ -5,18 +5,29 @@ package main import ( "fmt" + "sort" profiles "github.com/mgord9518/aisap/profiles" ) // Process flags func main() { + profileKeys := make([]string, 0, len(profiles.Profiles())) + + for key, _ := range profiles.Profiles() { + profileKeys = append(profileKeys, key) + } + + sort.Strings(profileKeys) + // Table header - fmt.Printf("## Current supported applications (%d)\n", len(profiles.RawProfiles)) + fmt.Printf("## Current supported applications (%d)\n", len(profileKeys)) fmt.Println("|name|level|devices|sockets|filesystem|") fmt.Println("|-|-|-|-|-|") - for _, profile := range profiles.RawProfiles { + for i := range profileKeys { + profile := profiles.Profiles()[profileKeys[i]] + // Name fmt.Printf("|%s", profile.Names[0]) diff --git a/docs/aisap-go.3.md b/docs/aisap-go.3.md index 2de4a86..dbb9fb5 100644 --- a/docs/aisap-go.3.md +++ b/docs/aisap-go.3.md @@ -31,13 +31,9 @@ Destroy() error // directory in '$XDG_RUNTIME_DIR/aisap/mount' Mount(dest ...string) error by -// If AppImage.Perms.Level > 0, the AppImage will be sandboxed with -// AppImage.Perms, if 0, it will run unsandboxed. args are passed directly to -// the AppImage -Run(args []string) error - -// Identical to AppImage.Run, except AppImage.Perms.Level equaling 0 is an -// error condition +// Executes AppImage through bwrap and creates a portable home if one doesn't +// already exist +// Returns error if AppImagePerms.Level < 1 Sandbox(args []string) error // Generates bwrap arguments for sandboxing based on the AppImage's permissions @@ -59,15 +55,15 @@ Thumbnail() (io.Reader, error) // Returns the type of the AppImage, currently only supports type 2 and shImg // (-2). PRs for supporting type 1 and possibly other (well-defined) unofficial // AppImage implementations are welcome -Type() +Type() int // Returns the TempDir of the AppImage, which only exists if the AppImage is // mounted, otherwise returns an empty string -TempDir() +TempDir() string // Returns the directory the AppImage is mounted to. If not mounted, it will // return an empty string -MountDir() +MountDir() string // Change the root directory exposed to the sandbox. This could be useful for // running an AppImage designed for another Linux distro diff --git a/helpers/helper.go b/helpers/helper.go index 5d6eaff..ff37155 100644 --- a/helpers/helper.go +++ b/helpers/helper.go @@ -5,13 +5,13 @@ import ( "bufio" "errors" "io" - "path/filepath" - "math/rand" + "math/rand" + "os" + "os/exec" "path" + "path/filepath" "strconv" - "strings" - "os" - "os/exec" + "strings" xdg "github.com/adrg/xdg" ) @@ -26,8 +26,10 @@ func SplitKey(str string) []string { } func Contains(s []string, str string) (int, bool) { - for i, val := range(s) { - if val == str { return i, true } + for i, val := range s { + if val == str { + return i, true + } } return -1, false @@ -35,9 +37,11 @@ func Contains(s []string, str string) (int, bool) { // Checks if an array contains any of the elements from another array func ContainsAny(s []string, s2 []string) (int, bool) { - for i := range(s2) { + for i := range s2 { n, present := Contains(s, s2[i]) - if present { return n, true } + if present { + return n, true + } } return -1, false @@ -102,7 +106,7 @@ func CleanFile(str string) string { } func CleanFiles(s []string) []string { - for i := range(s) { + for i := range s { s[i] = CleanFile(s[i]) } @@ -118,7 +122,7 @@ func CleanDevice(str string) string { } func CleanDevices(s []string) []string { - for i := range(s) { + for i := range s { s[i] = CleanDevice(s[i]) } @@ -226,24 +230,32 @@ func ExpandGenericDir(str string) string { func ExtractResource(aiPath string, src string, dest string) error { inF, err := ExtractResourceReader(aiPath, src) defer inF.Close() - if err != nil { return err } + if err != nil { + return err + } outF, err := os.Create(dest) defer outF.Close() - if err != nil { return err } + if err != nil { + return err + } - _, err = io.Copy(outF, inF) + _, err = io.Copy(outF, inF) return err } func ExtractResourceReader(aiPath string, src string) (io.ReadCloser, error) { zr, err := zip.OpenReader(aiPath) - if err != nil { return nil, err } + if err != nil { + return nil, err + } - for _, f := range(zr.File) { + for _, f := range zr.File { if f.Name == filepath.Join(".APPIMAGE_RESOURCES", src) { rc, err := f.Open() - if err != nil { return nil, err } + if err != nil { + return nil, err + } return rc, nil } @@ -253,7 +265,7 @@ func ExtractResourceReader(aiPath string, src string) (io.ReadCloser, error) { } // Get the home directory using `/etc/passwd`, discarding the $HOME variable. -// This is used in aisap so that its config files can be stored in +// This is used in aisap so that its config files can be stored in func RealHome() (string, error) { uid := strconv.Itoa(os.Getuid()) @@ -277,7 +289,9 @@ func RealHome() (string, error) { // Finds full path of running executable func GetWorkDir() (string, error) { e, err := os.Executable() - if err != nil { return "", err } + if err != nil { + return "", err + } return path.Dir(e), nil } @@ -285,12 +299,16 @@ func GetWorkDir() (string, error) { // Returns full path of command and true if in PATH or working directory func CommandExists(str string) (string, bool) { wd, err := GetWorkDir() - if err != nil { return "", false } + if err != nil { + return "", false + } cmd, err := exec.LookPath(filepath.Join(wd, str)) if err != nil { cmd, err = exec.LookPath(str) - if err != nil { return "", false } + if err != nil { + return "", false + } } return cmd, true diff --git a/helpers/offset.go b/helpers/offset.go index 55030f8..f7224b7 100644 --- a/helpers/offset.go +++ b/helpers/offset.go @@ -2,20 +2,22 @@ package helpers import ( "bufio" + "debug/elf" "encoding/binary" "errors" - "debug/elf" "io" "os" - "strings" "strconv" + "strings" ) // GetOffset takes an AppImage (either ELF or shappimage), returning the offset // of its SquashFS archive func GetOffset(src string) (int, error) { format, err := GetAppImageType(src) - if err != nil { return -1, err } + if err != nil { + return -1, err + } if format == -2 { return getShappImageSize(src) @@ -33,19 +35,23 @@ func GetOffset(src string) (int, error) { func getShappImageSize(src string) (int, error) { f, err := os.Open(src) defer f.Close() - if err != nil { return -1, err } + if err != nil { + return -1, err + } _, err = f.Stat() - if err != nil { return -1, err } + if err != nil { + return -1, err + } scanner := bufio.NewScanner(f) for scanner.Scan() { if len(scanner.Text()) > 10 && scanner.Text()[0:11] == "sfs_offset=" && - len(strings.Split(scanner.Text(), "=")) == 2 { + len(strings.Split(scanner.Text(), "=")) == 2 { offHex := strings.Split(scanner.Text(), "=")[1] - offHex = strings.ReplaceAll(offHex, "'", "") - offHex = strings.ReplaceAll(offHex, "\"", "") + offHex = strings.ReplaceAll(offHex, "'", "") + offHex = strings.ReplaceAll(offHex, "\"", "") o, err := strconv.Atoi(offHex) return int(o), err @@ -63,7 +69,9 @@ func getElfSize(src string) (int, error) { f, _ := os.Open(src) defer f.Close() e, err := elf.NewFile(f) - if err != nil { return -1, err } + if err != nil { + return -1, err + } // Find offsets based on arch sr := io.NewSectionReader(f, 0, 1<<63-1) @@ -74,23 +82,31 @@ func getElfSize(src string) (int, error) { hdr := new(elf.Header64) _, err = sr.Seek(0, 0) - if err != nil { return -1, err } + if err != nil { + return -1, err + } err = binary.Read(sr, e.ByteOrder, hdr) - if err != nil { return -1, err } + if err != nil { + return -1, err + } - shoff = int(hdr.Shoff) - shnum = int(hdr.Shnum) + shoff = int(hdr.Shoff) + shnum = int(hdr.Shnum) shentsize = int(hdr.Shentsize) case elf.ELFCLASS32: hdr := new(elf.Header32) _, err = sr.Seek(0, 0) - if err != nil { return -1, err } + if err != nil { + return -1, err + } err := binary.Read(sr, e.ByteOrder, hdr) - if err != nil { return -1, err } + if err != nil { + return -1, err + } - shoff = int(hdr.Shoff) - shnum = int(hdr.Shnum) + shoff = int(hdr.Shoff) + shnum = int(hdr.Shnum) shentsize = int(hdr.Shentsize) default: return 0, nil @@ -106,10 +122,14 @@ func getElfSize(src string) (int, error) { func GetAppImageType(src string) (int, error) { f, err := os.Open(src) defer f.Close() - if err != nil { return -1, err } + if err != nil { + return -1, err + } _, err = f.Stat() - if err != nil { return -1, err } + if err != nil { + return -1, err + } if HasMagic(f, "\x7fELF", 0) { if HasMagic(f, "AI\x01", 8) { @@ -134,11 +154,13 @@ func GetAppImageType(src string) (int, error) { // if identical, return true func HasMagic(r io.ReadSeeker, str string, offset int) bool { magic := make([]byte, len(str)) - + r.Seek(int64(offset), io.SeekStart) _, err := io.ReadFull(r, magic[:]) - if err != nil { return false } + if err != nil { + return false + } for i := 0; i < len(str); i++ { if magic[i] != str[i] { diff --git a/helpers/updateinfo.go b/helpers/updateinfo.go index d734df8..d9f70d0 100644 --- a/helpers/updateinfo.go +++ b/helpers/updateinfo.go @@ -2,15 +2,17 @@ package helpers import ( "bufio" - "strings" "bytes" - "errors" "debug/elf" + "errors" + "strings" ) func ReadUpdateInfo(src string) (string, error) { format, err := GetAppImageType(src) - if err != nil { return "", err } + if err != nil { + return "", err + } if format == 2 || format == 1 { return readUpdateInfoFromElf(src) @@ -49,7 +51,9 @@ func readUpdateInfoFromElf(src string) (string, error) { func readUpdateInfoFromShappimage(src string) (string, error) { f, err := ExtractResourceReader(src, "update_info") - if err != nil { return "", err } + if err != nil { + return "", err + } scanner := bufio.NewScanner(f) for scanner.Scan() { @@ -59,6 +63,6 @@ func readUpdateInfoFromShappimage(src string) (string, error) { return scanner.Text(), nil } } - + return "", errors.New("unable to find update information in shImg") } diff --git a/mount.go b/mount.go index ade4e82..22291ec 100644 --- a/mount.go +++ b/mount.go @@ -3,15 +3,15 @@ package aisap import ( "bufio" "bytes" + "errors" + "fmt" "os" "os/exec" "strconv" "strings" - "errors" - "fmt" + xdg "github.com/adrg/xdg" helpers "github.com/mgord9518/aisap/helpers" - xdg "github.com/adrg/xdg" ) // mount mounts the requested AppImage `src` to `dest` @@ -28,7 +28,7 @@ func mount(src string, dest string, offset int) error { // Convert the offset to a string and mount using squashfuse o := strconv.Itoa(offset) - mnt := exec.Command(squashfuse, "-o", "offset=" + o, src, dest) + mnt := exec.Command(squashfuse, "-o", "offset="+o, src, dest) mnt.Stderr = errBuf if mnt.Run() != nil { @@ -59,11 +59,15 @@ func (ai *AppImage) Mount(dest ...string) error { var err error - ai.tempDir, err = helpers.MakeTemp(xdg.RuntimeDir + "/aisap/tmp", ai.md5) - if err != nil { return err } + ai.tempDir, err = helpers.MakeTemp(xdg.RuntimeDir+"/aisap/tmp", ai.md5) + if err != nil { + return err + } - ai.mountDir, err = helpers.MakeTemp(xdg.RuntimeDir + "/aisap/mount", ai.md5) - if err != nil { return err } + ai.mountDir, err = helpers.MakeTemp(xdg.RuntimeDir+"/aisap/mount", ai.md5) + if err != nil { + return err + } fmt.Println(ai.mountDir) fmt.Println(ai.tempDir) @@ -94,7 +98,9 @@ func (ai *AppImage) Destroy() error { } err := unmountDir(ai.MountDir()) - if err != nil { return err } + if err != nil { + return err + } ai.mountDir = "" diff --git a/permissions/permissions.go b/permissions/permissions.go index b33fa27..17b5dcf 100644 --- a/permissions/permissions.go +++ b/permissions/permissions.go @@ -8,16 +8,16 @@ import ( "io/ioutil" "os" "path/filepath" - "strings" "strconv" + "strings" + xdg "github.com/adrg/xdg" helpers "github.com/mgord9518/aisap/helpers" - ini "gopkg.in/ini.v1" - xdg "github.com/adrg/xdg" + ini "gopkg.in/ini.v1" ) var ( - InvalidSocket = errors.New("socket invalid") + InvalidSocket = errors.New("socket invalid") ) type File struct { @@ -35,7 +35,7 @@ func SocketFromString(socketString string) (Socket, error) { return socket, InvalidSocket } - return socket, nil + return socket, nil } const ( @@ -73,18 +73,18 @@ var ( ) type AppImagePerms struct { - Level int `json:"level"` // How much access to system files - Files []string `json:"filesystem"` // Grant permission to access files - Devices []string `json:"devices"` // Access device files (eg: dri, input) - Sockets []Socket `json:"sockets"` // Use sockets (eg: x11, pulseaudio, network) + Level int `json:"level"` // How much access to system files + Files []string `json:"filesystem"` // Grant permission to access files + Devices []string `json:"devices"` // Access device files (eg: dri, input) + Sockets []Socket `json:"sockets"` // Use sockets (eg: x11, pulseaudio, network) // TODO: rename to PersistentHome or something - DataDir bool `json:"data_dir"` // Whether or not a data dir should be created (only + DataDir bool `json:"data_dir"` // Whether or not a data dir should be created (only // use if the AppImage saves ZERO data eg: 100% online or a game without // save files) // Only intended for unmarshalling, should not be used for other purposes - Names []string `json:"names"` + Names []string `json:"names"` } // FromIni attempts to read permissions from a provided *ini.File, if fail, it @@ -93,8 +93,8 @@ func FromIni(e *ini.File) (*AppImagePerms, error) { p := &AppImagePerms{} // Get permissions from keys - level := e.Section("X-App Permissions").Key("Level").Value() - filePerms := e.Section("X-App Permissions").Key("Files").Value() + level := e.Section("X-App Permissions").Key("Level").Value() + filePerms := e.Section("X-App Permissions").Key("Files").Value() devicePerms := e.Section("X-App Permissions").Key("Devices").Value() socketPerms := e.Section("X-App Permissions").Key("Sockets").Value() @@ -103,7 +103,7 @@ func FromIni(e *ini.File) (*AppImagePerms, error) { p.DataDir = false } else { p.DataDir = true - } + } l, err := strconv.Atoi(level) if err != nil || l < 0 || l > 3 { @@ -153,12 +153,16 @@ func FromSystem(name string) (*AppImagePerms, error) { func FromReader(r io.Reader) (*AppImagePerms, error) { b, err := ioutil.ReadAll(r) - if err != nil { return nil, err } + if err != nil { + return nil, err + } b = bytes.ReplaceAll(b, []byte(";"), []byte(";")) - + e, err := ini.Load(b) - if err != nil { return nil, err } + if err != nil { + return nil, err + } return FromIni(e) } @@ -177,13 +181,15 @@ func (p *AppImagePerms) AddDevices(s ...string) { } func (p *AppImagePerms) AddSockets(socketStrings ...string) error { - if len(socketStrings) == 0 { return nil} + if len(socketStrings) == 0 { + return nil + } p.RemoveSockets(socketStrings...) - for i := range(socketStrings) { + for i := range socketStrings { socket, err := SocketFromString(socketStrings[i]) - + if err != nil { return err } @@ -198,17 +204,17 @@ func (p *AppImagePerms) removeFile(str string) { // Done this way to ensure there is an `extension` eg: `:ro` on the string, // it will then be used to detect if that file already exists str = helpers.CleanFiles([]string{str})[0] - s := strings.Split(str, ":") + s := strings.Split(str, ":") str = strings.Join(s[:len(s)-1], ":") if i, present := helpers.ContainsAny(p.Files, - []string{ str + ":ro", str + ":rw" }); present { + []string{str + ":ro", str + ":rw"}); present { p.Files = append(p.Files[:i], p.Files[i+1:]...) } } func (p *AppImagePerms) RemoveFiles(s ...string) { - for i := range(s) { + for i := range s { p.removeFile(s[i]) } } @@ -220,7 +226,7 @@ func (p *AppImagePerms) removeDevice(str string) { } func (p *AppImagePerms) RemoveDevices(s ...string) { - for i := range(s) { + for i := range s { p.removeDevice(s[i]) } } @@ -235,7 +241,7 @@ func (p *AppImagePerms) removeSocket(str string) { } func (p *AppImagePerms) RemoveSockets(s ...string) { - for i := range(s) { + for i := range s { p.removeSocket(s[i]) } } diff --git a/profiles/README.md b/profiles/README.md index b79886a..558cddb 100644 --- a/profiles/README.md +++ b/profiles/README.md @@ -6,7 +6,7 @@ All items in this list are based on the name of their appropriate AppImage's desktop entry, as some of the same apps may have different names depending on the version, there is also a list of aliases that link to the original profile -## Current supported applications (105) +## Current supported applications (121) |name|level|devices|sockets|filesystem| |-|-|-|-|-| |0 a.d.|3|dri|x11, alsa, network|| @@ -14,7 +14,9 @@ the version, there is also a list of aliases that link to the original profile |apk editor studio|1|dri|x11|xdg-templates:rw, xdg-download:rw| |appimage pool|2|dri|wayland, x11, network|~/Applications:rw| |appimageupdate|2|dri|x11, network|~/Applications:rw| -|aranym jit|3|dri, input|x11, audio, network|xdg-download:ro, ~/Games:ro, ~/Roms:ro| +|aranym|3|dri, input|x11, audio, network|xdg-download:ro, ~/Games:ro, ~/Roms:ro| +|aranym|3|dri, input|x11, audio, network|xdg-download:ro, ~/Games:ro, ~/Roms:ro| +|aranym|3|dri, input|x11, audio, network|xdg-download:ro, ~/Games:ro, ~/Roms:ro| |armagetron advanced|3|dri, input|x11, audio, network|| |badlion client|2|dri|x11, audio, network|| |balenaetcher|0|||| @@ -22,10 +24,13 @@ the version, there is also a list of aliases that link to the original profile |brave|2|dri|x11, pulseaudio, network|xdg-download:rw| |bugdom|1|dri|x11, audio, network|| |calibre|2|dri|x11|xdg-documents:ro| +|cemu|2|dri, input|x11, alsa, network|xdg-download:ro, ~/Games:ro, ~/Roms:ro| |chromium|2|dri|x11, pulseaudio, network|xdg-download:rw| |conky|1|dri|x11, pid|| |cool retro term|2|dri|x11, network|~/.config/nvim:ro, ~/.profile:ro, ~/.bashrc:ro, ~/.zshrc:ro, ~/.viminfo:ro| |cro-mag rally|3|dri|x11, alsa|| +|deadbeef|2|dri|x11, audio, network|xdg-music:rw| +|deadbeef|2|dri|x11, audio, network|xdg-music:rw| |deemix-gui|2|dri|x11, audio, network|xdg-music:rw| |densify|2|dri|x11|xdg-documents:rw| |desmume|2|dri, input|x11, alsa|xdg-download:rw, ~/Games:rw, ~/Roms:rw| @@ -39,9 +44,13 @@ the version, there is also a list of aliases that link to the original profile |eternal lands (appimage)|1|dri|x11, audio, network|| |fireboy and watergirl: in the forest temple|1|dri|x11, pulseaudio|| |firefox|2|dri|x11, pulseaudio, network, dbus|xdg-download:rw| +|firefox|2|dri|x11, pulseaudio, network, dbus|xdg-download:rw| +|firefox|2|dri|x11, pulseaudio, network, dbus|xdg-download:rw| |fontforge|2|dri|x11|xdg-documents:rw, ~/.fonts:rw| |fractale|2|dri|x11|| -|freecad conda|1|dri|x11|xdg-documents:rw, xdg-templates:rw| +|freecad|1|dri|x11|xdg-documents:rw, xdg-templates:rw| +|freecad|1|dri|x11|xdg-documents:rw, xdg-templates:rw| +|freecad|1|dri|x11|xdg-documents:rw, xdg-templates:rw| |gambatte_qt|2|dri, input|x11, alsa|xdg-download:rw, ~/Games:rw, ~/Roms:rw| |geometrize|2|dri|x11|xdg-pictures:rw| |gnu image manipulation program|1|dri|x11|xdg-pictures:rw| @@ -85,8 +94,11 @@ the version, there is also a list of aliases that link to the original profile |pokete|3||alsa, network|| |potato presenter|2|dri|x11|xdg-documents:rw| |ppsspp|2|dri|x11, audio|xdg-download:ro, ~/Games:ro, ~/Roms:ro| -|python3.10.1|3|||| +|python|3|||| +|python|3|||| +|python|3|||| |runelite|1|dri|x11, audio, network|| +|ryujinx|2|dri, input|x11, alsa, network|xdg-download:ro, ~/Games:ro, ~/Roms:ro| |sengi|1|dri|x11, audio, network|| |signal|2|dri|x11, network|| |smallbasic|2|dri|x11|xdg-documents:rw| @@ -100,8 +112,10 @@ the version, there is also a list of aliases that link to the original profile |stellarium|1|dri|x11|| |stunt car remake|3|dri|x11, alsa|| |subsurface|1|dri|x11|xdg-documents:ro| +|yuzu|2|dri, input|x11, alsa, network|xdg-download:ro, ~/Games:ro, ~/Roms:ro| |supertux 2|3|dri, input|x11, audio, network|| |supertuxkart|3|dri, input|x11, audio, network|| +|yuzu|2|dri, input|x11, alsa, network|xdg-download:ro, ~/Games:ro, ~/Roms:ro| |synthein|3|dri|x11, alsa|| |tapecalc|2|dri|x11|| |texstudio|1|dri|x11|xdg-documents:rw, xdg-templates:rw| @@ -110,6 +124,8 @@ the version, there is also a list of aliases that link to the original profile |tiled|2|dri|x11|xdg-documents:rw, xdg-pictures:rw, xdg-templates:rw| |upscayl|1|dri|x11|xdg-pictures:rw| |visual studio code|2|dri|x11, network|xdg-documents:rw| +|visual studio code|2|dri|x11, network|xdg-documents:rw| +|waterfox|2|dri|x11, pulseaudio, network, dbus|xdg-download:rw| |waterfox|2|dri|x11, pulseaudio, network, dbus|xdg-download:rw| |xonotic|3|dri|x11, alsa, network|| |yabg|3|dri, input|x11, pulseaudio|| diff --git a/profiles/profiles.go b/profiles/profiles.go index 3ce61d5..8a2e175 100644 --- a/profiles/profiles.go +++ b/profiles/profiles.go @@ -1,11 +1,11 @@ package profiles import ( + "encoding/json" "errors" "strings" - "encoding/json" - helpers "github.com/mgord9518/aisap/helpers" + helpers "github.com/mgord9518/aisap/helpers" permissions "github.com/mgord9518/aisap/permissions" _ "embed" @@ -29,7 +29,7 @@ func FromName(name string) (*permissions.AppImagePerms, error) { return &p, nil } - return &permissions.AppImagePerms{ Level: -1 }, errors.New("cannot find permissions for app `" + name + "`") + return &permissions.AppImagePerms{Level: -1}, errors.New("cannot find permissions for app `" + name + "`") } //go:embed profile_database.json diff --git a/spooky/isspooky.go b/spooky/isspooky.go index 997cb40..a9a79ed 100644 --- a/spooky/isspooky.go +++ b/spooky/isspooky.go @@ -46,13 +46,13 @@ func IsSpooky(str string) bool { slice := strings.Split(str, ":") s1 := strings.Join(slice[:len(slice)-1], ":") - for _, val := range(spookyFiles) { + for _, val := range spookyFiles { if s1 == val { return true } } - for _, val := range(spookyDirs) { + for _, val := range spookyDirs { if len(s1) < len(val) { continue } diff --git a/wrap.go b/wrap.go index 1121d40..89e5c6c 100644 --- a/wrap.go +++ b/wrap.go @@ -9,60 +9,43 @@ import ( "strconv" "strings" - permissions "github.com/mgord9518/aisap/permissions" + xdg "github.com/adrg/xdg" helpers "github.com/mgord9518/aisap/helpers" - xdg "github.com/adrg/xdg" + permissions "github.com/mgord9518/aisap/permissions" ) -// Run the AppImage with appropriate sandboxing. If `ai.Perms.Level` == 0, use -// no sandbox. If > 0, sandbox -func (ai *AppImage) Run(args []string) error { - if !ai.IsMounted() { - return errors.New("AppImage must be mounted before running! call *AppImage.Mount() first") - } - - if ai.Perms.Level > 0 { - return ai.Sandbox(args) - } else if ai.Perms.Level < 0 { - return errors.New("invalid permissions level!") +// Executes AppImage through bwrap and creates a portable home if one doesn't +// already exist +// Returns error if AppImagePerms.Level < 1 +func (ai *AppImage) Sandbox(perms *permissions.AppImagePerms, args []string) error { + if perms.Level < 1 || perms.Level > 3 { + return errors.New("permissions level must be 1 - 3") } - os.Setenv("TMPDIR", ai.tempDir) - os.Setenv("APPDIR", ai.mountDir) - os.Setenv("APPIMAGE", ai.Path) - os.Setenv("ARGV0", path.Base(ai.Path)) - os.Setenv("XDG_CACHE_HOME", filepath.Join(xdg.CacheHome, "appimage", ai.md5)) - - cmd := exec.Command(filepath.Join(ai.mountDir, "AppRun"), args...) - - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - cmd.Stdin = os.Stdin - - return cmd.Run() -} - -// Executes AppImage through bwrap, fails if `ai.Perms.Level` < 1 -// Also automatically creates a portable home -func (ai *AppImage) Sandbox(args []string) error { if !helpers.DirExists(filepath.Join(xdg.CacheHome, "appimage", ai.md5)) { err := os.MkdirAll(filepath.Join(xdg.CacheHome, "appimage", ai.md5), 0744) - if err != nil { return err } + if err != nil { + return err + } } // Tell AppImages not to ask for integration - if ai.Perms.DataDir { - if !helpers.DirExists(filepath.Join(ai.dataDir, ".local/share/appimagekit")) { + if perms.DataDir { + if !helpers.DirExists(filepath.Join(ai.dataDir, ".local/share/appimagekit")) { err := os.MkdirAll(filepath.Join(ai.dataDir, ".local/share/appimagekit"), 0744) - if err != nil { return err } + if err != nil { + return err + } } noIntegrate, _ := os.Create(filepath.Join(ai.dataDir, ".local/share/appimagekit/no_desktopintegration")) noIntegrate.Close() } - cmdArgs, err := ai.WrapArgs(args) - if err != nil { return err } + cmdArgs, err := ai.WrapArgs(perms, args) + if err != nil { + return err + } bwrapStr, present := helpers.CommandExists("bwrap") if !present { @@ -72,13 +55,13 @@ func (ai *AppImage) Sandbox(args []string) error { bwrap := exec.Command(bwrapStr, cmdArgs...) bwrap.Stdout = os.Stdout bwrap.Stderr = os.Stderr - bwrap.Stdin = os.Stdin + bwrap.Stdin = os.Stdin return bwrap.Run() } // Returns the bwrap arguments to sandbox the AppImage -func (ai AppImage) WrapArgs(args []string) ([]string, error) { +func (ai AppImage) WrapArgs(perms *permissions.AppImagePerms, args []string) ([]string, error) { if !ai.IsMounted() { return []string{}, errors.New("AppImage must be mounted before getting its wrap arguments! call *AppImage.Mount() first") } @@ -86,110 +69,110 @@ func (ai AppImage) WrapArgs(args []string) ([]string, error) { home, present := unsetHome() defer restoreHome(home, present) - if ai.Perms.Level == 0 { return args, nil } + if perms.Level == 0 { + return args, nil + } - cmdArgs := ai.mainWrapArgs() + cmdArgs := ai.mainWrapArgs(perms) // Append console arguments provided by the user return append(cmdArgs, args...), nil } -func (ai *AppImage) mainWrapArgs() []string { +func (ai *AppImage) mainWrapArgs(perms *permissions.AppImagePerms) []string { uid := strconv.Itoa(os.Getuid()) home, present := unsetHome() defer restoreHome(home, present) // Basic arguments to be used at all sandboxing levels cmdArgs := []string{ - "--setenv", "TMPDIR", "/tmp", - "--setenv", "HOME", xdg.Home, - "--setenv", "APPDIR", "/tmp/.mount_"+ai.md5, - "--setenv", "APPIMAGE", filepath.Join("/app", path.Base(ai.Path)), - "--setenv", "ARGV0", filepath.Join(path.Base(ai.Path)), - "--setenv", "XDG_DESKTOP_DIR", filepath.Join(xdg.Home, "Desktop"), - "--setenv", "XDG_DOWNLOAD_DIR", filepath.Join(xdg.Home, "Downloads"), - "--setenv", "XDG_DOCUMENTS_DIR", filepath.Join(xdg.Home, "Documents"), - "--setenv", "XDG_MUSIC_DIR", filepath.Join(xdg.Home, "Music"), - "--setenv", "XDG_PICTURES_DIR", filepath.Join(xdg.Home, "Pictures"), - "--setenv", "XDG_VIDEOS_DIR", filepath.Join(xdg.Home, "Videos"), - "--setenv", "XDG_TEMPLATES_DIR", filepath.Join(xdg.Home, "Templates"), + "--setenv", "TMPDIR", "/tmp", + "--setenv", "HOME", xdg.Home, + "--setenv", "APPDIR", "/tmp/.mount_" + ai.md5, + "--setenv", "APPIMAGE", filepath.Join("/app", path.Base(ai.Path)), + "--setenv", "ARGV0", filepath.Join(path.Base(ai.Path)), + "--setenv", "XDG_DESKTOP_DIR", filepath.Join(xdg.Home, "Desktop"), + "--setenv", "XDG_DOWNLOAD_DIR", filepath.Join(xdg.Home, "Downloads"), + "--setenv", "XDG_DOCUMENTS_DIR", filepath.Join(xdg.Home, "Documents"), + "--setenv", "XDG_MUSIC_DIR", filepath.Join(xdg.Home, "Music"), + "--setenv", "XDG_PICTURES_DIR", filepath.Join(xdg.Home, "Pictures"), + "--setenv", "XDG_VIDEOS_DIR", filepath.Join(xdg.Home, "Videos"), + "--setenv", "XDG_TEMPLATES_DIR", filepath.Join(xdg.Home, "Templates"), "--setenv", "XDG_PUBLICSHARE_DIR", filepath.Join(xdg.Home, "Share"), - "--setenv", "XDG_DATA_HOME", filepath.Join(xdg.Home, ".local/share"), - "--setenv", "XDG_CONFIG_HOME", filepath.Join(xdg.Home, ".config"), - "--setenv", "XDG_CACHE_HOME", filepath.Join(xdg.Home, ".cache"), - "--setenv", "XDG_STATE_HOME", filepath.Join(xdg.Home, ".local/state"), - "--setenv", "XDG_RUNTIME_DIR", filepath.Join("/run/user", uid), + "--setenv", "XDG_DATA_HOME", filepath.Join(xdg.Home, ".local/share"), + "--setenv", "XDG_CONFIG_HOME", filepath.Join(xdg.Home, ".config"), + "--setenv", "XDG_CACHE_HOME", filepath.Join(xdg.Home, ".cache"), + "--setenv", "XDG_STATE_HOME", filepath.Join(xdg.Home, ".local/state"), + "--setenv", "XDG_RUNTIME_DIR", filepath.Join("/run/user", uid), "--die-with-parent", - "--perms", "0700", - "--dir", filepath.Join("/run/user", uid), - "--dev", "/dev", - "--proc", "/proc", - "--bind", filepath.Join(xdg.CacheHome, "appimage", ai.md5), filepath.Join(xdg.Home, ".cache"), - "--ro-bind-try", ai.resolve("opt"), "/opt", - "--ro-bind-try", ai.resolve("bin"), "/bin", - "--ro-bind-try", ai.resolve("sbin"), "/sbin", - "--ro-bind-try", ai.resolve("lib"), "/lib", - "--ro-bind-try", ai.resolve("lib32"), "/lib32", - "--ro-bind-try", ai.resolve("lib64"), "/lib64", - "--ro-bind-try", ai.resolve("usr/bin"), "/usr/bin", - "--ro-bind-try", ai.resolve("usr/sbin"), "/usr/sbin", - "--ro-bind-try", ai.resolve("usr/lib"), "/usr/lib", + "--perms", "0700", + "--dir", filepath.Join("/run/user", uid), + "--dev", "/dev", + "--proc", "/proc", + "--bind", filepath.Join(xdg.CacheHome, "appimage", ai.md5), filepath.Join(xdg.Home, ".cache"), + "--ro-bind-try", ai.resolve("opt"), "/opt", + "--ro-bind-try", ai.resolve("bin"), "/bin", + "--ro-bind-try", ai.resolve("sbin"), "/sbin", + "--ro-bind-try", ai.resolve("lib"), "/lib", + "--ro-bind-try", ai.resolve("lib32"), "/lib32", + "--ro-bind-try", ai.resolve("lib64"), "/lib64", + "--ro-bind-try", ai.resolve("usr/bin"), "/usr/bin", + "--ro-bind-try", ai.resolve("usr/sbin"), "/usr/sbin", + "--ro-bind-try", ai.resolve("usr/lib"), "/usr/lib", "--ro-bind-try", ai.resolve("usr/lib32"), "/usr/lib32", "--ro-bind-try", ai.resolve("usr/lib64"), "/usr/lib64", - "--dir", "/app", - "--bind", ai.Path, filepath.Join("/app", path.Base(ai.Path)), + "--dir", "/app", + "--bind", ai.Path, filepath.Join("/app", path.Base(ai.Path)), } // Level 1 is minimal sandboxing, grants access to most system files, all devices and only really attempts to isolate home files - if ai.Perms.Level == 1 { + if perms.Level == 1 { cmdArgs = append(cmdArgs, []string{ - "--dev-bind", "/dev", "/dev", - "--ro-bind", "/sys", "/sys", + "--dev-bind", "/dev", "/dev", + "--ro-bind", "/sys", "/sys", "--ro-bind-try", ai.resolve("usr"), "/usr", "--ro-bind-try", ai.resolve("etc"), "/etc", - "--ro-bind-try", ai.resolve("/run/systemd"), "/run/systemd", - "--ro-bind-try", filepath.Join(xdg.Home, ".fonts"), filepath.Join(xdg.Home, ".fonts"), - "--ro-bind-try", filepath.Join(xdg.ConfigHome, "fontconfig"), filepath.Join(xdg.Home, ".config", "fontconfig"), - "--ro-bind-try", filepath.Join(xdg.ConfigHome, "gtk-3.0"), filepath.Join(xdg.Home, ".config", "gtk-3.0"), - "--ro-bind-try", filepath.Join(xdg.ConfigHome, "kdeglobals"), filepath.Join(xdg.Home, ".config", "kdeglobals"), + "--ro-bind-try", ai.resolve("/run/systemd"), "/run/systemd", + "--ro-bind-try", filepath.Join(xdg.Home, ".fonts"), filepath.Join(xdg.Home, ".fonts"), + "--ro-bind-try", filepath.Join(xdg.ConfigHome, "fontconfig"), filepath.Join(xdg.Home, ".config", "fontconfig"), + "--ro-bind-try", filepath.Join(xdg.ConfigHome, "gtk-3.0"), filepath.Join(xdg.Home, ".config", "gtk-3.0"), + "--ro-bind-try", filepath.Join(xdg.ConfigHome, "kdeglobals"), filepath.Join(xdg.Home, ".config", "kdeglobals"), "--ro-bind-try", filepath.Join(xdg.ConfigHome, "lxde", "lxde.conf"), filepath.Join(xdg.Home, ".config", "lxde", "lxde.conf"), }...) - // Level 2 grants access to fewer system files, and all themes - // Likely to add more files here for compatability. - // This should be the standard level for GUI profiles - } else if ai.Perms.Level == 2 { + // Level 2 grants access to fewer system files, and all themes + // Likely to add more files here for compatability. + // This should be the standard level for GUI profiles + } else if perms.Level == 2 { cmdArgs = append(cmdArgs, []string{ - "--ro-bind-try", ai.resolve("etc/fonts"), "/etc/fonts", - "--ro-bind-try", ai.resolve("etc/ld.so.cache"), "/etc/ld.so.cache", - "--ro-bind-try", ai.resolve("etc/mime.types"), "/etc/mime.types", - "--ro-bind-try", ai.resolve("etc/xdg"), "/etc/xdg", - "--ro-bind-try", ai.resolve("usr/share/fontconfig"), "/usr/share/fontconfig", - "--ro-bind-try", ai.resolve("usr/share/fonts"), "/usr/share/fonts", - "--ro-bind-try", ai.resolve("usr/share/icons"), "/usr/share/icons", - "--ro-bind-try", ai.resolve("usr/share/themes"), "/usr/share/themes", + "--ro-bind-try", ai.resolve("etc/fonts"), "/etc/fonts", + "--ro-bind-try", ai.resolve("etc/ld.so.cache"), "/etc/ld.so.cache", + "--ro-bind-try", ai.resolve("etc/mime.types"), "/etc/mime.types", + "--ro-bind-try", ai.resolve("etc/xdg"), "/etc/xdg", + "--ro-bind-try", ai.resolve("usr/share/fontconfig"), "/usr/share/fontconfig", + "--ro-bind-try", ai.resolve("usr/share/fonts"), "/usr/share/fonts", + "--ro-bind-try", ai.resolve("usr/share/icons"), "/usr/share/icons", + "--ro-bind-try", ai.resolve("usr/share/themes"), "/usr/share/themes", "--ro-bind-try", ai.resolve("usr/share/applications"), "/usr/share/applications", - "--ro-bind-try", ai.resolve("usr/share/mime"), "/usr/share/mime", - "--ro-bind-try", ai.resolve("usr/share/libdrm"), "/usr/share/libdrm", - "--ro-bind-try", ai.resolve("usr/share/vulkan"), "/usr/share/vulkan", - "--ro-bind-try", ai.resolve("usr/share/glvnd"), "/usr/share/glvnd", - "--ro-bind-try", ai.resolve("usr/share/glib-2.0"), "/usr/share/glib-2.0", - "--ro-bind-try", ai.resolve("usr/share/terminfo"), "/usr/share/terminfo", - "--ro-bind-try", filepath.Join(xdg.Home, ".fonts"), filepath.Join(xdg.Home, ".fonts"), - "--ro-bind-try", filepath.Join(xdg.ConfigHome, "fontconfig"), filepath.Join(xdg.Home, ".config", "fontconfig"), - "--ro-bind-try", filepath.Join(xdg.ConfigHome, "gtk-3.0"), filepath.Join(xdg.Home, ".config", "gtk-3.0"), - "--ro-bind-try", filepath.Join(xdg.ConfigHome, "kdeglobals"), filepath.Join(xdg.Home, ".config", "kdeglobals"), + "--ro-bind-try", ai.resolve("usr/share/mime"), "/usr/share/mime", + "--ro-bind-try", ai.resolve("usr/share/libdrm"), "/usr/share/libdrm", + "--ro-bind-try", ai.resolve("usr/share/vulkan"), "/usr/share/vulkan", + "--ro-bind-try", ai.resolve("usr/share/glvnd"), "/usr/share/glvnd", + "--ro-bind-try", ai.resolve("usr/share/glib-2.0"), "/usr/share/glib-2.0", + "--ro-bind-try", ai.resolve("usr/share/terminfo"), "/usr/share/terminfo", + "--ro-bind-try", filepath.Join(xdg.Home, ".fonts"), filepath.Join(xdg.Home, ".fonts"), + "--ro-bind-try", filepath.Join(xdg.ConfigHome, "fontconfig"), filepath.Join(xdg.Home, ".config", "fontconfig"), + "--ro-bind-try", filepath.Join(xdg.ConfigHome, "gtk-3.0"), filepath.Join(xdg.Home, ".config", "gtk-3.0"), + "--ro-bind-try", filepath.Join(xdg.ConfigHome, "kdeglobals"), filepath.Join(xdg.Home, ".config", "kdeglobals"), "--ro-bind-try", filepath.Join(xdg.ConfigHome, "lxde", "lxde.conf"), filepath.Join(xdg.Home, ".config", "lxde", "lxde.conf"), }...) - } else if ai.Perms.Level > 3 || ai.Perms.Level < 1 { - return []string{} } - cmdArgs = append(cmdArgs, parseFiles(ai)...) - cmdArgs = append(cmdArgs, parseSockets(ai)...) - cmdArgs = append(cmdArgs, parseDevices(ai)...) + cmdArgs = append(cmdArgs, parseFiles(perms)...) + cmdArgs = append(cmdArgs, parseSockets(ai, perms)...) + cmdArgs = append(cmdArgs, parseDevices(ai, perms)...) cmdArgs = append(cmdArgs, "--", "/tmp/.mount_"+ai.md5+"/AppRun") - if ai.Perms.DataDir { + if perms.DataDir { cmdArgs = append([]string{ "--bind", ai.dataDir, xdg.Home, }, cmdArgs...) @@ -200,8 +183,8 @@ func (ai *AppImage) mainWrapArgs() []string { } cmdArgs = append([]string{ - "--bind", ai.tempDir, "/tmp", - "--bind", ai.mountDir, "/tmp/.mount_"+ai.md5, + "--bind", ai.tempDir, "/tmp", + "--bind", ai.mountDir, "/tmp/.mount_" + ai.md5, }, cmdArgs...) return cmdArgs @@ -220,13 +203,13 @@ func (ai AppImage) resolve(src string) string { return s } -func parseFiles(ai *AppImage) []string { +func parseFiles(perms *permissions.AppImagePerms) []string { var s []string // Convert requested files/ dirs to brap flags - for _, val := range(ai.Perms.Files) { - sl := strings.Split(val, ":") - ex := sl[len(sl)-1] + for _, val := range perms.Files { + sl := strings.Split(val, ":") + ex := sl[len(sl)-1] dir := strings.Join(sl[:len(sl)-1], ":") if ex == "rw" { @@ -240,11 +223,11 @@ func parseFiles(ai *AppImage) []string { } // Give all requried flags to add the devices -func parseDevices(ai *AppImage) []string { +func parseDevices(ai *AppImage, perms *permissions.AppImagePerms) []string { var d []string // Convert device perms to bwrap format - for _, v := range(ai.Perms.Devices) { + for _, v := range perms.Devices { if len(v) < 5 || v[0:5] != "/dev/" { v = filepath.Join("/dev", v) } @@ -253,22 +236,22 @@ func parseDevices(ai *AppImage) []string { } // Required files to go along with them - var devices = map[string][]string { + var devices = map[string][]string{ "dri": { - "--ro-bind", "/sys/dev/char", "/sys/dev/char", - "--ro-bind", "/sys/devices/pci0000:00", "/sys/devices/pci0000:00", - "--dev-bind-try", "/dev/nvidiactl", "/dev/nvidiactl", - "--dev-bind-try", "/dev/nvidia0", "/dev/nvidia0", - "--dev-bind-try", "/dev/nvidia-modeset", "/dev/nvidia-modeset", - "--ro-bind-try", ai.resolve("usr/share/glvnd"), "/usr/share/glvnd", + "--ro-bind", "/sys/dev/char", "/sys/dev/char", + "--ro-bind", "/sys/devices/pci0000:00", "/sys/devices/pci0000:00", + "--dev-bind-try", "/dev/nvidiactl", "/dev/nvidiactl", + "--dev-bind-try", "/dev/nvidia0", "/dev/nvidia0", + "--dev-bind-try", "/dev/nvidia-modeset", "/dev/nvidia-modeset", + "--ro-bind-try", ai.resolve("usr/share/glvnd"), "/usr/share/glvnd", }, "input": { "--ro-bind", "/sys/class/input", "/sys/class/input", }, } - for device, _ := range(devices) { - if _, present := helpers.Contains(ai.Perms.Devices, device); present { + for device, _ := range devices { + if _, present := helpers.Contains(perms.Devices, device); present { d = append(d, devices[device]...) } } @@ -276,16 +259,16 @@ func parseDevices(ai *AppImage) []string { return d } -func parseSockets(ai *AppImage) []string { +func parseSockets(ai *AppImage, perms *permissions.AppImagePerms) []string { var s []string uid := strconv.Itoa(os.Getuid()) // These vars will only be used if x11 socket is granted access xAuthority := os.Getenv("XAUTHORITY") - if xAuthority == "" { - xAuthority = xdg.Home + "/.Xauthority" - } + if xAuthority == "" { + xAuthority = xdg.Home + "/.Xauthority" + } xDisplay := strings.ReplaceAll(os.Getenv("DISPLAY"), ":", "") tempDir, present := os.LookupEnv("TMPDIR") @@ -298,93 +281,93 @@ func parseSockets(ai *AppImage) []string { wDisplay, waylandEnabled := os.LookupEnv("WAYLAND_DISPLAY") // Args if socket is enabled - var sockets = map[string][]string { + var sockets = map[string][]string{ // Encompasses ALSA, Pulse and pipewire. Easiest for convience, but for // more security, specify the specific audio system "alsa": { "--ro-bind-try", ai.resolve("/usr/share/alsa"), "/usr/share/alsa", - "--ro-bind-try", ai.resolve("/etc/alsa"), "/etc/alsa", - "--ro-bind-try", ai.resolve("/etc/group"), "/etc/group", - "--dev-bind", ai.resolve("/dev/snd"), "/dev/snd", + "--ro-bind-try", ai.resolve("/etc/alsa"), "/etc/alsa", + "--ro-bind-try", ai.resolve("/etc/group"), "/etc/group", + "--dev-bind", ai.resolve("/dev/snd"), "/dev/snd", }, "audio": { - "--ro-bind-try", filepath.Join(xdg.RuntimeDir, "pulse"), "/run/user/"+uid+"/pulse", - "--ro-bind-try", ai.resolve("/usr/share/alsa"), "/usr/share/alsa", - "--ro-bind-try", ai.resolve("/usr/share/pulseaudio"), "/usr/share/pulseaudio", - "--ro-bind-try", ai.resolve("/etc/alsa"), "/etc/alsa", - "--ro-bind-try", ai.resolve("/etc/group"), "/etc/group", - "--ro-bind-try", ai.resolve("/etc/pulse"), "/etc/pulse", - "--dev-bind", ai.resolve("/dev/snd"), "/dev/snd", + "--ro-bind-try", filepath.Join(xdg.RuntimeDir, "pulse"), "/run/user/" + uid + "/pulse", + "--ro-bind-try", ai.resolve("/usr/share/alsa"), "/usr/share/alsa", + "--ro-bind-try", ai.resolve("/usr/share/pulseaudio"), "/usr/share/pulseaudio", + "--ro-bind-try", ai.resolve("/etc/alsa"), "/etc/alsa", + "--ro-bind-try", ai.resolve("/etc/group"), "/etc/group", + "--ro-bind-try", ai.resolve("/etc/pulse"), "/etc/pulse", + "--dev-bind", ai.resolve("/dev/snd"), "/dev/snd", }, "cgroup": {}, "dbus": { - "--ro-bind-try", filepath.Join(xdg.RuntimeDir, "bus"), "/run/user/"+uid+"/bus", + "--ro-bind-try", filepath.Join(xdg.RuntimeDir, "bus"), "/run/user/" + uid + "/bus", }, - "ipc": {}, + "ipc": {}, "network": { - "--share-net", - "--ro-bind-try", ai.resolve("/etc/ca-certificates"), "/etc/ca-certificates", - "--ro-bind-try", ai.resolve("/etc/resolv.conf"), "/etc/resolv.conf", - "--ro-bind-try", ai.resolve("/etc/ssl"), "/etc/ssl", - "--ro-bind-try", ai.resolve("/etc/pki"), "/etc/pki", - "--ro-bind-try", ai.resolve("/usr/share/ca-certificates"), "/usr/share/ca-certificates", + "--share-net", + "--ro-bind-try", ai.resolve("/etc/ca-certificates"), "/etc/ca-certificates", + "--ro-bind-try", ai.resolve("/etc/resolv.conf"), "/etc/resolv.conf", + "--ro-bind-try", ai.resolve("/etc/ssl"), "/etc/ssl", + "--ro-bind-try", ai.resolve("/etc/pki"), "/etc/pki", + "--ro-bind-try", ai.resolve("/usr/share/ca-certificates"), "/usr/share/ca-certificates", }, "pid": {}, "pipewire": { - "--ro-bind-try", filepath.Join(xdg.RuntimeDir, "pipewire-0"), "/run/user/"+uid+"/pipewire-0", + "--ro-bind-try", filepath.Join(xdg.RuntimeDir, "pipewire-0"), "/run/user/" + uid + "/pipewire-0", }, "pulseaudio": { - "--ro-bind-try", filepath.Join(xdg.RuntimeDir, "pulse"), "/run/user/"+uid+"/pulse", + "--ro-bind-try", filepath.Join(xdg.RuntimeDir, "pulse"), "/run/user/" + uid + "/pulse", // TODO: fix bwrap error when running in level 1 - "--ro-bind-try", ai.resolve("/etc/pulse"), "/etc/pulse", + "--ro-bind-try", ai.resolve("/etc/pulse"), "/etc/pulse", }, "session": {}, "user": {}, "uts": {}, "wayland": { - "--ro-bind-try", filepath.Join(xdg.RuntimeDir, wDisplay), "/run/user/"+uid+"/wayland-0", - "--ro-bind-try", ai.resolve("/usr/share/X11"), "/usr/share/X11", + "--ro-bind-try", filepath.Join(xdg.RuntimeDir, wDisplay), "/run/user/" + uid + "/wayland-0", + "--ro-bind-try", ai.resolve("/usr/share/X11"), "/usr/share/X11", // TODO: Add more enviornment variables for app compatability // maybe theres a better way to do this? - "--setenv", "WAYLAND_DISPLAY", "wayland-0", + "--setenv", "WAYLAND_DISPLAY", "wayland-0", "--setenv", "_JAVA_AWT_WM_NONREPARENTING", "1", - "--setenv", "MOZ_ENABLE_WAYLAND", "1", - "--setenv", "XDG_SESSION_TYPE", "wayland", + "--setenv", "MOZ_ENABLE_WAYLAND", "1", + "--setenv", "XDG_SESSION_TYPE", "wayland", }, // For some reason sometimes it doesn't work when binding X0 to another // socket ...but sometimes it does. X11 should be avoided if looking // for security anyway, as it easilly allows control of the keyboard // and mouse "x11": { - "--ro-bind-try", xAuthority, xdg.Home+"/.Xauthority", - "--ro-bind-try", tempDir+"/.X11-unix/X"+xDisplay, "/tmp/.X11-unix/X"+xDisplay, - "--ro-bind-try", ai.resolve("/usr/share/X11"), "/usr/share/X11", + "--ro-bind-try", xAuthority, xdg.Home + "/.Xauthority", + "--ro-bind-try", tempDir + "/.X11-unix/X" + xDisplay, "/tmp/.X11-unix/X" + xDisplay, + "--ro-bind-try", ai.resolve("/usr/share/X11"), "/usr/share/X11", //"--setenv", "DISPLAY", ":"+xDisplay, - "--setenv", "QT_QPA_PLATFORM", "xcb", - "--setenv", "XAUTHORITY", xdg.Home+"/.Xauthority", + "--setenv", "QT_QPA_PLATFORM", "xcb", + "--setenv", "XAUTHORITY", xdg.Home + "/.Xauthority", }, } // Args to disable sockets if not given - var unsocks = map[string][]string { + var unsocks = map[string][]string{ "alsa": {}, "audio": {}, - "cgroup": { "--unshare-cgroup-try" }, - "ipc": { "--unshare-ipc" }, - "network": { "--unshare-net" }, - "pid": { "--unshare-pid" }, + "cgroup": {"--unshare-cgroup-try"}, + "ipc": {"--unshare-ipc"}, + "network": {"--unshare-net"}, + "pid": {"--unshare-pid"}, "pipewire": {}, "pulseaudio": {}, - "session": { "--new-session" }, - "user": { "--unshare-user-try" }, - "uts": { "--unshare-uts" }, + "session": {"--new-session"}, + "user": {"--unshare-user-try"}, + "uts": {"--unshare-uts"}, "wayland": {}, "x11": {}, } - for socketString, _ := range(sockets) { + for socketString, _ := range sockets { var present = false - for _, sock := range ai.Perms.Sockets { + for _, sock := range perms.Sockets { if sock == permissions.Socket(socketString) { present = true } @@ -394,7 +377,7 @@ func parseSockets(ai *AppImage) []string { // Don't give access to X11 if wayland is running on the machine // and the app supports it var waylandApp = false - for _, sock := range ai.Perms.Sockets { + for _, sock := range perms.Sockets { if sock == permissions.Socket("wayland") { waylandApp = true } @@ -405,7 +388,7 @@ func parseSockets(ai *AppImage) []string { } // If level 1, do not try to share /etc files again - if socketString == "network" && ai.Perms.Level == 1 { + if socketString == "network" && perms.Level == 1 { s = append(s, "--share-net") continue } @@ -431,7 +414,7 @@ func unsetHome() (string, bool) { os.Setenv("HOME", newHome) xdg.Reload() - return home, present + return home, present } // Return the HOME variable to normal diff --git a/zig/build.zig.zon b/zig/build.zig.zon index 5accf26..b342ad1 100644 --- a/zig/build.zig.zon +++ b/zig/build.zig.zon @@ -1,6 +1,6 @@ .{ .name = "aisap", - .version = "0.9.11-alpha", + .version = "0.10.11-alpha", .paths = [][]const u8 {""}, .dependencies = .{ .squashfuse = .{