/ modules / sandboxes.am
sandboxes.am
  1  #!/bin/sh
  2  
  3  ###################################################################################################
  4  # THIS MODULE INCLUDES ALL ACTIONS INTENDED TO ISOLATE DOTFILES OR CONTAINERIZE INSTALLED APPIMAGES
  5  ###################################################################################################
  6  
  7  AMCLIPATH_ORIGIN="$AMCLIPATH"
  8  SUDOCMD_ORIGIN="$SUDOCMD"
  9  
 10  # Get xdg variables for _configure_dirs_access
 11  for DIR in DESKTOP DOCUMENTS DOWNLOAD GAMES MUSIC PICTURES VIDEOS; do
 12  	eval XDG_DIR="$(xdg-user-dir $DIR 2>/dev/null)"
 13  	[ "$XDG_DIR" = "$HOME" ] || [ "$XDG_DIR" = "$HOME/" ] && XDG_DIR=""
 14  	eval $DIR="$XDG_DIR"
 15  done
 16  DESKTOP="$(echo "${DESKTOP:-~/Desktop}" | sed "s|$HOME|~|g")"
 17  DOCUMENTS="$(echo "${DOCUMENTS:-~/Documents}" | sed "s|$HOME|~|g")"
 18  DOWNLOAD="$(echo "${DOWNLOAD:-~/Downloads}" | sed "s|$HOME|~|g")"
 19  GAMES="$(echo "${GAMES:-~/Games}" | sed "s|$HOME|~|g")"
 20  MUSIC="$(echo "${MUSIC:-~/Music}" | sed "s|$HOME|~|g")"
 21  PICTURES="$(echo "${PICTURES:-~/Pictures}" | sed "s|$HOME|~|g")"
 22  VIDEOS="$(echo "${VIDEOS:-~/Videos}" | sed "s|$HOME|~|g")"
 23  
 24  _check_appimage() {
 25  	_determine_args
 26  	TARGET="$(command -v "$1" 2>/dev/null)"
 27  	APPIMAGE="$(readlink "$TARGET" 2>/dev/null)"
 28  	APPIMAGEDIR="$(dirname "$APPIMAGE" 2>/dev/null)"
 29  	if grep "aisap-am" "$TARGET" >/dev/null 2>&1; then
 30  		echo " $1 is already sandboxed!"
 31  		return 1
 32  	elif [ ! -e "$APPIMAGE" ]; then
 33  		echo " ERROR: \"$1\" is not installed"
 34  		return 1
 35  	elif ! grep -Eaoq -m 1 -- '--appimage-extract' "$APPIMAGE"; then
 36  		echo " ERROR: \"$1\" is NOT an AppImage"
 37  		return 1
 38  	fi
 39  }
 40  
 41  _home() {
 42  	if [ -d "$APPIMAGE.home" ]; then
 43  		echo " ERROR: \"$1\" already contains a home dir"
 44  		return 1
 45  	fi
 46  	mkdir "$APPIMAGE.home" || return 1
 47  	echo " \$HOME set to \"$APPIMAGE.home\" for \"$1\""
 48  }
 49  
 50  _config() {
 51  	if [ -d "$APPIMAGE.config" ]; then
 52  		echo " ERROR: \"$1\" already contains a config dir"
 53  		return 1
 54  	fi
 55  	mkdir "$APPIMAGE.config" || return 1
 56  	echo " \$XDG_CONFIG_HOME set to \"$APPIMAGE.config\" for \"$1\""
 57  }
 58  
 59  _disable_sandbox() {
 60  	TARGET="$(command -v "$1")"
 61  	if ! grep "aisap-am sandboxing script" "$TARGET" >/dev/null 2>&1; then
 62  		echo " ERROR: Not a sandboxed AppImage, aborting"
 63  		return 1
 64  	fi
 65  	"$1" --disable-sandbox
 66  }
 67  
 68  _check_aisap() {
 69  	if [ "$1" = "aisap" ]; then
 70  		echo " Error: You can't sandbox aisap"
 71  		return 1
 72  	elif ! command -v aisap 1>/dev/null; then
 73  		printf '\n%s\n\n' " Error: You need aisap for this script work"
 74  		read -r -p " ◆ DO YOU WISH TO INSTALL AISAP? Install size <5 MiB? (Y/n) " yn
 75  		if echo "$yn" | grep -i '^n' >/dev/null 2>&1; then
 76  			echo " OPERATION ABORTED!"
 77  			return 1
 78  		fi
 79  		if [ "$CLI" = am ] && [ -f "$APPMANCONFIG"/appman-config ]; then
 80  			read -r -p " ◆ DO YOU WISH TO INSTALL AISAP LOCALLY? (Y/n) " yn
 81  			if echo "$yn" | grep -i '^n' >/dev/null 2>&1; then
 82  				"$AMCLIPATH_ORIGIN" -i aisap >/dev/null 2>&1
 83  			else
 84  				"$AMCLIPATH_ORIGIN" -i --user aisap >/dev/null 2>&1
 85  			fi
 86  		else
 87  			"$AMCLIPATH_ORIGIN" -i aisap >/dev/null 2>&1
 88  		fi
 89  		command -v aisap 1>/dev/null || return 1
 90  		echo " aisap installed successfully!"
 91  	fi
 92  	if [ -f "$BINDIR"/"$1" ]; then
 93  		SUDOCMD=""
 94  	fi
 95  }
 96  
 97  _generate_sandbox_script() {
 98  	echo "$DIVIDING_LINE"
 99  	printf '\n%s\n' " Making aisap sandbox script for \"$1\"..."
100  	tmpscript="$(cat <<-'HEREDOC'
101  	#!/bin/sh
102  	# aisap-am sandboxing script, aisap: https://github.com/mgord9518/aisap
103  	# Thanks a lot to mgord9518 for making aisap!
104  	# Run this script with --disable-sandbox to do what the flag name implies
105  	# The default location for the sandboxed home is in $HOME/.local/am-sandboxes
106  	# But that location can be changed by setting the $SANDBOXDIR env variable
107  
108  	# Dependency check
109  	if ! command -v aisap 1>/dev/null; then
110  	  echo "You need aisap for this to work"
111  	  notify-send -u critical "Sandbox error: Missing aisap dependency!"
112  	  exit 1
113  	fi
114  	# Set variables and create sandboxed dir.
115  	APPEXEC=DUMMY
116  	chmod a-x "$APPEXEC" # Prevents accidental launch of app outside the sandbox
117  	APPNAME="$(echo "$APPEXEC" | awk -F "/" '{print $NF}')"
118  	SANDBOXDIR="${SANDBOXDIR:-$HOME/.local/am-sandboxes}"
119  	DATADIR="${XDG_DATA_HOME:-$HOME/.local/share}"
120  	CONFIGDIR="${XDG_CONFIG_HOME:-$HOME/.config}"
121  	CACHEDIR="${XDG_CACHE_HOME:-$HOME/.cache}"
122  	DBUS="$(ls /tmp/dbus* 2>/dev/null | head -1)"
123  	# get xdg user dirs, unset if var = $HOME
124  	for DIR in DESKTOP DOCUMENTS DOWNLOAD GAMES MUSIC PICTURES VIDEOS; do
125  	  eval XDG_DIR="$(xdg-user-dir $DIR 2>/dev/null)"
126  	  [ "$XDG_DIR" = "$HOME" ] || [ "$XDG_DIR" = "$HOME/" ] && XDG_DIR=""
127  	  eval $DIR="$XDG_DIR"
128  	done
129  	# Use default location if var is not set
130  	DESKTOP="${DESKTOP:-~/Desktop}"
131  	DOCUMENTS="${DOCUMENTS:-~/Documents}"
132  	DOWNLOAD="${DOWNLOAD:-~/Downloads}"
133  	GAMES="${GAMES:-~/Games}"
134  	MUSIC="${MUSIC:-~/Music}"
135  	PICTURES="${PICTURES:-~/Pictures}"
136  	VIDEOS="${VIDEOS:-~/Videos}"
137  	# Try find the right name of the app conf/data dir
138  	APPDATA=$( ls "$DATADIR" | grep -i "$APPNAME" | head -1 )
139  	APPCONF=$( ls "$CONFIGDIR" | grep -i "$APPNAME" | head -1 )
140  	# Disable sandbox
141  	if [ "$1" = "--disable-sandbox" ]; then
142  	  APPIMAGEDIR="${APPEXEC%/*}"
143  	  echo ""
144  	  echo "✔ Giving exec permissions back to $APPEXEC..." \
145  	    | fold -sw 77 | sed 's/^/   /g; s/   ✔/ ✔/g'
146  	  chmod a+x "$APPEXEC" || exit 1
147  	  echo "✔ Patching $APPIMAGEDIR/AM-updater to give permissions back..." \
148  	    | fold -sw 77 | sed 's/^/   /g; s/   ✔/ ✔/g'
149  	  tmpsedEEE="$(sed 's|chmod a-x|chmod a+x|g' "$APPIMAGEDIR/AM-updater" 2>/dev/null)"
150  	  [ -n "$tmpsedEEE" ] && echo "$tmpsedEEE" > "$APPIMAGEDIR/AM-updater" || return 1
151  	  unset tmpsedEEE
152  	  THISFILE="$(realpath "$0")"
153  	  echo "✔ Replacing $THISFILE with a link to the AppImage..." \
154  	    | fold -sw 77 | sed 's/^/   /g; s/   ✔/ ✔/g'
155  	  SUDO ln -sf "$APPEXEC" "$THISFILE" || exit 1
156  	  printf '\033[32m\n%s\n\033[0m\n' "✔ $APPEXEC successfully unsandboxed!" \
157  	    | fold -sw 77 | sed 's/^/   /g; s/   ✔/ ✔/g'
158  	  exit 0
159  	fi
160  	mkdir -p "$SANDBOXDIR/$APPNAME"
161  	[ -z "$APPNAME" ] && exit 1
162  	# Start at sandboxed home
163  	# Edit below this to add or remove access to parts of the system
164  	exec aisap --trust-once --level 2 \
165  	--data-dir "$SANDBOXDIR/$APPNAME" \
166  	--add-file "$DATADIR/${APPDATA:-$APPNAME}":rw \
167  	--add-file "$DATADIR"/themes \
168  	--add-file "$DATADIR"/icons \
169  	--add-file "$CONFIGDIR/${APPCONF:-$APPNAME}":rw \
170  	--add-file "$CONFIGDIR"/dconf \
171  	--add-file "$CONFIGDIR"/gtk3.0 \
172  	--add-file "$CONFIGDIR"/gtk4.0 \
173  	--add-file "$CONFIGDIR"/kdeglobals \
174  	--add-file "$CONFIGDIR"/qt5ct \
175  	--add-file "$CONFIGDIR"/qt6ct \
176  	--add-file "$CONFIGDIR"/Kvantum \
177  	--add-file "$HOME"/.local/lib \
178  	--add-file /usr/share \
179  	--rm-file /NOPATH \
180  	--rm-file "$DESKTOP" \
181  	--rm-file "$DOCUMENTS" \
182  	--rm-file "$DOWNLOAD" \
183  	--rm-file "$GAMES" \
184  	--rm-file "$MUSIC" \
185  	--rm-file "$PICTURES" \
186  	--rm-file "$VIDEOS" \
187  	--add-file /var/lib/dbus \
188  	--add-file "${DBUS:-/tmp/dbus}" \
189  	--add-socket pulseaudio \
190  	--add-socket dbus \
191  	--add-socket network \
192  	--add-socket x11 \
193  	--add-socket wayland \
194  	--add-device dri -- \
195  	"$APPEXEC" "$@"
196  	HEREDOC
197  	)"
198  }
199  
200  _configure_dirs_access() {
201  	printf '\033[33m\n'
202  	read -r -p " Do you want configure access to directories? (Y/n): " yn
203  	if echo "$yn" | grep -i '^n' >/dev/null 2>&1; then
204  		return 0
205  	fi
206  	printf '\033[36m'
207  	for DIR in DESKTOP DOCUMENTS DOWNLOAD GAMES MUSIC PICTURES VIDEOS; do
208  		eval XDG_DIR=\$$DIR
209  		read -r -p " Allow $1 access to \"$XDG_DIR\"? (y/N) " yn
210  		if echo "$yn" | grep -i '^y' >/dev/null 2>&1; then
211  			tmpscript=$(echo "$tmpscript" \
212  				| sed "s#--rm-file \"\$$DIR\"#--add-file \"\$$DIR\":rw#g" )
213  		fi
214  	done
215  	sleep 0.5
216  	printf '\033[31m'
217  	read -r -p " Allow $1 access to a specific directory? (y/N) " yn
218  	if echo "$yn" | grep -i '^y' >/dev/null 2>&1; then
219  		echo "  WARNING: Giving access to all of $HOME or / and similar is not safe"
220  		echo "  Also aisap might not let $1 start when such paths are given"
221  		printf '\033[33m%s\n' "  Type the path to the directory"
222  		read -r -p "  Example: /media/external-drive or ~/Backups: " NEWDIR
223  		case "$NEWDIR" in
224  			'$HOME'|'$HOME/'|"$HOME"|"$HOME/"|"/"|"~"|"~/"|"/home"|"/home/"|\
225  			"$DATADIR"|'$XDG_DATA_HOME'|"$CONFIGDIR"|'$XDG_CONFIG_HOME'|"$BINDIR")
226  				notify-send -u critical "DO YOU WANT THE FBI TO GET YA?"
227  				printf '\033[31m\n'
228  				read -r -p "  SPOOKY LOCATION! ARE YOU SURE? IF SO TYPE \"YES\": " YES
229  				[ "$YES" != "YES" ] && echo " That's not \"YES\", aborting" && return 1
230  				;;
231  			'')
232  				printf '\033[31m\n%s\n\n' " No path given, aborting"
233  				return 1
234  				;;
235  		esac
236  		echo "  Giving access to \"$NEWDIR\"..."
237  		tmpscript=$(echo "$tmpscript" \
238  			| sed "s#--rm-file /NOPATH#--add-file \"$NEWDIR\":rw#g")
239  	fi
240  	printf '\n\033[32m%s\n' " User directories access configured successfully!"
241  }
242  
243  _install_sandbox_script() {
244  	tmpscript=$(echo "$tmpscript" | sed "s#DUMMY#$APPIMAGE#g; s#SUDO#$SUDOCMD#g")
245  	# Remove exec permission from AppImage and its updater for better safety™
246  	chmod a-x "$APPIMAGE" || return 1
247  	tmpsedEEE="$(sed 's|chmod a+x|chmod a-x|g' "$APPIMAGEDIR/AM-updater" 2>/dev/null)"
248  	[ -n "$tmpsedEEE" ] && echo "$tmpsedEEE" > "$APPIMAGEDIR/AM-updater" || return 1
249  	unset tmpsedEEE
250  	# Install the script
251  	$SUDOCMD rm -f "$TARGET" || return 1
252  	echo "$tmpscript" | $SUDOCMD tee "$TARGET" >/dev/null 2>&1 || return 1
253  	$SUDOCMD chmod a+x "$TARGET"
254  	SANDBOXDIR="${SANDBOXDIR:-$HOME/.local/am-sandboxes}"
255  	printf '\033[32m\n%s\n\033[0m' " \"$1\" successfully sandboxed!"
256  	printf '\n%s\n' " $1 will be sandboxed in \"$SANDBOXDIR\""
257  	printf '%s\n\n' " once launched"
258  	printf '%s\n' " Set the \$SANDBOXDIR env variable to move the location"
259  	printf '\n%s' ' Use '
260  	printf '\033[33m%s' '--disable-sandbox'
261  	printf '\033[0m%s\033[33m\n' " to revert the changes, in this case that is:"
262  	printf '\033[33m%s\033[0m' " $1 --disable-sandbox"
263  	printf '%s\033[33m%s\n\033[0m\n' " or " "$AMCLI --disable-sandbox $1"
264  	SUDOCMD="$SUDOCMD_ORIGIN"
265  }
266  
267  # Main logic
268  [ -z "$2" ] && echo " USAGE: $AMCLI $1 [ARGUMENT]" && exit 1
269  case "$1" in
270  	'--sandbox')
271  		shift
272  		while [ "$#" -gt 0 ]; do
273  			_check_appimage "${@}" && _check_aisap "${@}" \
274  			&& _generate_sandbox_script "${@}" \
275  			&& _configure_dirs_access "${@}" \
276  			&& _install_sandbox_script "${@}"
277  			shift
278  		done
279  	;;
280  
281  	'--disable-sandbox')
282  		shift
283  		while [ "$#" -gt 0 ]; do
284  			echo "$DIVIDING_LINE"
285  			_disable_sandbox "${@}"
286  			shift
287  		done
288  		;;
289  
290  	'-H'|'--home')
291  		shift
292  		while [ "$#" -gt 0 ]; do
293  			_check_appimage "${@}" && _home "${@}"
294  			shift
295  		done
296  		;;
297  
298  	'-C'|'--config')
299  		shift
300  		while [ "$#" -gt 0 ]; do
301  			_check_appimage "${@}" && _config "${@}"
302  			shift
303  		done
304  		;;
305  esac
306  _remove_info_files