[BUG] HTTP lifecycle dispatch treats a NO_PROXY-only environment as corporate egress and skips the resolve-and-pinned session
Maintainers usually reply within 1 day
@bodapatisaikrishna is already working on this.
Since Nov 12, 2026.
- #3575 by @bodapatisaikrishna — open
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 76/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- python
- Domain
- cli, networking
Research direction
Start in src/apm_cli/core/script_executors.py at _environ_proxies_for() and check how requests.utils.get_environ_proxies() and requests.utils.select_proxy() determine whether a proxy applies to the URL. The report names regression coverage in tests/unit/test_lifecycle_executor_paths.py, including TestHttpExecutor dispatch tests. Done when a NO_PROXY-only environment uses the guarded session and the tests pass, while destinations with a selected proxy retain the corporate-egress path.
Written by the indexing model from the issue text.
Description
Describe the bug
_environ_proxies_for() in src/apm_cli/core/script_executors.py decides whether an HTTP lifecycle script should route through the operator's outbound proxy ("corporate egress") or use the direct, resolve-and-pinned guarded session. It currently treats any truthy return from requests.utils.get_environ_proxies() as corporate egress, but that function returns a non-empty dict in a common case where no proxy would ever be selected:
$ NO_PROXY=127.0.0.1,localhost,::1 python -c "
import requests
print(requests.utils.get_environ_proxies('https://example.com/hook', no_proxy=None))
"
{'no': '127.0.0.1,localhost,::1'}
With NO_PROXY exported but no HTTP_PROXY/HTTPS_PROXY set — a very common environment in containers, CI runners, and macOS shells — the mapping contains only the no (no-proxy) entry. requests.utils.select_proxy() picks no proxy for any URL from such a mapping, so the request goes out directly. But _dispatch_http_request() only checks the dict's truthiness, so it takes the "corporate egress" branch and sends the request on the unpinned capturing session (or bare requests.post) instead of the resolve-and-pinned guarded session. The resolve-and-pin DNS defense that _build_guarded_session() provides is therefore silently skipped on hosts that merely export NO_PROXY.
Two concrete symptoms:
- Test integrity. Two unit tests in
tests/unit/test_lifecycle_executor_paths.pyfail on any such machine, because they patch_get_guarded_sessionand expect the guarded session to be used:
FAILED tests/unit/test_lifecycle_executor_paths.py::TestHttpExecutor::test_public_url_dispatches_and_logs
FAILED tests/unit/test_lifecycle_executor_paths.py::TestHttpExecutor::test_batch_dispatch_drains_all_entries
CI can't see this because GitHub-hosted runners don't export NO_PROXY, so the suite is red for external contributors with a false "main is broken" signal. On my machine the failures were reproducible at the current main (0a71750c), and unsetting NO_PROXY for the pytest process made both tests pass.
- Consistency of egress selection. The docstring of
_environ_proxies_forsays a non-empty result means "the environment mandates an outbound proxy for this destination". For the{'no': ...}mapping that claim is wrong, and the code path taken (capturing session, proxy-ACL rebind story) doesn't match the actual network behavior (direct, unpinned).
To Reproduce
- On a machine with
NO_PROXYexported (no proxy variables set), check out currentmain. - Run:
$ pytest tests/unit/test_lifecycle_executor_paths.py -p no:cacheprovider
- The two
TestHttpExecutortests above fail. Run again withNO_PROXYunset for the process and all 58 tests pass.
Alternatively, in a REPL:
import requests
se._environ_proxies_for("https://example.com/hook") # with NO_PROXY exported
# -> {'no': '127.0.0.1,localhost,::1'} (truthy => treated as corporate egress)
Expected behavior
_environ_proxies_for should return a non-empty dict only when a proxy would actually be selected for the destination URL — matching its documented contract and keeping direct-egress dispatch on the resolve-and-pinned guarded session. The natural check is requests.utils.select_proxy(url, raw): if it returns None, no proxy applies and the function should return {}.
Environment (please complete the following information):
- OS: macOS 26 (reproduces on any host exporting
NO_PROXYwithout proxy variables) - Python Version: 3.11.7 / 3.13
- APM Version: current
main(0a71750c), also present in 0.32.x line - VSCode Version: n/a
Logs
Standard pytest failure output for the two tests, ending in:
E assert <MagicMock name='mock.post' ...>.called
The mocked guarded session never sees a .post call because dispatch took the capturing-session branch.
Additional context
Proposed fix (small, in _environ_proxies_for only): keep get_environ_proxies, then confirm with select_proxy(url, raw) that a proxy would actually be used for the URL before returning the mapping; otherwise return {}. This restores the documented direct-egress contract, keeps genuinely proxied environments on the corporate-egress path unchanged, and makes the two affected tests hermetic with respect to NO_PROXY. I have a regression test + fix ready and will link the PR here.
- Dominant language
- Python
- Stars
- 4.3k
- Forks
- 415
- Avg merge
- 3d 3h
- Merged PRs (30d)
- 120
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from microsoft/apm
-
type/automation type/performance
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
Maintainers usually reply within 1 day
-
area/cli type/automation
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
Maintainers usually reply within 1 day
-
type/automation type/performance
Difficulty 2/5 1-3 hours Newbie friendliness 67/100
Maintainers usually reply within 1 day
-
[BUG] Global Codex instruction compilation ignores CODEX_HOMEPossibly taken @xubenqiang claimed this 5 days ago. Openstatus/accepted
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
microsoft/apm#3483 · 2 comments ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 60/100
Maintainers usually reply within 1 day
Similar issues
-
external
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
langchain-ai/docs#7652 ·
Maintainers usually reply within 1 day
-
[oblt-aw][autodoc] Document new e2e-secret-leak-artifacts workflow and update e2e-all leaf listOpen
Difficulty 2/5 1-3 hours Newbie friendliness 79/100
elastic/oblt-aw#2513 · 1 comment ·
Maintainers usually reply within 1 day
-
bug p2
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
Maintainers usually reply within 1 day
-
enhancement
Difficulty 1/5 Under an hour Newbie friendliness 75/100
NCAR/CLM-Tutorial#113 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
qazbnm456/awesome-web-security#279 ·
Maintainers usually reply within 10 days