Description
The documented contract
The validator documents and intends an os-restricted-to-environ/path policy, in three places in its own source:
ALLOWED_IMPORTS admits os explicitly 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 os attribute allow-list. Anything outside ALLOWED_OS_ATTRS (file I/O, process control, mutating helpers, etc.) is rejected so the validator matches the documented os.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=0
Error 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")
...
Description
The documented contract
The validator documents and intends an
os-restricted-to-environ/pathpolicy, in three places in its own source:ALLOWED_IMPORTSadmitsosexplicitly scoped:visit_Attribute’s own comment states the contract:So the intended, documented behaviour is unambiguous: through
os, onlyenvironandpathshould be reachable.The inconsistency
Enforcement is keyed on the literal receiver name
os. Invisit_Attribute:visit_Importadmits the module but never records its bound name (asname), and assignments aren’t tracked. So any name other thanosbound to theosmodule escapes the check entirely, even though the accessed attribute (system,popen,execvp,fork,unlink,chmod, …) is identical.Notably,
visit_ImportFromalready enforces this same policy for thefrom os import systemform:Code Sample
Error 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
osand check membership instead of a literal== "os"— mirroring the enforcement already present invisit_ImportFrom: