initial commit

This commit is contained in:
Jason Swank
2026-01-14 13:17:43 -05:00
commit 8cf31d4c32
3 changed files with 679 additions and 0 deletions

54
Taskfile.yaml Normal file
View File

@@ -0,0 +1,54 @@
# This Taskfile helps generate binst installation scripts for different software
version: '3'
vars:
CONFIG_DIR: ./config
SCRIPT_DIR: ./scripts
tasks:
init:
desc: Initialize binst configuration for a GitHub project.
summary: |
Initialize a binst configuration for a GitHub project.
The config file will be saved to {{.CONFIG_DIR}}/{{.OUTPUT_FILE}}.
By default, the output file is named BINARY.binstaller.yml where BINARY is the
repository name (like trufflehog) extracted from the repo input.
Invoke this task like:
task init REPO=trufflesecurity/trufflehog
vars:
BINARY: "{{.REPO | base}}"
OUTPUT_FILE: "{{.BINARY}}.binstaller.yml"
cmd: |
binst init --source=github --repo {{.REPO}} -o {{.CONFIG_DIR}}/{{.OUTPUT_FILE}}
requires:
vars: ["REPO"]
embed-checksums:
desc: Embed checksums into a binst configuration file.
summary: |
Embed checksums into a binst configuration file for a specific version.
Invoke this task like:
task embed-checksums BINARY=trufflehog VERSION=v3.92.4
cmd: |
binst embed-checksums --config {{.CONFIG_DIR}}/{{.BINARY}}.binstaller.yml --version {{.VERSION}} --mode download
requires:
vars: ["BINARY", "VERSION"]
gen:
desc: Generate installation script from binst configuration.
summary: |
Generate an installation script from a binst configuration file.
The generated script will be saved to {{.SCRIPT_DIR}}/{{.BINARY}}-install.sh.
Invoke this task like:
task gen BINARY=trufflehog
cmd: |
binst gen --config={{.CONFIG_DIR}}/{{.BINARY}}.binstaller.yml -o {{.SCRIPT_DIR}}/{{.BINARY}}-install.sh
requires:
vars: ["BINARY"]

View File

@@ -0,0 +1,23 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/binary-install/binstaller/main/schema/InstallSpec.json
schema: v1
repo: trufflesecurity/trufflehog
asset:
template: trufflehog_${VERSION}_${OS}_${ARCH}${EXT}
default_extension: .tar.gz
checksums:
algorithm: sha256
template: trufflehog_${VERSION}_checksums.txt
embedded_checksums:
v3.92.4:
- filename: trufflehog_3.92.4_darwin_amd64.tar.gz
hash: 7599e25b2d9c8eea30aac873114bad54cb254a3dbc4b01cac52b28ef9f013f0e
- filename: trufflehog_3.92.4_darwin_arm64.tar.gz
hash: 37df43ad0cda48931c4586fe78064d1dd33016c8253ec27339c735fc956264f8
- filename: trufflehog_3.92.4_linux_amd64.tar.gz
hash: 98937dbfe048695bf1f34e8b76e6ef4caf75260760c0b5777dddf9bf34803052
- filename: trufflehog_3.92.4_linux_arm64.tar.gz
hash: 85868c235ed8871e87191c252c3b8cb6b7da6942aba210f048848a583b964fa4
- filename: trufflehog_3.92.4_windows_amd64.tar.gz
hash: 39357653008d4af2245e3e1c02e118a9dc80b203284f25f9a2b11a573d418938
- filename: trufflehog_3.92.4_windows_arm64.tar.gz
hash: 613d4d0fd180e9f5be21568150e7a71cad0cfe53b71ef1fac970a68a5a06e411

602
scripts/trufflehog-install.sh Executable file
View File

