Changes: - Added 80+ scripts with organized structure - payloads/ for third-party pentesting tools - pentesting/ for custom security scripts - Daily drivers remain flat for fast access - Converted wes() function to proper script - Removed .sh extensions from pentesting scripts - Cleaned up aliases (removed 31 redundant lines) - Added kanata/, build artifacts to gitignore - Removed old fre.sh scripts and empty a.out - Updated configs: helix, tmux, zsh, ulauncher, redshift Security: All sensitive data excluded via gitignore
528 lines
13 KiB
Bash
Executable file
528 lines
13 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Script Name: clip
|
|
# Description: Smart clipboard manager with history, search, and categories
|
|
# Usage: clip # Show history with fzf search
|
|
# clip pin # Pin current clipboard item
|
|
# clip cat 5 # Show 5th history item
|
|
# clip search "192" # Search history
|
|
# clip clear # Clear history
|
|
|
|
VERSION="1.0.0"
|
|
CLIP_DIR="$HOME/.clipboard"
|
|
HISTORY_FILE="$CLIP_DIR/history.txt"
|
|
PINS_FILE="$CLIP_DIR/pins.txt"
|
|
MAX_HISTORY=100
|
|
|
|
# Colors
|
|
readonly GREEN='\033[0;32m'
|
|
readonly YELLOW='\033[1;33m'
|
|
readonly BLUE='\033[0;34m'
|
|
readonly RED='\033[0;31m'
|
|
readonly CYAN='\033[0;36m'
|
|
readonly MAGENTA='\033[0;35m'
|
|
readonly BOLD='\033[1m'
|
|
readonly NC='\033[0m'
|
|
|
|
# Initialize clip directory
|
|
init_clip() {
|
|
if [[ ! -d "$CLIP_DIR" ]]; then
|
|
mkdir -p "$CLIP_DIR"
|
|
touch "$HISTORY_FILE"
|
|
touch "$PINS_FILE"
|
|
fi
|
|
}
|
|
|
|
show_help() {
|
|
echo -e "${BOLD}clip${NC} - Smart Clipboard Manager v${VERSION}"
|
|
echo
|
|
echo -e "${BOLD}USAGE:${NC}"
|
|
echo " clip [COMMAND]"
|
|
echo
|
|
echo -e "${BOLD}COMMANDS:${NC}"
|
|
echo -e " ${CYAN}(no args)${NC} Show history with fzf search"
|
|
echo -e " ${CYAN}pin${NC} Pin current clipboard item"
|
|
echo -e " ${CYAN}pins${NC} Show pinned items"
|
|
echo -e " ${CYAN}cat N${NC} Show Nth history item"
|
|
echo -e " ${CYAN}search TERM${NC} Search history"
|
|
echo -e " ${CYAN}list${NC} List recent history (last 20)"
|
|
echo -e " ${CYAN}save${NC} Save current clipboard to history"
|
|
echo -e " ${CYAN}clear${NC} Clear history"
|
|
echo -e " ${CYAN}delete N${NC} Delete Nth history item"
|
|
echo -e " ${CYAN}unpin N${NC} Delete Nth pinned item"
|
|
echo -e " ${CYAN}stats${NC} Show statistics"
|
|
echo -e " ${CYAN}-h, --help${NC} Show this help"
|
|
echo
|
|
echo -e "${BOLD}EXAMPLES:${NC}"
|
|
echo " clip # Interactive search"
|
|
echo " clip pin # Pin important item"
|
|
echo " clip search \"192.168\" # Find IP addresses"
|
|
echo " clip cat 1 # Show most recent"
|
|
echo " clip delete 5 # Delete 5th item from history"
|
|
echo " clip unpin 1 # Delete 1st pinned item"
|
|
echo
|
|
echo -e "${BOLD}FEATURES:${NC}"
|
|
echo " - Automatic history (monitors clipboard)"
|
|
echo " - Pattern detection (URLs, IPs, hashes)"
|
|
echo " - Security: Auto-expire sensitive data"
|
|
echo " - Pin important items"
|
|
echo " - fzf integration for search"
|
|
echo
|
|
echo -e "${BOLD}NOTES:${NC}"
|
|
echo " History: $HISTORY_FILE"
|
|
echo " Pins: $PINS_FILE"
|
|
echo " Max history: $MAX_HISTORY items"
|
|
}
|
|
|
|
# Detect content type
|
|
detect_type() {
|
|
local content="$1"
|
|
|
|
# URL
|
|
if [[ "$content" =~ ^https?:// ]]; then
|
|
echo "url"
|
|
# IPv4
|
|
elif [[ "$content" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3} ]]; then
|
|
echo "ip"
|
|
# Hash (32 or 40 or 64 hex chars)
|
|
elif [[ "$content" =~ ^[a-f0-9]{32}$ ]] || [[ "$content" =~ ^[a-f0-9]{40}$ ]] || [[ "$content" =~ ^[a-f0-9]{64}$ ]]; then
|
|
echo "hash"
|
|
# Code (contains common code markers)
|
|
elif [[ "$content" =~ (function|const|var|class|def|import|export) ]]; then
|
|
echo "code"
|
|
# Credential patterns (don't save these!)
|
|
elif [[ "$content" =~ (password|secret|token|key|bearer|api[_-]?key) ]]; then
|
|
echo "credential"
|
|
else
|
|
echo "text"
|
|
fi
|
|
}
|
|
|
|
# Save item to history
|
|
save_to_history() {
|
|
local content="$1"
|
|
local type=$(detect_type "$content")
|
|
|
|
# Don't save credentials
|
|
if [[ "$type" == "credential" ]]; then
|
|
echo -e "${YELLOW}⚠${NC} Sensitive data detected - not saved to history" >&2
|
|
return 0
|
|
fi
|
|
|
|
# Don't save empty
|
|
if [[ -z "$content" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
# Don't save if it's already the most recent
|
|
if [[ -f "$HISTORY_FILE" ]]; then
|
|
last_entry=$(tail -1 "$HISTORY_FILE" | cut -d'|' -f3-)
|
|
if [[ "$last_entry" == "$content" ]]; then
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
# Save: timestamp|type|content
|
|
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
echo "$timestamp|$type|$content" >> "$HISTORY_FILE"
|
|
|
|
# Trim history to max size
|
|
if [[ $(wc -l < "$HISTORY_FILE") -gt $MAX_HISTORY ]]; then
|
|
tail -n $MAX_HISTORY "$HISTORY_FILE" > "$HISTORY_FILE.tmp"
|
|
mv "$HISTORY_FILE.tmp" "$HISTORY_FILE"
|
|
fi
|
|
}
|
|
|
|
# Get current clipboard
|
|
get_clipboard() {
|
|
if command -v xsel &>/dev/null; then
|
|
xsel --output --clipboard
|
|
elif command -v xclip &>/dev/null; then
|
|
xclip -selection clipboard -o
|
|
else
|
|
echo "Error: No clipboard tool found" >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Set clipboard
|
|
set_clipboard() {
|
|
if command -v xsel &>/dev/null; then
|
|
echo -n "$1" | xsel --input --clipboard
|
|
elif command -v xclip &>/dev/null; then
|
|
echo -n "$1" | xclip -selection clipboard
|
|
else
|
|
echo "Error: No clipboard tool found" >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Interactive history browser with fzf
|
|
show_history_fzf() {
|
|
if ! command -v fzf &>/dev/null; then
|
|
echo -e "${RED}Error:${NC} fzf not found" >&2
|
|
echo "Install it with: sudo apt install fzf" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -f "$HISTORY_FILE" ]] || [[ ! -s "$HISTORY_FILE" ]]; then
|
|
echo -e "${YELLOW}No history yet${NC}" >&2
|
|
exit 0
|
|
fi
|
|
|
|
# Format for fzf: index | time | type | preview
|
|
selected=$(tac "$HISTORY_FILE" | awk -F'|' '
|
|
{
|
|
idx = NR
|
|
time = $1
|
|
type = $2
|
|
content = $3
|
|
for (i=4; i<=NF; i++) content = content "|" $i
|
|
|
|
# Truncate preview
|
|
preview = content
|
|
if (length(preview) > 60) {
|
|
preview = substr(preview, 1, 60) "..."
|
|
}
|
|
|
|
printf "%3d | %s | %-10s | %s\n", idx, time, "[" type "]", preview
|
|
}
|
|
' | fzf --height=60% --layout=reverse \
|
|
--header="Select item to copy (ESC to cancel)" \
|
|
--preview='echo {}' \
|
|
--preview-window=up:3:wrap)
|
|
|
|
if [[ -n "$selected" ]]; then
|
|
# Extract the full content
|
|
index=$(echo "$selected" | awk '{print $1}')
|
|
full_content=$(tac "$HISTORY_FILE" | sed -n "${index}p" | cut -d'|' -f3-)
|
|
|
|
# Copy to clipboard
|
|
set_clipboard "$full_content"
|
|
echo -e "${GREEN}✓${NC} Copied to clipboard"
|
|
fi
|
|
}
|
|
|
|
# Pin current clipboard
|
|
pin_item() {
|
|
local content=$(get_clipboard)
|
|
|
|
if [[ -z "$content" ]]; then
|
|
echo -e "${RED}Error:${NC} Clipboard is empty" >&2
|
|
exit 1
|
|
fi
|
|
|
|
type=$(detect_type "$content")
|
|
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
|
|
|
|
echo "$timestamp|$type|$content" >> "$PINS_FILE"
|
|
echo -e "${GREEN}✓${NC} Pinned item (type: $type)"
|
|
}
|
|
|
|
# Show pins
|
|
show_pins() {
|
|
if [[ ! -f "$PINS_FILE" ]] || [[ ! -s "$PINS_FILE" ]]; then
|
|
echo -e "${YELLOW}No pinned items${NC}"
|
|
exit 0
|
|
fi
|
|
|
|
echo -e "${BOLD}${CYAN}Pinned Items:${NC}"
|
|
echo
|
|
|
|
if command -v fzf &>/dev/null; then
|
|
# Interactive selection
|
|
selected=$(cat "$PINS_FILE" | awk -F'|' '
|
|
{
|
|
idx = NR
|
|
time = $1
|
|
type = $2
|
|
content = $3
|
|
for (i=4; i<=NF; i++) content = content "|" $i
|
|
|
|
preview = content
|
|
if (length(preview) > 60) {
|
|
preview = substr(preview, 1, 60) "..."
|
|
}
|
|
|
|
printf "%3d | %s | %-10s | %s\n", idx, time, "[" type "]", preview
|
|
}
|
|
' | fzf --height=60% --layout=reverse \
|
|
--header="Select pinned item to copy (ESC to cancel)" \
|
|
--preview='echo {}')
|
|
|
|
if [[ -n "$selected" ]]; then
|
|
index=$(echo "$selected" | awk '{print $1}')
|
|
full_content=$(sed -n "${index}p" "$PINS_FILE" | cut -d'|' -f3-)
|
|
set_clipboard "$full_content"
|
|
echo -e "${GREEN}✓${NC} Copied pinned item to clipboard"
|
|
fi
|
|
else
|
|
# Just list them
|
|
cat "$PINS_FILE" | awk -F'|' '
|
|
{
|
|
idx = NR
|
|
time = $1
|
|
type = $2
|
|
content = $3
|
|
for (i=4; i<=NF; i++) content = content "|" $i
|
|
|
|
preview = content
|
|
if (length(preview) > 60) {
|
|
preview = substr(preview, 1, 60) "..."
|
|
}
|
|
|
|
printf "%3d | %s | %-10s | %s\n", idx, time, "[" type "]", preview
|
|
}
|
|
'
|
|
fi
|
|
}
|
|
|
|
# Show specific item
|
|
show_item() {
|
|
local index=$1
|
|
|
|
if [[ ! -f "$HISTORY_FILE" ]]; then
|
|
echo -e "${RED}Error:${NC} No history" >&2
|
|
exit 1
|
|
fi
|
|
|
|
content=$(tac "$HISTORY_FILE" | sed -n "${index}p" | cut -d'|' -f3-)
|
|
|
|
if [[ -z "$content" ]]; then
|
|
echo -e "${RED}Error:${NC} No item at index $index" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "$content"
|
|
}
|
|
|
|
# Search history
|
|
search_history() {
|
|
local query="$1"
|
|
|
|
if [[ ! -f "$HISTORY_FILE" ]]; then
|
|
echo -e "${RED}Error:${NC} No history" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo -e "${BOLD}${CYAN}Search results for: ${query}${NC}"
|
|
echo
|
|
|
|
grep -i "$query" "$HISTORY_FILE" | awk -F'|' '
|
|
{
|
|
time = $1
|
|
type = $2
|
|
content = $3
|
|
for (i=4; i<=NF; i++) content = content "|" $i
|
|
|
|
preview = content
|
|
if (length(preview) > 60) {
|
|
preview = substr(preview, 1, 60) "..."
|
|
}
|
|
|
|
printf "%s | %-10s | %s\n", time, "[" type "]", preview
|
|
}
|
|
'
|
|
}
|
|
|
|
# List recent items
|
|
list_recent() {
|
|
if [[ ! -f "$HISTORY_FILE" ]]; then
|
|
echo -e "${YELLOW}No history${NC}"
|
|
exit 0
|
|
fi
|
|
|
|
echo -e "${BOLD}${CYAN}Recent Clipboard History (last 20):${NC}"
|
|
echo
|
|
|
|
tail -20 "$HISTORY_FILE" | tac | awk -F'|' '
|
|
{
|
|
idx = NR
|
|
time = $1
|
|
type = $2
|
|
content = $3
|
|
for (i=4; i<=NF; i++) content = content "|" $i
|
|
|
|
preview = content
|
|
if (length(preview) > 60) {
|
|
preview = substr(preview, 1, 60) "..."
|
|
}
|
|
|
|
printf "%3d | %s | %-10s | %s\n", idx, time, "[" type "]", preview
|
|
}
|
|
'
|
|
}
|
|
|
|
# Statistics
|
|
show_stats() {
|
|
if [[ ! -f "$HISTORY_FILE" ]]; then
|
|
echo -e "${YELLOW}No history${NC}"
|
|
exit 0
|
|
fi
|
|
|
|
total=$(wc -l < "$HISTORY_FILE")
|
|
pins=$(wc -l < "$PINS_FILE" 2>/dev/null || echo 0)
|
|
|
|
echo -e "${BOLD}${CYAN}Clipboard Statistics:${NC}"
|
|
echo
|
|
echo " Total items: $total"
|
|
echo " Pinned items: $pins"
|
|
echo " Max history: $MAX_HISTORY"
|
|
echo
|
|
|
|
echo -e "${BOLD}${CYAN}By Type:${NC}"
|
|
awk -F'|' '{print $2}' "$HISTORY_FILE" | sort | uniq -c | sort -rn | awk '
|
|
{printf " %-15s %d\n", $2, $1}'
|
|
}
|
|
|
|
# Delete specific item from history
|
|
delete_item() {
|
|
local index=$1
|
|
|
|
if [[ ! -f "$HISTORY_FILE" ]]; then
|
|
echo -e "${RED}Error:${NC} No history" >&2
|
|
exit 1
|
|
fi
|
|
|
|
total=$(wc -l < "$HISTORY_FILE")
|
|
if [[ $index -lt 1 ]] || [[ $index -gt $total ]]; then
|
|
echo -e "${RED}Error:${NC} Invalid index (1-$total)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Show what we're deleting
|
|
content=$(tac "$HISTORY_FILE" | sed -n "${index}p" | cut -d'|' -f3-)
|
|
preview="${content:0:60}"
|
|
if [[ ${#content} -gt 60 ]]; then
|
|
preview="${preview}..."
|
|
fi
|
|
|
|
echo -e "${YELLOW}Delete item $index:${NC} $preview"
|
|
echo -n "Continue? (y/N) "
|
|
read -r response
|
|
|
|
if [[ "$response" =~ ^[Yy]$ ]]; then
|
|
# Delete line (remember tac reverses order)
|
|
actual_line=$((total - index + 1))
|
|
sed -i "${actual_line}d" "$HISTORY_FILE"
|
|
echo -e "${GREEN}✓${NC} Deleted item $index"
|
|
else
|
|
echo "Cancelled"
|
|
fi
|
|
}
|
|
|
|
# Delete pinned item
|
|
delete_pin() {
|
|
local index=$1
|
|
|
|
if [[ ! -f "$PINS_FILE" ]] || [[ ! -s "$PINS_FILE" ]]; then
|
|
echo -e "${YELLOW}No pinned items${NC}"
|
|
exit 0
|
|
fi
|
|
|
|
total=$(wc -l < "$PINS_FILE")
|
|
if [[ $index -lt 1 ]] || [[ $index -gt $total ]]; then
|
|
echo -e "${RED}Error:${NC} Invalid index (1-$total)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Show what we're deleting
|
|
content=$(sed -n "${index}p" "$PINS_FILE" | cut -d'|' -f3-)
|
|
preview="${content:0:60}"
|
|
if [[ ${#content} -gt 60 ]]; then
|
|
preview="${preview}..."
|
|
fi
|
|
|
|
echo -e "${YELLOW}Unpin item $index:${NC} $preview"
|
|
echo -n "Continue? (y/N) "
|
|
read -r response
|
|
|
|
if [[ "$response" =~ ^[Yy]$ ]]; then
|
|
sed -i "${index}d" "$PINS_FILE"
|
|
echo -e "${GREEN}✓${NC} Unpinned item $index"
|
|
else
|
|
echo "Cancelled"
|
|
fi
|
|
}
|
|
|
|
# Clear history
|
|
clear_history() {
|
|
echo -n "Clear clipboard history? (y/N) "
|
|
read -r response
|
|
if [[ "$response" =~ ^[Yy]$ ]]; then
|
|
> "$HISTORY_FILE"
|
|
echo -e "${GREEN}✓${NC} History cleared"
|
|
else
|
|
echo "Cancelled"
|
|
fi
|
|
}
|
|
|
|
# Initialize
|
|
init_clip
|
|
|
|
# Parse command
|
|
if [[ $# -eq 0 ]]; then
|
|
show_history_fzf
|
|
exit 0
|
|
fi
|
|
|
|
case $1 in
|
|
-h|--help|help)
|
|
show_help
|
|
;;
|
|
save)
|
|
content=$(get_clipboard)
|
|
save_to_history "$content"
|
|
echo -e "${GREEN}✓${NC} Saved to history"
|
|
;;
|
|
pin)
|
|
pin_item
|
|
;;
|
|
pins)
|
|
show_pins
|
|
;;
|
|
cat)
|
|
if [[ $# -lt 2 ]]; then
|
|
echo -e "${RED}Error:${NC} Item index required" >&2
|
|
exit 1
|
|
fi
|
|
show_item "$2"
|
|
;;
|
|
search|s)
|
|
if [[ $# -lt 2 ]]; then
|
|
echo -e "${RED}Error:${NC} Search query required" >&2
|
|
exit 1
|
|
fi
|
|
shift
|
|
search_history "$*"
|
|
;;
|
|
list|ls|l)
|
|
list_recent
|
|
;;
|
|
delete|del|rm)
|
|
if [[ $# -lt 2 ]]; then
|
|
echo -e "${RED}Error:${NC} Item index required" >&2
|
|
exit 1
|
|
fi
|
|
delete_item "$2"
|
|
;;
|
|
unpin)
|
|
if [[ $# -lt 2 ]]; then
|
|
echo -e "${RED}Error:${NC} Item index required" >&2
|
|
exit 1
|
|
fi
|
|
delete_pin "$2"
|
|
;;
|
|
clear)
|
|
clear_history
|
|
;;
|
|
stats)
|
|
show_stats
|
|
;;
|
|
*)
|
|
echo -e "${RED}Error:${NC} Unknown command: $1" >&2
|
|
echo "Run 'clip --help' for usage" >&2
|
|
exit 1
|
|
;;
|
|
esac
|