@testzugang/pi-plugin-dependency-audit 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -0
- package/package.json +23 -0
- package/skills/dependency-audit/README.md +105 -0
- package/skills/dependency-audit/SKILL.md +353 -0
- package/skills/dependency-audit/config.json +3 -0
- package/skills/dependency-audit/examples/github-actions-static-audit.yml +46 -0
- package/skills/dependency-audit/examples/sample-commands.md +110 -0
- package/skills/dependency-audit/rules/iocs.txt +23 -0
- package/skills/dependency-audit/rules/review-policy.md +38 -0
- package/skills/dependency-audit/scripts/npm_ts_static_triage.py +1345 -0
- package/skills/dependency-audit/scripts/pi-check-all-updates.sh +15 -0
- package/skills/dependency-audit/scripts/pi-check-current-global-versions.sh +37 -0
- package/skills/dependency-audit/scripts/pi-check-git-source-updates.sh +57 -0
- package/skills/dependency-audit/scripts/pi-check-latest-npm-versions.sh +25 -0
- package/skills/dependency-audit/scripts/pi-default-git-repos.txt +4 -0
- package/skills/dependency-audit/scripts/pi-default-packages.txt +16 -0
- package/skills/dependency-audit/scripts/pi-interactive-update.py +151 -0
- package/skills/dependency-audit/scripts/run_pi_dependency_audit.py +528 -0
- package/skills/dependency-audit/scripts/summarize_pi_dependency_audit.py +242 -0
- package/skills/dependency-audit/templates/report.md +102 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
|
|
6
|
+
echo "== Current global versions =="
|
|
7
|
+
bash "$SCRIPT_DIR/pi-check-current-global-versions.sh" "$@"
|
|
8
|
+
|
|
9
|
+
echo
|
|
10
|
+
echo "== Latest npm versions =="
|
|
11
|
+
bash "$SCRIPT_DIR/pi-check-latest-npm-versions.sh" "$@"
|
|
12
|
+
|
|
13
|
+
echo
|
|
14
|
+
echo "== Git source update status =="
|
|
15
|
+
bash "$SCRIPT_DIR/pi-check-git-source-updates.sh"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
DEFAULT_PACKAGES_FILE="$SCRIPT_DIR/pi-default-packages.txt"
|
|
6
|
+
GLOBAL_ROOT="${NPM_GLOBAL_ROOT:-$(npm root -g 2>/dev/null || true)}"
|
|
7
|
+
|
|
8
|
+
if [[ -z "$GLOBAL_ROOT" ]]; then
|
|
9
|
+
echo "Could not determine npm global root. Set NPM_GLOBAL_ROOT or ensure npm is available." >&2
|
|
10
|
+
exit 2
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
read_packages() {
|
|
14
|
+
if (( $# > 0 )); then
|
|
15
|
+
printf '%s\n' "$@"
|
|
16
|
+
return
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
if [[ ! -f "$DEFAULT_PACKAGES_FILE" ]]; then
|
|
20
|
+
echo "Missing package list: $DEFAULT_PACKAGES_FILE" >&2
|
|
21
|
+
exit 2
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
grep -vE '^\s*(#|$)' "$DEFAULT_PACKAGES_FILE"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
while IFS= read -r pkg; do
|
|
28
|
+
[[ -z "$pkg" ]] && continue
|
|
29
|
+
pkg_json="$GLOBAL_ROOT/$pkg/package.json"
|
|
30
|
+
|
|
31
|
+
if [[ -f "$pkg_json" ]]; then
|
|
32
|
+
version="$(node -p "require('$pkg_json').version" 2>/dev/null || echo UNKNOWN)"
|
|
33
|
+
echo "$pkg: current=$version"
|
|
34
|
+
else
|
|
35
|
+
echo "$pkg: NOT FOUND at $pkg_json"
|
|
36
|
+
fi
|
|
37
|
+
done < <(read_packages "$@")
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
DEFAULT_REPOS_FILE="$SCRIPT_DIR/pi-default-git-repos.txt"
|
|
6
|
+
|
|
7
|
+
read_repos() {
|
|
8
|
+
if (( $# > 0 )); then
|
|
9
|
+
printf '%s\n' "$@"
|
|
10
|
+
return
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
if [[ ! -f "$DEFAULT_REPOS_FILE" ]]; then
|
|
14
|
+
echo "Missing repo list: $DEFAULT_REPOS_FILE" >&2
|
|
15
|
+
exit 2
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
grep -vE '^\s*(#|$)' "$DEFAULT_REPOS_FILE"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
while IFS= read -r repo; do
|
|
22
|
+
[[ -z "$repo" ]] && continue
|
|
23
|
+
|
|
24
|
+
if [[ ! -d "$repo" ]]; then
|
|
25
|
+
echo "Directory $repo NOT FOUND"
|
|
26
|
+
continue
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
if ! git -C "$repo" rev-parse --git-dir >/dev/null 2>&1; then
|
|
30
|
+
echo "Directory $repo is not a git repository"
|
|
31
|
+
continue
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
echo "--- $repo ---"
|
|
35
|
+
|
|
36
|
+
branch="$(git -C "$repo" rev-parse --abbrev-ref HEAD 2>/dev/null || echo DETACHED)"
|
|
37
|
+
current_commit="$(git -C "$repo" rev-parse HEAD 2>/dev/null || echo UNKNOWN)"
|
|
38
|
+
|
|
39
|
+
git -C "$repo" fetch --quiet origin 2>/dev/null || true
|
|
40
|
+
|
|
41
|
+
remote_commit="NO_REMOTE_BRANCH"
|
|
42
|
+
if [[ "$branch" != "HEAD" && "$branch" != "DETACHED" ]]; then
|
|
43
|
+
remote_commit="$(git -C "$repo" rev-parse "origin/$branch" 2>/dev/null || echo NO_REMOTE_BRANCH)"
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
echo "Branch: $branch"
|
|
47
|
+
echo "Current Commit: $current_commit"
|
|
48
|
+
echo "Remote Commit: $remote_commit"
|
|
49
|
+
|
|
50
|
+
if [[ "$remote_commit" == "NO_REMOTE_BRANCH" ]]; then
|
|
51
|
+
echo "Status: UNKNOWN"
|
|
52
|
+
elif [[ "$current_commit" != "$remote_commit" ]]; then
|
|
53
|
+
echo "Status: UPDATE_AVAILABLE"
|
|
54
|
+
else
|
|
55
|
+
echo "Status: UP_TO_DATE"
|
|
56
|
+
fi
|
|
57
|
+
done < <(read_repos "$@")
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
DEFAULT_PACKAGES_FILE="$SCRIPT_DIR/pi-default-packages.txt"
|
|
6
|
+
|
|
7
|
+
read_packages() {
|
|
8
|
+
if (( $# > 0 )); then
|
|
9
|
+
printf '%s\n' "$@"
|
|
10
|
+
return
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
if [[ ! -f "$DEFAULT_PACKAGES_FILE" ]]; then
|
|
14
|
+
echo "Missing package list: $DEFAULT_PACKAGES_FILE" >&2
|
|
15
|
+
exit 2
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
grep -vE '^\s*(#|$)' "$DEFAULT_PACKAGES_FILE"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
while IFS= read -r pkg; do
|
|
22
|
+
[[ -z "$pkg" ]] && continue
|
|
23
|
+
latest="$(npm view "$pkg" version 2>/dev/null || echo NOT_IN_REGISTRY)"
|
|
24
|
+
echo "$pkg: latest=$latest"
|
|
25
|
+
done < <(read_packages "$@")
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Default global pi-related npm packages to audit.
|
|
2
|
+
pi-mcp-adapter
|
|
3
|
+
pi-subagents
|
|
4
|
+
pi-web-access
|
|
5
|
+
pi-claude-bridge
|
|
6
|
+
pi-mermaid
|
|
7
|
+
pi-tool-display
|
|
8
|
+
pi-total-recall
|
|
9
|
+
pi-terminal-signals
|
|
10
|
+
@aliou/pi-guardrails
|
|
11
|
+
@fgladisch/pi-bash-approval
|
|
12
|
+
@fgladisch/pi-persistent-history
|
|
13
|
+
@fgladisch/pi-user-select
|
|
14
|
+
@fgladisch/pi-welcome-message
|
|
15
|
+
pi-powerline
|
|
16
|
+
pi-suggest
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
SCRIPT_DIR = Path(__file__).resolve().parent
|
|
9
|
+
RUN_AUDIT_SCRIPT = SCRIPT_DIR / "run_pi_dependency_audit.py"
|
|
10
|
+
AGGREGATED_JSON = Path("/tmp/pi_audit_aggregated.json")
|
|
11
|
+
|
|
12
|
+
def main():
|
|
13
|
+
print("\n🛡️ Starte Sicherheits-Audit der Pi-Abhängigkeiten...")
|
|
14
|
+
|
|
15
|
+
# 1. Run the audit script
|
|
16
|
+
audit_res = subprocess.run(["python3", str(RUN_AUDIT_SCRIPT)], text=True)
|
|
17
|
+
if audit_res.returncode != 0:
|
|
18
|
+
print("❌ Audit fehlgeschlagen oder abgebrochen.")
|
|
19
|
+
return 1
|
|
20
|
+
|
|
21
|
+
if not AGGREGATED_JSON.exists():
|
|
22
|
+
print("❌ Audit-Ergebnisdatei nicht gefunden.")
|
|
23
|
+
return 1
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
results = json.loads(AGGREGATED_JSON.read_text(encoding="utf-8"))
|
|
27
|
+
except Exception as e:
|
|
28
|
+
print(f"❌ Fehler beim Lesen der Audit-Ergebnisse: {e}")
|
|
29
|
+
return 1
|
|
30
|
+
|
|
31
|
+
# 2. Filter updates
|
|
32
|
+
updates = [i for i in results if i.get("status") in {"update_available", "too_fresh"}]
|
|
33
|
+
if not updates:
|
|
34
|
+
print("\n✅ Alle Pi-Erweiterungen sind auf dem neuesten Stand!")
|
|
35
|
+
return 0
|
|
36
|
+
|
|
37
|
+
print("\n🔄 Ausstehende Updates und Audit-Ergebnisse:")
|
|
38
|
+
print("=" * 80)
|
|
39
|
+
|
|
40
|
+
# Helper to get source name
|
|
41
|
+
def get_pi_source(item):
|
|
42
|
+
if item.get("type") == "npm":
|
|
43
|
+
return f"npm:{item['name']}"
|
|
44
|
+
source = item.get("source")
|
|
45
|
+
if source:
|
|
46
|
+
return str(source)
|
|
47
|
+
name = item.get("name", "")
|
|
48
|
+
if name == "pi-skills":
|
|
49
|
+
return "git:github.com/fgladisch/pi-skills"
|
|
50
|
+
if name == "pi-plugins":
|
|
51
|
+
return "git:github.com/testzugang/pi-plugins"
|
|
52
|
+
if name == "pi-community-themes":
|
|
53
|
+
return "git:https://github.com/hasit/pi-community-themes"
|
|
54
|
+
return f"git:github.com/testzugang/{name}"
|
|
55
|
+
|
|
56
|
+
# Print menu
|
|
57
|
+
for idx, item in enumerate(updates, start=1):
|
|
58
|
+
name = item.get("name")
|
|
59
|
+
current = item.get("current")[:8] if item.get("type") == "git" and item.get("current") else item.get("current")
|
|
60
|
+
latest = item.get("latest")[:8] if item.get("type") == "git" and item.get("latest") else item.get("latest")
|
|
61
|
+
decision = item.get("decision", "UNKNOWN")
|
|
62
|
+
status = item.get("status")
|
|
63
|
+
|
|
64
|
+
# Format decision string
|
|
65
|
+
if decision in {"QUARANTINE", "BLOCK_UNTIL_REVIEW"}:
|
|
66
|
+
dec_str = f"❌ {decision}"
|
|
67
|
+
elif decision == "REVIEW_BEFORE_USE":
|
|
68
|
+
dec_str = f"🟡 {decision}"
|
|
69
|
+
elif status == "too_fresh":
|
|
70
|
+
dec_str = f"⏱️ TOO FRESH (Alterssperre)"
|
|
71
|
+
else:
|
|
72
|
+
dec_str = f"✅ SAFE"
|
|
73
|
+
|
|
74
|
+
print(f"[{idx}] {name} ({current} -> {latest})")
|
|
75
|
+
print(f" Urteil: {dec_str} | Quelle: {get_pi_source(item)}")
|
|
76
|
+
print("-" * 80)
|
|
77
|
+
|
|
78
|
+
print("\nOptionen:")
|
|
79
|
+
print(" - Gib die Nummern ein, die du aktualisieren willst (z.B. 1, 3, 5)")
|
|
80
|
+
print(" - 'safe' für alle als sicher eingestuften Updates")
|
|
81
|
+
print(" - 'all' für alle Updates (inkl. Warnungen/Quarantäne, auf eigene Gefahr!)")
|
|
82
|
+
print(" - 'q' zum Abbrechen")
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
user_input = input("\nDeine Auswahl: ").strip().lower()
|
|
86
|
+
except KeyboardInterrupt:
|
|
87
|
+
print("\nAbgebrochen.")
|
|
88
|
+
return 0
|
|
89
|
+
|
|
90
|
+
if user_input in {"q", "quit", "exit", ""}:
|
|
91
|
+
print("Abgebrochen.")
|
|
92
|
+
return 0
|
|
93
|
+
|
|
94
|
+
selected_items = []
|
|
95
|
+
|
|
96
|
+
if user_input == "safe":
|
|
97
|
+
selected_items = [i for i in updates if i.get("decision") not in {"BLOCK_UNTIL_REVIEW", "QUARANTINE"} and i.get("status") != "too_fresh"]
|
|
98
|
+
if not selected_items:
|
|
99
|
+
print("Keine sicheren Updates vorhanden.")
|
|
100
|
+
return 0
|
|
101
|
+
elif user_input == "all":
|
|
102
|
+
selected_items = updates
|
|
103
|
+
else:
|
|
104
|
+
# Parse numbers
|
|
105
|
+
parts = [p.strip() for p in user_input.split(",")]
|
|
106
|
+
for p in parts:
|
|
107
|
+
try:
|
|
108
|
+
idx = int(p)
|
|
109
|
+
if 1 <= idx <= len(updates):
|
|
110
|
+
selected_items.append(updates[idx - 1])
|
|
111
|
+
else:
|
|
112
|
+
print(f"⚠️ Ungültige Nummer: {idx}")
|
|
113
|
+
except ValueError:
|
|
114
|
+
if p:
|
|
115
|
+
print(f"⚠️ Ungültige Eingabe übersprungen: '{p}'")
|
|
116
|
+
|
|
117
|
+
if not selected_items:
|
|
118
|
+
print("Keine Updates ausgewählt.")
|
|
119
|
+
return 0
|
|
120
|
+
|
|
121
|
+
print(f"\n🚀 Folgende {len(selected_items)} Updates werden installiert:")
|
|
122
|
+
for item in selected_items:
|
|
123
|
+
print(f" - {item.get('name')} ({get_pi_source(item)})")
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
confirm = input("\nFortfahren? [J/n]: ").strip().lower()
|
|
127
|
+
except KeyboardInterrupt:
|
|
128
|
+
print("\nAbgebrochen.")
|
|
129
|
+
return 0
|
|
130
|
+
|
|
131
|
+
if confirm not in {"", "j", "ja", "yes", "y"}:
|
|
132
|
+
print("Abgebrochen.")
|
|
133
|
+
return 0
|
|
134
|
+
|
|
135
|
+
# Execute actual pi update commands
|
|
136
|
+
for item in selected_items:
|
|
137
|
+
source = get_pi_source(item)
|
|
138
|
+
print(f"\n========================================================")
|
|
139
|
+
print(f"📦 Führe aus: pi update {source}")
|
|
140
|
+
print(f"========================================================")
|
|
141
|
+
subprocess.run(["pi", "update", source])
|
|
142
|
+
|
|
143
|
+
print("\n✅ Update-Prozess beendet!")
|
|
144
|
+
return 0
|
|
145
|
+
|
|
146
|
+
if __name__ == "__main__":
|
|
147
|
+
try:
|
|
148
|
+
sys.exit(main())
|
|
149
|
+
except KeyboardInterrupt:
|
|
150
|
+
print("\nAbgebrochen.")
|
|
151
|
+
sys.exit(0)
|