fix: version parsing and SSH config comment/quote handling

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 <noreply@anthropic.com>
This commit is contained in:
Flo
2026-03-30 23:40:51 +02:00
parent b227ec1f73
commit d204ae5a9a
2 changed files with 111 additions and 4 deletions

View File

@@ -65,6 +65,24 @@ die() {
exit 1 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() { print_ok() {
printf '%b[OK]%b %s\n' "$GREEN" "$RESET" "$1" >&2 printf '%b[OK]%b %s\n' "$GREEN" "$RESET" "$1" >&2
AUDIT_OK=$((AUDIT_OK + 1)) AUDIT_OK=$((AUDIT_OK + 1))
@@ -209,7 +227,10 @@ check_dependencies() {
fi fi
local git_version 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 if ! version_gte "$git_version" "2.34.0"; then
die "git >= 2.34.0 required (found $git_version)" die "git >= 2.34.0 required (found $git_version)"
fi fi
@@ -395,6 +416,7 @@ audit_ssh_directive() {
local current local current
current="$(grep -i "^[[:space:]]*${directive}[[:space:]]" "$SSH_CONFIG" 2>/dev/null | head -1 | sed 's/^[[:space:]]*[^ ]*[[:space:]]*//' || true)" 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 if [ -z "$current" ]; then
print_miss "SSH: $directive (expected: $expected)" print_miss "SSH: $directive (expected: $expected)"
@@ -615,11 +637,11 @@ detect_existing_keys() {
if [ -f "$SSH_CONFIG" ]; then if [ -f "$SSH_CONFIG" ]; then
local identity_path local identity_path
while IFS= read -r identity_path; do 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 # Expand tilde safely
identity_path="${identity_path/#\~/$HOME}" 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" pub_path="${identity_path}.pub"
if [ -f "$pub_path" ]; then if [ -f "$pub_path" ]; then
@@ -827,6 +849,7 @@ apply_ssh_directive() {
# Check if directive already exists with correct value (case-insensitive directive match) # Check if directive already exists with correct value (case-insensitive directive match)
local current local current
current="$(grep -i "^[[:space:]]*${directive}[[:space:]]" "$SSH_CONFIG" 2>/dev/null | head -1 | sed 's/^[[:space:]]*[^ ]*[[:space:]]*//' || true)" 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 if [ "$current" = "$value" ]; then
return return

View File

@@ -108,6 +108,60 @@ source_functions() {
assert_failure 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 # Audit: git config settings
# =========================================================================== # ===========================================================================
@@ -493,6 +547,36 @@ SSHEOF
[ "$SIGNING_PUB_PATH" = "${TEST_HOME}/.ssh/my_custom_key.pub" ] [ "$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" <<SSHEOF
Host github.com
IdentityFile ${TEST_HOME}/.ssh/my_key # signing key
SSHEOF
source_functions
detect_existing_keys
[ "$SIGNING_KEY_FOUND" = true ]
[ "$SIGNING_PUB_PATH" = "${TEST_HOME}/.ssh/my_key.pub" ]
}
@test "detect_existing_keys handles quoted IdentityFile path" {
ssh-keygen -t ed25519 -f "${TEST_HOME}/.ssh/my_key" -N "" -q
cat > "${TEST_HOME}/.ssh/config" <<SSHEOF
Host github.com
IdentityFile "${TEST_HOME}/.ssh/my_key"
SSHEOF
source_functions
detect_existing_keys
[ "$SIGNING_KEY_FOUND" = true ]
[ "$SIGNING_PUB_PATH" = "${TEST_HOME}/.ssh/my_key.pub" ]
}
@test "detect_existing_keys finds configured key via user.signingkey" { @test "detect_existing_keys finds configured key via user.signingkey" {
ssh-keygen -t ed25519 -f "${TEST_HOME}/.ssh/signing_key" -N "" -q ssh-keygen -t ed25519 -f "${TEST_HOME}/.ssh/signing_key" -N "" -q
git config --global user.signingkey "${TEST_HOME}/.ssh/signing_key.pub" git config --global user.signingkey "${TEST_HOME}/.ssh/signing_key.pub"