sane-sandboxed: expand symlinks before binding them into the sandbox

This commit is contained in:
Colin 2024-05-12 21:41:49 +00:00
parent 89135d08cb
commit d148b19767

View File

@ -135,6 +135,19 @@ usage() {
## UTILITIES/BOILERPLATE
# `relativeTo base-dir path`
# if `path` is absolute, returns `path`
# otherwise, joins `path` onto `base-dir`
relativeTo() {
local base="$1"
local path="$2"
if [ "${path:0:1}" == "/" ]; then
echo "$path"
else
echo "$base/$path"
fi
}
# `normPath outVar "$path"`
# remove duplicate //, reduce '.' and '..' (naively).
# expects a full path as input
@ -226,6 +239,19 @@ urldecode() {
declare -g "$outVar"="$(echo -e "${i//%/\\x}")"
}
# `contains needle ${haystack[0]} ${haystack[1]} ...`
# evals `true` if the first argument is equal to any of the other args
contains() {
local needle="$1"
shift
for item in "$@"; do
if [ "$needle" = "$item" ]; then
return 0
fi
done
return 1
}
## HELPERS
@ -769,41 +795,57 @@ 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.
_normPaths=()
for _path in "${paths[@]}"; do
normPath _canonPath "$_path"
_normPaths+=("$_canonPath")
local normPaths=()
for path in "${paths[@]}"; do
normPath _canonPath "$path"
normPaths+=("$_canonPath")
done
paths=("${normPaths[@]}")
}
expandLink() {
local target="$(readlink "$1")"
if [ -n "$target" ]; then
# make absolute
target="$(relativeTo "$(dirname "$1")" "$target")"
# add + expand the symlink further, but take care to avoid infinite recursion
normPath _canonTarget "$target"
if ! $(contains "$_canonTarget" "${paths[@]}"); then
paths+=("$_canonTarget")
expandLink "$_canonTarget"
fi
fi
}
### expand `paths` until it contains no symlinks whose target isn't also in `paths`
expandLinks() {
for _path in "${paths[@]}"; do
expandLink "$_path"
done
}
removeSubpaths() {
# remove subpaths, but the result might include duplicates.
_toplevelPaths=()
for _path in "${_normPaths[@]}"; do
_isSubpath=
for _other in "${_normPaths[@]}"; do
local toplevelPaths=()
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.
_isSubpath=1
isSubpath=1
fi
done
if [ -z "$_isSubpath" ]; then
_toplevelPaths+=("$_path")
if [ -z "$isSubpath" ]; then
toplevelPaths+=("$_path")
fi
done
# remove duplicated paths.
canonicalizedPaths=()
for _path in "${_toplevelPaths[@]}"; do
_isAlreadyListed=
for _other in "${canonicalizedPaths[@]}"; do
if [ "$_path" = "$_other" ]; then
_isAlreadyListed=1
fi
done
if [ -z "$_isAlreadyListed" ]; then
local canonicalizedPaths=()
for _path in "${toplevelPaths[@]}"; do
if ! $(contains "$_path" "${canonicalizedPaths[@]}"); then
canonicalizedPaths+=("$_path")
fi
done
paths=("${canonicalizedPaths[@]}")
}
@ -826,7 +868,7 @@ parseArgsAndEnvironment() {
### convert generic args into sandbox-specific args
# order matters: for firejail, early args override the later --profile args
ingestForBackend() {
for _path in "${canonicalizedPaths[@]}"; do
for _path in "${paths[@]}"; do
"$method"IngestPath "$_path"
done
@ -872,6 +914,8 @@ if [ -z "$isDisable" ]; then
"$method"Setup
maybeAutodetectPaths
canonicalizePaths
expandLinks
removeSubpaths
ingestForBackend
"$method"GetCli