From f2670e6db9365a37e5dc0f73f184eb85f28a698c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Jan 2026 21:40:56 +0000 Subject: [PATCH] Security improvements: fix sourcing, temp files, validation, and error handling Co-authored-by: kuhyx <147418882+kuhyx@users.noreply.github.com> --- .../setup_thesis_work_tracker.sh | 10 ++- .../digital_wellbeing/thesis_work_status.sh | 18 +++-- .../digital_wellbeing/thesis_work_tracker.sh | 69 +++++++++++++------ 3 files changed, 70 insertions(+), 27 deletions(-) diff --git a/scripts/digital_wellbeing/setup_thesis_work_tracker.sh b/scripts/digital_wellbeing/setup_thesis_work_tracker.sh index 7a710d9..8a72eb0 100755 --- a/scripts/digital_wellbeing/setup_thesis_work_tracker.sh +++ b/scripts/digital_wellbeing/setup_thesis_work_tracker.sh @@ -89,7 +89,7 @@ require_root() { } usage() { - sed -n '2,/^set -euo pipefail/p' "$0" | sed 's/^# \{0,1\}//' + head -n 31 "$0" | tail -n +2 | sed 's/^# \{0,1\}//' } check_dependencies() { @@ -128,6 +128,10 @@ while [[ $# -gt 0 ]]; do err "--work-quota requires a value" exit 2 } + if ! [[ $WORK_QUOTA_MINUTES =~ ^[0-9]+$ ]] || [[ $WORK_QUOTA_MINUTES -le 0 ]]; then + err "--work-quota must be a positive integer (got: $WORK_QUOTA_MINUTES)" + exit 2 + fi shift 2 ;; --decay-rate) @@ -136,6 +140,10 @@ while [[ $# -gt 0 ]]; do err "--decay-rate requires a value" exit 2 } + if ! [[ $DECAY_RATE_MINUTES =~ ^[0-9]+$ ]] || [[ $DECAY_RATE_MINUTES -lt 0 ]]; then + err "--decay-rate must be a non-negative integer (got: $DECAY_RATE_MINUTES)" + exit 2 + fi shift 2 ;; --vscode-repo) diff --git a/scripts/digital_wellbeing/thesis_work_status.sh b/scripts/digital_wellbeing/thesis_work_status.sh index 1bf0ab8..92c7859 100755 --- a/scripts/digital_wellbeing/thesis_work_status.sh +++ b/scripts/digital_wellbeing/thesis_work_status.sh @@ -29,12 +29,18 @@ fi # Temporarily remove immutable to read sudo chattr -i "$STATE_FILE" 2>/dev/null || true -# Source the state file -# shellcheck source=/dev/null -source "$STATE_FILE" 2>/dev/null || { - echo -e "${RED}Error:${NC} Could not read state file" - exit 1 -} +# Parse state file safely without using source +# Only extract the numeric values we need +TOTAL_WORK_SECONDS=$(grep "^TOTAL_WORK_SECONDS=" "$STATE_FILE" 2>/dev/null | cut -d= -f2 || echo "0") +STEAM_ACCESS_GRANTED=$(grep "^STEAM_ACCESS_GRANTED=" "$STATE_FILE" 2>/dev/null | cut -d= -f2 || echo "0") +CURRENT_SESSION_SECONDS=$(grep "^CURRENT_SESSION_SECONDS=" "$STATE_FILE" 2>/dev/null | cut -d= -f2 || echo "0") +LAST_WORK_SESSION_START=$(grep "^LAST_WORK_SESSION_START=" "$STATE_FILE" 2>/dev/null | cut -d= -f2 || echo "0") + +# Validate that values are numeric +if ! [[ $TOTAL_WORK_SECONDS =~ ^[0-9]+$ ]]; then TOTAL_WORK_SECONDS=0; fi +if ! [[ $STEAM_ACCESS_GRANTED =~ ^[01]$ ]]; then STEAM_ACCESS_GRANTED=0; fi +if ! [[ $CURRENT_SESSION_SECONDS =~ ^[0-9]+$ ]]; then CURRENT_SESSION_SECONDS=0; fi +if ! [[ $LAST_WORK_SESSION_START =~ ^[0-9]+$ ]]; then LAST_WORK_SESSION_START=0; fi # Re-apply immutable sudo chattr +i "$STATE_FILE" 2>/dev/null || true diff --git a/scripts/digital_wellbeing/thesis_work_tracker.sh b/scripts/digital_wellbeing/thesis_work_tracker.sh index e20cd96..e7366d4 100755 --- a/scripts/digital_wellbeing/thesis_work_tracker.sh +++ b/scripts/digital_wellbeing/thesis_work_tracker.sh @@ -121,7 +121,9 @@ LAST_WORK_SESSION_START=0 CURRENT_SESSION_SECONDS=0 EOF sudo chmod 600 "$STATE_FILE" - sudo chattr +i "$STATE_FILE" 2>/dev/null || true + if ! sudo chattr +i "$STATE_FILE" 2>/dev/null; then + log_warn "Failed to set immutable flag on state file - protections may be weaker" + fi fi } @@ -135,9 +137,19 @@ load_state() { # Temporarily remove immutable flag to read sudo chattr -i "$STATE_FILE" 2>/dev/null || true - # Source the state file - # shellcheck source=/dev/null - source "$STATE_FILE" + # Parse state file safely without using source + # Only extract the numeric values we need + TOTAL_WORK_SECONDS=$(grep "^TOTAL_WORK_SECONDS=" "$STATE_FILE" 2>/dev/null | cut -d= -f2 || echo "0") + STEAM_ACCESS_GRANTED=$(grep "^STEAM_ACCESS_GRANTED=" "$STATE_FILE" 2>/dev/null | cut -d= -f2 || echo "0") + CURRENT_SESSION_SECONDS=$(grep "^CURRENT_SESSION_SECONDS=" "$STATE_FILE" 2>/dev/null | cut -d= -f2 || echo "0") + LAST_WORK_SESSION_START=$(grep "^LAST_WORK_SESSION_START=" "$STATE_FILE" 2>/dev/null | cut -d= -f2 || echo "0") + LAST_UPDATE_TIMESTAMP=$(grep "^LAST_UPDATE_TIMESTAMP=" "$STATE_FILE" 2>/dev/null | cut -d= -f2 || echo "0") + + # Validate that values are numeric + if ! [[ $TOTAL_WORK_SECONDS =~ ^[0-9]+$ ]]; then TOTAL_WORK_SECONDS=0; fi + if ! [[ $STEAM_ACCESS_GRANTED =~ ^[01]$ ]]; then STEAM_ACCESS_GRANTED=0; fi + if ! [[ $CURRENT_SESSION_SECONDS =~ ^[0-9]+$ ]]; then CURRENT_SESSION_SECONDS=0; fi + if ! [[ $LAST_WORK_SESSION_START =~ ^[0-9]+$ ]]; then LAST_WORK_SESSION_START=0; fi # Re-apply immutable flag sudo chattr +i "$STATE_FILE" 2>/dev/null || true @@ -168,7 +180,9 @@ EOF sudo chmod 600 "$STATE_FILE" # Re-apply immutable flag - sudo chattr +i "$STATE_FILE" 2>/dev/null || true + if ! sudo chattr +i "$STATE_FILE" 2>/dev/null; then + log_warn "Failed to set immutable flag on state file after save" + fi } # Check if a process is running @@ -211,21 +225,11 @@ is_vscode_on_thesis_repo() { # VS Code window titles typically contain the folder/workspace name # Look for the repo name in the window title + # Window title format is usually: "filename - reponame - Visual Studio Code" if [[ $window_title == *"$VSCODE_REQUIRED_REPO"* ]]; then return 0 fi - # Also check if VS Code has the repo open by checking recent workspaces - # VS Code stores workspace info in ~/.config/Code/User/workspaceStorage - if [[ -d "$HOME/.config/Code/User/workspaceStorage" ]]; then - if find "$HOME/.config/Code/User/workspaceStorage" -type f -name "workspace.json" -exec grep -l "$VSCODE_REQUIRED_REPO" {} \; 2>/dev/null | grep -q .; then - # Additional check: is this window actually VS Code? - if [[ $window_title == *"Visual Studio Code"* ]]; then - return 0 - fi - fi - fi - return 1 } @@ -246,7 +250,10 @@ is_thesis_work_active() { # Check each thesis application for proc_pattern in "${!THESIS_APPS[@]}"; do - if [[ $process_name == *"$proc_pattern"* ]] || [[ $window_title == *"${THESIS_APPS[$proc_pattern]}"* ]]; then + local app_name="${THESIS_APPS[$proc_pattern]}" + + # Check window title for application name (more reliable than process name) + if [[ $window_title == *"$app_name"* ]]; then # Special handling for VS Code - must be on thesis repo if [[ $proc_pattern == "Code" ]] || [[ $proc_pattern == "code" ]]; then if is_vscode_on_thesis_repo "$window_title"; then @@ -258,7 +265,24 @@ is_thesis_work_active() { fi fi - log_debug "Thesis work detected: ${THESIS_APPS[$proc_pattern]}" + log_debug "Thesis work detected: $app_name" + return 0 + fi + + # Also check process name with exact match + if [[ $process_name == "$proc_pattern" ]]; then + # Special handling for VS Code - must be on thesis repo + if [[ $proc_pattern == "Code" ]] || [[ $proc_pattern == "code" ]]; then + if is_vscode_on_thesis_repo "$window_title"; then + log_debug "Thesis work detected: VS Code on $VSCODE_REQUIRED_REPO" + return 0 + else + log_debug "VS Code detected but not on thesis repo" + continue + fi + fi + + log_debug "Thesis work detected: $app_name" return 0 fi done @@ -298,8 +322,13 @@ unblock_distractions() { # Remove immutable flag temporarily sudo chattr -i /etc/hosts 2>/dev/null || true - # Remove blocking entries - local temp_hosts="/tmp/hosts.tmp.$$" + # Remove blocking entries using mktemp for security + local temp_hosts + temp_hosts=$(mktemp) || { + log_error "Failed to create temporary file" + return 1 + } + sudo cp /etc/hosts "$temp_hosts" for domain in "${STEAM_DOMAINS[@]}" "${DISTRACTION_DOMAINS[@]}"; do