Skip to content

Python: Reject encoded dot-segment paths in OpenAPI plugin (.NET and Python)#14086

Open
rogerbarreto wants to merge 2 commits into
microsoft:mainfrom
rogerbarreto:rogerbarreto/openapi-encoded-dotsegment-14085
Open

Python: Reject encoded dot-segment paths in OpenAPI plugin (.NET and Python)#14086
rogerbarreto wants to merge 2 commits into
microsoft:mainfrom
rogerbarreto:rogerbarreto/openapi-encoded-dotsegment-14085

Conversation

@rogerbarreto

Copy link
Copy Markdown
Member

Motivation and Context

Closes #14085.

The .NET and Python OpenAPI plugins select operations using the raw OpenAPI path, but build the runtime request URL from a canonicalized path. Encoded dot-segments such as /resources/%2e%2e/admin pass an OperationSelectionPredicate path check, yet System.Uri (.NET) and urljoin (Python) collapse them to a different route at request time. The selected path and the actual request target can therefore diverge.

Description

  • .NET (RestApiOperation.ValidatePathSegments): decodes each path segment until stable (handling double encoding like %252e and encoded separators %2f/%5c) before rejecting canonical . or .. segments.
  • Python (RestApiOperation.build_path): adds equivalent decode-then-reject validation before the URL is built.

Regression tests on both stacks cover literal and encoded dot-segments, mixed case, encoded slashes, and double encoding, plus negative tests confirming legitimate encoded characters still work.

Test results:

  • .NET: 482/482 Functions.UnitTests OpenApi tests pass.
  • Python: 109/109 openapi_plugin unit tests pass.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • The PR follows the SK Contribution Guidelines
  • All unit tests pass, and I have added new tests where possible
  • I didn't break anyone 😄

The OpenAPI plugin selects operations using the raw path but builds the request
URL from a canonicalized path. Encoded dot-segments such as /resources/%2e%2e/admin
pass an OperationSelectionPredicate path check, then System.Uri (.NET) and urljoin
(Python) collapse them to a different route at request time.

.NET: ValidatePathSegments now decodes each path segment (until stable, handling
double encoding and encoded slashes) before rejecting canonical . or .. segments.

Python: build_path now validates path segments with the same decode-then-reject
logic before the URL is built.

Adds regression tests on both stacks covering literal and encoded dot-segments,
mixed case, encoded slashes, and double encoding, plus negative tests for
legitimate encoded characters.
Copilot AI review requested due to automatic review settings June 16, 2026 10:01
@rogerbarreto rogerbarreto requested review from a team as code owners June 16, 2026 10:01
@moonbox3 moonbox3 added .NET Issue or Pull requests regarding .NET code python Pull requests for the Python Semantic Kernel labels Jun 16, 2026
@rogerbarreto rogerbarreto self-assigned this Jun 16, 2026
@github-actions github-actions Bot changed the title Reject encoded dot-segment paths in OpenAPI plugin (.NET and Python) .Net: Reject encoded dot-segment paths in OpenAPI plugin (.NET and Python) Jun 16, 2026
@github-actions github-actions Bot changed the title .Net: Reject encoded dot-segment paths in OpenAPI plugin (.NET and Python) Python: Reject encoded dot-segment paths in OpenAPI plugin (.NET and Python) Jun 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 hardens the OpenAPI plugins (.NET and Python) against operation-selection bypasses caused by percent-encoded dot-segments that get canonicalized differently when building runtime request URLs.

Changes:

  • (.NET) Enhances RestApiOperation.ValidatePathSegments to decode percent-encoding (including double-encoding) and reject . / .. after re-splitting on decoded separators.
  • (Python) Adds _validate_path_segments and calls it from RestApiOperation.build_path to reject encoded dot-segments before URL construction.
  • Adds regression tests in both stacks for dot-segment rejection and allows legitimate encoded characters.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
python/tests/unit/connectors/openapi_plugin/test_sk_openapi.py Adds Python unit tests asserting dot-segment rejection and preserving legitimate encoded characters.
python/semantic_kernel/connectors/openapi_plugin/models/rest_api_operation.py Adds decode-then-reject dot-segment validation during path building (before urljoin).
dotnet/src/Functions/Functions.UnitTests/OpenApi/RestApiOperationTests.cs Adds .NET unit tests covering encoded dot-segments, encoded separators, and non-dot encoded characters.
dotnet/src/Functions/Functions.OpenApi/Model/RestApiOperation.cs Strengthens dot-segment validation by decoding (including double-encoding) and handling decoded separators.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread python/semantic_kernel/connectors/openapi_plugin/models/rest_api_operation.py Outdated
Comment thread python/tests/unit/connectors/openapi_plugin/test_sk_openapi.py
@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
connectors/openapi_plugin/models
   rest_api_operation.py2482490%101–102, 111–112, 121–122, 132, 141–142, 161–162, 171–172, 191–192, 228–229, 263, 266–269, 271, 416
TOTAL28721563480% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
3989 23 💤 0 ❌ 0 🔥 1m 49s ⏱️

@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: 4 | Confidence: 92%

✗ Security Reliability

The Python _validate_path_segments implementation has a security bypass that the .NET equivalent correctly handles. After iteratively decoding a segment, the .NET code re-splits on '/' and '' to catch encoded path separators (e.g., %2f, %5c) embedded within a segment before checking for dot-segments. The Python code only checks if the fully-decoded segment is exactly '.' or '..', missing the case where an encoded slash is part of the segment (e.g., '%2e%2fadmin' decodes to '../admin', which is not in ('.', '..'). The .NET test suite covers this vector ('/resources/%2e%2fadmin') but the Python tests and implementation do not.

✓ Test Coverage

The test coverage is solid for the core encoded dot-segment cases on both platforms. However, there is an asymmetry between the .NET and Python implementations: the .NET version re-splits decoded segments on '/' and '' after decoding (catching vectors like '%2e%2fadmin' which decodes to '../admin'), with a corresponding test case. The Python implementation lacks this re-split logic and the corresponding test. While Python's urljoin doesn't currently canonicalize %2f, some downstream servers do, so this is a defense-in-depth gap. The mixed-literal/encoded cases ('%2e.' and '.%2e') tested in .NET are also absent from the Python parametrized tests, though the Python implementation would catch them.

✗ Failure Modes

The Python _validate_path_segments implementation is missing a critical re-split on decoded path separators ('/' and '') that the .NET implementation includes. A path like '/resources/%2e%2fadmin' decodes to '../admin' as a single segment, which passes the Python check (since '../admin' is neither '.' nor '..') but is correctly rejected by the .NET version. This creates an asymetric security bypass in the Python implementation for the exact attack vector this PR is designed to prevent.

✓ Design Approach

The .NET side closes the encoded-separator variant, but the Python port does not mirror that logic, so the cross-language fix is incomplete for paths where decoding reveals a hidden slash before a dot-segment.

Flagged Issues

  • Python _validate_path_segments (rest_api_operation.py:309) does not re-split decoded segments on '/' or '' after iterative decoding, allowing encoded-slash traversal payloads like '/resources/%2e%2e%2fadmin' (decodes to '../admin') to bypass the if decoded in (".", "..") check. The .NET implementation correctly re-splits decoded segments on both separators (RestApiOperation.cs:442) and tests this vector (RestApiOperationTests.cs:1475).

Automated review by rogerbarreto's agents

Comment thread python/semantic_kernel/connectors/openapi_plugin/models/rest_api_operation.py Outdated
Comment thread python/semantic_kernel/connectors/openapi_plugin/models/rest_api_operation.py Outdated
@github-actions

Copy link
Copy Markdown
Contributor

Flagged issue

Python _validate_path_segments (rest_api_operation.py:309) does not re-split decoded segments on '/' or '' after iterative decoding, allowing encoded-slash traversal payloads like '/resources/%2e%2e%2fadmin' (decodes to '../admin') to bypass the if decoded in (".", "..") check. The .NET implementation correctly re-splits decoded segments on both separators (RestApiOperation.cs:442) and tests this vector (RestApiOperationTests.cs:1475).


Source: automated DevFlow PR review

@rogerbarreto rogerbarreto deployed to integration June 16, 2026 10:14 — with GitHub Actions Active
@rogerbarreto rogerbarreto enabled auto-merge June 16, 2026 10:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

.NET Issue or Pull requests regarding .NET code python Pull requests for the Python Semantic Kernel

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: OpenAPI plugin: encoded dot-segment path canonicalization bypasses operation selection (.NET and Python)

4 participants