-
-
Notifications
You must be signed in to change notification settings - Fork 63
Fix RCE via unescaped OpenAPI path/header in Refit attributes (GHSA-3fhm-p725-h3g3) #1175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
christianhelle
wants to merge
16
commits into
main
from
fix-ghsa-3fhm-p725-h3g3-rce-attribute-injection
Closed
Changes from all commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
793d263
fix: escape OpenAPI path in Refit verb attribute
christianhelle 63a19b0
fix: escape header parameter and security scheme names
christianhelle ab4d8e4
fix: escape spec-derived media types in Headers attribute
christianhelle f2ed3dc
feat: reject paths/headers with breakout chars in validation
christianhelle ced3426
test: add regression tests for attribute injection (GHSA-3fhm-p725-h3g3)
christianhelle 465350c
fix: apply CodeRabbit auto-fixes
coderabbitai[bot] a06db08
fix: apply CodeRabbit auto-fixes
coderabbitai[bot] 673bc99
fix: restore OpenAPI namespace import
Copilot 5952ac8
test: align security scheme injection checks
Copilot 7a7f7d1
chore: finalize CI fix verification
Copilot 83d4d69
test: attribute string validator
christianhelle ecf4760
feat: reject unsafe content-type keys in validation (GHSA-p32v-8v8j-j…
christianhelle 150869d
test: add content-type injection regression tests (GHSA-p32v-8v8j-j534)
christianhelle ec99260
test: cover content-type key validation for OpenAPI 3 and 2
christianhelle 854b098
docs: reference all three injection advisories in validator
christianhelle 83f3ad2
style: sort System import first in validator tests
christianhelle File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
118 changes: 118 additions & 0 deletions
118
src/Refitter.Core/Validation/AttributeStringValidator.cs
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,118 @@ | ||
| using System.Linq; | ||
| using Microsoft.OpenApi; | ||
| using Microsoft.OpenApi.Reader; | ||
|
|
||
| namespace Refitter.Core.Validation; | ||
|
|
||
| /// <summary> | ||
| /// Rejects OpenAPI paths, header names, and content-type keys containing characters that could break | ||
| /// out of the generated Refit attribute string literals (e.g. quotes, backslashes, newlines). Generated | ||
| /// code escapes these characters, but rejecting them gives a clear error unless validation is skipped. | ||
| /// See GHSA-3fhm-p725-h3g3 (path), GHSA-58x9-vjvp-6mx8 (header name), GHSA-p32v-8v8j-j534 (content-type). | ||
| /// </summary> | ||
| internal static class AttributeStringValidator | ||
| { | ||
| internal static bool ContainsUnsafeCharacters(string? value) => | ||
| value != null && value.Any(c => c is '"' or '\\' || char.IsControl(c)); | ||
|
|
||
| internal static void Validate(OpenApiDocument? document, OpenApiDiagnostic diagnostic) | ||
|
Check failure on line 18 in src/Refitter.Core/Validation/AttributeStringValidator.cs
|
||
| { | ||
| if (document == null) | ||
| return; | ||
|
|
||
| // Validate security scheme names for API-key schemes with header location | ||
| if (document.Components?.SecuritySchemes != null) | ||
| { | ||
| foreach (var securityScheme in document.Components.SecuritySchemes) | ||
| { | ||
| if (securityScheme.Value?.Type == SecuritySchemeType.ApiKey | ||
| && securityScheme.Value.In == ParameterLocation.Header | ||
| && ContainsUnsafeCharacters(securityScheme.Value.Name)) | ||
| { | ||
| diagnostic.Errors.Add(new OpenApiError( | ||
| securityScheme.Key, | ||
| $"Security scheme '{securityScheme.Key}' has header name '{securityScheme.Value.Name}' containing illegal characters and is rejected to prevent code injection into Refit attributes. Use --skip-validation to bypass.")); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (document.Paths == null) | ||
| return; | ||
|
|
||
| // Accept/Content-Type headers are only emitted from content map keys for OpenAPI 3.0+, | ||
| // so only reject unsafe content-type keys for those documents to avoid Swagger 2.0 false positives. | ||
| var validateContentTypes = diagnostic.SpecificationVersion != OpenApiSpecVersion.OpenApi2_0; | ||
|
|
||
| foreach (var path in document.Paths) | ||
| { | ||
| if (ContainsUnsafeCharacters(path.Key)) | ||
| { | ||
| diagnostic.Errors.Add(new OpenApiError( | ||
| path.Key, | ||
| $"Path '{path.Key}' contains illegal characters (quotes, backslashes, or control characters) and is rejected to prevent code injection into Refit attributes. Use --skip-validation to bypass.")); | ||
| } | ||
|
|
||
| if (path.Value?.Operations == null) | ||
| continue; | ||
|
|
||
| foreach (var operation in path.Value.Operations.Values) | ||
| { | ||
| if (operation == null) | ||
| continue; | ||
|
|
||
| if (operation.Parameters != null) | ||
| { | ||
| foreach (var parameter in operation.Parameters) | ||
| { | ||
| if (parameter.In == ParameterLocation.Header && ContainsUnsafeCharacters(parameter.Name)) | ||
| { | ||
| diagnostic.Errors.Add(new OpenApiError( | ||
| parameter.Name ?? string.Empty, | ||
| $"Header parameter name '{parameter.Name}' contains illegal characters and is rejected to prevent code injection into Refit attributes. Use --skip-validation to bypass.")); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if (validateContentTypes) | ||
| { | ||
| ValidateContentTypeKeys(operation, diagnostic); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private static void ValidateContentTypeKeys(OpenApiOperation operation, OpenApiDiagnostic diagnostic) | ||
| { | ||
| if (operation.RequestBody?.Content != null) | ||
| { | ||
| foreach (var contentType in operation.RequestBody.Content.Keys) | ||
| { | ||
| AddContentTypeErrorIfUnsafe(contentType, diagnostic); | ||
| } | ||
| } | ||
|
|
||
| if (operation.Responses == null) | ||
| return; | ||
|
|
||
| foreach (var response in operation.Responses.Values) | ||
| { | ||
| if (response?.Content == null) | ||
| continue; | ||
|
|
||
| foreach (var contentType in response.Content.Keys) | ||
| { | ||
| AddContentTypeErrorIfUnsafe(contentType, diagnostic); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private static void AddContentTypeErrorIfUnsafe(string contentType, OpenApiDiagnostic diagnostic) | ||
| { | ||
| if (ContainsUnsafeCharacters(contentType)) | ||
| { | ||
| diagnostic.Errors.Add(new OpenApiError( | ||
| contentType, | ||
| $"Content type '{contentType}' contains illegal characters and is rejected to prevent code injection into Refit attributes. Use --skip-validation to bypass.")); | ||
| } | ||
| } | ||
| } | ||
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
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
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
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
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.