Skip to content

Fix RCE attribute injection in generated Refit clients (GHSA-3fhm-p725-h3g3, GHSA-58x9-vjvp-6mx8, GHSA-p32v-8v8j-j534)#1178

Open
christianhelle wants to merge 18 commits into
mainfrom
fix-ghsa-3fhm-p725-h3g3-rce-attribute-injection
Open

Fix RCE attribute injection in generated Refit clients (GHSA-3fhm-p725-h3g3, GHSA-58x9-vjvp-6mx8, GHSA-p32v-8v8j-j534)#1178
christianhelle wants to merge 18 commits into
mainfrom
fix-ghsa-3fhm-p725-h3g3-rce-attribute-injection

Conversation

@christianhelle

@christianhelle christianhelle commented Jun 30, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes three related critical security advisories (CVSS 9.3, CWE-94) in which Refitter emitted attacker-influenceable OpenAPI strings verbatim into Refit attribute string literals. A crafted single-line value closes the attribute string, the interface, and the namespace, injects a top-level file class with a [ModuleInitializer] method, then reopens namespace/interface so the file still compiles — yielding remote code execution at assembly load in any application that compiles the generated client. No --skip-validation is required.

Advisory Injection source Sink
GHSA-3fhm-p725-h3g3 OpenAPI path key [Get("…")] (and other verbs)
GHSA-58x9-vjvp-6mx8 header parameter name (parameters[].name, in: header) [Header("…")]
GHSA-p32v-8v8j-j534 content-type key (requestBody/responses content map key) [Headers("Accept: …", "Content-Type: …")]

