Reference audit: Huntarr.io commit fa475ab6 (2026-02-23)
Comparison date: 2026-03-03
NeutArr version: 1.0.0
This document maps every finding from the Huntarr security review against the current NeutArr codebase. The short version: 7 findings are directly resolved, 11 are not applicable (the vulnerable features were never added or were removed), and 0 are open after the fixes made during this review.
| Severity | Total | Resolved | Not Applicable | Open |
|---|---|---|---|---|
| Critical | 7 | 2 | 5 | 0 |
| High | 6 | 3 | 3 | 0 |
| Medium | 5 | 1 | 3 | 0 |
| OSS Best-Practice | 2 | 2 | 0 | 0 |
| Total | 20 | 8 | 11 | 0 |
Status: RESOLVED
NeutArr rebuilt the auth system from scratch. The bypass list is a frozenset of explicit paths checked by exact equality (ALWAYS_PUBLIC_PATHS in src/primary/auth.py). /api/settings/* is not in it. All /api/* endpoints require a valid JWT or API key. The credential-exposure issue is moot since the endpoint itself is auth-gated.
Status: NOT APPLICABLE
Plex auth was introduced in Huntarr v7.x. NeutArr forks at v6.6.3, which predates it entirely. No Plex routes, no plex_auth_routes.py.
Status: NOT APPLICABLE
Same as #2.
Status: NOT APPLICABLE
2FA (pyotp, qrcode) was removed in its entirety during the auth overhaul. No /api/user/2fa/ routes exist.
Status: NOT APPLICABLE
NeutArr has no recovery key system. Not present in v6.6.3 and not added.
Status: NOT APPLICABLE
No backup_routes.py in NeutArr. Backup/restore was introduced in a later Huntarr version and is not part of this fork.
Status: NOT APPLICABLE
No /api/setup/clear or skip-setup endpoint exists. Initial account creation through /api/auth/setup requires a one-time first-run token supplied through NEUTARR_SETUP_TOKEN or generated into the owner-only /config/.setup-token file. First-user creation is serialized and becomes permanently unavailable after the account is persisted.
Status: RESOLVED
X-Forwarded-For is only read when the immediate peer is inside a network configured by TRUSTED_PROXIES. Without it, only request.remote_addr is used for local bypass checks. Proxy Auth Mode also requires that trusted peer to provide the non-empty identity header named by NEUTARR_PROXY_AUTH_HEADER; it fails closed when either setting is absent or invalid (src/primary/auth.py).
Status: NOT APPLICABLE
All Windows service and install scripts were deleted in Phase 1. NeutArr is Linux/container only.
Status: NOT APPLICABLE
NeutArr integrates only with *arr apps directly (Sonarr, Radarr, Lidarr, Readarr, Whisparr, Eros). TMDB, Trakt, Prowlarr, and all third-party service integrations were added in Huntarr v7.x+ and are not present.
Status: RESOLVED
Consequence of #1 being resolved: the settings endpoint requires authentication, so the disclosure path is closed.
Status: NOT APPLICABLE
No backup functionality. See #6.
Status: RESOLVED
ALWAYS_PUBLIC_PATHS is a frozenset (exact equality). ALWAYS_PUBLIC_PREFIXES covers only /static/ and /logo/ (prefix match, no suffix or substring patterns). The specific patterns cited in the audit — '/api/user/2fa/' in request.path, request.path.endswith('/setup'), request.path.endswith('/ping') — are not present.
Status: RESOLVED
bcrypt replaces SHA-256 entirely. auth.py uses bcrypt.hashpw / bcrypt.checkpw with bcrypt.gensalt().
Status: NOT APPLICABLE
No recovery key system. See #5.
Status: NOT APPLICABLE
The affected calls were all in Plex auth routes, which do not exist in NeutArr. All *arr API calls in NeutArr pass an api_timeout parameter.
Status: NOT APPLICABLE
RSS sync, NZB parser, and import list fetchers are all v7.x+ additions. NeutArr has no XML parsing of external data.
Status: RESOLVED
pyproject.toml constraint updated to flask = ">=3.1.3,<4.0.0" which requires the patched version. NeutArr is stateless (no Flask sessions — JWT only), so practical impact was already minimal, but the constraint is now ahead of the CVE regardless.
Status: RESOLVED
All GitHub Actions in all four workflow files are now pinned to immutable commit SHAs with the version tag in a trailing comment for readability:
| Action | Tag | SHA |
|---|---|---|
actions/checkout |
v7 | 9c091bb2... |
actions/setup-python |
v6.3.0 | ece7cb06... |
docker/setup-qemu-action |
v4.1.0 | 06116385... |
docker/setup-buildx-action |
v4.2.0 | bb05f3f5... |
docker/login-action |
v4.4.0 | af1e73f9... |
docker/build-push-action |
v7.3.0 | 53b7df96... |
actions/github-script |
v9.0.0 | 3a2844b7... |
googleapis/release-please-action |
v5.0.0 | 45996ed1... |
webiny/action-conventional-commits |
v1.4.2 | 7f91b159... |
dependabot.yml monitors both github-actions and pip weekly, targeting dev, and will open PRs to update SHA pins when new versions are released.
Status: RESOLVED
Dockerfile creates a neutarr system group and user (UID 1000). The container now starts through an entrypoint that can remap to PUID/PGID, repairs /config ownership for bind mounts, and then drops privileges to the configured runtime user before launching the app.
Why so many "Not Applicable" findings?
NeutArr forks at Huntarr v6.6.3 specifically because it predates the features that introduced most of the critical vulnerabilities: Plex OAuth (v7.x), backup/restore (v7.x), 2FA (v6.x but removed here), recovery keys, and the scope-expanding integrations. The choice of fork point was partly a security decision — the attack surface in v6.6.3 is dramatically smaller.
Automated CI enforcement
The following checks run on every PR and push to dev:
| Check | Tool | What it catches |
|---|---|---|
| Static analysis | Bandit | Python security anti-patterns |
| Dependency CVEs | pip-audit | Known vulnerabilities in pinned deps |
| Lint | Ruff | Code quality, undefined names |
| Conventional commits | webiny/action-conventional-commits | Changelog hygiene |
The CI security job exports poetry's locked dependency tree to a requirements.txt and runs pip-audit against it, so new CVEs in transitive dependencies will be caught automatically on each PR.