Skip to content

.NET: Fix LocalCodeAct validation and package checks#7138

Merged
eavanvalkenburg merged 2 commits into
microsoft:mainfrom
eavanvalkenburg:hl_fix
Jul 16, 2026
Merged

.NET: Fix LocalCodeAct validation and package checks#7138
eavanvalkenburg merged 2 commits into
microsoft:mainfrom
eavanvalkenburg:hl_fix

Conversation

@eavanvalkenburg

@eavanvalkenburg eavanvalkenburg commented Jul 16, 2026

Copy link
Copy Markdown
Member

Motivation & Context

The LocalCodeAct validator currently enforces the os.environ/os.path policy only when the module is referenced by the literal name os, allowing aliases and simple re-bindings to reach disallowed APIs. In addition, the repository's package-install smoke test does not consume Microsoft.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

  • What are the major changes? Track names bound to the os module from direct and aliased imports plus simple assignments, enforce the allow-list for those names, add regression coverage for blocked and permitted access, and install Microsoft.Agents.AI.LocalCodeAct in the existing packed-artifact smoke test.
  • What is the impact of these changes? Disallowed os APIs cannot bypass validation by changing the variable name. Existing os.environ, os.path, and import os.path as p usage remains allowed. CI now verifies that the LocalCodeAct package is emitted and consumable from local release artifacts.
  • What do you want reviewers to focus on? Confirm the alias/import binding semantics, especially import os.path versus import 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

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR is linked to an issue and there is no other open PR for this issue (see Related Issue above).
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after a language prefix) — a workflow keeps the label and title prefix in sync automatically.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: aecf332f-b940-41a7-ac3a-6fbbe9892141
Copilot AI review requested due to automatic review settings July 16, 2026 07:40
@giles17 giles17 added the .NET Usage: [Issues, PRs], Target: .Net label Jul 16, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 os module via import os as x, import os.path (implicit os binding), and simple assignments, then enforce the ALLOWED_OS_ATTRS allow-list for all tracked aliases.
  • Add integration test coverage for both blocked (os.system/popen via aliases) and permitted (environ/path) os access patterns.
  • Extend the existing “Package install check” workflow step to also install Microsoft.Agents.AI.LocalCodeAct from 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.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated Code Review

Reviewers: 5 | Confidence: 89%

✓ Correctness

The PR correctly implements alias tracking for the os module in the Python code validator, closing a bypass where renaming the module reference could evade the attribute allow-list. The logic for visit_Import, visit_Assign, visit_AnnAssign, and the updated visit_Attribute check is sound for the stated scope of simple name bindings. The CI workflow change is straightforward. One minor robustness issue: validate() resets self._errors but not self._os_aliases, creating a stale-state risk if the same _CodeValidator instance 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 updated visit_Attribute check is sound for the patterns it covers. However, visit_Assign only tracks bare ast.Name targets, so tuple-unpacking rebindings like a, b = os, 1 are not caught, leaving a similar-complexity bypass path open. The _os_aliases set is not reset in validate() (only _errors is), 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_Import code paths (direct import, aliased import, simple rebinding, chained rebinding, and import os.path) have blocked-access test cases, and three permitted-access scenarios verify that os.environ, os.path, and import os.path as p remain allowed. The one gap is that the new visit_AnnAssign handler (lines 340–348 of validator.py) has no test coverage at all — annotated re-bindings like x: Any = os followed by x.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_aliases set is not reset in validate() alongside _errors, but this is not a live issue because the entry point (validate_code at line 440) creates a fresh _CodeValidator per 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 before visit_Attribute checks 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 os renames and straight a = os chains, but it still leaves ordinary destructuring assignments untracked, so the os allow-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-337 only tracks os aliases when the right-hand side is a bare ast.Name and the target is a bare ast.Name, so code like import os; a, _ = (os, 1); a.system('id') still bypasses the os attribute allow-list enforced at validator.py:383-384.

Automated review by eavanvalkenburg's agents

Comment thread dotnet/src/Microsoft.Agents.AI.LocalCodeAct/Resources/validator.py Outdated
@github-actions

Copy link
Copy Markdown
Contributor

Flagged issue

dotnet/src/Microsoft.Agents.AI.LocalCodeAct/Resources/validator.py:334-337 only tracks os aliases when the right-hand side is a bare ast.Name and the target is a bare ast.Name, so code like import os; a, _ = (os, 1); a.system('id') still bypasses the os attribute allow-list enforced at validator.py:383-384.


Source: automated DevFlow PR review

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: aecf332f-b940-41a7-ac3a-6fbbe9892141
@eavanvalkenburg

Copy link
Copy Markdown
Member Author

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.

@eavanvalkenburg eavanvalkenburg added this pull request to the merge queue Jul 16, 2026
Merged via the queue into microsoft:main with commit 5282c15 Jul 16, 2026
27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

.NET Usage: [Issues, PRs], Target: .Net

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: .NET: [Bug]: _CodeValidator does not enforce the documented os.environ/os.path-only policy for aliased imports

5 participants