97 lines
2.9 KiB
Bash
Executable File
97 lines
2.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# link-sanitizer — Purges surveillance tracking tokens & parasitic telemetry from URLs
|
|
# Part of the Techné zero-bloat toolkit for the small-web & dissident networks.
|
|
# CC0 1.0 Universal / Public Domain. Author: --kof
|
|
|
|
set -euo pipefail
|
|
|
|
clean_url() {
|
|
local raw_url="$1"
|
|
|
|
# Strip invisible zero-width spaces (ZWSP, ZWNJ, ZWJ, BOM) & non-breaking spaces
|
|
local url
|
|
url=$(printf "%s" "$raw_url" | sed 's/\xc2\xa0/ /g; s/\xe2\x80\x8b//g; s/\xe2\x80\x8c//g; s/\xe2\x80\x8d//g; s/\xef\xbb\xbf//g; s/^[[:space:]]*//; s/[[:space:]]*$//')
|
|
|
|
if [[ -z "$url" ]]; then
|
|
return
|
|
fi
|
|
|
|
# Python one-liner to parse query parameters and strip tracking tokens
|
|
python3 -c "
|
|
import sys
|
|
from urllib.parse import urlparse, parse_qsl, urlencode, urlunparse
|
|
|
|
TRACKING_KEYS = {
|
|
# UTM & Campaign parameters
|
|
'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'utm_id',
|
|
# Meta / Facebook / Instagram
|
|
'fbclid', 'fref', 'igshid',
|
|
# Google / Alphabet
|
|
'gclid', 'gclsrc', 'dclid', 'gbraid', 'wbraid',
|
|
# YouTube tracking
|
|
'si', 'feature', 'pp',
|
|
# Twitter / X
|
|
'ref_src', 'ref_url',
|
|
# Newsletters / CRM
|
|
'mc_cid', 'mc_eid', 'vero_id', 'vero_conv', 'wickedid',
|
|
# TikTok & Bytedance
|
|
'_r', '_t', 'share_app_name',
|
|
# Misc telemetry & affiliate bloat
|
|
'msclkid', 'yclid', 'rb_clickid', 's_kwcid', '_openstat', 'action_object_map',
|
|
'action_type_map', 'action_ref_map'
|
|
}
|
|
|
|
for line in sys.stdin:
|
|
raw = line.strip()
|
|
if not raw:
|
|
continue
|
|
# If raw doesn't have scheme but looks like url, handle gracefully
|
|
parsed = urlparse(raw)
|
|
if not parsed.netloc and not raw.startswith('http'):
|
|
print(raw)
|
|
continue
|
|
|
|
# Filter query parameters
|
|
query_pairs = parse_qsl(parsed.query, keep_blank_values=True)
|
|
filtered = [(k, v) for k, v in query_pairs if k.lower() not in TRACKING_KEYS]
|
|
|
|
# Clean query string
|
|
new_query = urlencode(filtered)
|
|
cleaned = urlunparse((
|
|
parsed.scheme,
|
|
parsed.netloc,
|
|
parsed.path,
|
|
parsed.params,
|
|
new_query,
|
|
parsed.fragment
|
|
))
|
|
# Strip trailing ? if query is now empty
|
|
if cleaned.endswith('?'):
|
|
cleaned = cleaned[:-1]
|
|
print(cleaned)
|
|
" <<< "$url"
|
|
}
|
|
|
|
if [[ "${1:-}" == "-c" || "${1:-}" == "--clipboard" ]]; then
|
|
if command -v xclip >/dev/null 2>&1; then
|
|
clip=$(xclip -selection clipboard -o 2>/dev/null || true)
|
|
if [[ -n "$clip" ]]; then
|
|
cleaned=$(clean_url "$clip")
|
|
printf "%s" "$cleaned" | xclip -selection clipboard -i
|
|
printf "Sanitized clipboard: %s\n" "$cleaned"
|
|
fi
|
|
else
|
|
echo "Error: xclip not found" >&2
|
|
exit 1
|
|
fi
|
|
elif [[ $# -gt 0 ]]; then
|
|
for arg in "$@"; do
|
|
clean_url "$arg"
|
|
done
|
|
else
|
|
# Process from stdin line by line
|
|
while IFS= read -r line || [[ -n "$line" ]]; do
|
|
clean_url "$line"
|
|
done
|
|
fi
|