diff --git a/git-harden.sh b/git-harden.sh index 5c662ec..c1924da 100755 --- a/git-harden.sh +++ b/git-harden.sh @@ -65,6 +65,24 @@ die() { exit 1 } +# Strip inline comments and surrounding quotes from an SSH config value. +# Handles: value # comment, "value", 'value', "value" # comment +strip_ssh_value() { + local val="$1" + # Remove inline comment (not inside quotes): strip ' #...' from end + # Be careful: only strip ' #' preceded by space (not part of path) + val="$(printf '%s' "$val" | sed 's/[[:space:]]#.*$//')" + # Remove surrounding double quotes + val="${val#\"}" + val="${val%\"}" + # Remove surrounding single quotes + val="${val#\'}" + val="${val%\'}" + # Trim whitespace + val="$(printf '%s' "$val" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" + printf '%s' "$val" +} + print_ok() { printf '%b[OK]%b %s\n' "$GREEN" "$RESET" "$1" >&2 AUDIT_OK=$((AUDIT_OK + 1)) @@ -209,7 +227,10 @@ check_dependencies() { fi local git_version - git_version="$(git --version | sed 's/[^0-9.]//g')" + git_version="$(git --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)" + if [ -z "$git_version" ]; then + die "Could not parse git version from: $(git --version)" + fi if ! version_gte "$git_version" "2.34.0"; then die "git >= 2.34.0 required (found $git_version)" fi @@ -395,6 +416,7 @@ audit_ssh_directive() { local current current="$(grep -i "^[[:space:]]*${directive}[[:space:]]" "$SSH_CONFIG" 2>/dev/null | head -1 | sed 's/^[[:space:]]*[^ ]*[[:space:]]*//' || true)" + current="$(strip_ssh_value "$current")" if [ -z "$current" ]; then print_miss "SSH: $directive (expected: $expected)" @@ -615,11 +637,11 @@ detect_existing_keys() { if [ -f "$SSH_CONFIG" ]; then local identity_path while IFS= read -r identity_path; do + # Strip inline comments and quotes + identity_path="$(strip_ssh_value "$identity_path")" + [ -z "$identity_path" ] && continue # Expand tilde safely identity_path="${identity_path/#\~/$HOME}" - # Strip leading/trailing whitespace - identity_path="$(printf '%s' "$identity_path" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" - [ -z "$identity_path" ] && continue pub_path="${identity_path}.pub" if [ -f "$pub_path" ]; then @@ -827,6 +849,7 @@ apply_ssh_directive() { # Check if directive already exists with correct value (case-insensitive directive match) local current current="$(grep -i "^[[:space:]]*${directive}[[:space:]]" "$SSH_CONFIG" 2>/dev/null | head -1 | sed 's/^[[:space:]]*[^ ]*[[:space:]]*//' || true)" + current="$(strip_ssh_value "$current")" if [ "$current" = "$value" ]; then return diff --git a/test/git-harden.bats b/test/git-harden.bats index dd02484..84d9f9e 100755 --- a/test/git-harden.bats +++ b/test/git-harden.bats @@ -108,6 +108,60 @@ source_functions() { assert_failure } +# =========================================================================== +# Version extraction (grep-based, not sed) +# =========================================================================== + +@test "version extraction handles standard git output" { + local ver + ver="$(echo "git version 2.39.5" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)" + [ "$ver" = "2.39.5" ] +} + +@test "version extraction handles Apple Git suffix" { + local ver + ver="$(echo "git version 2.39.5 (Apple Git-154)" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)" + [ "$ver" = "2.39.5" ] +} + +@test "version extraction handles rc suffix" { + local ver + ver="$(echo "git version 2.45.0-rc1" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)" + [ "$ver" = "2.45.0" ] +} + +# =========================================================================== +# strip_ssh_value helper +# =========================================================================== + +@test "strip_ssh_value removes inline comment" { + source_functions + local result + result="$(strip_ssh_value "~/.ssh/id_ed25519 # signing key")" + [ "$result" = "~/.ssh/id_ed25519" ] +} + +@test "strip_ssh_value removes surrounding double quotes" { + source_functions + local result + result="$(strip_ssh_value '"~/.ssh/my key"')" + [ "$result" = "~/.ssh/my key" ] +} + +@test "strip_ssh_value removes quotes and comment together" { + source_functions + local result + result="$(strip_ssh_value '"~/.ssh/my key" # comment')" + [ "$result" = "~/.ssh/my key" ] +} + +@test "strip_ssh_value handles plain value" { + source_functions + local result + result="$(strip_ssh_value "accept-new")" + [ "$result" = "accept-new" ] +} + # =========================================================================== # Audit: git config settings # =========================================================================== @@ -493,6 +547,36 @@ SSHEOF [ "$SIGNING_PUB_PATH" = "${TEST_HOME}/.ssh/my_custom_key.pub" ] } +@test "detect_existing_keys handles IdentityFile with inline comment" { + ssh-keygen -t ed25519 -f "${TEST_HOME}/.ssh/my_key" -N "" -q + + cat > "${TEST_HOME}/.ssh/config" < "${TEST_HOME}/.ssh/config" <