Repository navigation
Python: .NET: [Bug]: _CodeValidator does not enforce the documented os.environ/os.path-only policy for aliased imports #7068
Description
Description
The documented contract
The validator documents and intends an os-restricted-to-environ/path policy, in three places in its own source:
ALLOWED_IMPORTSadmitsosexplicitly scoped:"os", # Limited to os.environ, os.path - validated via attribute access
- The attribute allow-list:
ALLOWED_OS_ATTRS: set[str] = {"environ", "path"}
visit_Attribute’s own comment states the contract:Enforce the
osattribute allow-list. Anything outsideALLOWED_OS_ATTRS(file I/O, process control, mutating helpers, etc.) is rejected so the validator matches the documentedos.environ/os.path-only contract.
So the intended, documented behaviour is unambiguous: through os, only environ and path should be reachable.
The inconsistency
Enforcement is keyed on the literal receiver name os. In visit_Attribute:
if isinstance(node.value, ast.Name) and node.value.id == "os" and node.attr not in self._allowed_os_attrs:
self._errors.append(f"Access to os.{node.attr} is not allowed")visit_Import admits the module but never records its bound name (asname), and assignments aren’t tracked. So any name other than os bound to the os module escapes the check entirely, even though the accessed attribute (system, popen, execvp, fork, unlink, chmod, …) is identical.
Notably, visit_ImportFrom already enforces this same policy for the from os import system form:
elif module_name == "os":
for alias_node in node.names:
if alias_node.name not in self._allowed_os_attrs:
self._errors.append(f"Import from 'os' of '{alias_node.name}' is not allowed")Code Sample
#### Reproduction
Standalone against `validator.py`, same commit:
# (a) direct — policy enforced ✔
$ echo '{"code":"import os\nos.system(\"id\")"}' | python -I validator.py
{"errors": ["Access to os.system is not allowed"]} exit=1
# (b) from-import — policy enforced ✔ (existing visit_ImportFrom guard)
$ echo '{"code":"from os import system\nsystem(\"id\")"}' | python -I validator.py
{"errors": ["Import from 'os' of 'system' is not allowed"]} exit=1
# (c) module alias — SAME os.system, NOT enforced (X)
$ echo '{"code":"import os as x\nx.system(\"id\")"}' | python -I validator.py
(no errors) exit=0
# (d) assignment alias — NOT enforced ✗
$ echo '{"code":"import os\n_o = os\n_o.system(\"id\")"}' | python -I validator.py
(no errors) exit=0Error Messages / Stack Traces
Package Versions
Microsoft.Agents.AI.LocalCodeAct: source build @ main 68136ee
.NET Version
.NET 8.0
Additional Context
Suggested fix
Track the names bound to os and check membership instead of a literal == "os" — mirroring the enforcement already present in visit_ImportFrom:
def __init__(self, ...):
...
self._os_aliases: set[str] = {"os"}
def visit_Import(self, node: ast.Import) -> None:
for alias in node.names:
if alias.name.split(".")[0] == "os":
self._os_aliases.add(alias.asname or "os")
self.generic_visit(node)
def visit_Assign(self, node: ast.Assign) -> None:
if (isinstance(node.value, ast.Name) and node.value.id in self._os_aliases
and len(node.targets) == 1 and isinstance(node.targets[0], ast.Name)):
self._os_aliases.add(node.targets[0].id)
self.generic_visit(node)
def visit_Attribute(self, node: ast.Attribute) -> None:
if (isinstance(node.value, ast.Name) and node.value.id in self._os_aliases
and node.attr not in self._allowed_os_attrs):
self._errors.append(f"Access to os.{node.attr} is not allowed")
...Metadata
Metadata
Labels
Type
Projects
- StatusShow more project fieldsDone
Activity
[-].NET: [Bug]: `_CodeValidator` does not enforce the documented `os.environ`/`os.path`-only policy for aliased imports[/-][+]Python: .NET: [Bug]: `_CodeValidator` does not enforce the documented `os.environ`/`os.path`-only policy for aliased imports[/+]