diff --git a/references/check-catalog.md b/references/check-catalog.md index 592a8aa..c47733f 100644 --- a/references/check-catalog.md +++ b/references/check-catalog.md @@ -246,6 +246,12 @@ severity, description, and remediation. - **Description:** Changes to permission rules are not logged. Malicious permission escalation cannot be detected after the fact. - **Remediation:** Enable audit logging for permission changes. +### CHK-PRM-013 -- SSH private key has overly permissive permissions +- **Severity:** Critical +- **Description:** SSH private key files (e.g., `~/.ssh/id_rsa`, `~/.ssh/id_ed25519`) have permissions more permissive than 600. SSH clients often refuse to use keys with incorrect permissions, and they can be read by other users on the system. +- **Remediation:** Set SSH key permissions to 600 (read/write owner only): `chmod 600 ~/.ssh/id_rsa` +- **Auto-fix:** `chmod 600 "$KEY_PATH"` + --- ## Cron (CHK-CRN) diff --git a/scripts/helpers/test_integration.sh b/scripts/helpers/test_integration.sh index ca88dcd..e423f5f 100755 --- a/scripts/helpers/test_integration.sh +++ b/scripts/helpers/test_integration.sh @@ -308,6 +308,55 @@ test_chmod_600() { [[ "$perms" == "600" ]] } +# --------------------------------------------------------------------------- +# Test: CHK-PRM-013 auto-fix (SSH private key permissions) +# --------------------------------------------------------------------------- +test_prm_013() { + # Create mock SSH directory + local ssh_dir="${TEST_DIR}/.ssh" + mkdir -p "$ssh_dir" + + # Create test SSH private keys + local test_key="${ssh_dir}/id_test_rsa" + local test_pem="${ssh_dir}/test.pem" + + cat > "$test_key" <<'EOF' +-----BEGIN RSA PRIVATE KEY----- +fake key content +-----END RSA PRIVATE KEY----- +EOF + + cat > "$test_pem" <<'EOF' +-----BEGIN PRIVATE KEY----- +fake pem content +-----END PRIVATE KEY----- +EOF + + for key_file in "$test_key" "$test_pem"; do + # Set insecure permissions + chmod 644 "$key_file" + + # Run the auto-fix command + chmod 600 "$key_file" + + # Verify permissions were fixed + local perms + if [[ "$(uname -s)" == "Darwin" ]]; then + perms=$(stat -f "%Lp" "$key_file") + else + perms=$(stat -c "%a" "$key_file") + fi + + if [[ "$perms" != "600" ]]; then + return 1 + fi + done + # NOTE: This test verifies the chmod auto-fix command works correctly, but + # does not exercise the scanner's find/detect logic itself. This is + # consistent with other integration tests in this file which focus on + # auto-fix command validation rather than scanner detection coverage. +} + # --------------------------------------------------------------------------- # Main test execution # --------------------------------------------------------------------------- @@ -336,6 +385,7 @@ run_test "CHK-CFG-010: Enable sensitive data redaction" "test_cfg_010" run_test "CHK-CFG-011: Enable browser restrictions" "test_cfg_011" run_test "CHK-CFG-012: Disable network discovery" "test_cfg_012" run_test "File permissions: chmod 600" "test_chmod_600" +run_test "CHK-PRM-013: Fix SSH key permissions" "test_prm_013" echo "─────────────────────────────────────────────────────────────────────────────" echo "" diff --git a/scripts/scan_permissions.sh b/scripts/scan_permissions.sh index 168f4e1..94df089 100755 --- a/scripts/scan_permissions.sh +++ b/scripts/scan_permissions.sh @@ -646,6 +646,62 @@ check_cloud_sync() { fi } +# ─── CHK-PRM-013: SSH private key permissions ─────────────────────────────── + +check_ssh_keys() { + log_info "CHK-PRM-013: Checking SSH private key permissions" + + local ssh_dir="$HOME/.ssh" + local found=0 + + if [[ ! -d "$ssh_dir" ]]; then + add_finding "CHK-PRM-013" "info" \ + "No SSH directory found" \ + "No ~/.ssh directory exists on this system" "" "" "" + return + fi + + # Search for id_* files and *.pem files + while IFS= read -r -d '' f; do + found=1 + local perms + perms="$(get_perms "$f")" + [[ -z "$perms" ]] && continue + + if [[ "$perms" != "600" ]]; then + # Validate filename contains only safe characters before using in auto_fix. + # The safe_exec whitelist pattern for chmod requires paths matching + # [a-zA-Z0-9/._-]+, so we must not emit auto_fix for filenames with + # characters outside that set (e.g., spaces, quotes, shell metacharacters). + # Standard SSH key names (id_rsa, id_ed25519, *.pem) always pass this check. + local auto_fix_cmd="" remediation_msg="Manual fix required: chmod 600 [file]" + if [[ "$f" =~ ^[a-zA-Z0-9/._-]+$ ]]; then + auto_fix_cmd="chmod 600 $f" + remediation_msg="Run: chmod 600 $f" + fi + add_finding "CHK-PRM-013" "critical" \ + "SSH private key has insecure permissions" \ + "SSH private keys must be chmod 600 or SSH clients will refuse to use them. Current: $perms" \ + "$f mode $perms" \ + "$remediation_msg" \ + "$auto_fix_cmd" + else + add_finding "CHK-PRM-013" "ok" \ + "SSH private key permissions correct" \ + "SSH private key is properly restricted to owner read/write" \ + "$f mode $perms" "" "" + fi + done < <(find "$ssh_dir" -maxdepth 1 \( \ + -iname 'id_*' -o -iname '*.pem' \ + \) -type f ! -iname '*.pub' -print0 2>/dev/null) + + if [[ "$found" -eq 0 ]]; then + add_finding "CHK-PRM-013" "info" \ + "No SSH private keys found" \ + "No SSH private key files detected in ~/.ssh directory" "" "" "" + fi +} + # ─── Run all checks ───────────────────────────────────────────────────────── main() { @@ -663,6 +719,7 @@ main() { check_credentials_dir check_log_secrets check_cloud_sync + check_ssh_keys log_info "Permissions scan complete: ${#FINDINGS[@]} finding(s)"