[Bug]: 5 reproducible runtime crashes in agent.py scripts (found by executing all 809 skills)
Summary
While evaluating the skill catalog, I executed every skills/*/scripts/agent.py (809 scripts) once via subprocess on Python 3.14 (Linux/WSL2, Ubuntu) and again on Python 3.12 (Windows). Most scripts behave as expected (run on built-in demo data, or exit cleanly asking for an input artifact / API key). This report collects 5 genuine, reproducible runtime defects — code bugs independent of missing dependencies, missing input, or missing credentials.
Two of them (#1, #2) fail at module import, so the affected skill cannot start at all.
I verified each on Linux with the relevant libraries installed, so these are not "missing dependency" artifacts. Happy to open a PR if useful.
| # | Skill | File:Line | Error | When |
|---|---|---|---|---|
| 1 | configuring-snort-ids-for-intrusion-detection |
scripts/agent.py:17 |
NameError: name 'DAQ_DIR' is not defined |
import time (always) |
| 2 | deploying-ransomware-canary-files |
scripts/agent.py:281 |
NameError: name 'FileSystemEventHandler' is not defined |
import time, when watchdog not installed |
| 3 | analyzing-linux-elf-malware |
scripts/agent.py:127-129 |
ValueError: stdout and stderr arguments may not be used with capture_output |
whenever a target is analyzed |
| 4 | monitoring-darkweb-sources |
scripts/agent.py:149 |
KeyError: 'name' |
default run (no HIBP API key) |
| 5 | performing-log-analysis-for-forensic-investigation |
scripts/agent.py:191/194 |
ValueError: dict contains fields not in fieldnames: 'service', 'host', 'message' |
when parsing syslog + another source together |
1. configuring-snort-ids — self-referential default (NameError at import)
# line 13-17
SNORT_BIN = os.environ.get("SNORT_BIN", "/usr/local/bin/snort")
SNORT_CONF = os.environ.get("SNORT_CONF", "/usr/local/etc/snort/snort.lua")
RULES_DIR = os.environ.get("SNORT_RULES_DIR", "/usr/local/etc/snort/rules")
LOG_DIR = os.environ.get("SNORT_LOG_DIR", "/var/log/snort")
DAQ_DIR = os.environ.get("SNORT_DAQ_DIR", DAQ_DIR) # <-- DAQ_DIR used as its own defaultDAQ_DIR is referenced as its own fallback before it exists. The other four vars use a literal default; this one was likely a copy/paste miss.
Fix: give it a literal default like the others, e.g.
DAQ_DIR = os.environ.get("SNORT_DAQ_DIR", "/usr/local/lib/daq")2. deploying-ransomware-canary-files — unguarded class base (NameError at import)
# line 22-24
try:
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
except ImportError:
... # import guarded
...
# line 281 (module scope)
class CanaryFileHandler(FileSystemEventHandler): # <-- base undefined if watchdog missingThe import is guarded, but the class inherits FileSystemEventHandler at module scope, so when watchdog is absent the module raises NameError at load — before any friendly "pip install watchdog" message can print.
Fix: guard the class (or the whole watchdog-dependent block), e.g.
_Base = FileSystemEventHandler if HAS_WATCHDOG else object
class CanaryFileHandler(_Base):
...or declare watchdog a hard requirement and fail with a clear message.
3. analyzing-linux-elf-malware — invalid subprocess.run args (ValueError on every analysis)
# check_packing(), line 127-129
stdout, _, _ = subprocess.run(["upx", "-t", filepath],
capture_output=True, text=True,
stderr=subprocess.STDOUT, timeout=120).stdout, "", 0subprocess.run rejects stderr= together with capture_output=True. This raises unconditionally as soon as check_packing() runs (i.e. any real ELF target), before UPX is even invoked.
Fix: drop stderr=subprocess.STDOUT (capture_output already captures stderr):
proc = subprocess.run(["upx", "-t", filepath], capture_output=True, text=True, timeout=120)
stdout = proc.stdout4. monitoring-darkweb-sources — hard key access on optional field (KeyError)
# line 149
lines.append(f" - {b['name']} ({b['breach_date']}) - {b['pwn_count']:,} accounts")On the default run (no HIBP API key), the breach records passed to the report do not carry a name key, so this crashes. Note lines 43-44 already use .get(...) defensively for the same records.
Fix: be consistent and defensive:
lines.append(f" - {b.get('name','Unknown')} ({b.get('breach_date','?')}) - {b.get('pwn_count',0):,} accounts")5. performing-log-analysis-for-forensic-investigation — CSV fieldnames from first row only (ValueError)
# line 191
writer = csv.DictWriter(f, fieldnames=list(timeline[0].keys()))
writer.writeheader()
for event in timeline:
# line 194
writer.writerow({k: str(v)[:200] for k, v in event.items()})fieldnames is derived from timeline[0] only. When the timeline mixes event types (e.g. syslog entries add service, host, message that the first event lacks), writerow raises. Reproduced by passing both a syslog and another source.
Fix: compute the union of keys, or ignore extras:
fieldnames = sorted({k for e in timeline for k in e})
writer = csv.DictWriter(f, fieldnames=fieldnames, extrasaction="ignore")Reproduction
python3 skills/configuring-snort-ids-for-intrusion-detection/scripts/agent.py # NameError at import
python3 skills/deploying-ransomware-canary-files/scripts/agent.py # NameError at import (no watchdog)
python3 skills/analyzing-linux-elf-malware/scripts/agent.py /bin/ls # ValueError in check_packing
python3 skills/monitoring-darkweb-sources/scripts/agent.py # KeyError: 'name'Environment: Python 3.14.4 (Ubuntu/WSL2) and Python 3.12 (Windows); libraries pyelftools, scapy, pefile, boto3, requests, watchdog(absent for #2) as relevant.
Source: mukul975/Anthropic-Cybersecurity-Skills