Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/shell-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ jobs:
scripts/health-check.sh \
scripts/tool-health-check.sh \
tests/test-installer-functions.sh \
tests/test-shell-functions.sh \
tests/docker-ubuntu-smoke.sh \
macos/defaults.sh \
macos/dock.sh \
Expand All @@ -70,6 +71,7 @@ jobs:
scripts/health-check.sh \
scripts/tool-health-check.sh \
tests/test-installer-functions.sh \
tests/test-shell-functions.sh \
tests/docker-ubuntu-smoke.sh \
macos/defaults.sh \
macos/dock.sh \
Expand Down Expand Up @@ -170,6 +172,7 @@ jobs:
fi

export SHELL="$shell_bin"
timeout 30 "$shell_bin" tests/test-shell-functions.sh
./install.sh --minimal

mkdir -p "$HOME/.local/bin"
Expand Down
27 changes: 27 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Security policy

## Reporting a vulnerability

Use [GitHub private vulnerability reporting](https://github.com/alsd4git/dotfiles/security/advisories/new).
Do not disclose exploit details or sensitive data in public issues or pull requests.

Include the affected commit, operating system, shell, expected impact and minimal
reproduction steps. Use synthetic examples and redact screenshots and logs.
If your own credential was exposed, revoke or rotate it. Report credentials you
do not control promptly and privately; do not use or attempt to revoke them.

## Versions and maintenance

Check the current default branch, `main`, before reporting an issue. Fixes, when
provided, target that branch; older revisions do not receive separate backports.
Reports are handled on a best-effort basis without guaranteed response or
remediation times.

## Safe reproduction

Use disposable home directories or containers and the repository's test harnesses.
Do not run installers, uninstallers, package bootstraps or macOS defaults changes
against a real user account merely to demonstrate an issue. Never include SSH
private keys, tokens, credential stores, private hostnames or unredacted shell
history in a report. Issues in third-party software should also be reported to
the relevant upstream maintainer.
4 changes: 3 additions & 1 deletion docs/linux.md
Original file line number Diff line number Diff line change
Expand Up @@ -391,4 +391,6 @@ Run the full disposable Ubuntu 24.04 Bash/Zsh lifecycle test when Docker is avai
./tests/docker-ubuntu-smoke.sh
```

The container test mounts the repository read-only and verifies install, interactive shell loading, health checks, and uninstall in isolated home directories.
The container test mounts the repository read-only and verifies install, interactive shell loading, health checks, and uninstall in isolated home directories. It also runs `tests/test-shell-functions.sh` in Bash and Zsh, checking Watchtower arguments with a simulated Docker command and `netuse` exit codes without updating real containers.

Watchtower uses `${DOCKER_CONFIG:-$HOME/.docker}/config.json` when readable, mounted read-only. Configurations referencing `credsStore` or `credHelpers` produce a warning: mounting the JSON alone does not make host credential helpers or keychains available inside Watchtower. Use a dedicated Docker configuration suitable for the container if private-registry authentication requires it; never commit registry credentials.
26 changes: 16 additions & 10 deletions general/.functions
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,15 @@ function watchtower_run_once() {
return 1
fi

local docker_config="$HOME/.docker/config.json"
local docker_config="${DOCKER_CONFIG:-$HOME/.docker}/config.json"
local -a docker_args
docker_args=(run --rm -v /var/run/docker.sock:/var/run/docker.sock)

if [ -r "$docker_config" ]; then
# Warn conservatively on helper keys, including multiline JSON.
if grep -Eq '"(credsStore|credHelpers)"' "$docker_config"; then
printf '%s\n' 'Warning: Docker config references credential helpers. Mounting the config alone does not provide the host keychain or helper binaries to Watchtower; private-registry authentication may fail. Use a dedicated DOCKER_CONFIG with credentials usable inside the container.' >&2
fi
docker_args+=(-v "$docker_config:/config.json:ro")
fi

Expand Down Expand Up @@ -147,20 +151,21 @@ function netuse() {
echo -e "${dim}Interface: $iface | Refresh: ${interval}s${nc}"
echo ""

local tmpdir fifo cmd_pid
local tmpdir fifo cmd_pid done_marker cmd_status
tmpdir=$(mktemp -d "${TMPDIR:-/tmp}/netuse.XXXXXX") || return 1
fifo="$tmpdir/output"
done_marker="__NETUSE_DONE_${tmpdir}__"
if ! mkfifo "$fifo"; then
rmdir "$tmpdir" 2>/dev/null || true
return 1
fi

(
"$@" 2>&1 | while IFS= read -r line; do
printf "\r\033[K${green}[CMD]${nc} %s\n" "$line"
done
echo "__DONE__"
) >"$fifo" &
# Keep exit/exec inside the command from bypassing the completion marker.
if ("$@"); then cmd_status=0; else cmd_status=$?; fi
printf '\n%s\n' "$done_marker"
exit "$cmd_status"
) >"$fifo" 2>&1 &
cmd_pid=$!

exec 3<"$fifo"
Expand All @@ -172,10 +177,10 @@ function netuse() {

while $running; do
if read -t "$interval" -r line <&3; then
if [[ "$line" == "__DONE__" ]]; then
if [[ "$line" == "$done_marker" ]]; then
running=false
else
echo "$line"
printf "\r\033[K${green}[CMD]${nc} %s\n" "$line"
fi
fi

Expand All @@ -200,7 +205,7 @@ function netuse() {
done

exec 3<&-
wait "$cmd_pid" 2>/dev/null || true
if wait "$cmd_pid"; then cmd_status=0; else cmd_status=$?; fi
rm -f -- "$fifo"
rmdir "$tmpdir" 2>/dev/null || true

Expand All @@ -215,6 +220,7 @@ function netuse() {
if ((total_time > 0)); then
echo -e " Media: $(_hu_speed $((total_rx / total_time)))"
fi
return "$cmd_status"
}

# ===== Notes and Future Enhancements =====
Expand Down
1 change: 1 addition & 0 deletions tests/docker-ubuntu-smoke.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ docker run --rm \
apt-get install -y --no-install-recommends ca-certificates git zsh >/dev/null

for shell_bin in /bin/bash /usr/bin/zsh; do
timeout 30 "$shell_bin" /workspace/tests/test-shell-functions.sh
test_home=$(mktemp -d /tmp/dotfiles-home.XXXXXX)
export HOME="$test_home"
export SHELL="$shell_bin"
Expand Down
75 changes: 75 additions & 0 deletions tests/test-shell-functions.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env bash
# Run with Bash or Zsh. Docker calls are always simulated.

set -eu
repo_root=$(cd "$(dirname "$0")/.." && pwd)
test_root=$(mktemp -d "${TMPDIR:-/tmp}/dotfiles-shell-tests.XXXXXX")
cleanup() {
rm -f "$test_root/config dir/config.json" "$test_root/calls" "$test_root/warnings" "$test_root/netuse"
rmdir "$test_root/config dir" "$test_root"
}
trap cleanup EXIT
# shellcheck source=general/.functions
source "$repo_root/general/.functions"

docker() {
printf '<%s>\n' "$@" >"$test_root/calls"
return "${docker_result:-0}"
}

export DOCKER_CONFIG="$test_root/config dir"
mkdir -p "$DOCKER_CONFIG"
watchtower_run_once 'container with spaces' 2>"$test_root/warnings"
if grep -Fq '/config.json:ro' "$test_root/calls"; then exit 1; fi
grep -Fxq '<container with spaces>' "$test_root/calls"
test ! -s "$test_root/warnings"

printf '%s\n' '{"auths":{}}' >"$DOCKER_CONFIG/config.json"
watchtower_run_once 2>"$test_root/warnings"
grep -Fxq "<$DOCKER_CONFIG/config.json:/config.json:ro>" "$test_root/calls"
test ! -s "$test_root/warnings"

for config_fixture in '{"credsStore":"desktop"}' '{"credHelpers":{"registry.example":"helper"}}' '{
"credsStore"
: "desktop"
}'; do
printf '%s\n' "$config_fixture" >"$DOCKER_CONFIG/config.json"
watchtower_run_once 2>"$test_root/warnings"
grep -Fq 'credential helpers' "$test_root/warnings"
if grep -Fq 'registry.example' "$test_root/warnings"; then exit 1; fi
done
docker_result=17
actual_result=0
watchtower_run_once 2>"$test_root/warnings" || actual_result=$?
test "$actual_result" -eq 17

if [ "$(uname -s)" = Linux ]; then
netuse --iface lo true >"$test_root/netuse"
fail_command() {
printf '%s\n' '__DONE__'
printf 'stderr without newline' >&2
return 23
}
actual_result=0
netuse --iface lo fail_command >"$test_root/netuse" || actual_result=$?
test "$actual_result" -eq 23
grep -Fq '__DONE__' "$test_root/netuse"
grep -Fq 'stderr without newline' "$test_root/netuse"
grep -Fq 'Riepilogo' "$test_root/netuse"

for expected_result in 0 7; do
actual_result=0
netuse --iface lo exit "$expected_result" >"$test_root/netuse" || actual_result=$?
test "$actual_result" -eq "$expected_result"
grep -Fq 'Riepilogo' "$test_root/netuse"
done

actual_result=0
netuse --iface lo exec sh -c 'printf "exec output\n"; exit 29' >"$test_root/netuse" || actual_result=$?
test "$actual_result" -eq 29
grep -Fq 'exec output' "$test_root/netuse"
grep -Fq 'Riepilogo' "$test_root/netuse"
else
printf '%s\n' 'netuse runtime tests skipped: Linux /proc required'
fi
printf '%s\n' 'Shell function tests passed'
Loading