Last active
March 20, 2026 12:38
-
-
Save cristobal/5363919f877b02addee8d99fa564b135 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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