CR: netapi v0.4.0 Security Hardening
Change Summary
Change ID |
CR-2026-03-25-netapi-security-hardening |
Type |
Security / Code Quality |
Priority |
P0 - Critical |
Status |
Completed |
Requested |
2026-03-25 |
Completed |
2026-03-25 |
Owner |
Evan Rosado |
Background
After pushing netapi to GitHub, Dependabot identified 4 security vulnerabilities (2 HIGH, 1 MEDIUM, 1 LOW). As netapi is a "daily driver" P0 tool used for infrastructure automation, security vulnerabilities are unacceptable.
Additionally, the codebase lacked a unified exception hierarchy, making error handling inconsistent across 17+ vendor integrations.
Vulnerabilities Identified
| Package | Severity | CVE | Issue |
|---|---|---|---|
|
HIGH |
GHSA-xxxx |
SECT curve subgroup attack |
|
HIGH |
GHSA-xxxx |
DoS via unbounded recursion |
|
MEDIUM |
GHSA-xxxx |
Insecure temp file reuse |
|
MEDIUM |
GHSA-xxxx |
RCE via pickle deserialization |
|
LOW |
GHSA-xxxx |
ReDoS (no patch available) |
Implementation
Phase 1: Dependency Patching
Updated pyproject.toml with explicit version floors for vulnerable packages:
dependencies = [
"requests>=2.33.0", # CVE fix
"cryptography>=46.0.5", # CVE fix
"pyasn1>=0.6.3", # CVE fix
"python-socketio>=5.14.0", # CVE fix
# ... other deps
]
Phase 2: genie/pyats Removal
Problem: Cisco genie/pyats pins pyasn1⇐0.6.2 which conflicts with the security fix.
Solution: Removed genie/pyats from the parsing extra entirely.
# CLI output parsing
parsing = [
"textfsm>=1.1",
"ntc-templates>=6.0",
"ttp>=0.9",
]
# NOTE: Cisco pyATS/Genie removed due to unfixed CVEs (pyasn1, socketio)
# If needed, install separately: pip install genie pyats
Phase 3: Unified Exception Hierarchy
Created netapi.primitives.exceptions with vendor-aware base classes:
class NetapiError(Exception):
"""Base exception for all netapi errors."""
vendor: str = "netapi"
class NetapiApiError(NetapiError):
"""HTTP API returned error (4xx, 5xx)."""
status_code: int | None
class NetapiAuthError(NetapiError):
"""Authentication failed."""
class NetapiConnectionError(NetapiError):
"""Network unreachable."""
class NetapiNotFoundError(NetapiError):
"""Resource not found (404)."""
class NetapiRateLimitError(NetapiError):
"""Rate limit exceeded."""
class NetapiTimeoutError(NetapiError):
"""Request timeout."""
Phase 4: Vendor Exception Migration
Migrated vendor-specific exceptions to inherit from NetapiError:
| Vendor | Status | Notes |
|---|---|---|
pfSense |
✅ Complete |
|
WLC |
✅ Complete |
|
GitHub |
✅ Complete |
|
GitLab |
✅ Complete |
|
Gitea |
✅ Complete |
|
ISE |
✅ Complete |
8 clients: ERS, MnT, OpenAPI, pxGrid, SAML, Certs, Backup, DataConnect |
Synology |
✅ Complete |
|
Infoblox |
✅ Complete |
|
Keycloak |
✅ Complete |
|
KVM |
✅ Complete |
|
FMC |
✅ Complete |
|
ASA |
✅ Complete |
|
IOS-XE |
✅ Complete |
|
Vault |
N/A |
No custom exceptions |
Wazuh |
N/A |
No custom exceptions |
Cloudflare |
N/A |
No custom exceptions |
Phase 5: Bare Except Cleanup
Fixed bare except: clauses that swallow all exceptions:
| File | Fix |
|---|---|
|
|
|
|
Verification
# Regenerate lockfile
uv lock
# Verify patched versions installed
uv sync --extra all
uv pip list | grep -E "cryptography|pyasn1|requests|socketio"
# Verify exception hierarchy
python -c "from netapi.primitives.exceptions import NetapiError; print('OK')"
# Test vendor exceptions inherit correctly
python -c "
from netapi.vendors.github import GitHubError
from netapi.primitives.exceptions import NetapiError
assert issubclass(GitHubError, NetapiError)
print('OK')
"
Breaking Changes
| Change | Migration Path |
|---|---|
|
Install separately: |
Files Modified
-
pyproject.toml- Dependency versions, removed genie/pyats -
uv.lock- Regenerated with patched versions -
netapi/primitives/exceptions.py- New unified hierarchy -
netapi/vendors/pfsense/client.py- Bare except fix, inherit NetapiError -
netapi/vendors/cisco/wlc/ssh_client.py- Inherit NetapiError -
netapi/vendors/github/client.py- Inherit NetapiError -
netapi/vendors/gitlab/client.py- Inherit NetapiError -
netapi/vendors/gitea/client.py- Inherit NetapiError -
netapi/vendors/cisco/ise/exceptions.py- NEW: Shared ISE exception hierarchy -
netapi/vendors/cisco/ise/ers_client.py- Import from shared exceptions -
netapi/vendors/cisco/ise/mnt_client.py- Import from shared exceptions -
netapi/vendors/cisco/ise/openapi_client.py- Import from shared exceptions -
netapi/vendors/cisco/ise/pxgrid_client.py- Import from shared exceptions -
netapi/vendors/cisco/ise/saml_client.py- Import from shared exceptions -
netapi/vendors/cisco/ise/certs_client.py- Import from shared exceptions -
netapi/vendors/cisco/ise/backup_client.py- Import from shared exceptions -
netapi/vendors/cisco/ise/dataconnect_client.py- Import from shared exceptions -
netapi/vendors/cisco/firewall/asa_client.py- Inherit NetapiError -
netapi/vendors/cisco/firewall/fmc_client.py- Inherit NetapiError -
netapi/vendors/cisco/ios/client.py- Inherit NetapiError -
netapi/vendors/cisco/ios/ssh_client.py- Inherit NetapiError -
netapi/vendors/infoblox/client.py- Inherit NetapiError -
netapi/vendors/keycloak/client.py- Inherit NetapiError -
netapi/vendors/kvm/client.py- Inherit NetapiError -
netapi/vendors/synology/client.py- Inherit NetapiError
Commits
| Repository | Commit |
|---|---|
netapi |
|
netapi |
|
domus-captures |
|
domus-netapi-docs |
|
Remaining Work
-
✅ Complete vendor exception migration (17/17 complete)
-
Add unit tests for exception hierarchy
-
Consider direnv for auto
uv sync
Changelog
| Date | Author | Change |
|---|---|---|
2026-03-25 |
Evan Rosado |
Initial CR created, security fixes completed |
2026-03-25 |
Evan Rosado |
All vendor exceptions migrated to NetapiError hierarchy (17/17) |