Skip to content

Instantly share code, notes, and snippets.

@cristobal
Last active March 20, 2026 12:38
Show Gist options
  • Select an option

  • Save cristobal/5363919f877b02addee8d99fa564b135 to your computer and use it in GitHub Desktop.

Select an option

Save cristobal/5363919f877b02addee8d99fa564b135 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bash
#
# pnpm-audit-table - Display pnpm audit vulnerabilities as a formatted table
#
# Usage: pnpm-audit-table [options]
# pnpm-audit-table -h|--help
#
# Options:
# -D, --dev Only audit devDependencies
# -P, --prod Only audit dependencies
# -t, --title Show vulnerability title inline after CVE
# -l, --level, --audit-level <level> Only show advisories at or above: low, moderate, high, critical
#
# Examples:
# pnpm-audit-table
# pnpm-audit-table -P
# pnpm-audit-table -D -l high
# pnpm-audit-table -P -t
#
# Requires: pnpm, jq
set -euo pipefail
NC=$'\033[0m'
FG_RESET=$'\033[39m'
DIM=$'\033[2m'
get_severity_color() {
case "$1" in
critical) printf '\033[1;31m' ;;
high) printf '\033[31m' ;;
moderate) printf '\033[33m' ;;
low) printf '\033[32m' ;;
*) printf '' ;;
esac
}
AUDIT_LEVEL=""
DEV_ONLY=""
PROD_ONLY=""
SHOW_TITLE=""
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
grep '^#' "$0" | grep -v '^#!/' | sed 's/^# \{0,1\}//'
exit 0
;;
--audit-level|--level|-l)
AUDIT_LEVEL="$2"
shift 2
;;
-D|--dev)
DEV_ONLY="1"
shift
;;
-P|--prod)
PROD_ONLY="1"
shift
;;
-t|--title)
SHOW_TITLE="1"
shift
;;
*)
echo "Unknown flag: $1" >&2
exit 1
;;
esac
done
PNPM_ARGS=(--json)
if [[ -n "$AUDIT_LEVEL" ]]; then
PNPM_ARGS+=(--audit-level "$AUDIT_LEVEL")
fi
if [[ -n "$DEV_ONLY" ]]; then
PNPM_ARGS+=(--dev)
fi
if [[ -n "$PROD_ONLY" ]]; then
PNPM_ARGS+=(--prod)
fi
JSON_OUTPUT=$(pnpm audit "${PNPM_ARGS[@]}" 2>/dev/null) || true
if [[ -z "$JSON_OUTPUT" ]] || [[ "$JSON_OUTPUT" == "null" ]]; then
echo "No vulnerabilities found."
exit 0
fi
SEVERITY_ORDER='{"critical":0,"high":1,"moderate":2,"low":3}'
keys=$(printf '%s' "$JSON_OUTPUT" | jq -r '.advisories | keys[]' 2>/dev/null || echo "")
if [[ -z "$keys" ]]; then
echo "No vulnerabilities found."
exit 0
fi
declare -a rows
while IFS= read -r adv_id; do
adv=$(printf '%s' "$JSON_OUTPUT" | jq -r ".advisories[\"$adv_id\"]")
name=$(printf '%s' "$adv" | jq -r '.module_name // "unknown"')
criticality=$(printf '%s' "$adv" | jq -r '.severity // "unknown"')
vuln_version=$(printf '%s' "$adv" | jq -r '.vulnerable_versions // ""')
patched=$(printf '%s' "$adv" | jq -r '.patched_versions // ""')
cvss=$(printf '%s' "$adv" | jq -r '.cvss.score // ""')
cve=$(printf '%s' "$adv" | jq -r '.cves[0] // ""')
path=$(printf '%s' "$adv" | jq -r '.findings[0].paths[0] // ""')
title=$(printf '%s' "$adv" | jq -r '.title // "" | gsub("\\|"; " ")')
severity_rank=$(printf '%s' "$SEVERITY_ORDER" | jq -r ".$criticality // 99")
rows+=("$severity_rank|$criticality|$name|$vuln_version|$patched|$cvss|$cve|$path|$title")
done <<< "$keys"
sorted=$(printf '%s\n' "${rows[@]}" | sort -t'|' -k1,1n -k3,3 -k8,8)
col1=${#name}
col2=12
col3=15
col4=15
col5=14 # CVE
col_cvss=4
while IFS='|' read -r rank severity name vuln patched cvss cve path title; do
[[ ${#name} -gt $col1 ]] && col1=${#name}
[[ ${#vuln} -gt $col3 ]] && col3=${#vuln}
[[ ${#patched} -gt $col4 ]] && col4=${#patched}
[[ ${#cve} -gt $col5 ]] && col5=${#cve}
done <<< "$sorted"
[[ $col1 -lt 6 ]] && col1=6
[[ $col3 -lt 15 ]] && col3=15
[[ $col4 -lt 15 ]] && col4=15
[[ $col5 -lt 3 ]] && col5=3
row_width=$((col1 + 2 + col2 + 2 + col_cvss + 2 + col3 + 2 + col4 + 2 + col5))
separator=$(printf '%*s' "$row_width" | tr ' ' '')
printf "%-${col1}s %-${col2}s %-${col_cvss}s %-${col3}s %-${col4}s %-${col5}s\n" "Package" "Criticality" "CVSS" "Version" "Patched" "CVE"
printf "%s\n" "$separator"
prev_name=""
prev_path=""
while IFS='|' read -r rank severity name vuln patched cvss cve path title; do
color=$(get_severity_color "$severity")
if [[ "$name" != "$prev_name" ]]; then
[[ -n "$prev_name" ]] && printf "${DIM}%s${NC}\n" "$separator"
printf "%s\n" "$name"
[[ -n "$path" ]] && printf " ${DIM}└── %s${NC}\n" "$path"
prev_path="$path"
prev_name="$name"
elif [[ "$path" != "$prev_path" && -n "$path" ]]; then
printf " ${DIM}└── %s${NC}\n" "$path"
prev_path="$path"
fi
if [[ -n "$SHOW_TITLE" ]]; then
printf "%*s ${color}%-${col2}s${FG_RESET} %-${col_cvss}s %-${col3}s %-${col4}s %-${col5}s${NC} ${DIM}%s${NC}\n" "$col1" '' "$severity" "$cvss" "$vuln" "$patched" "$cve" "$title"
else
printf "%*s ${color}%-${col2}s${FG_RESET} %-${col_cvss}s %-${col3}s %-${col4}s %-${col5}s${NC}\n" "$col1" '' "$severity" "$cvss" "$vuln" "$patched" "$cve"
fi
done <<< "$sorted"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment