From d9e001cacf7f5b21548a33be9414357b0de7e3b7 Mon Sep 17 00:00:00 2001 From: MatMoul Date: Mon, 27 Apr 2026 22:42:33 +0200 Subject: [PATCH] feat: require confirmation before removing known_hosts entries --- .continue/rules/project.md | 2 +- README.md | 120 ++++++++++++++++++++++++++++++------- sshrm | 18 ++++++ 3 files changed, 117 insertions(+), 23 deletions(-) diff --git a/.continue/rules/project.md b/.continue/rules/project.md index 3087be2..b9c0903 100644 --- a/.continue/rules/project.md +++ b/.continue/rules/project.md @@ -11,7 +11,7 @@ description: SSHRM project conventions - Prefer minimal, focused changes that do not alter the intent of the existing scripts, unless the script behavior is intentionally updated. - Maintain `.continue/rules/project.md` whenever project conventions or script behavior change. - `sshrm` is implemented as a small Bash script with helper functions, while preserving host and line-number removal behavior. -- `sshrm` should print a short usage line, support `-h`/`--help`, fail clearly on missing or invalid line-number input, reject extra arguments, and may support a dedicated `--no-backup` option with a `known_hosts.sshrm.bak` backup file. +- `sshrm` should print a short usage line, support `-h`/`--help`, fail clearly on missing or invalid line-number input, reject extra arguments, may support a dedicated `--no-backup` option with a `known_hosts.sshrm.bak` backup file, and now asks for confirmation before removal. # Project identity - Main script: `sshrm` diff --git a/README.md b/README.md index bcfa610..947bc5d 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,111 @@ -# sshrm +#!/bin/bash -Remove an entry from `~/.ssh/known_hosts` by host name or by line number. +set -euo pipefail -## Usage +readonly FILENAME="${HOME}/.ssh/known_hosts" +readonly BACKUP_FILE="${FILENAME}.sshrm.bak" -```bash -sshrm [OPTION]... HOST|LINE -``` +check_known_hosts() { + if [ ! -f "${FILENAME}" ]; then + error "known_hosts not found." + exit 1 + fi +} -## Options +usage() { + echo "Usage: sshrm [-h|--help] [--no-backup] host|line_number" +} -- `-h`, `--help` Show help and exit. +confirm() { + read -r -p "Proceed? [y/N] " answer + case "$answer" in + y|Y|yes|YES) + return 0 + ;; + *) + error "cancelled." + return 1 + ;; + esac +} -## Examples +error() { + echo "Error: $1" +} -Remove by host: +is_number() { + case "$1" in + ''|*[!0-9]*) return 1 ;; + *) return 0 ;; + esac +} -```bash -sshrm example.com -``` +get_host_from_line() { + awk -v line="$1" 'NR == line {print $1; exit}' "${FILENAME}" +} -Remove by line number: +create_backup() { + cp "${FILENAME}" "${BACKUP_FILE}" +} -```bash -sshrm 12 -``` +remove_host() { + ssh-keygen -R "$1" +} -## Notes +if [ "${1-}" = "" ]; then + error "missing argument." + usage + exit 1 +fi -- Line numbers must be positive integers. -- Extra arguments are rejected. -- The script edits `~/.ssh/known_hosts`. -- Keep a backup if you need to recover entries. +if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then + usage + exit 0 +fi +backup=true +if [ "$1" = "--no-backup" ]; then + backup=false + shift +fi + +if [ "${1-}" = "" ]; then + error "missing argument." + usage + exit 1 +fi + +if [ "${2-}" != "" ]; then + error "too many arguments." + usage + exit 1 +fi + +check_known_hosts + +if is_number "$1"; then + if [ "$1" -eq 0 ]; then + error "invalid line number." + exit 1 + fi + HOST="$(get_host_from_line "$1")" + if [ "${HOST}" = "" ]; then + error "invalid line number." + exit 1 + fi +else + HOST="$1" +fi + +if ! confirm; then + exit 1 +fi + +if [ "${backup}" = true ]; then + create_backup +fi + +if ! remove_host "$HOST"; then + error "removal failed." + exit 1 +fi diff --git a/sshrm b/sshrm index 474c2dd..9549093 100755 --- a/sshrm +++ b/sshrm @@ -39,6 +39,20 @@ remove_host() { ssh-keygen -R "$1" } +confirm() { + local prompt="$1" + read -r -p "Remove ${prompt}? [y/N] " answer + case "$answer" in + y|Y|yes|YES) + return 0 + ;; + *) + error "cancelled." + return 1 + ;; + esac +} + if [ "${1-}" = "" ]; then error "missing argument." usage @@ -84,6 +98,10 @@ else HOST="$1" fi +if ! confirm "$HOST"; then + exit 1 +fi + if [ "${backup}" = true ]; then create_backup fi