sane-sandboxed: use local where applicable

This commit is contained in:
Colin 2024-05-12 22:15:34 +00:00
parent 016df3ff74
commit 980fe6b33c

View File

@ -154,39 +154,39 @@ relativeTo() {
# chomps trailing slashes. # chomps trailing slashes.
# does not resolve symlinks, nor check for existence of any component of the path. # does not resolve symlinks, nor check for existence of any component of the path.
normPath() { normPath() {
_npOut="$1" local npOut="$1"
_npUnparsed="$2" local npUnparsed="$2"
_npComps=() local npComps=()
while [ -n "$_npUnparsed" ]; do while [ -n "$npUnparsed" ]; do
# chomp leading `/` # chomp leading `/`
_npUnparsed="${_npUnparsed:1}" npUnparsed="${npUnparsed:1}"
# split into <first></re/st/...> # split into <first></re/st/...>
# in the case of `//`, or more, _npThisComp is empty, # in the case of `//`, or more, npThisComp is empty,
# and we push nothing to _npComps, # and we push nothing to npComps,
# but we're guaranteed to make progress. # but we're guaranteed to make progress.
_npThisComp="${_npUnparsed%%/*}" npThisComp="${npUnparsed%%/*}"
_npThisLen="${#_npThisComp}" npThisLen="${#npThisComp}"
_npUnparsed="${_npUnparsed:$_npThisLen}" npUnparsed="${npUnparsed:$npThisLen}"
if [ "$_npThisComp" = ".." ]; then if [ "$npThisComp" = ".." ]; then
# "go up" path component => delete the leaf dir (if any) # "go up" path component => delete the leaf dir (if any)
if [ ${#_npComps[@]} -ne 0 ]; then if [ ${#npComps[@]} -ne 0 ]; then
unset _npComps[-1] unset npComps[-1]
fi fi
elif [ "$_npThisComp" != "." ] && [ -n "$_npThisComp" ]; then elif [ "$npThisComp" != "." ] && [ -n "$npThisComp" ]; then
# normal, non-empty path component => append it # normal, non-empty path component => append it
_npComps+=("$_npThisComp") npComps+=("$npThisComp")
fi fi
done done
# join the components # join the components
if [ ${#_npComps[@]} -eq 0 ]; then if [ ${#npComps[@]} -eq 0 ]; then
declare -g "$_npOut"="/" declare -g "$npOut"="/"
else else
_npJoined= local npJoined=
for _npComp in "${_npComps[@]}"; do for npComp in "${npComps[@]}"; do
_npJoined="$_npJoined/$_npComp" npJoined="$npJoined/$npComp"
done done
declare -g "$_npOut"="$_npJoined" declare -g "$npOut"="$npJoined"
fi fi
} }
@ -263,21 +263,21 @@ loadProfileByPath() {
} }
tryLoadProfileByName() { tryLoadProfileByName() {
_profile="$1" local profile="$1"
if [ "${_profile:0:1}" = "/" ]; then if [ "${profile:0:1}" = "/" ]; then
# absolute path to profile. # absolute path to profile.
# consider it an error if it doesn't exist. # consider it an error if it doesn't exist.
# in general, prefer to use `--sane-sandbox-profile-dir` and specify the profile by name. # in general, prefer to use `--sane-sandbox-profile-dir` and specify the profile by name.
# doing so maximizes compatibility with anything else that uses the name, like firejail. # doing so maximizes compatibility with anything else that uses the name, like firejail.
loadProfileByPath "$_profile" loadProfileByPath "$profile"
else else
profilesNamed+=("$_profile") profilesNamed+=("$profile")
for _profileDir in "${profileDirs[@]}"; do for profileDir in "${profileDirs[@]}"; do
_profilePath="$_profileDir/$_profile.profile" local profilePath="$profileDir/$profile.profile"
debug "try profile at path: '$_profilePath'" debug "try profile at path: '$profilePath'"
if [ -f "$_profilePath" ]; then if [ -f "$profilePath" ]; then
loadProfileByPath "$_profilePath" loadProfileByPath "$profilePath"
break break
fi fi
done done
@ -291,55 +291,54 @@ initDefaultProfileDirs() {
} }
# subroutine of `tryArgAsPath` for after the arg has been converted into a valid (but possibly not existing) path. # subroutine of `tryArgAsPath` for after the arg has been converted into a valid (but possibly not existing) path.
# adds an entry to `cliPathArgs` and evals `true` on success; # adds an entry to `paths` and evals `true` on success;
# evals `false` if the path couldn't be added, for any reason. # evals `false` if the path couldn't be added, for any reason.
cliPathArgs=()
tryPath() { tryPath() {
_path="$1" local path="$1"
_how="$2" local how="$2"
if [ "$_how" = "existing" ]; then if [ "$how" = "existing" ]; then
# the caller wants to access either a file, or a directory (possibly a symlink to such a thing) # the caller wants to access either a file, or a directory (possibly a symlink to such a thing)
if [ -e "$_path" ]; then if [ -e "$path" ]; then
cliPathArgs+=("$(relativeTo "$PWD" "$_path")") paths+=("$(relativeTo "$PWD" "$path")")
true true
fi fi
false false
elif [ "$_how" = "existingFile" ]; then elif [ "$how" = "existingFile" ]; then
# the caller wants to access a file, and explicitly *not* a directory (though it could be a symlink *to a file*) # the caller wants to access a file, and explicitly *not* a directory (though it could be a symlink *to a file*)
if [ -f "$_path" ]; then if [ -f "$path" ]; then
cliPathArgs+=("$(relativeTo "$PWD" "$_path")") paths+=("$(relativeTo "$PWD" "$path")")
true true
fi fi
false false
elif [ "$_how" = "parent" ]; then elif [ "$how" = "parent" ]; then
# the caller wants access to the entire directory containing this directory regardless of the file's existence. # the caller wants access to the entire directory containing this directory regardless of the file's existence.
parent _tryPathParent "$_path" parent _tryPathParent "$path"
tryPath "$_tryPathParent" "existing" tryPath "$_tryPathParent" "existing"
elif [ "$_how" = "existingOrParent" ]; then elif [ "$how" = "existingOrParent" ]; then
# the caller wants access to the path, or write access to the parent directory so it may create the path if it doesn't exist. # the caller wants access to the path, or write access to the parent directory so it may create the path if it doesn't exist.
tryPath "$_path" "existing" || tryPath "$_path" "parent" tryPath "$path" "existing" || tryPath "$path" "parent"
elif [ "$_how" = "existingFileOrParent" ]; then elif [ "$how" = "existingFileOrParent" ]; then
# the caller wants access to the file, or write access to the parent directory so it may create the file if it doesn't exist. # the caller wants access to the file, or write access to the parent directory so it may create the file if it doesn't exist.
tryPath "$_path" "existingFile" || tryPath "$_path" "parent" tryPath "$path" "existingFile" || tryPath "$path" "parent"
fi fi
} }
# if the argument looks path-like, then add it to cliPathArgs. # if the argument looks path-like, then add it to paths.
# this function ingests absolute, relative, or file:///-type URIs. # this function ingests absolute, relative, or file:///-type URIs.
# but it converts any such path into an absolute path before adding it to cliPathArgs. # but it converts any such path into an absolute path before adding it to paths.
tryArgAsPath() { tryArgAsPath() {
_arg="$1" local arg="$1"
_how="$2" local how="$2"
_path= _path=
if [ "${_arg:0:1}" = "/" ]; then if [ "${arg:0:1}" = "/" ]; then
# absolute path # absolute path
_path="$_arg" _path="$_arg"
elif [ "${_arg:0:8}" = "file:///" ]; then elif [ "${arg:0:8}" = "file:///" ]; then
# URI to an absolute path which is presumably on this vfs # URI to an absolute path which is presumably on this vfs
# commonly found when xdg-open/mimeo passes a path on to an application # commonly found when xdg-open/mimeo passes a path on to an application
# if URIs to relative paths exist, this implementation doesn't support them # if URIs to relative paths exist, this implementation doesn't support them
urldecode _path "${_arg:7}" urldecode _path "${arg:7}"
elif [ "${_path:0:1}" = "-" ]; then elif [ "${_path:0:1}" = "-" ]; then
# 99% chance it's a CLI argument. if not, use `./-<...>` # 99% chance it's a CLI argument. if not, use `./-<...>`
return return
@ -348,7 +347,7 @@ tryArgAsPath() {
_path="$PWD/$_arg" _path="$PWD/$_arg"
fi fi
tryPath "$_path" "$_how" tryPath "$_path" "$how"
} }
@ -358,9 +357,9 @@ tryArgAsPath() {
parseArgsExtra=() parseArgsExtra=()
parseArgs() { parseArgs() {
while [ "$#" -ne 0 ]; do while [ "$#" -ne 0 ]; do
_arg="$1" local arg="$1"
shift shift
case "$_arg" in case "$arg" in
(--) (--)
# rest of args are for the CLI, and not for us. # rest of args are for the CLI, and not for us.
# consider two cases: # consider two cases:
@ -410,9 +409,9 @@ parseArgs() {
shift shift
;; ;;
(--sane-sandbox-cap) (--sane-sandbox-cap)
_cap="$1" local cap="$1"
shift shift
capabilities+=("$_cap") capabilities+=("$cap")
;; ;;
(--sane-sandbox-portal) (--sane-sandbox-portal)
# instruct glib/gtk apps to perform actions such as opening external files via dbus calls to org.freedesktop.portal.*. # instruct glib/gtk apps to perform actions such as opening external files via dbus calls to org.freedesktop.portal.*.
@ -427,41 +426,40 @@ parseArgs() {
;; ;;
(--sane-sandbox-dns) (--sane-sandbox-dns)
# N.B.: these named temporary variables ensure that "set -x" causes $1 to be printed # N.B.: these named temporary variables ensure that "set -x" causes $1 to be printed
_dns="$1" local dns="$1"
shift shift
dns+=("$_dns") dns+=("$dns")
;; ;;
(--sane-sandbox-firejail-arg) (--sane-sandbox-firejail-arg)
_fjFlag="$1" local fjFlag="$1"
shift shift
firejailFlags+=("$_fjFlag") firejailFlags+=("$fjFlag")
;; ;;
(--sane-sandbox-bwrap-arg) (--sane-sandbox-bwrap-arg)
_bwrapFlag="$1" local bwrapFlag="$1"
shift shift
bwrapFlags+=("$_bwrapFlag") bwrapFlags+=("$bwrapFlag")
;; ;;
(--sane-sandbox-net) (--sane-sandbox-net)
net="$1" net="$1"
shift shift
;; ;;
(--sane-sandbox-keep-namespace) (--sane-sandbox-keep-namespace)
_namespace="$1" local namespace="$1"
shift shift
if [ "$_namespace" = all ]; then if [ "$namespace" = all ]; then
keepNamespace+=("cgroup" "ipc" "pid" "uts") keepNamespace+=("cgroup" "ipc" "pid" "uts")
else else
keepNamespace+=("$_namespace") keepNamespace+=("$namespace")
fi fi
;; ;;
(--sane-sandbox-path) (--sane-sandbox-path)
_path="$1" local path="$1"
shift shift
paths+=("$_path") paths+=("$(relativeTo "$PWD" "$path")")
;; ;;
(--sane-sandbox-add-pwd) (--sane-sandbox-add-pwd)
_path="$PWD" paths+=("$PWD")
paths+=("$_path")
;; ;;
(--sane-sandbox-profile) (--sane-sandbox-profile)
# load the profile *immediately*, inline. # load the profile *immediately*, inline.
@ -472,12 +470,12 @@ parseArgs() {
(--sane-sandbox-profile-dir) (--sane-sandbox-profile-dir)
# add another directory in which to search for profiles, # add another directory in which to search for profiles,
# and give it *greater* precedence than the existing search directories (i.e. override the default profile) # and give it *greater* precedence than the existing search directories (i.e. override the default profile)
_dir="$1" local dir="$1"
shift shift
profileDirs=("$_dir" "${profileDirs[@]}") profileDirs=("$dir" "${profileDirs[@]}")
;; ;;
(*) (*)
parseArgsExtra+=("$_arg") parseArgsExtra+=("$arg")
;; ;;
esac esac
done done
@ -511,12 +509,12 @@ firejailIngestProfile() {
firejailName="$1" firejailName="$1"
fi fi
if [ -z "$firejailProfile" ]; then if [ -z "$firejailProfile" ]; then
_fjProfileDirs=(@firejailProfileDirs@) local fjProfileDirs=(@firejailProfileDirs@)
for _fjProfileDir in "${_fjProfileDirs[@]}"; do for fjProfileDir in "${fjProfileDirs[@]}"; do
_fjProfile="$_fjProfileDir/$1.profile" local fjProfile="$fjProfileDir/$1.profile"
debug "try firejail profile at path: '$_fjProfile'" debug "try firejail profile at path: '$fjProfile'"
if [ -f "$_fjProfile" ]; then if [ -f "$fjProfile" ]; then
firejailProfile="$_fjProfile" firejailProfile="$fjProfile"
fi fi
done done
fi fi
@ -564,8 +562,8 @@ bwrapIngestPath() {
# may be possible to place ever mount in a subdir, and mount the super dir? # may be possible to place ever mount in a subdir, and mount the super dir?
# or maybe configure remote mounts to somehow never hang. # or maybe configure remote mounts to somehow never hang.
# test -r "$1" && bwrapFlags+=("--dev-bind-try" "$1" "$1") # test -r "$1" && bwrapFlags+=("--dev-bind-try" "$1" "$1")
local target="$(readlink "$1")" if [ -L "$1" ]; then
if [ -n "$target" ]; then local target="$(readlink "$1")"
bwrapFlags+=("--symlink" "$target" "$1") bwrapFlags+=("--symlink" "$target" "$1")
else else
bwrapFlags+=("--dev-bind-try" "$1" "$1") bwrapFlags+=("--dev-bind-try" "$1" "$1")
@ -783,13 +781,8 @@ noneGetCli() {
# so allow access to it. # so allow access to it.
maybeAutodetectPaths() { maybeAutodetectPaths() {
if [ -n "$autodetect" ]; then if [ -n "$autodetect" ]; then
for _arg in "${cliArgs[@]:1}"; do for arg in "${cliArgs[@]:1}"; do
tryArgAsPath "$_arg" "$autodetect" tryArgAsPath "$arg" "$autodetect"
done
for _path in "${cliPathArgs[@]}"; do
# TODO: might want to also mount the directory *above* this file,
# to access e.g. adjacent album art in the media's folder.
paths+=("$_path")
done done
fi fi
} }
@ -800,16 +793,14 @@ maybeAutodetectPaths() {
# for more sophisticated (i.e. complex) backends like firejail, this may break subpaths which were blacklisted earlier. # for more sophisticated (i.e. complex) backends like firejail, this may break subpaths which were blacklisted earlier.
canonicalizePaths() { canonicalizePaths() {
# remove '//' and simplify '.', '..' paths, into canonical absolute logical paths. # remove '//' and simplify '.', '..' paths, into canonical absolute logical paths.
local normPaths=()
for path in "${paths[@]}"; do for path in "${paths[@]}"; do
normPath _canonPath "$path" normPath _canonPath "$path"
normPaths+=("$_canonPath") paths+=("$_canonPath")
done done
paths=("${normPaths[@]}")
} }
expandLink() { expandLink() {
local target="$(readlink "$1")" if [ -L "$1" ]; then
if [ -n "$target" ]; then local target="$(readlink "$1")"
# make absolute # make absolute
target="$(relativeTo "$(dirname "$1")" "$target")" target="$(relativeTo "$(dirname "$1")" "$target")"
# add + expand the symlink further, but take care to avoid infinite recursion # add + expand the symlink further, but take care to avoid infinite recursion
@ -822,32 +813,32 @@ expandLink() {
} }
### expand `paths` until it contains no symlinks whose target isn't also in `paths` ### expand `paths` until it contains no symlinks whose target isn't also in `paths`
expandLinks() { expandLinks() {
for _path in "${paths[@]}"; do for path in "${paths[@]}"; do
expandLink "$_path" expandLink "$path"
done done
} }
removeSubpaths() { removeSubpaths() {
# remove subpaths, but the result might include duplicates. # remove subpaths, but the result might include duplicates.
local toplevelPaths=() local toplevelPaths=()
for _path in "${paths[@]}"; do for path in "${paths[@]}"; do
local isSubpath= local isSubpath=
for _other in "${paths[@]}"; do for other in "${paths[@]}"; do
if [[ "$_path" =~ ^$_other/.* ]] || [ "$_other" = "/" ] && [ "$_path" != "/" ]; then if [[ "$path" =~ ^$other/.* ]] || [ "$other" = "/" ] && [ "$path" != "/" ]; then
# N.B.: $_path lacks a trailing slash, so this never matches self. # N.B.: $path lacks a trailing slash, so this never matches self.
# UNLESS $_path or $_other is exactly `/`, which we special-case. # UNLESS $path or $other is exactly `/`, which we special-case.
isSubpath=1 isSubpath=1
fi fi
done done
if [ -z "$isSubpath" ]; then if [ -z "$isSubpath" ]; then
toplevelPaths+=("$_path") toplevelPaths+=("$path")
fi fi
done done
# remove duplicated paths. # remove duplicated paths.
local canonicalizedPaths=() local canonicalizedPaths=()
for _path in "${toplevelPaths[@]}"; do for path in "${toplevelPaths[@]}"; do
if ! $(contains "$_path" "${canonicalizedPaths[@]}"); then if ! $(contains "$path" "${canonicalizedPaths[@]}"); then
canonicalizedPaths+=("$_path") canonicalizedPaths+=("$path")
fi fi
done done
paths=("${canonicalizedPaths[@]}") paths=("${canonicalizedPaths[@]}")
@ -873,28 +864,28 @@ parseArgsAndEnvironment() {
### convert generic args into sandbox-specific args ### convert generic args into sandbox-specific args
# order matters: for firejail, early args override the later --profile args # order matters: for firejail, early args override the later --profile args
ingestForBackend() { ingestForBackend() {
for _path in "${paths[@]}"; do for path in "${paths[@]}"; do
"$method"IngestPath "$_path" "$method"IngestPath "$path"
done done
for _cap in "${capabilities[@]}"; do for cap in "${capabilities[@]}"; do
"$method"IngestCapability "$_cap" "$method"IngestCapability "$cap"
done done
if [ -n "$net" ]; then if [ -n "$net" ]; then
"$method"IngestNet "$net" "$method"IngestNet "$net"
fi fi
for _addr in "${dns[@]}"; do for addr in "${dns[@]}"; do
"$method"IngestDns "$_addr" "$method"IngestDns "$addr"
done done
for _ns in "${keepNamespace[@]}"; do for ns in "${keepNamespace[@]}"; do
"$method"IngestKeepNamespace "$_ns" "$method"IngestKeepNamespace "$ns"
done done
for _prof in "${profilesNamed[@]}"; do for prof in "${profilesNamed[@]}"; do
"$method"IngestProfile "$_prof" "$method"IngestProfile "$prof"
done done
} }