.NET: Fix LocalCodeAct validation and package checks#7138
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aecf332f-b940-41a7-ac3a-6fbbe9892141
There was a problem hiding this comment.
Pull request overview
This PR tightens the Python AST validator embedded in Microsoft.Agents.AI.LocalCodeAct so the documented os.environ / os.path-only policy can’t be bypassed via simple aliasing, and it extends CI’s packed-artifact smoke test to ensure the LocalCodeAct NuGet package is produced and installable from locally packed artifacts.
Changes:
- Track names bound to the
osmodule viaimport os as x,import os.path(implicitosbinding), and simple assignments, then enforce theALLOWED_OS_ATTRSallow-list for all tracked aliases. - Add integration test coverage for both blocked (
os.system/popenvia aliases) and permitted (environ/path)osaccess patterns. - Extend the existing “Package install check” workflow step to also install
Microsoft.Agents.AI.LocalCodeActfrom the locally-packed artifacts feed.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| dotnet/src/Microsoft.Agents.AI.LocalCodeAct/Resources/validator.py | Tracks os aliases across imports and simple re-bindings and applies the os attribute allow-list to all aliases. |
| dotnet/tests/Microsoft.Agents.AI.LocalCodeAct.UnitTests/LocalExecuteCodeFunctionIntegrationTests.cs | Adds regression coverage for disallowed os.* access through aliases and confirms permitted os.environ / os.path usage still works. |
| .github/workflows/dotnet-build-and-test.yml | Extends packed-artifact package-install smoke test to include Microsoft.Agents.AI.LocalCodeAct. |
There was a problem hiding this comment.
Automated Code Review
Reviewers: 5 | Confidence: 89%
✓ Correctness
The PR correctly implements alias tracking for the
osmodule in the Python code validator, closing a bypass where renaming the module reference could evade the attribute allow-list. The logic forvisit_Import,visit_Assign,visit_AnnAssign, and the updatedvisit_Attributecheck is sound for the stated scope of simple name bindings. The CI workflow change is straightforward. One minor robustness issue:validate()resetsself._errorsbut notself._os_aliases, creating a stale-state risk if the same_CodeValidatorinstance were ever reused—though the current public API always creates a fresh instance, making this non-exploitable in practice.
✓ Security Reliability
The PR correctly addresses the stated os-alias bypass by tracking import aliases and simple assignment rebindings of the os module. The implementation for
visit_Import,visit_Assign,visit_AnnAssign, and the updatedvisit_Attributecheck is sound for the patterns it covers. However,visit_Assignonly tracks bareast.Nametargets, so tuple-unpacking rebindings likea, b = os, 1are not caught, leaving a similar-complexity bypass path open. The_os_aliasesset is not reset invalidate()(only_errorsis), which is currently harmless since the public API creates a new instance per call, but is a latent inconsistency. Test coverage for the added patterns is good.
✓ Test Coverage
The test coverage for the validator alias-tracking fix is solid: all five
visit_Importcode paths (direct import, aliased import, simple rebinding, chained rebinding, andimport os.path) have blocked-access test cases, and three permitted-access scenarios verify thatos.environ,os.path, andimport os.path as premain allowed. The one gap is that the newvisit_AnnAssignhandler (lines 340–348 of validator.py) has no test coverage at all — annotated re-bindings likex: Any = osfollowed byx.system('id')are tracked in production code but never exercised by a test.
✓ Failure Modes
The alias-tracking changes to the Python validator are well-structured and correctly close the stated bypass. The
_os_aliasesset is not reset invalidate()alongside_errors, but this is not a live issue because the entry point (validate_codeat line 440) creates a fresh_CodeValidatorper call and the C# side spawns a new subprocess per validation. All tracked alias patterns (direct import, aliased import, submodule import, simple assignment, annotated assignment) correctly update the set beforevisit_Attributechecks it. The CI smoke-test addition is straightforward. No concrete failure paths were found.
✗ Design Approach
I found one design-level gap in the validator change. The new alias tracking closes direct
osrenames and straighta = oschains, but it still leaves ordinary destructuring assignments untracked, so theosallow-list can still be bypassed through tuple/list rebinding. I did not find another evidence-backed design issue in the package-smoke-test change within the bounded review pass.
Flagged Issues
-
dotnet/src/Microsoft.Agents.AI.LocalCodeAct/Resources/validator.py:334-337only tracksosaliases when the right-hand side is a bareast.Nameand the target is a bareast.Name, so code likeimport os; a, _ = (os, 1); a.system('id')still bypasses theosattribute allow-list enforced atvalidator.py:383-384.
Automated review by eavanvalkenburg's agents
|
Flagged issue
Source: automated DevFlow PR review |
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: aecf332f-b940-41a7-ac3a-6fbbe9892141
|
Addressed the actionable review feedback in 83c5114: tuple/list destructuring now propagates os aliases recursively, annotated and destructuring cases have regression coverage, and validator alias state resets between validations. The inline security thread has been replied to and resolved. |
Motivation & Context
The LocalCodeAct validator currently enforces the
os.environ/os.pathpolicy only when the module is referenced by the literal nameos, allowing aliases and simple re-bindings to reach disallowed APIs. In addition, the repository's package-install smoke test does not consumeMicrosoft.Agents.AI.LocalCodeAct, so a missing or unusable package artifact can go unnoticed. This change closes the validator bypass and adds package coverage for the LocalCodeAct distribution path.Description & Review Guide
osmodule from direct and aliased imports plus simple assignments, enforce the allow-list for those names, add regression coverage for blocked and permitted access, and installMicrosoft.Agents.AI.LocalCodeActin the existing packed-artifact smoke test.osAPIs cannot bypass validation by changing the variable name. Existingos.environ,os.path, andimport os.path as pusage remains allowed. CI now verifies that the LocalCodeAct package is emitted and consumable from local release artifacts.import os.pathversusimport os.path as p, and confirm the package smoke test uses the same local artifact source as the existing package check.Related Issue
Fixes #7068
Related to #6824 (the repository-side package smoke check is included; the actual NuGet publication is handled by an external release pipeline).
PR #7071 contained an overlapping implementation of the #7068 validator fix and was closed as superseded by this PR. Its submitter was thanked for the investigation and regression coverage.
Contribution Checklist
breaking changelabel (or add "[BREAKING]" to the title prefix, before or after a language prefix) — a workflow keeps the label and title prefix in sync automatically.