Skip to content

fix: gate TypeNameHandling.Auto with a resolver-scoped SerializationBinder [AIS-255] - #399

Merged
Tyler Pina (tylerpina) merged 5 commits into
masterfrom
fix/insecure-deserialization-typename-handling
Jul 22, 2026
Merged

fix: gate TypeNameHandling.Auto with a resolver-scoped SerializationBinder [AIS-255]#399
Tyler Pina (tylerpina) merged 5 commits into
masterfrom
fix/insecure-deserialization-typename-handling

Conversation

@tylerpina

@tylerpina Tyler Pina (tylerpina) commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Hardens the delivery/preview client against Newtonsoft.Json gadget-chain deserialization (CWE-502) while preserving IContentTypeResolver support.

IContentTypeResolver requires TypeNameHandling.Auto so the serializer can instantiate developer-registered concrete CLR types from a $type marker. The problem: Auto on the global SerializerSettings honors $type on every object/dynamic/interface-typed member — including ones that never pass through ContentJsonConverter (e.g. ContentfulCollection.IncludedEntries, which is Entry<dynamic>). Without a binder, any $type in a raw API response could load an arbitrary CLR type.

This PR closes that vector across four layers:

  1. ContentTypeResolverBinder (ISerializationBinder) — the actual guard. It binds a $type value only to a type the client explicitly allowed. ResolveContentTypes() whitelists each resolved type immediately before writing its $type, so only developer-configured resolver mappings are ever bindable. Every other $type (including attacker-injected ones) is rejected with a JsonSerializationException.
  2. ContentfulClient constructor — sets TypeNameHandling.Auto (needed for the resolver) and installs the binder on SerializerSettings.
  3. ResolveLinks — previously called Type.GetType directly on the entry's $type. Not a gadget sink (the result is only used for reflection, not instantiation), but it still resolved an arbitrary type from an attacker-influenceable string. Now routed through the same whitelist (TryResolveAllowed, a non-throwing lookup); an injected $type is ignored and resolution falls back to the caller-provided type.
  4. ContentJsonConverter.ReadJson — the $type branch is additionally constrained to IContent implementors as defense-in-depth on the rich-text node path.

Why Auto + binder rather than None

TypeNameHandling.None fully closes the vector but breaks IContentTypeResolver (it can no longer instantiate concrete types from the injected $type). Auto + a whitelist binder keeps the resolver working while constraining $type resolution to exactly the set of types the developer registered — no attacker-controlled type is loadable.

Tickets

Closes AIS-255

Test plan

  • Full Contentful.Core suite passes (677 tests) — no regressions
  • Contentful.AspNetCore suite passes (17 tests)
  • Negative: InjectedTypeInApiResponseShouldBeRejectedBySerializationBinder — an injected, genuinely-loadable gadget type is rejected and never constructed. Verified this test fails with the binder disabled (proves it exercises the guard, not an assembly-not-found accident). The injected $type sits on items[0], so it also exercises the hardened ResolveLinks path.
  • Positive: ResolverProducedTypesShouldStillDeserializeUnderSerializationBinder — resolver-produced types still deserialize correctly under Auto + binder.
  • Existing UsingAContentTypeResolver* (incl. with-includes, which relies on ResolveLinks honoring $type) and rich-text rendering tests unaffected.

🤖 Generated with Claude Code

Summary by Bito

This PR hardens the Contentful delivery/preview client against Newtonsoft.Json gadget-chain deserialization (CWE-502) by introducing a whitelist-based serialization binder while preserving IContentTypeResolver functionality. The binder constrains TypeNameHandling.Auto so only developer-registered types can be deserialized, preventing arbitrary CLR type loading from injected $type values in API responses.

Detailed Changes
  • Introduces ContentTypeResolverBinder (ISerializationBinder) that only allows $type resolution to types explicitly whitelisted via IContentTypeResolver, blocking gadget-chain attacks on object/dynamic members like ContentfulCollection.IncludedEntries.
  • Updates ContentfulClient constructor to install the binder on SerializerSettings.SerializationBinder and sets TypeNameHandling.Auto, while ResolveContentTypes() calls binder.Allow() before writing each $type property.
  • Hardens ResolveLinks by replacing unsafe Type.GetType with TryResolveAllowed() whitelist check, preventing arbitrary type loading from attacker-influenceable $type strings in API responses.
  • Adds defense-in-depth in ContentJsonConverter.ReadJson constraining the $type branch to IContent implementors, preventing arbitrary type loading through rich-text node deserialization.
  • Adds MaliciousGadget test class and two security tests validating that injected loadable types are rejected by the binder while resolver-produced types deserialize correctly.