@@ -0,0 +1,602 @@
#!/bin/sh
# Code generated by binstaller. DO NOT EDIT.
#
set -e
usage() {
this=$1
cat <<EOF
$this: download ${NAME} from ${REPO}
Usage: $this [-b bindir] [-d] [-q] [-n] [tag]
-b sets bindir or installation directory, Defaults to ${BINSTALLER_BIN:-${HOME}/.local/bin}
-d turns on debug logging
-q turns on quiet mode (errors only)
-n turns on dry run mode
[tag] is a tag from
https://github.com/trufflesecurity/trufflehog/releases
If tag is missing, then latest will be used.
Environment variables:
BINSTALLER_NO_PROGRESS=1 Disable progress indicators
BINSTALLER_OS=... Override OS detection
BINSTALLER_ARCH=... Override architecture detection
Generated by binstaller
https://github.com/binary-install/binstaller
EOF
exit 2
}
cat /dev/null <<EOF
------------------------------------------------------------------------
https://github.com/client9/shlib - portable posix shell functions
Public domain - http://unlicense.org
https://github.com/client9/shlib/blob/master/LICENSE.md
but credit (and pull requests) appreciated.
------------------------------------------------------------------------
EOF
is_command() {
command -v "$1" >/dev/null
}
echoerr() {
echo "$@" 1>&2
}
_logp=6
log_set_priority() {
_logp="$1"
}
log_priority() {
if test -z "$1"; then
echo "$_logp"
return
fi
[ "$1" -le "$_logp" ]
}
log_tag() {
case $1 in
0) echo "emerg" ;;
1) echo "alert" ;;
2) echo "crit" ;;
3) echo "err" ;;
4) echo "warning" ;;
5) echo "notice" ;;
6) echo "info" ;;
7) echo "debug" ;;
*) echo "$1" ;;
esac
}
log_debug() {
log_priority 7 || return 0
echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
}
log_info() {
log_priority 6 || return 0
echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
}
log_err() {
log_priority 3 || return 0
echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
}
log_crit() {
log_priority 2 || return 0
echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
}
uname_os() {
os=$(uname -s | tr '[:upper:]' '[:lower:]')
case "$os" in
msys*) os="windows" ;;
mingw*) os="windows" ;;
cygwin*) os="windows" ;;
esac
if [ "$os" = "sunos" ]; then
if [ "$(uname -o)" = "illumos" ]; then
os="illumos"
else
os="solaris"
fi
fi
echo "$os"
}
uname_arch() {
arch=$(uname -m)
case $arch in
x86_64) arch="amd64" ;;
i86pc) arch="amd64" ;;
x86) arch="386" ;;
i686) arch="386" ;;
i386) arch="386" ;;
aarch64) arch="arm64" ;;
armv5*) arch="armv5" ;;
armv6*) arch="armv6" ;;
armv7*) arch="armv7" ;;
esac
echo "${arch}"
}
uname_os_check() {
os=$(uname_os)
case "$os" in
darwin) return 0 ;;
dragonfly) return 0 ;;
freebsd) return 0 ;;
linux) return 0 ;;
android) return 0 ;;
midnightbsd) return 0 ;;
nacl) return 0 ;;
netbsd) return 0 ;;
openbsd) return 0 ;;
plan9) return 0 ;;
solaris) return 0 ;;
illumos) return 0 ;;
windows) return 0 ;;
esac
log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
return 1
}
uname_arch_check() {
arch=$(uname_arch)
case "$arch" in
386) return 0 ;;
amd64) return 0 ;;
arm64) return 0 ;;
armv5) return 0 ;;
armv6) return 0 ;;
armv7) return 0 ;;
ppc64) return 0 ;;
ppc64le) return 0 ;;
mips) return 0 ;;
mipsle) return 0 ;;
mips64) return 0 ;;
mips64le) return 0 ;;
s390x) return 0 ;;
amd64p32) return 0 ;;
esac
log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value. Please file bug report at https://github.com/client9/shlib"
return 1
}
cat /dev/null <<EOF
------------------------------------------------------------------------
End of functions from https://github.com/client9/shlib
------------------------------------------------------------------------
EOF
hash_sha256() {
TARGET=${1:-/dev/stdin}
if is_command gsha256sum; then
hash=$(gsha256sum "$TARGET") || return 1
echo "$hash" | cut -d ' ' -f 1
elif is_command sha256sum; then
hash=$(sha256sum "$TARGET") || return 1
echo "$hash" | cut -d ' ' -f 1
elif is_command shasum; then
hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
echo "$hash" | cut -d ' ' -f 1
elif is_command openssl; then
hash=$(openssl dgst -sha256 "$TARGET") || return 1
echo "$hash" | cut -d ' ' -f 2
else
log_crit "hash_sha256 unable to find command to compute sha-256 hash"
return 1
fi
}
hash_compute() {
hash_sha256 "$1"
}
# shellcheck shell=sh
# Terminal progress reporting functions
progress_init() {
# Only show progress on interactive terminals and when not disabled
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
return 0
fi
# OSC 9;4 sequences are safely ignored by unsupporting terminals
# Only need special handling for tmux passthrough
if [ -n "$TMUX" ]; then
# Tmux passthrough: DCS tmux; <doubled ESC sequence> ST
# ESC characters in the wrapped sequence must be doubled
# Format: ESC P tmux; ESC ESC ] 9;4; ... ESC ESC \ ESC \
PROGRESS_START=$(printf '\033Ptmux;\033\033]9;4;')
# shellcheck disable=SC1003
PROGRESS_END=$(printf '\033\033\\\033\\')
else
# Direct OSC 9;4 - terminals that don't support it will safely ignore
PROGRESS_START=$(printf '\033]9;4;')
PROGRESS_END=$(printf '\007')
fi
}
# Start pulsing progress animation
progress_pulse_start() {
# Only show progress on interactive terminals and when not disabled
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
return 0
fi
# Send OSC 9;4 with state 3 (indeterminate/pulsing) once
# The terminal will handle the continuous animation
printf "%s3%s" "$PROGRESS_START" "$PROGRESS_END" >&2
}
# Clear progress indicator
progress_clear() {
# Only show progress on interactive terminals and when not disabled
if [ ! -t 2 ] || [ "${BINSTALLER_NO_PROGRESS}" = "1" ]; then
return 0
fi
printf "%s0;%s" "$PROGRESS_START" "$PROGRESS_END" >&2
}
untar() {
tarball=$1
strip_components=${2:-0} # default 0
case "${tarball}" in
*.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" --strip-components "${strip_components}" ;;
*.tar.xz) tar --no-same-owner -xJf "${tarball}" --strip-components "${strip_components}" ;;
*.tar.bz2) tar --no-same-owner -xjf "${tarball}" --strip-components "${strip_components}" ;;
*.tar) tar --no-same-owner -xf "${tarball}" --strip-components "${strip_components}" ;;
*.gz) gunzip "${tarball}" ;;
*.zip)
# unzip doesn't have a standard --strip-components
# Workaround: extract to a subdir and move contents up if stripping
if [ "$strip_components" -gt 0 ]; then
extract_dir=$(basename "${tarball%.zip}")_extracted
unzip -q "${tarball}" -d "${extract_dir}"
# Move contents of the *first* directory found inside extract_dir up
# This assumes wrap_in_directory=true convention
first_subdir=$(find "${extract_dir}" -mindepth 1 -maxdepth 1 -type d -print -quit)
if [ -n "$first_subdir" ]; then
# Move all contents (* includes hidden files)
mv "${first_subdir}"/* .
# Optionally remove the now-empty subdir and the extract_dir
rmdir "${first_subdir}"
rmdir "${extract_dir}"
else
log_warn "Could not find subdirectory in zip to strip components from ${extract_dir}"
# Files are extracted in current dir anyway, proceed
fi
else
unzip -q "${tarball}"
fi
;;
*)
log_err "untar unknown archive format for ${tarball}"
return 1
;;
esac
}
hash_verify() {
TARGET_PATH=$1
SUMFILE=$2
if [ -z "${SUMFILE}" ]; then
log_err "hash_verify checksum file not specified in arg2"
return 1
fi
got=$(hash_compute "$TARGET_PATH")
if [ -z "${got}" ]; then
log_err "failed to calculate hash: ${TARGET_PATH}"
return 1
fi
BASENAME=${TARGET_PATH##*/}
# Check for line matches in checksum file
# Format: "<hash> <filename>" or "<hash> *<filename>"
# Filename may include path prefix (e.g., "deployment/m2/file.tar.gz")
while IFS= read -r line || [ -n "$line" ]; do
# Normalize tabs to spaces
line=$(echo "$line" | tr '\t' ' ')
# Remove trailing spaces for hash-only line check
line_trimmed=$(echo "$line" | sed 's/[[:space:]]*$//')
# Check for hash-only line (no filename) - early return
if [ "$line_trimmed" = "$got" ]; then
return 0
fi
# Extract hash and filename parts
# First field is the hash, rest is filename (which may contain spaces)
line_hash=$(echo "$line" | cut -d' ' -f1)
# Skip if hash doesn't match
if [ "$line_hash" != "$got" ]; then
continue
fi
# Hash matches, now check filename
# Remove the hash part from the beginning of the line
line_rest="${line#"$got"}"
# Remove leading spaces
while [ "${line_rest#[ ]}" != "$line_rest" ]; do
line_rest="${line_rest#[ ]}"
done
# Remove leading asterisk if present (binary mode indicator)
if [ "${line_rest#\*}" != "$line_rest" ]; then
line_rest="${line_rest#\*}"
fi
# Extract just the filename without any path
line_filename="${line_rest##*/}"
# Check if the filename matches
if [ "$line_filename" = "$BASENAME" ]; then
return 0
fi
done < "$SUMFILE"
log_err "hash_verify checksum for '$TARGET_PATH' did not verify"
log_err " Expected hash: ${got}"
log_err " Checksum file content:"
cat "$SUMFILE" >&2
return 1
}
# GitHub HTTP download functions with GITHUB_TOKEN support
github_http_download_curl() {
local_file=$1
source_url=$2
header=$3
if [ -n "$GITHUB_TOKEN" ]; then
log_debug "Using GITHUB_TOKEN for authentication"
if [ -z "$header" ]; then
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -o "$local_file" "$source_url"
else
curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -H "$header" -o "$local_file" "$source_url"
fi
else
if [ -z "$header" ]; then
curl -fsSL -o "$local_file" "$source_url"
else
curl -fsSL -H "$header" -o "$local_file" "$source_url"
fi
fi
}
github_http_download_wget() {
local_file=$1
source_url=$2
header=$3
if [ -n "$GITHUB_TOKEN" ]; then
log_debug "Using GITHUB_TOKEN for authentication"
if [ -z "$header" ]; then
wget -q --header "Authorization: Bearer $GITHUB_TOKEN" -O "$local_file" "$source_url"
else
wget -q --header "Authorization: Bearer $GITHUB_TOKEN" --header "$header" -O "$local_file" "$source_url"
fi
else
if [ -z "$header" ]; then
wget -q -O "$local_file" "$source_url"
else
wget -q --header "$header" -O "$local_file" "$source_url"
fi
fi
}
github_http_download() {
log_debug "github_http_download $2"
if is_command curl; then
github_http_download_curl "$@"
return
elif is_command wget; then
github_http_download_wget "$@"
return
fi
log_crit "github_http_download unable to find wget or curl"
return 1
}
github_http_copy() {
tmp=$(mktemp)
github_http_download "${tmp}" "$@" || return 1
body=$(cat "$tmp")
rm -f "${tmp}"
echo "$body"
}
github_release() {
owner_repo=$1
version=$2
test -z "$version" && version="latest"
giturl="https://github.com/${owner_repo}/releases/${version}"
json=$(github_http_copy "$giturl" "Accept:application/json")
test -z "$json" && return 1
version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
test -z "$version" && return 1
echo "$version"
}
# --- Embedded Checksums (Format: VERSION:FILENAME:HASH) ---
EMBEDDED_CHECKSUMS="
3.92.4:trufflehog_3.92.4_darwin_amd64.tar.gz:7599e25b2d9c8eea30aac873114bad54cb254a3dbc4b01cac52b28ef9f013f0e
3.92.4:trufflehog_3.92.4_darwin_arm64.tar.gz:37df43ad0cda48931c4586fe78064d1dd33016c8253ec27339c735fc956264f8
3.92.4:trufflehog_3.92.4_linux_amd64.tar.gz:98937dbfe048695bf1f34e8b76e6ef4caf75260760c0b5777dddf9bf34803052
3.92.4:trufflehog_3.92.4_linux_arm64.tar.gz:85868c235ed8871e87191c252c3b8cb6b7da6942aba210f048848a583b964fa4
3.92.4:trufflehog_3.92.4_windows_amd64.tar.gz:39357653008d4af2245e3e1c02e118a9dc80b203284f25f9a2b11a573d418938
3.92.4:trufflehog_3.92.4_windows_arm64.tar.gz:613d4d0fd180e9f5be21568150e7a71cad0cfe53b71ef1fac970a68a5a06e411"
# Find embedded checksum for a given version and filename
find_embedded_checksum() {
version="$1"
filename="$2"
echo "$EMBEDDED_CHECKSUMS" | grep -E "^${version}:${filename}:" | cut -d':' -f3
}
parse_args() {
BINDIR="${BINSTALLER_BIN:-${HOME}/.local/bin}"
DRY_RUN=0
while getopts "b:dqh?xn" arg; do
case "$arg" in
b) BINDIR="$OPTARG" ;;
d) log_set_priority 10 ;;
q) log_set_priority 3 ;;
h | \?) usage "$0" ;;
x) set -x ;;
n) DRY_RUN=1 ;;
esac
done
shift $((OPTIND - 1))
TAG="${1:-latest}"
}
tag_to_version() {
if [ "$TAG" = "latest" ]; then
log_info "checking GitHub for latest tag"
REALTAG=$(github_release "${REPO}" "${TAG}") && true
test -n "$REALTAG" || {
log_crit "Could not determine latest tag for ${REPO}"
exit 1
}
else
# Assume TAG is a valid tag/version string
REALTAG="$TAG"
fi
if test -z "$REALTAG"; then
log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${REPO}/releases for details"
exit 1
fi
VERSION=${REALTAG#v} # Strip leading 'v'
TAG="$REALTAG" # Use the resolved tag
log_info "Resolved version: ${VERSION} (tag: ${TAG})"
}
resolve_asset_filename() {
# --- Apply Rules ---
ASSET_FILENAME=""
if [ -z "${ASSET_FILENAME}" ]; then
ASSET_FILENAME="trufflehog_${VERSION}_${OS}_${ARCH}${EXT}"
fi
}
# Cleanup function to remove temporary files and stop progress
cleanup() {
# Stop progress animation
progress_clear
if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
log_debug "Cleaning up temporary directory: $TMPDIR"
rm -rf -- "$TMPDIR"
fi
}
execute() {
STRIP_COMPONENTS=0
CHECKSUM_FILENAME="trufflehog_${VERSION}_checksums.txt"
# --- Construct URLs ---
GITHUB_DOWNLOAD="https://github.com/${REPO}/releases/download"
ASSET_URL="${GITHUB_DOWNLOAD}/${TAG}/${ASSET_FILENAME}"
CHECKSUM_URL=""
if [ -n "$CHECKSUM_FILENAME" ]; then
CHECKSUM_URL="${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM_FILENAME}"
fi
# --- Download and Verify ---
TMPDIR=$(mktemp -d)
# Set up cleanup trap that includes progress clearing
trap cleanup EXIT HUP INT TERM
log_debug "Downloading files into ${TMPDIR}"
log_info "Downloading ${ASSET_URL}"
github_http_download "${TMPDIR}/${ASSET_FILENAME}" "${ASSET_URL}"
# Try to find embedded checksum first
EMBEDDED_HASH=$(find_embedded_checksum "$VERSION" "$ASSET_FILENAME")
if [ -n "$EMBEDDED_HASH" ]; then
log_info "Using embedded checksum for verification"
# Verify using embedded hash
got=$(hash_compute "${TMPDIR}/${ASSET_FILENAME}")
if [ "$got" != "$EMBEDDED_HASH" ]; then
log_crit "Checksum verification failed for ${ASSET_FILENAME}"
log_crit "Expected: ${EMBEDDED_HASH}"
log_crit "Got: ${got}"
return 1
fi
log_info "Checksum verification successful"
elif [ -n "$CHECKSUM_URL" ]; then
# Fall back to downloading checksum file
log_info "Downloading checksums from ${CHECKSUM_URL}"
github_http_download "${TMPDIR}/${CHECKSUM_FILENAME}" "${CHECKSUM_URL}"
log_info "Verifying checksum ..."
hash_verify "${TMPDIR}/${ASSET_FILENAME}" "${TMPDIR}/${CHECKSUM_FILENAME}"
else
log_info "No checksum found, skipping verification."
fi
if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
log_debug "Target is raw binary"
else
log_info "Extracting ${ASSET_FILENAME}..."
(cd "${TMPDIR}" && untar "${ASSET_FILENAME}" "${STRIP_COMPONENTS}")
fi
BINARY_NAME='trufflehog'
if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
BINARY_PATH="${TMPDIR}/${ASSET_FILENAME}"
else
BINARY_PATH="${TMPDIR}/trufflehog"
fi
if [ "${UNAME_OS}" = "windows" ]; then
case "${BINARY_NAME}" in *.exe) ;; *) BINARY_NAME="${BINARY_NAME}.exe" ;; esac
case "${BINARY_PATH}" in *.exe) ;; *) BINARY_PATH="${BINARY_PATH}.exe" ;; esac
fi
if [ ! -f "${BINARY_PATH}" ]; then
log_crit "Binary not found: ${BINARY_PATH}"
log_crit "Listing contents of ${TMPDIR} ..."
if command -v find >/dev/null 2>&1; then
cd "${TMPDIR}" && find .
else
cd "${TMPDIR}" && ls -R .
fi
return 1
fi
progress_clear
# Install the binary
INSTALL_PATH="${BINDIR}/${BINARY_NAME}"
if [ "$DRY_RUN" = "1" ]; then
log_info "[DRY RUN] ${BINARY_NAME} dry-run installation succeeded! (Would install to: ${INSTALL_PATH})"
else
log_info "Installing binary to ${INSTALL_PATH}"
test ! -d "${BINDIR}" && install -d "${BINDIR}"
install "${BINARY_PATH}" "${INSTALL_PATH}"
log_info "${BINARY_NAME} installation complete!"
fi
}
# --- Configuration ---
NAME='trufflehog'
REPO='trufflesecurity/trufflehog'
EXT='.tar.gz'
# use in logging routines
log_prefix() {
echo "${REPO}"
}
parse_args "$@"
progress_init
progress_pulse_start
# --- Determine target platform ---
OS="${BINSTALLER_OS:-$(uname_os)}"
UNAME_OS="${OS}"
ARCH="${BINSTALLER_ARCH:-$(uname_arch)}"
log_info "Detected Platform: ${OS}/${ARCH}"
# --- Validate platform ---
uname_os_check "$OS"
uname_arch_check "$ARCH"
tag_to_version
resolve_asset_filename
execute