Changes

  • Escape all spec-derived attribute strings via ParameterShared.EscapeString (escapes ", \, and control characters). This is always-on, even with --skip-validation, and is the primary defense:
    • path → InterfaceGenerator
    • header parameter & security-scheme names → HeaderParameterExtractor
    • Accept / Content-Type media types → MethodAttributeGenerator
  • Reject breakout characters during validation (AttributeStringValidator, unless --skip-validation) for:
    • OpenAPI path keys
    • header parameter names and header API-key security-scheme names
    • content-type keys (OpenAPI 3.0+, matching where the headers are emitted)
  • Regression tests proving injected payloads stay inert and compile, and that validation rejects them — across the path, header, security-scheme, and content-type sinks (OpenAPI 3.x and Swagger 2.0).

Verification

  • Full solution build is green (0 warnings/errors).
  • Unit and source-generator tests pass on net8.0 and net10.0; the security suite includes a C# compile check confirming the escaped output is inert (no module-initializer execution).
  • Malicious specs are rejected by default; --skip-validation still produces safe, escaped code.

Supersedes #1175 (which documented only GHSA-3fhm-p725-h3g3).

Summary by CodeRabbit

  • Bug Fixes
    • Improved escaping for paths, header names, content types, and API-key header values in generated Refit attributes to prevent invalid or unsafe C# literals.
    • Added stricter validation that flags OpenAPI documents containing quote, backslash, or control characters that could break attribute strings (with guidance to skip validation when needed).
  • Tests
    • Added new regression and injection-focused test suites covering both OpenAPI v2 and v3 to confirm generated code remains inert, compiles, and is rejected when unsafe.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5b12ff09-3dd0-4f9c-942b-925a0e5301f7

📥 Commits

Reviewing files that changed from the base of the PR and between 83f3ad2 and 7e7d7bb.

📒 Files selected for processing (2)
  • src/Refitter.Core/Validation/AttributeStringValidator.cs
  • src/Refitter.Tests/Scenarios/SecurityAttributeInjectionTests.cs
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/Refitter.Core/Validation/AttributeStringValidator.cs
  • src/Refitter.Tests/Scenarios/SecurityAttributeInjectionTests.cs

📝 Walkthrough

Walkthrough

Generators now escape OpenAPI path, header-name, and content-type values before emitting Refit attributes. A new validator flags unsafe characters in those inputs and is called from OpenApiValidator.Validate. New unit and scenario tests cover validation and generated-code injection cases.

Changes

Attribute String Injection Hardening

Layer / File(s) Summary
Escape strings in attribute generators
src/Refitter.Core/Generation/InterfaceGenerator.cs, src/Refitter.Core/Generation/MethodAttributeGenerator.cs, src/Refitter.Core/ParameterExtraction/HeaderParameterExtractor.cs
ParameterShared.EscapeString is applied to op.Path, content-type strings, p.Name, and securityScheme.Name before they are interpolated into generated C# attribute literals.
AttributeStringValidator and OpenApiValidator wiring
src/Refitter.Core/Validation/AttributeStringValidator.cs, src/Refitter.Core/Validation/OpenApiValidator.cs
New AttributeStringValidator adds ContainsUnsafeCharacters and Validate, which checks API-key header security scheme names, operation header parameter names, and OpenAPI 3 content-type keys. OpenApiValidator.Validate now calls AttributeStringValidator.Validate after document parsing.
Unit tests for AttributeStringValidator
src/Refitter.Tests/OpenApi/AttributeStringValidatorTests.cs
Tests cover ContainsUnsafeCharacters edge cases, null-document handling, security-scheme validation, header parameter and path pointer errors, and content-type key validation differentiated by OpenAPI 2 vs 3.
End-to-end injection scenario tests
src/Refitter.Tests/Scenarios/SecurityAttributeInjectionTests.cs
Regression tests for path, header name, security-scheme header, and content-type injection vectors in both v2 and v3: verifies generated code is inert and compiles, and that OpenApiValidator marks malicious specs as invalid.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • christianhelle/refitter#663: Sanitizes generated variable names for API-key header parameters — directly adjacent to the header-name escaping in HeaderParameterExtractor changed here.

Suggested labels

bug

Poem

🐇 I sniffed a quote in the OpenAPI air,
But EscapeString hopped in with proper care.
Paths, headers, and types now stay snug and tight,
While validator ears twitch through the night.
No breakout tricks, no attribute fuss —
Just safe little strings for all of us.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main security fix: preventing attribute injection in generated Refit clients.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-ghsa-3fhm-p725-h3g3-rce-attribute-injection

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (2)
src/Refitter.Tests/Scenarios/SecurityAttributeInjectionTests.cs (1)

381-408: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer explicit types over var per repo convention.

swaggerFile, result, settings, generator, and directory use var; the rest of the new test suite (AttributeStringValidatorTests) uses explicit types. Aligning here keeps style consistent.

As per coding guidelines: "Discourage use of var keyword in C# (csharp_style_var_* = false:silent per .editorconfig)".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Refitter.Tests/Scenarios/SecurityAttributeInjectionTests.cs` around lines
381 - 408, The test helper in SecurityAttributeInjectionTests should follow the
repo’s explicit-type convention instead of using inferred locals. Update
GenerateCode and CleanUp to declare the local values with their concrete types,
matching the style used in AttributeStringValidatorTests and the
csharp_style_var_* settings, and keep the existing helper method names
(GenerateCode, CleanUp) unchanged so the rest of the test suite remains
consistent.

Source: Coding guidelines

src/Refitter.Core/Validation/AttributeStringValidator.cs (1)

18-82: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Split Validate into per-sink passes.

Sonar is already flagging this method at 35 cognitive complexity. Extracting separate validators for security schemes, paths, header parameters, and content types would make future sink additions much easier to audit.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Refitter.Core/Validation/AttributeStringValidator.cs` around lines 18 -
82, The Validate method in AttributeStringValidator is doing too many sink
checks in one pass, which is driving cognitive complexity. Split it into focused
helpers such as one for security schemes, one for path validation, one for
header parameter validation, and one for content type validation, then have
Validate orchestrate those passes while preserving the existing early returns
and the validateContentTypes gating. Use the existing Validate and
ValidateContentTypeKeys symbols as the entry points to refactor around.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/Refitter.Core/Validation/AttributeStringValidator.cs`:
- Around line 18-82: The Validate method in AttributeStringValidator is doing
too many sink checks in one pass, which is driving cognitive complexity. Split
it into focused helpers such as one for security schemes, one for path
validation, one for header parameter validation, and one for content type
validation, then have Validate orchestrate those passes while preserving the
existing early returns and the validateContentTypes gating. Use the existing
Validate and ValidateContentTypeKeys symbols as the entry points to refactor
around.

In `@src/Refitter.Tests/Scenarios/SecurityAttributeInjectionTests.cs`:
- Around line 381-408: The test helper in SecurityAttributeInjectionTests should
follow the repo’s explicit-type convention instead of using inferred locals.
Update GenerateCode and CleanUp to declare the local values with their concrete
types, matching the style used in AttributeStringValidatorTests and the
csharp_style_var_* settings, and keep the existing helper method names
(GenerateCode, CleanUp) unchanged so the rest of the test suite remains
consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f660d4c9-686d-4a72-bc10-ac589ac22586

📥 Commits

Reviewing files that changed from the base of the PR and between 884cb69 and 83f3ad2.

⛔ Files ignored due to path filters (3)
  • src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/SwaggerPetstoreMultipleInterfaces.g.cs is excluded by !**/generated/**
  • src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/SwaggerPetstoreSingleInterfaceWithHttpResilience.g.cs is excluded by !**/generated/**
  • src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/SwaggerPetstoreSingleInterfaceWithPolly.g.cs is excluded by !**/generated/**
📒 Files selected for processing (7)
  • src/Refitter.Core/Generation/InterfaceGenerator.cs
  • src/Refitter.Core/Generation/MethodAttributeGenerator.cs
  • src/Refitter.Core/ParameterExtraction/HeaderParameterExtractor.cs
  • src/Refitter.Core/Validation/AttributeStringValidator.cs
  • src/Refitter.Core/Validation/OpenApiValidator.cs
  • src/Refitter.Tests/OpenApi/AttributeStringValidatorTests.cs
  • src/Refitter.Tests/Scenarios/SecurityAttributeInjectionTests.cs

@sonarqubecloud

Copy link
Copy Markdown

@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.44262% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.79%. Comparing base (fe42089) to head (7e7d7bb).
⚠️ Report is 7 commits behind head on main.

Files with missing lines Patch % Lines
...fitter.Core/Validation/AttributeStringValidator.cs 92.59% 1 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1178      +/-   ##
==========================================
- Coverage   94.82%   94.79%   -0.04%     
==========================================
  Files          80       81       +1     
  Lines        3557     3612      +55     
==========================================
+ Hits         3373     3424      +51     
- Misses         75       76       +1     
- Partials      109      112       +3     
Flag Coverage Δ
unittests 94.79% <93.44%> (-0.04%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@christianhelle christianhelle self-assigned this Jun 30, 2026
@christianhelle christianhelle added enhancement New feature, bug fix, or request .NET Pull requests that contain changes to .NET code bugfix Pull requests that contain bug fixes labels Jun 30, 2026
@christianhelle
christianhelle requested a review from Copilot July 5, 2026 09:15

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 Refitter’s generated Refit clients against attribute-string literal injection leading to potential RCE by escaping spec-derived strings at generation time and adding validation that rejects unsafe values by default.

Changes:

  • Escape OpenAPI-derived values before emitting them into Refit attribute string literals (paths, header names, Accept/Content-Type).
  • Add AttributeStringValidator and wire it into OpenApiValidator to reject paths/header names/content-type keys containing breakout characters (unless validation is skipped).
  • Add regression/unit tests covering injection attempts and validator behavior across OpenAPI v2/v3.

Reviewed changes

Copilot reviewed 7 out of 10 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/Refitter.Tests/Scenarios/SecurityAttributeInjectionTests.cs New regression suite for injection payloads across path/header/security-scheme/content-type sinks.
src/Refitter.Tests/OpenApi/AttributeStringValidatorTests.cs Unit tests for the new validator’s unsafe-character detection and document traversal behavior.
src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/SwaggerPetstoreSingleInterfaceWithPolly.g.cs Updated generated fixture output (namespace-level using placement).
src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/SwaggerPetstoreSingleInterfaceWithHttpResilience.g.cs Updated generated fixture output (namespace-level using placement / formatting).
src/Refitter.SourceGenerator.Tests/AdditionalFiles/Generated/SwaggerPetstoreMultipleInterfaces.g.cs Updated generated fixture output (namespace-level using placement / formatting).
src/Refitter.Core/Validation/OpenApiValidator.cs Invokes AttributeStringValidator as part of OpenAPI validation.
src/Refitter.Core/Validation/AttributeStringValidator.cs New validator rejecting unsafe characters in paths/header names/content-type keys to prevent attribute literal breakouts.
src/Refitter.Core/ParameterExtraction/HeaderParameterExtractor.cs Escapes header names (including security-scheme-derived headers) before emitting [Header("...")].
src/Refitter.Core/Generation/MethodAttributeGenerator.cs Escapes media types used in generated Accept / Content-Type header attributes.
src/Refitter.Core/Generation/InterfaceGenerator.cs Escapes operation paths before emitting [Get/Post/...("...")].

Comment on lines +16 to +17
private const string MaliciousPath =
@"/p"")] Task<string> Dummy(); } } file class Evil { [System.Runtime.CompilerServices.ModuleInitializer] internal static void Init(){ } } namespace N2 { public partial interface I2 { [Get(""/q";
Comment on lines +19 to +20
private const string MaliciousHeaderName =
@"X"")] string a); } file class H {} public partial interface J { [Header(""Y";
Comment on lines +22 to +23
private const string MaliciousContentType =
@"application/json"")] Task<string> Dummy(); } } file class CtEvil { [System.Runtime.CompilerServices.ModuleInitializer] internal static void Init(){ } } namespace N3 { public partial interface I3 { [Get(""/ctq";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix Pull requests that contain bug fixes enhancement New feature, bug fix, or request .NET Pull requests that contain changes to .NET code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants