From d204ae5a9af05e9568237fc0fb1442b73eebe5c0 Mon Sep 17 00:00:00 2001 From: Flo Date: Mon, 30 Mar 2026 23:40:51 +0200 Subject: [PATCH] fix: version parsing and SSH config comment/quote handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace sed 's/[^0-9.]//g' with grep -oE for semver extraction — fixes breakage on Apple Git suffix and rc versions. Add strip_ssh_value() helper to strip inline comments and surrounding quotes from SSH config values. Applied to IdentityFile scanning, audit_ssh_directive, and apply_ssh_directive. 9 new tests (62 total). Closes: #8 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- git-harden.sh | 31 +++++++++++++--- test/git-harden.bats | 84 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 4 deletions(-) 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" <