Skip to content

fix(skillscan): detect os.environ access via from-import pattern - #4087

Merged
WillemJiang merged 2 commits into
bytedance:mainfrom
nankingjing:fix/skillscan-from-import-environ
Jul 12, 2026
Merged

WillemJiang merged 2 commits into
bytedance:mainfrom
nankingjing:fix/skillscan-from-import-environ

Conversation

@nankingjing

Copy link
Copy Markdown
Contributor

Problem

_scan_python's os.environ check only inspected ast.Attribute nodes (matching os.environ), missing bare ast.Name nodes produced by from os import environ.

The alias resolver _collect_python_aliases already correctly mapped environ -> os.environ for from os import environ, but the isinstance(node, ast.Attribute) gate filtered out Name nodes before _python_name could resolve the alias.

This meant a skill script using from os import environ + a network sink would bypass the python-env-dump-exfil detection entirely.

Fix

Changed the check from isinstance(node, ast.Attribute) to isinstance(node, (ast.Attribute, ast.Name)) so both forms trigger env-dump detection:

  • import os; os.environ (Attribute)
  • from os import environ; environ (Name, resolved via alias)

Test

Added two regression tests:

  • test_python_env_dump_exfil_detects_from_os_import_environ — from os import environ + requests.post
  • test_python_env_dump_exfil_detects_import_os_environ_attribute — import os + os.environ (existing behavior preserved)

🤖 Generated with Claude Code

