feat(cli): refuse non-loopback bind when QWENPAW_AUTH_ENABLED is unset#4038
Open
huangcheng wants to merge 2 commits intoagentscope-ai:mainfrom
Open
feat(cli): refuse non-loopback bind when QWENPAW_AUTH_ENABLED is unset#4038huangcheng wants to merge 2 commits intoagentscope-ai:mainfrom
huangcheng wants to merge 2 commits intoagentscope-ai:mainfrom
Conversation
The HTTP gateway can invoke host-affecting tools (shell commands, file
IO, external APIs). Auth is opt-in via QWENPAW_AUTH_ENABLED, so binding
to a non-loopback host with auth disabled silently exposes a tool-
enabled agent on the network with no gate.
Refuse this configuration with a helpful message that points to the
three safe options:
1. Bind 127.0.0.1 + reverse proxy / VPN / Tailscale (recommended).
2. Set QWENPAW_AUTH_ENABLED=true to enable built-in auth.
3. Pass --allow-unauth-public (or QWENPAW_ALLOW_UNAUTH_PUBLIC=true)
when upstream auth is enforced and you accept the trade-off.
Loopback binds and auth-enabled binds are unchanged. The override flag
prints a loud warning but proceeds.
Closes agentscope-ai#4037
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Hi @huangcheng, thank you for your first Pull Request! 🎉 📋 About PR TemplateTo help maintainers review your PR faster, please make sure to include:
Complete PR information helps speed up the review process. You can edit the PR description to add these details. 🙌 Join Developer CommunityThanks so much for your contribution! We'd love to invite you to join the official QwenPaw developer group! You can find the Discord and DingTalk group links under the "Developer Community" section on our docs page: We truly appreciate your enthusiasm—and look forward to your future contributions! 😊 We'll review your PR soon. |
- Extract _addr_is_loopback helper to drop _enforce_unauth_public_bind_safety / _host_is_loopback below the too-many-return-statements threshold. - Disable protected-access and redefined-outer-name in the new test module (intentional pytest patterns: testing the underscore helpers, and the click runner fixture). - Re-run black + add-trailing-comma sweep on touched files. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Description
Closes #4037.
qwenpaw appexposes an HTTP gateway that can invoke host-affecting tools (shell, file IO, external APIs). Authentication is opt-in viaQWENPAW_AUTH_ENABLED, so the current default lets an operator silently expose a tool-enabled agent on the public internet by passing--host 0.0.0.0with no warning at startup.This PR makes that configuration impossible by accident:
--hostis non-loopback andQWENPAW_AUTH_ENABLEDis unset/falsy and the new--allow-unauth-publicflag (orQWENPAW_ALLOW_UNAUTH_PUBLICenv var) is not set, the CLI exits with a clear message pointing to three safe paths:127.0.0.1and front with a reverse proxy / VPN / Tailscale (recommended).export QWENPAW_AUTH_ENABLED=trueto use the built-in auth.--allow-unauth-public(or setQWENPAW_ALLOW_UNAUTH_PUBLIC=truein the service environment) when upstream auth is enforced and the trade-off is accepted.127.0.0.1,::1,localhost) and any bind with auth enabled are unchanged — no friction for the recommended configuration.Loopback detection handles IPv4/IPv6 literals and a small allowlist of well-known hostnames; arbitrary hostnames are resolved via
socket.getaddrinfoand treated as loopback only when every resolved address is loopback.Type of Change
Not a breaking change for the recommended configuration (loopback bind) or for any deployment that already sets
QWENPAW_AUTH_ENABLED=true. Operators currently relying on--host 0.0.0.0without auth will see a one-time refusal with instructions for the override flag.Component(s) Affected
src/qwenpaw/cli/app_cmd.py)tests/unit/cli/test_cli_app_safety.py)Why this and not just docs
The auth system is already implemented end-to-end (login UI, JWT, AuthGuard). The gap is purely defaults plus a CLI guard.
SECURITY.mdand theqwenpaw initSECURITY_WARNINGare valuable, but they live in places an operator who runspip install qwenpaw && supervisorctl start qwenpawmay never see. The CLI is the last place to catch the mistake before it commits.For consistency with comparable agent CLIs (Claude Code, Codex, OpenCode all gate any network-exposed gateway with a token/auth or require explicit opt-out), this brings QwenPaw to the same default posture.
Out of scope (intentionally)
QWENPAW_AUTH_ENABLED(would silently change behavior for existing local deployments).Testing
20 new unit tests in
tests/unit/cli/test_cli_app_safety.pycover:_host_is_loopbackclassifier across IPv4/IPv6 literals, hostnames, and the empty string._enforce_unauth_public_bind_safetyfor: loopback (passes), public + auth-on (passes), public + flag (passes with warning), public + env override (passes with warning), public + neither (exits with code 2).CliRunner-driven integration ofapp_cmdinvocations using mockeduvicorn.runandis_auth_enabled, asserting the refuse path emits the expected guidance and that the recommended/override/auth-enabled paths invokeuvicorn.run.Checklist
pre-commit run --all-files(on changed files — see verification below)SECURITY.mdalready documents the trust model; the CLI itself now surfaces the guidance)Local Verification Evidence