…tType in ContentJsonConverter [AIS-255]

TypeNameHandling.All causes Newtonsoft.Json to honor any $type discriminator
present in deserialized JSON, enabling RCE via gadget chains in untrusted input.
Switched to TypeNameHandling.None and removed the ContentJsonConverter path that
called Type.GetType on a $type value from the JSON object — all IContent node
types are handled by the existing safe switch statement instead.
The ResolveContentTypes/$type round-trip in ContentfulClient is unaffected: it
only writes developer-configured CLR types, not attacker-controlled values.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@tylerpina
Tyler Pina (tylerpina) requested a review from a team as a code owner July 21, 2026 13:20
@bito-code-review

bito-code-review Bot commented Jul 21, 2026

Copy link
Copy Markdown

Code Review Agent Run #0a94d9

Actionable Suggestions - 0
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • Contentful.Core/Configuration/ContentJsonConverter.cs - 1
Review Details
  • Files reviewed - 2 · Commit Range: 1c94a18..1c94a18
    • Contentful.Core/Configuration/ContentJsonConverter.cs
    • Contentful.Core/ContentfulClient.cs
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Csharp (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Default Agent You can customize the agent settings here or contact your Bito workspace admin at jared.jolton@contentful.com.

Documentation & Help

AI Code Review powered by Bito Logo

@bito-code-review

bito-code-review Bot commented Jul 21, 2026

Copy link
Copy Markdown

Changelist by Bito

This pull request implements the following key changes.

Key Change Files Impacted Summary
Bug Fix - Serialization Binder Security Implementation
Introduces ContentTypeResolverBinder (ISerializationBinder) that whitelists only types produced by IContentTypeResolver, hardening against CWE-502 gadget-chain deserialization attacks.
Bug Fix - ResolveLinks Type Resolution Hardening
Replaces unsafe Type.GetType call with TryResolveAllowed whitelist check in ResolveLinks, preventing arbitrary type resolution from attacker-influenceable $type values.
Bug Fix - ContentJsonConverter Defense-in-Depth
Adds IContent interface constraint to $type branch in ReadJson, preventing arbitrary type loading through the rich-text node deserialization path.
Testing - Serialization Security Tests
Adds MaliciousGadget test class and two security tests: InjectedTypeInApiResponseShouldBeRejectedBySerializationBinder (negative) and ResolverProducedTypesShouldStillDeserializeUnderSerializationBinder (positive).

@bito-code-review

bito-code-review Bot commented Jul 21, 2026

Copy link
Copy Markdown

Functional Validation by Bito

SourceRequirement / Code AreaStatusNotes
AIS-255Fix insecure deserialization vulnerability in ContentfulClient by replacing TypeNameHandling.All with safe type resolution that prevents arbitrary type loading from attacker-influenced $type discriminators✅ MetThe insecure deserialization vulnerability in ContentfulClient has been fixed. The PR replaced `TypeNameHandling.All` (line 269) with `TypeNameHandling.Auto` combined with a custom ContentTypeResolverBinder (line 277). The ResolveLinks method now uses `_typeResolverBinder.TryResolveAllowed()` instead of direct `Type.GetType()` call on untrusted `$type` fields (lines 301-304). Types are explicitly whitelisted via `_typeResolverBinder.Allow(type)` in ResolveContentTypes (line 287) before `$type` is written, ensuring only developer-configured resolver types can be bound. This prevents arbitrary type loading from attacker-influenced `$type` discriminators (CWE-502).
AIS-255Fix insecure deserialization in ContentJsonConverter by implementing safe type resolution for the $type discriminator instead of direct Type.GetType call✅ MetThe insecure deserialization vulnerability in ContentJsonConverter has been fixed. In ReadJson (lines 131-134), the code now checks `typeof(IContent).IsAssignableFrom(typeinfo)` before calling `jObject.ToObject(typeinfo, serializer)`. This ensures that only types implementing the `IContent` interface can be loaded via the `$type` discriminator, preventing arbitrary CLR type loading from attacker-influenced JSON. Combined with the SerializationBinder in ContentfulClient, this provides defense-in-depth against CWE-502 gadget-chain deserialization attacks.

@bito-code-review

bito-code-review Bot commented Jul 21, 2026

Copy link
Copy Markdown

Impact Analysis by Bito

Interaction Diagram
sequenceDiagram
participant Dev as Developer
participant Client as ContentfulClient<br/>🔄 Updated | ●●● High
participant Binder as ContentTypeResolverBinder<br/>🟩 Added | ●●● High
participant Conv as ContentJsonConverter<br/>🔄 Updated | ●●○ Medium
participant API as Contentful API
participant Test as Test Suite
participant Gadget as MaliciousGadget<br/>🟩 Added | ●○○ Low

Note over Client, Binder: Security fix for CWE-502 deserialization vulnerability

Dev->>Client: Configure IContentTypeResolver
Client->>Binder: Initialize SerializationBinder
Binder-->>Client: Binder configured

alt Positive Path: Valid Type Resolution
Client->>API: GetEntries<T>() request
API-->>Client: Return JSON with entries
Client->>Client: ResolveContentTypes()
Client->>Binder: Allow(developer-registered type)
Binder->>Binder: Add type to allowlist
Client->>Conv: Deserialize with $type
Conv->>Conv: Verify type implements IContent
Conv-->>Client: Return deserialized object
    end

alt Negative Path: Attack Prevention
Test->>Client: Inject malicious $type in response
Client->>Binder: TryResolveAllowed(maliciousType)
Binder->>Binder: Check allowlist (not found)
Binder-->>Client: Return false
Client->>Client: Fall back to caller-provided type
Conv->>Conv: Deserialize without $type
Conv-->>Test: JsonSerializationException thrown
Test->>Gadget: Verify WasConstructed = false
    end
Loading

This MR implements a security fix for CWE-502 (deserialization of untrusted data) by introducing ContentTypeResolverBinder, an ISerializationBinder that restricts $type resolution to types explicitly allowed by the developer-configured IContentTypeResolver. The ContentfulClient now uses TypeNameHandling.Auto with the custom binder instead of TypeNameHandling.All, preventing arbitrary gadget-chain attacks while maintaining legitimate type resolution functionality. ContentJsonConverter adds an IContent interface check for additional safety. No cross-repo dependencies detected.

Cross-Repository Impact Analysis
What Changed Impact of Change Suggested Review Actions
Security fix for CWE-502: Changed TypeNameHandling from All to Auto and added ContentTypeResolverBinder to restrict $type deserialization to IContent-implementing types only - No cross-repo consumers found for ContentTypeResolverBinder: This is a new security component added to the Contentful.NET SDK. Search across all indexed repositories found zero usages of ContentTypeResolverBinder, TypeNameHandling, IContentTypeResolver, or ContentfulClient. - Verify that existing consumers of Contentful.NET SDK using custom IContentTypeResolver implementations still function correctly
- Test that types implementing IContent interface are properly whitelisted and deserialized
- Confirm that non-IContent types in API responses are correctly rejected with JsonSerializationException
- Review any downstream packages that may depend on the previous TypeNameHandling.All behavior
Code Paths Analyzed

Impact:
Security hardening against CWE-502 (Deserialization of Untrusted Data). The change restricts arbitrary type loading during JSON deserialization by validating that only IContent-implementing types from registered IContentTypeResolver mappings can be instantiated.

Flow:
API Response → ContentfulClient.ResolveContentTypes() → ContentTypeResolverBinder.Allow() → JsonSerializer with SerializationBinder → ContentJsonConverter.ReadJson() → IContent type validation → Safe deserialization

Direct Changes (Diff Files):
• Contentful.Core/Configuration/ContentTypeResolverBinder.cs [1-251] — New ISerializationBinder implementation that maintains an allow-list of types safe for deserialization
• Contentful.Core/ContentfulClient.cs [27, 54-61, 285-288, 301-305] — Integrated ContentTypeResolverBinder, changed TypeNameHandling.All to Auto, added type whitelisting in ResolveContentTypes
• Contentful.Core/Configuration/ContentJsonConverter.cs [55-65] — Added IContent interface constraint before deserializing $type values
• Contentful.Core.Tests/ClientTestsBase.cs [10-16, 24-41] — Added test helper and MaliciousGadget class for security testing
• Contentful.Core.Tests/ContentfulClientTests.cs [66-112] — Added security test cases for positive and negative deserialization paths

Repository Impact:
Contentful.Core deserialization pipeline: Core deserialization logic now validates types against IContent interface and allow-list
ContentfulClient initialization: SerializerSettings now use TypeNameHandling.Auto with custom SerializationBinder
Contentful.AspNetCore (if present): Any ASP.NET Core integration using ContentfulClient will inherit the new security behavior

Cross-Repository Dependencies:
No high-confidence cross-repository impacts detected: Search across all indexed repositories found no consumers of Contentful.NET SDK types. This appears to be a standalone SDK package.

Database/Caching Impact:
• None

API Contract Violations:
• None detected. The change maintains backward compatibility for legitimate IContentTypeResolver usage while blocking potentially malicious $type values.

Infrastructure Dependencies:
• No infrastructure changes required. Security fix is purely code-level.

Additional Insights:
Security posture: Significantly reduces attack surface for deserialization vulnerabilities (CWE-502) by implementing defense-in-depth: SerializationBinder + type interface constraints

Testing Recommendations

Frontend Impact:
• No frontend impact detected - this is a backend SDK security fix

Service Integration:
• Test end-to-end content retrieval with custom IContentTypeResolver implementations
• Verify that existing Contentful space queries return correct typed objects

Data Serialization:
• Validate that $type values from legitimate IContentTypeResolver are correctly deserialized
• Confirm that injected $type values targeting non-IContent types are rejected with JsonSerializationException
• Test edge cases: null $type, missing $type, malformed $type strings

Privacy Compliance:
• No PII handling changes detected

Backward Compatibility:
• Test with existing codebases using Contentful.NET SDK to ensure IContentTypeResolver mappings still work
• Verify that types implementing IContent interface deserialize correctly
• Check for any reliance on TypeNameHandling.All behavior in consumer code

OAuth Functionality:
• None

Cross-Service Communication:
• No cross-service communication changes

Reliability Testing:
• None

Additional Insights:
• Run security-focused penetration testing against the deserialization pipeline
• Update documentation to reflect the new security guarantees
• Consider adding a changelog entry highlighting the CWE-502 fix

Analysis based on known dependency patterns and edges. Actual impact may vary.

@bito-code-review

Copy link
Copy Markdown

✅ Review Settings Overridden

Status: Overridden Successfully

Guidelines:

  • Accepted:

    • General : Review Posture, Repo Truth And Alignment, Domain Invariants

Note: Extra guidelines beyond 3 general purpose guidelines and 1 language specific guideline per language are not processed. Guidelines are fetched from the source branch.

bito-code-review[bot]
bito-code-review Bot previously approved these changes Jul 21, 2026
…ut enabling gadget chains [AIS-255]

TypeNameHandling.None broke the ContentTypeResolver mechanism: when
GetEntries<IMarker>() is called, Newtonsoft needs to read the $type
values that ResolveContentTypes() writes (from developer-registered
IContentTypeResolver mappings) in order to instantiate concrete CLR
types for abstract/interface-typed generics.

TypeNameHandling.Auto only activates $type resolution when the
declared target type is abstract or an interface — it never kicks in
for concrete types coming from the Contentful API. The actual
gadget-chain attack surface (Type.GetType on attacker-controlled
$type from raw API JSON in ContentJsonConverter) was already removed
in the prior commit, so Auto does not reopen AIS-255.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@bito-code-review bito-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review Agent Run #35e4ba

Actionable Suggestions - 1
  • Contentful.Core/ContentfulClient.cs - 1
    • CWE-502: Unpatched deserialization vector · Line 53-58
Review Details
  • Files reviewed - 1 · Commit Range: 1c94a18..56ad3c4
    • Contentful.Core/ContentfulClient.cs
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Csharp (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Default Agent You can customize the agent settings here or contact your Bito workspace admin at jared.jolton@contentful.com.

Documentation & Help

AI Code Review powered by Bito Logo

Comment thread Contentful.Core/ContentfulClient.cs Outdated
…mplementors [AIS-255]

TypeNameHandling.Auto restored IContentTypeResolver support but
ContentJsonConverter intercepts IContent deserialization before
TypeNameHandling logic runs. Restore $type reading in the converter,
but constrain it to types that implement IContent — the gadget-chain
types exploited by the original vulnerability (ObjectDataProvider,
etc.) do not implement IContent, so the attack surface remains closed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@bito-code-review

bito-code-review Bot commented Jul 21, 2026

Copy link
Copy Markdown

Code Review Agent Run #b0332b

Actionable Suggestions - 0
Review Details
  • Files reviewed - 1 · Commit Range: 56ad3c4..9b80c92
    • Contentful.Core/Configuration/ContentJsonConverter.cs
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Csharp (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Default Agent You can customize the agent settings here or contact your Bito workspace admin at jared.jolton@contentful.com.

Documentation & Help

AI Code Review powered by Bito Logo

…inder [AIS-255]

TypeNameHandling.Auto is required for IContentTypeResolver to instantiate
concrete CLR types, but on its own it lets any $type in an API response load
an arbitrary type on object/dynamic members that never pass through
ContentJsonConverter (e.g. ContentfulCollection.IncludedEntries) — the CWE-502
gadget-chain vector the earlier commits left open.

Add ContentTypeResolverBinder, an ISerializationBinder that only binds $type
values to types explicitly produced by a developer-configured
IContentTypeResolver. ResolveContentTypes() whitelists each resolved type
before writing its $type; every other $type is rejected with a
JsonSerializationException.

Tests: negative test proves an injected, genuinely-loadable gadget type is
rejected and never constructed (verified to fail with the binder disabled);
positive test confirms resolver-produced types still deserialize under Auto.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bito-code-review

bito-code-review Bot commented Jul 22, 2026

Copy link
Copy Markdown

Code Review Agent Run #8bc1f5

Actionable Suggestions - 0
Review Details
  • Files reviewed - 4 · Commit Range: 9b80c92..5e25086
    • Contentful.Core.Tests/ClientTestsBase.cs
    • Contentful.Core.Tests/ContentfulClientTests.cs
    • Contentful.Core/Configuration/ContentTypeResolverBinder.cs
    • Contentful.Core/ContentfulClient.cs
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Csharp (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Default Agent You can customize the agent settings here or contact your Bito workspace admin at jared.jolton@contentful.com.

Documentation & Help

AI Code Review powered by Bito Logo

…IS-255]

ResolveLinks called Type.GetType directly on the entry's $type value to pick
the CLR type used for nested-property reflection. The result is not
instantiated there, so it is not a gadget sink, but it still triggers
arbitrary type/assembly resolution from an attacker-influenceable string.

Route it through ContentTypeResolverBinder.TryResolveAllowed, a non-throwing
lookup that only returns types previously whitelisted from a developer
IContentTypeResolver mapping. An injected $type is now ignored (Type.GetType
is never called on it) and link resolution falls back to the caller-provided
type — mirroring the SerializationBinder gate used during deserialization.

Behavior for legitimate resolver-produced $type values is unchanged (they are
always whitelisted before being written). Full Core suite green (677).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@bito-code-review

bito-code-review Bot commented Jul 22, 2026

Copy link
Copy Markdown

Code Review Agent Run #aac027

Actionable Suggestions - 0
Review Details
  • Files reviewed - 2 · Commit Range: 5e25086..a805de7
    • Contentful.Core/Configuration/ContentTypeResolverBinder.cs
    • Contentful.Core/ContentfulClient.cs
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Csharp (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers a full AI review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Default Agent You can customize the agent settings here or contact your Bito workspace admin at jared.jolton@contentful.com.

Documentation & Help

AI Code Review powered by Bito Logo

@tylerpina Tyler Pina (tylerpina) changed the title fix: replace TypeNameHandling.All with None, remove unsafe Type.GetType in ContentJsonConverter [AIS-255] fix: gate TypeNameHandling.Auto with a resolver-scoped SerializationBinder [AIS-255] Jul 22, 2026
@tylerpina
Tyler Pina (tylerpina) merged commit 0908893 into master Jul 22, 2026
10 checks passed
@tylerpina
Tyler Pina (tylerpina) deleted the fix/insecure-deserialization-typename-handling branch July 22, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants