fix(php): Escape $ in double-quoted string literals to prevent code injection#7863
Merged
Conversation
…njection (MSRC 123931)
PHP interpolates $var, ${...} and {$...} inside double-quoted strings. The shared SanitizeDoubleQuote helper does not escape $, so attacker-controlled OpenAPI input (descriptions, enum values, content types, discriminator/query names) emitted into PHP double-quoted literals could inject and execute arbitrary PHP/OS commands when the generated client runs.
Add SanitizePhpDoubleQuoteLiteral on PhpConventionService (mirroring the existing SanitizeDartDoubleQuoteLiteral on DartConventionService) that additionally escapes $, and use it at all PHP double-quote emission sites. Add a regression test.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR hardens Kiota’s PHP code generator against generated-source injection / unintended PHP variable interpolation by ensuring schema-derived values emitted into PHP double-quoted string literals also escape $ (in addition to the existing SanitizeDoubleQuote() behavior).
Changes:
- Added
PhpConventionService.SanitizePhpDoubleQuoteLiteralto escape$afterSanitizeDoubleQuote(). - Routed PHP writer emission sites that use double-quoted literals (enum wire values, request content-type, accept header, discriminator property name, and query-parameter annotation) through the new sanitizer.
- Added a regression test ensuring
$in enum wire values is emitted as\$and not${...}.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Kiota.Builder.Tests/Writers/Php/CodeEnumWriterTests.cs | Adds regression coverage to ensure $ is escaped in generated PHP enum constants. |
| src/Kiota.Builder/Writers/Php/PhpConventionService.cs | Introduces PHP-specific double-quote literal sanitizer that escapes $ to prevent interpolation. |
| src/Kiota.Builder/Writers/Php/CodePropertyWriter.cs | Uses the new sanitizer for query-parameter annotation string content. |
| src/Kiota.Builder/Writers/Php/CodeMethodWriter.cs | Uses the new sanitizer for request body content-type, Accept header value, and discriminator property name string literals. |
| src/Kiota.Builder/Writers/Php/CodeEnumWriter.cs | Uses the new sanitizer when emitting enum wire values into double-quoted PHP literals. |
peombwa
marked this pull request as ready for review
June 25, 2026 23:14
Code Coverage OverviewLanguages: C# C# / code-coverage/dotnetThe overall coverage in the branch is 71%. Coverage data for the branch is not yet available. Show a code coverage summary of the most covered files.
Code Coverage is in Public Preview. Learn more and provide us with your feedback. |
peombwa
enabled auto-merge (squash)
June 25, 2026 23:16
gavinbarron
approved these changes
Jun 25, 2026
This was referenced Jun 26, 2026
This was referenced Jul 3, 2026
GuillaumeBodson
pushed a commit
to GuillaumeBodson/Filer
that referenced
this pull request
Jul 10, 2026
Updated [Microsoft.AspNetCore.Components.Authorization](https://github.com/dotnet/dotnet) from 10.0.8 to 10.0.9. <details> <summary>Release notes</summary> _Sourced from [Microsoft.AspNetCore.Components.Authorization's releases](https://github.com/dotnet/dotnet/releases)._ No release notes found for this version range. Commits viewable in [compare view](https://github.com/dotnet/dotnet/commits). </details> Updated [Microsoft.AspNetCore.Components.Web](https://github.com/dotnet/dotnet) from 10.0.8 to 10.0.9. <details> <summary>Release notes</summary> _Sourced from [Microsoft.AspNetCore.Components.Web's releases](https://github.com/dotnet/dotnet/releases)._ No release notes found for this version range. Commits viewable in [compare view](https://github.com/dotnet/dotnet/commits). </details> Updated [Microsoft.AspNetCore.Components.WebAssembly](https://github.com/dotnet/dotnet) from 10.0.8 to 10.0.9. <details> <summary>Release notes</summary> _Sourced from [Microsoft.AspNetCore.Components.WebAssembly's releases](https://github.com/dotnet/dotnet/releases)._ No release notes found for this version range. Commits viewable in [compare view](https://github.com/dotnet/dotnet/commits). </details> Updated [Microsoft.AspNetCore.Mvc.Testing](https://github.com/dotnet/dotnet) from 10.0.8 to 10.0.9. <details> <summary>Release notes</summary> _Sourced from [Microsoft.AspNetCore.Mvc.Testing's releases](https://github.com/dotnet/dotnet/releases)._ No release notes found for this version range. Commits viewable in [compare view](https://github.com/dotnet/dotnet/commits). </details> Updated [Microsoft.AspNetCore.OpenApi](https://github.com/dotnet/dotnet) from 10.0.8 to 10.0.9. <details> <summary>Release notes</summary> _Sourced from [Microsoft.AspNetCore.OpenApi's releases](https://github.com/dotnet/dotnet/releases)._ No release notes found for this version range. Commits viewable in [compare view](https://github.com/dotnet/dotnet/commits). </details> Updated [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 18.6.0 to 18.7.0. <details> <summary>Release notes</summary> _Sourced from [Microsoft.NET.Test.Sdk's releases](https://github.com/microsoft/vstest/releases)._ ## 18.7.0 ## What's Changed * Add ARM64 msdia140.dll support to test platform packages by @jamesmcroft in microsoft/vstest#15689 * Update System.Memory from 4.5.5 to 4.6.3 by @nohwnd in microsoft/vstest#15706 ## New Contributors * @jamesmcroft made their first contribution in microsoft/vstest#15689 **Full Changelog**: microsoft/vstest@v18.6.0...v18.7.0 Commits viewable in [compare view](microsoft/vstest@v18.6.0...v18.7.0). </details> Updated [microsoft.openapi.kiota](https://github.com/microsoft/kiota) from 1.32.2 to 1.32.5. <details> <summary>Release notes</summary> _Sourced from [microsoft.openapi.kiota's releases](https://github.com/microsoft/kiota/releases)._ ## 1.32.5 ## Added - Added the `--allowed-external-origins` parameter to commands that load OpenAPI descriptions. External references are now not loaded by default to improve security; use this parameter to allow `*`, full URIs, URI patterns, full paths, relative paths, or path patterns. - Added support for the `isNonConsequential` confirmation property in the AI capabilities extension (`x-ai-capabilities`) and mapped it to the generated plugin manifest confirmation object (plugin manifest 2.4). [#7857](microsoft/kiota#7857) - Added a DOM-surface regression integration test that diffs the kiota public API export between the published NuGet generator and the current changeset to catch breaking changes in generated SDKs. [#7858](microsoft/kiota#7858) ### Changed - Rejected unsafe `static_template.file` references in generated plugin manifests, preventing path traversal outside the manifest package. - Workspace client and plugin generation now rejects workspace configuration entries whose `outputPath` is rooted or escapes the workspace. - Sanitized client class and namespace names loaded from settings or the `x-ms-kiota-info` OpenAPI extension before using them in generated code and file paths. - Removed support for specifying dependency install commands through the `x-ms-kiota-info` OpenAPI description extension. ## 1.32.4 ## Added ### Changed - Fixed a code injection vulnerability in PHP generation by escaping `$` in double-quoted string literals. [#7863](microsoft/kiota#7863) ## 1.32.3 ## Added ### Changed - Fixed a generated-source code injection vulnerability (CWE-94) in the C# emitter where newlines in an OpenAPI `externalDocs.description` could break out of the `///` doc comment and inject arbitrary code. [#7831](microsoft/kiota#7831) - Fixed a bug that generares an empty model when allOf inheritance schema is reached via a composed type [#7791](microsoft/kiota#7791) - Fixed a bug in PHP generation where `parent::__construct()` was generated for parent classes without a constructor [#7809](microsoft/kiota#7809) [#7810](microsoft/kiota#7810) Commits viewable in [compare view](microsoft/kiota@v1.32.2...v1.32.5). </details> Updated [Testcontainers.PostgreSql](https://github.com/testcontainers/testcontainers-dotnet) from 4.12.0 to 4.13.0. <details> <summary>Release notes</summary> _Sourced from [Testcontainers.PostgreSql's releases](https://github.com/testcontainers/testcontainers-dotnet/releases)._ ## 4.13.0 # What's Changed Thank you to everyone who contributed and shared their feedback 🤜🤛. The NuGet packages for this release have been attested for supply chain security using [`actions/attest`](https://github.com/actions/attest). This confirms the integrity and provenance of the artifacts and helps ensure they can be trusted: [#33686956](https://github.com/testcontainers/testcontainers-dotnet/attestations/33686956). ## 🚀 Features * feat: Add Aspire dashboard module (#1194) @NikiforovAll * feat: Add image name substitution hook (#1710) @HofmeisterAn * feat(CosmosDb): Add get method AccountEndpoint (#1707) @srollinet * feat: Improve image build failure messages (#1700) @HofmeisterAn ## 🐛 Bug Fixes * fix: Restore tar archive write performance regressed by padding trim (#1719) @HofmeisterAn * chore: Bump Docker.DotNet from 4.3.2 to 4.3.3 (#1714) @HofmeisterAn * chore(AspireDashboard): Cover connection string provider (#1713) @HofmeisterAn ## 📖 Documentation * docs: Add missing TC languages and reorder docs navigation (#1711) @mdelapenya * docs: Add note about unsupported BuildKit Dockerfile features (#1696) @HofmeisterAn * docs: Explain immutable builder behavior (#1693) @HofmeisterAn ## 🧹 Housekeeping * chore: Enable Dependabot cooldown (#1716) @HofmeisterAn * chore: Add nuget.config (#1715) @Rob-Hague * chore(AspireDashboard): Cover connection string provider (#1713) @HofmeisterAn * chore: Bump Docker.DotNet from 4.2.0 to 4.3.2 (#1712) @HofmeisterAn * chore: Bump sshd-docker image from 1.3.0 to 1.4.0 (#1709) @HofmeisterAn * chore: Rename runtime label and add buildkit and stale labels (#1703) @HofmeisterAn * fix: Guard expensive argument evaluation when logging (#1702) @HofmeisterAn * chore: Defer container ID truncation in logging (#1701) @HofmeisterAn * chore: Migrate to LoggerMessageAttribute (#1697) @HofmeisterAn ## 📦 Dependency Updates * chore(deps): Bump the actions group with 2 updates (#1721) @[dependabot[bot]](https://github.com/apps/dependabot) * chore(deps): Bump the actions group with 7 updates (#1717) @[dependabot[bot]](https://github.com/apps/dependabot) * chore: Bump Docker.DotNet from 4.3.2 to 4.3.3 (#1714) @HofmeisterAn * chore: Bump Docker.DotNet from 4.2.0 to 4.3.2 (#1712) @HofmeisterAn * chore(deps): Bump the actions group with 4 updates (#1698) @[dependabot[bot]](https://github.com/apps/dependabot) Commits viewable in [compare view](testcontainers/testcontainers-dotnet@4.12.0...4.13.0). </details> Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions </details> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This was referenced Jul 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Kiota's PHP writer emits schema-derived strings into PHP double-quoted literals using
SanitizeDoubleQuote(), which escapes",\, and control characters but not$. Because PHP evaluates$var,${...}, and{$...}inside double-quoted strings, an attacker-controlled OpenAPI description (e.g. a media type likeapplication/json${system('whoami && id')}) is emitted verbatim into the generated SDK source and executes arbitrary PHP/OS commands when the generated client runs.This is a regression/coverage gap in the literal-injection hardening pass (#7603), which already added the equivalent
$escape for the Dart writer (Dart interpolates$the same way). The PHP surface was missed.Root cause
SanitizeForQuotedLiteralinStringExtensions.csis language-agnostic and has no$case, so$falls through unescaped. The 2024 change that switched PHP enum values from single- to double-quotes (d472aeb2e) widened the exposure.Fix
Add a PHP-specific
SanitizePhpDoubleQuoteLiteralonPhpConventionServicethat runs the sharedSanitizeDoubleQuote()and then escapes$→\$, and route every PHP double-quoted emission site through it. This mirrors the existingSanitizeDartDoubleQuoteLiteralonDartConventionServiceexactly (same name pattern, signature, placement, and implementation), satisfying the project's cross-language consistency rule.