~kris/dots

srice

ref: e9b48d06a8541f3eda5c4db90382ab3c77183afb srice/.local/bin/security/shred -rwxr-xr-x 2.4 KiB
e9b48d06 — Kris Yotam xprofile: systemd-aware pipewire start + blueman-applet; sb-internet: tolerate missing /proc/net/wireless 2 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#!/bin/sh

# Secure file destruction: strip metadata, overwrite, shred, remove
# Usage: nuke <file1> [file2] ...
# nuke -d <dir>       nuke an entire directory

set -e

die() { echo "nuke: $*" >&2; exit 1; }

strip_metadata() {
    file="$1"
    mime=$(file -b --mime-type "$file" 2>/dev/null || echo "unknown")
    case "$mime" in
        image/*|video/*|audio/*|application/pdf)
            if command -v exiftool >/dev/null 2>&1; then
                exiftool -all= -overwrite_original "$file" 2>/dev/null || true
            fi
            if command -v mat2 >/dev/null 2>&1; then
                mat2 --inplace "$file" 2>/dev/null || true
            fi
            ;;
    esac
}

nuke_file() {
    file="$1"
    [ -f "$file" ] || return

    # 1. strip all metadata
    strip_metadata "$file"

    # 2. get file size, overwrite with /dev/urandom
    size=$(stat -c%s "$file" 2>/dev/null || echo 0)
    if [ "$size" -gt 0 ]; then
        dd if=/dev/urandom of="$file" bs=1 count="$size" conv=notrunc 2>/dev/null || true
    fi

    # 3. shred: 7 passes random + 1 pass zeros to hide shredding
    /usr/bin/shred -vzn 7 "$file" 2>/dev/null

    # 4. truncate to zero
    truncate -s 0 "$file" 2>/dev/null || true

    # 5. rename to random name before unlinking (obscure filename from journal)
    dir=$(dirname "$file")
    rand=$(head -c 16 /dev/urandom | xxd -p)
    mv "$file" "$dir/$rand" 2>/dev/null && sync && rm -f "$dir/$rand"
    if [ -f "$file" ]; then
        sync
        rm -f "$file"
    fi

    echo "nuked: $file"
}

nuke_dir() {
    dir="$1"
    [ -d "$dir" ] || die "not a directory: $dir"

    # nuke all files recursively
    find "$dir" -type f | while read -r f; do
        nuke_file "$f"
    done

    # remove empty directory tree
    rm -rf "$dir"
    echo "nuked directory: $dir"
}

[ -z "$1" ] && die "usage: nuke [-d] <files/dirs>"

if [ "$1" = "-d" ]; then
    shift
    [ -z "$1" ] && die "usage: nuke -d <directory>"
    for d in "$@"; do
        nuke_dir "$d"
    done
else
    for f in "$@"; do
        if [ -d "$f" ]; then
            printf "'%s' is a directory. use nuke -d to nuke directories. skip? [Y/n] " "$f"
            read -r ans
            case "$ans" in
                n|N) nuke_dir "$f" ;;
                *) echo "skipped: $f" ;;
            esac
        elif [ -f "$f" ]; then
            nuke_file "$f"
        else
            echo "not found: $f"
        fi
    done
fi