@github-actions github-actions Bot added area:skills Skills under skills/ or the skills harness risk:medium Medium risk: regular code changes size/S PR changes 20-100 lines labels Jul 11, 2026
@WillemJiang
WillemJiang merged commit 897be7e into bytedance:main Jul 12, 2026
11 checks passed
lllyfff added a commit to qin-chenghan/deer-flow that referenced this pull request Jul 13, 2026
Brings in 4fd521e..2bd0f56: sandbox fixes (bytedance#4051/bytedance#4053/bytedance#4058/bytedance#4078/bytedance#4079/bytedance#4110),
CVE-2026-33128/bytedance#4089/bytedance#4093/bytedance#4106, subagents (bytedance#4056/bytedance#4059/bytedance#4088), skillscan (bytedance#4087),
channels (bytedance#4055/bytedance#4069/bytedance#4100), stream_bridge (bytedance#4071), frontend (bytedance#4058/bytedance#4068/bytedance#4094/
bytedance#4114/bytedance#4126), event-store serialization (bytedance#4077), streaming (bytedance#4082/bytedance#4085), mcp (bytedance#4084),
guardrails (bytedance#4067), and the memory fixes bytedance#4073/bytedance#4074/bytedance#4075/bytedance#4076/bytedance#4119.

Memory conflict resolution (upstream is still monolithic agents/memory/, we are
vendored backends/deermem/deermem/core/ - git rename-detected host->vendored):
- core/prompt.py, core/updater.py: keep ours (vendored with all ports); upstream's
  host-side bytedance#4119/bytedance#4074/bytedance#4075/bytedance#4076 changes are the same fixes already ported.
- core/queue.py: auto-merged cleanly (both sides had identical bytedance#4073 change).
- test_memory_queue.py: replaced the stale pre-bytedance#4073 test with upstream's three
  bytedance#4073 tests, adapted from host API (MemoryUpdateQueue()) to vendored (_queue()).
- test_memory_search.py: kept our vendored tests + added a vendored
  test_null_confidence_does_not_crash_sort (bytedance#4076 coverage).

Post-merge: 171 memory tests pass (+2 module-skipped), 7164 collected, no
import/collection errors. All 8 upstream memory fixes since the 8be7411
divergence are adapted (bytedance#4044/bytedance#4028/bytedance#4097/bytedance#4119/bytedance#4073/bytedance#4074/bytedance#4075/bytedance#4076).

Co-Authored-By: Claude <noreply@anthropic.com>
AnnaSuSu added a commit to AnnaSuSu/deer-flow that referenced this pull request Jul 14, 2026
_call_is_network_sink missed the HEAD/OPTIONS verbs on requests/httpx,
socket.create_connection, and urllib.request.urlretrieve. A bulk env dump
or reverse shell shipped through any of these slipped past the CRITICAL
exfil/reverse-shell rules whenever the URL was assembled at runtime (the
non-literal case the string-literal URL check can't cover). Also treat
socket.create_connection as the socket primitive in the reverse-shell shape.

http.client.HTTP(S)Connection is intentionally left out: only the lazy
constructor is statically visible (the request()/connect() that performs the
I/O is an instance method the call-name analyzer can't resolve), so flagging
the constructor would hard-block benign code that only builds a connection
object.

Cover the alias-resolved forms too: the sink check runs on the name after
from-import / import-as resolution, a path the suite exercised only on the
env-read side (bytedance#4087) and not on the sink side.
WillemJiang pushed a commit that referenced this pull request Jul 14, 2026
_call_is_network_sink missed the HEAD/OPTIONS verbs on requests/httpx,
socket.create_connection, and urllib.request.urlretrieve. A bulk env dump
or reverse shell shipped through any of these slipped past the CRITICAL
exfil/reverse-shell rules whenever the URL was assembled at runtime (the
non-literal case the string-literal URL check can't cover). Also treat
socket.create_connection as the socket primitive in the reverse-shell shape.

http.client.HTTP(S)Connection is intentionally left out: only the lazy
constructor is statically visible (the request()/connect() that performs the
I/O is an instance method the call-name analyzer can't resolve), so flagging
the constructor would hard-block benign code that only builds a connection
object.

Cover the alias-resolved forms too: the sink check runs on the name after
from-import / import-as resolution, a path the suite exercised only on the
env-read side (#4087) and not on the sink side.
marvin9551 pushed a commit to marvin9551/deer-flow that referenced this pull request Aug 21, 2026
…edance#4087)

* fix(skillscan): detect os.environ access via from-import pattern

* fix(skillscan): detect os.environ access via from-import pattern
marvin9551 pushed a commit to marvin9551/deer-flow that referenced this pull request Aug 21, 2026
…#4153)

_call_is_network_sink missed the HEAD/OPTIONS verbs on requests/httpx,
socket.create_connection, and urllib.request.urlretrieve. A bulk env dump
or reverse shell shipped through any of these slipped past the CRITICAL
exfil/reverse-shell rules whenever the URL was assembled at runtime (the
non-literal case the string-literal URL check can't cover). Also treat
socket.create_connection as the socket primitive in the reverse-shell shape.

http.client.HTTP(S)Connection is intentionally left out: only the lazy
constructor is statically visible (the request()/connect() that performs the
I/O is an instance method the call-name analyzer can't resolve), so flagging
the constructor would hard-block benign code that only builds a connection
object.

Cover the alias-resolved forms too: the sink check runs on the name after
from-import / import-as resolution, a path the suite exercised only on the
env-read side (bytedance#4087) and not on the sink side.
jihtsan pushed a commit to jihtsan/dnx-deer-flow that referenced this pull request Aug 29, 2026
…edance#4087)

* fix(skillscan): detect os.environ access via from-import pattern

* fix(skillscan): detect os.environ access via from-import pattern
jihtsan pushed a commit to jihtsan/dnx-deer-flow that referenced this pull request Aug 29, 2026
…#4153)

_call_is_network_sink missed the HEAD/OPTIONS verbs on requests/httpx,
socket.create_connection, and urllib.request.urlretrieve. A bulk env dump
or reverse shell shipped through any of these slipped past the CRITICAL
exfil/reverse-shell rules whenever the URL was assembled at runtime (the
non-literal case the string-literal URL check can't cover). Also treat
socket.create_connection as the socket primitive in the reverse-shell shape.

http.client.HTTP(S)Connection is intentionally left out: only the lazy
constructor is statically visible (the request()/connect() that performs the
I/O is an instance method the call-name analyzer can't resolve), so flagging
the constructor would hard-block benign code that only builds a connection
object.

Cover the alias-resolved forms too: the sink check runs on the name after
from-import / import-as resolution, a path the suite exercised only on the
env-read side (bytedance#4087) and not on the sink side.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:skills Skills under skills/ or the skills harness risk:medium Medium risk: regular code changes size/S PR changes 20-100 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants