You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
## Summary`SECUSCAN_ALLOW_LOOPBACK_SCANS` (default `true`) is documented and implemented as the setting that permits scanning `127.0.0.1`/loopback targets. Safe Mode (`validation.py`) honors it, but the independent Network Policy layer (`network_policy.py`) does not — its `network_denylist` unconditionally includes `127.0.0.0/8` with no check against the setting. As a result, loopback scans are always blocked by Network Policy regardless of this setting, on a completely default install.
## Why this matters
This isn't a theoretical edge case — it breaks the project's own Quick Start. The README's literal example under "API Examples" is:
```bash
curl -X POST http://127.0.0.1:8000/api/v1/task/start \
-H "Content-Type: application/json" \
-H "X-API-Key: $API_KEY" \
-d '{ "plugin_id": "nmap", "inputs": {"target": "127.0.0.1"}, "consent_granted": true }'
Any new contributor who follows the README exactly, on a fresh clone with only cp .env.example .env and a generated SECUSCAN_VAULT_KEY, gets an immediate, confusing failure instead of a working first scan. SECUSCAN_ALLOW_LOOPBACK_SCANS=true is even left uncommented/active in .env.example, signaling it should take effect by default — but it silently doesn't for this code path.
Reproduction steps
Fresh clone, cp .env.example .env, set SECUSCAN_VAULT_KEY (all other settings left at their defaults — SECUSCAN_ALLOW_LOOPBACK_SCANS is true by default in both .env.example and config.py).
Run the exact nmap/127.0.0.1 curl command from the README's "API Examples" section (above), using the generated backend/data/.api_key.
Poll the task status: GET /api/v1/task/{task_id}/status.
Expected behavior
The task should be accepted and (assuming nmap is installed) proceed to execute, since SECUSCAN_ALLOW_LOOPBACK_SCANS=true is the default and is documented as enabling exactly this.
Actual behavior
The task is accepted (status: queued) but fails almost immediately with:
{
"status": "failed",
"error_message": "Network policy denied access to 127.0.0.1: Blocked by denylist rule: Operator configured denylist (matched: 127.0.0.0/8)"
}
This happens even though the same request passes Safe Mode's own loopback check — Network Policy is a second, independent gate that never consults allow_loopback_scans.
Scope
Suggested files: backend/secuscan/network_policy.py (the check_access/denylist logic — needs to skip or exempt the 127.0.0.0/8 rule when settings.allow_loopback_scans is true), backend/secuscan/config.py (default network_denylist, allow_loopback_scans)
For reference, backend/secuscan/validation.py:161 and :200 show the pattern already used to make Safe Mode respect this setting (if ip.is_loopback and not settings.allow_loopback_scans) — Network Policy has no equivalent check.
Network Policy's denylist check exempts loopback ranges when settings.allow_loopback_scans is true, consistent with how Safe Mode already handles it
README's Quick Start nmap/127.0.0.1 example succeeds (past the network-policy gate) on a fresh default install
Test added covering allow_loopback_scans=true + Network Policy for a 127.0.0.1 target
Docs/.env.example comment updated if the interaction between Safe Mode and Network Policy for loopback needs clarifying
Additional context
Two independent guardrail layers (Safe Mode vs. Network Policy) currently implement the loopback exception inconsistently. Worth a short code comment in network_policy.py explaining the intended relationship between the two, to prevent this drifting again.
Labels:
type:bug, priority:mediumAny new contributor who follows the README exactly, on a fresh clone with only
cp .env.example .envand a generatedSECUSCAN_VAULT_KEY, gets an immediate, confusing failure instead of a working first scan.SECUSCAN_ALLOW_LOOPBACK_SCANS=trueis even left uncommented/active in.env.example, signaling it should take effect by default — but it silently doesn't for this code path.Reproduction steps
cp .env.example .env, setSECUSCAN_VAULT_KEY(all other settings left at their defaults —SECUSCAN_ALLOW_LOOPBACK_SCANSistrueby default in both.env.exampleandconfig.py).python -m uvicorn backend.secuscan.main:app --host 127.0.0.1 --port 8000.nmap/127.0.0.1curl command from the README's "API Examples" section (above), using the generatedbackend/data/.api_key.GET /api/v1/task/{task_id}/status.Expected behavior
The task should be accepted and (assuming
nmapis installed) proceed to execute, sinceSECUSCAN_ALLOW_LOOPBACK_SCANS=trueis the default and is documented as enabling exactly this.Actual behavior
The task is accepted (
status: queued) but fails almost immediately with:{ "status": "failed", "error_message": "Network policy denied access to 127.0.0.1: Blocked by denylist rule: Operator configured denylist (matched: 127.0.0.0/8)" }This happens even though the same request passes Safe Mode's own loopback check — Network Policy is a second, independent gate that never consults
allow_loopback_scans.Scope
backend/secuscan/network_policy.py(thecheck_access/denylist logic — needs to skip or exempt the127.0.0.0/8rule whensettings.allow_loopback_scansis true),backend/secuscan/config.py(defaultnetwork_denylist,allow_loopback_scans)backend/secuscan/validation.py:161and:200show the pattern already used to make Safe Mode respect this setting (if ip.is_loopback and not settings.allow_loopback_scans) — Network Policy has no equivalent check.network_allowlistblocking all public egress; its fix explicitly "preserve[d] denylist protection," which is what still unconditionally blocks loopback today.Evidence
Verified locally (Windows 11, fresh clone, default
.env):Environment
Definition of done
settings.allow_loopback_scansistrue, consistent with how Safe Mode already handles itnmap/127.0.0.1example succeeds (past the network-policy gate) on a fresh default installallow_loopback_scans=true+ Network Policy for a127.0.0.1target.env.examplecomment updated if the interaction between Safe Mode and Network Policy for loopback needs clarifyingAdditional context
Two independent guardrail layers (Safe Mode vs. Network Policy) currently implement the loopback exception inconsistently. Worth a short code comment in
network_policy.pyexplaining the intended relationship between the two, to prevent this drifting again.