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.
# does not resolve symlinks, nor check for existence of any component of the path.
normPath() {
_npOut="$1"
_npUnparsed="$2"
_npComps=()
while [ -n "$_npUnparsed" ]; do
local npOut="$1"
local npUnparsed="$2"
local npComps=()
while [ -n "$npUnparsed" ]; do
# chomp leading `/`
_npUnparsed="${_npUnparsed:1}"
npUnparsed="${npUnparsed:1}"
# split into <first></re/st/...>
# in the case of `//`, or more, _npThisComp is empty,
# and we push nothing to _npComps,
# in the case of `//`, or more, npThisComp is empty,
# and we push nothing to npComps,
# but we're guaranteed to make progress.
_npThisComp="${_npUnparsed%%/*}"
_npThisLen="${#_npThisComp}"
_npUnparsed="${_npUnparsed:$_npThisLen}"
if [ "$_npThisComp" = ".." ]; then
npThisComp="${npUnparsed%%/*}"
npThisLen="${#npThisComp}"
npUnparsed="${npUnparsed:$npThisLen}"
if [ "$npThisComp" = ".." ]; then
# "go up" path component => delete the leaf dir (if any)
if [ ${#_npComps[@]} -ne 0 ]; then
unset _npComps[-1]
if [ ${#npComps[@]} -ne 0 ]; then
unset npComps[-1]
fi
elif [ "$_npThisComp" != "." ] && [ -n "$_npThisComp" ]; then
elif [ "$npThisComp" != "." ] && [ -n "$npThisComp" ]; then
# normal, non-empty path component => append it
_npComps+=("$_npThisComp")
npComps+=("$npThisComp")
fi
done
# join the components
if [ ${#_npComps[@]} -eq 0 ]; then
declare -g "$_npOut"="/"
if [ ${#npComps[@]} -eq 0 ]; then
declare -g "$npOut"="/"
else
_npJoined=
for _npComp in "${_npComps[@]}"; do
_npJoined="$_npJoined/$_npComp"
local npJoined=
for npComp in "${npComps[@]}"; do
npJoined="$npJoined/$npComp"
done
declare -g "$_npOut"="$_npJoined"
declare -g "$npOut"="$npJoined"
fi
}
@ -263,21 +263,21 @@ loadProfileByPath() {
}
tryLoadProfileByName() {
_profile="$1"
if [ "${_profile:0:1}" = "/" ]; then
local profile="$1"
if [ "${profile:0:1}" = "/" ]; then
# absolute path to profile.
# consider it an error if it doesn't exist.
# 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.
loadProfileByPath "$_profile"
loadProfileByPath "$profile"
else
profilesNamed+=("$_profile")
profilesNamed+=("$profile")
for _profileDir in "${profileDirs[@]}"; do
_profilePath="$_profileDir/$_profile.profile"
debug "try profile at path: '$_profilePath'"
if [ -f "$_profilePath" ]; then
loadProfileByPath "$_profilePath"
for profileDir in "${profileDirs[@]}"; do
local profilePath="$profileDir/$profile.profile"
debug "try profile at path: '$profilePath'"
if [ -f "$profilePath" ]; then
loadProfileByPath "$profilePath"
break
fi
done
@ -291,55 +291,54 @@ initDefaultProfileDirs() {
}
# 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.
cliPathArgs=()
tryPath() {
_path="$1"
_how="$2"
local path="$1"
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)
if [ -e "$_path" ]; then
cliPathArgs+=("$(relativeTo "$PWD" "$_path")")
if [ -e "$path" ]; then
paths+=("$(relativeTo "$PWD" "$path")")
true
fi
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*)
if [ -f "$_path" ]; then
cliPathArgs+=("$(relativeTo "$PWD" "$_path")")
if [ -f "$path" ]; then
paths+=("$(relativeTo "$PWD" "$path")")
true
fi
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.
parent _tryPathParent "$_path"
parent _tryPathParent "$path"
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.
tryPath "$_path" "existing" || tryPath "$_path" "parent"
elif [ "$_how" = "existingFileOrParent" ]; then
tryPath "$path" "existing" || tryPath "$path" "parent"
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.
tryPath "$_path" "existingFile" || tryPath "$_path" "parent"
tryPath "$path" "existingFile" || tryPath "$path" "parent"
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.
# 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() {
_arg="$1"
_how="$2"
local arg="$1"
local how="$2"
_path=
if [ "${_arg:0:1}" = "/" ]; then
if [ "${arg:0:1}" = "/" ]; then
# absolute path
_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
# 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
urldecode _path "${_arg:7}"
urldecode _path "${arg:7}"
elif [ "${_path:0:1}" = "-" ]; then
# 99% chance it's a CLI argument. if not, use `./-<...>`
return
@ -348,7 +347,7 @@ tryArgAsPath() {
_path="$PWD/$_arg"
fi
tryPath "$_path" "$_how"
tryPath "$_path" "$how"
}
@ -358,9 +357,9 @@ tryArgAsPath() {
parseArgsExtra=()
parseArgs() {
while [ "$#" -ne 0 ]; do
_arg="$1"
local arg="$1"
shift
case "$_arg" in
case "$arg" in
(--)
# rest of args are for the CLI, and not for us.
# consider two cases:
@ -410,9 +409,9 @@ parseArgs() {
shift
;;
(--sane-sandbox-cap)
_cap="$1"
local cap="$1"
shift
capabilities+=("$_cap")
capabilities+=("$cap")
;;
(--sane-sandbox-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)
# N.B.: these named temporary variables ensure that "set -x" causes $1 to be printed
_dns="$1"
local dns="$1"
shift
dns+=("$_dns")
dns+=("$dns")
;;
(--sane-sandbox-firejail-arg)
_fjFlag="$1"
local fjFlag="$1"
shift
firejailFlags+=("$_fjFlag")
firejailFlags+=("$fjFlag")
;;
(--sane-sandbox-bwrap-arg)
_bwrapFlag="$1"
local bwrapFlag="$1"
shift
bwrapFlags+=("$_bwrapFlag")
bwrapFlags+=("$bwrapFlag")
;;
(--sane-sandbox-net)
net="$1"
shift
;;
(--sane-sandbox-keep-namespace)
_namespace="$1"
local namespace="$1"
shift
if [ "$_namespace" = all ]; then
if [ "$namespace" = all ]; then
keepNamespace+=("cgroup" "ipc" "pid" "uts")
else
keepNamespace+=("$_namespace")
keepNamespace+=("$namespace")
fi
;;
(--sane-sandbox-path)
_path="$1"
local path="$1"
shift
paths+=("$_path")
paths+=("$(relativeTo "$PWD" "$path")")
;;
(--sane-sandbox-add-pwd)
_path="$PWD"
paths+=("$_path")
paths+=("$PWD")
;;
(--sane-sandbox-profile)
# load the profile *immediately*, inline.
@ -472,12 +470,12 @@ parseArgs() {
(--sane-sandbox-profile-dir)
# 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)
_dir="$1"
local dir="$1"
shift
profileDirs=("$_dir" "${profileDirs[@]}")
profileDirs=("$dir" "${profileDirs[@]}")
;;
(*)
parseArgsExtra+=("$_arg")
parseArgsExtra+=("$arg")
;;
esac
done
@ -511,12 +509,12 @@ firejailIngestProfile() {
firejailName="$1"
fi
if [ -z "$firejailProfile" ]; then
_fjProfileDirs=(@firejailProfileDirs@)
for _fjProfileDir in "${_fjProfileDirs[@]}"; do
_fjProfile="$_fjProfileDir/$1.profile"
debug "try firejail profile at path: '$_fjProfile'"
if [ -f "$_fjProfile" ]; then
firejailProfile="$_fjProfile"
local fjProfileDirs=(@firejailProfileDirs@)
for fjProfileDir in "${fjProfileDirs[@]}"; do
local fjProfile="$fjProfileDir/$1.profile"
debug "try firejail profile at path: '$fjProfile'"
if [ -f "$fjProfile" ]; then
firejailProfile="$fjProfile"
fi
done
fi
@ -564,8 +562,8 @@ bwrapIngestPath() {
# may be possible to place ever mount in a subdir, and mount the super dir?
# or maybe configure remote mounts to somehow never hang.
# test -r "$1" && bwrapFlags+=("--dev-bind-try" "$1" "$1")
local target="$(readlink "$1")"
if [ -n "$target" ]; then
if [ -L "$1" ]; then
local target="$(readlink "$1")"
bwrapFlags+=("--symlink" "$target" "$1")
else
bwrapFlags+=("--dev-bind-try" "$1" "$1")
@ -783,13 +781,8 @@ noneGetCli() {
# so allow access to it.
maybeAutodetectPaths() {
if [ -n "$autodetect" ]; then
for _arg in "${cliArgs[@]:1}"; do
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")
for arg in "${cliArgs[@]:1}"; do
tryArgAsPath "$arg" "$autodetect"
done
fi
}
@ -800,16 +793,14 @@ maybeAutodetectPaths() {
# for more sophisticated (i.e. complex) backends like firejail, this may break subpaths which were blacklisted earlier.
canonicalizePaths() {
# remove '//' and simplify '.', '..' paths, into canonical absolute logical paths.
local normPaths=()
for path in "${paths[@]}"; do
normPath _canonPath "$path"
normPaths+=("$_canonPath")
paths+=("$_canonPath")
done
paths=("${normPaths[@]}")
}
expandLink() {
local target="$(readlink "$1")"
if [ -n "$target" ]; then
if [ -L "$1" ]; then
local target="$(readlink "$1")"
# make absolute
target="$(relativeTo "$(dirname "$1")" "$target")"
# 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`
expandLinks() {
for _path in "${paths[@]}"; do
expandLink "$_path"
for path in "${paths[@]}"; do
expandLink "$path"
done
}
removeSubpaths() {
# remove subpaths, but the result might include duplicates.
local toplevelPaths=()
for _path in "${paths[@]}"; do
for path in "${paths[@]}"; do
local isSubpath=
for _other in "${paths[@]}"; do
if [[ "$_path" =~ ^$_other/.* ]] || [ "$_other" = "/" ] && [ "$_path" != "/" ]; then
# N.B.: $_path lacks a trailing slash, so this never matches self.
# UNLESS $_path or $_other is exactly `/`, which we special-case.
for other in "${paths[@]}"; do
if [[ "$path" =~ ^$other/.* ]] || [ "$other" = "/" ] && [ "$path" != "/" ]; then
# N.B.: $path lacks a trailing slash, so this never matches self.
# UNLESS $path or $other is exactly `/`, which we special-case.
isSubpath=1
fi
done
if [ -z "$isSubpath" ]; then
toplevelPaths+=("$_path")
toplevelPaths+=("$path")
fi
done
# remove duplicated paths.
local canonicalizedPaths=()
for _path in "${toplevelPaths[@]}"; do
if ! $(contains "$_path" "${canonicalizedPaths[@]}"); then
canonicalizedPaths+=("$_path")
for path in "${toplevelPaths[@]}"; do
if ! $(contains "$path" "${canonicalizedPaths[@]}"); then
canonicalizedPaths+=("$path")
fi
done
paths=("${canonicalizedPaths[@]}")
@ -873,28 +864,28 @@ parseArgsAndEnvironment() {
### convert generic args into sandbox-specific args
# order matters: for firejail, early args override the later --profile args
ingestForBackend() {
for _path in "${paths[@]}"; do
"$method"IngestPath "$_path"
for path in "${paths[@]}"; do
"$method"IngestPath "$path"
done
for _cap in "${capabilities[@]}"; do
"$method"IngestCapability "$_cap"
for cap in "${capabilities[@]}"; do
"$method"IngestCapability "$cap"
done
if [ -n "$net" ]; then
"$method"IngestNet "$net"
fi
for _addr in "${dns[@]}"; do
"$method"IngestDns "$_addr"
for addr in "${dns[@]}"; do
"$method"IngestDns "$addr"
done
for _ns in "${keepNamespace[@]}"; do
"$method"IngestKeepNamespace "$_ns"
for ns in "${keepNamespace[@]}"; do
"$method"IngestKeepNamespace "$ns"
done
for _prof in "${profilesNamed[@]}"; do
"$method"IngestProfile "$_prof"
for prof in "${profilesNamed[@]}"; do
"$method"IngestProfile "$prof"
done
}