-
Notifications
You must be signed in to change notification settings - Fork 553
Open
Labels
Description
🐛 Bug Report
Description
Resource validation operations are failing with NullReferenceException
in the ParameterCompatibleFilter.ParseResource()
method when processing Parameters resources that don't contain the expected "resource" parameter.
Error Details
System.NullReferenceException: Object reference not set to an instance of an object.
at Microsoft.Health.Fhir.Api.Features.Filters.ParameterCompatibleFilter.ParseResource(Resource resource)
in /_/src/Microsoft.Health.Fhir.Shared.Api/Features/Filters/ParameterCompatibleFilter.cs:line 26
at Microsoft.Health.Fhir.Api.Features.Filters.ValidateResourceTypeFilterAttribute.OnActionExecuting(ActionExecutingContext context)
in /_/src/Microsoft.Health.Fhir.Shared.Api/Features/Filters/ValidateResourceTypeFilterAttribute.cs:line 33
Root Cause Analysis
The issue occurs in ParameterCompatibleFilter.cs
line 26:
protected Resource ParseResource(Resource resource)
{
if (_allowParametersResource && resource.TypeName == KnownResourceTypes.Parameters)
{
resource = ((Parameters)resource).Parameter.Find(param => param.Name.Equals("resource", StringComparison.OrdinalIgnoreCase)).Resource; // LINE 26 - NULL REFERENCE
}
return resource;
}
The issue occurs when:
resource.TypeName == KnownResourceTypes.Parameters
is true((Parameters)resource).Parameter.Find(...)
returns null because:- No parameter with name "resource" exists
- The Parameter collection is null
- The found parameter has a null Resource property
- Accessing
.Resource
on a null parameter causes the NullReferenceException
Reproduction Steps
- Create a Parameters resource with either:
- No parameter named "resource"
- A parameter named "resource" with a null Resource property
- A null Parameter collection
- Send this Parameters resource through the validation pipeline
- The NullReferenceException will be thrown at line 26
Minimal Reproduction Example
var parameters = new Parameters();
parameters.Parameter = new List<Parameters.ParameterComponent>
{
new Parameters.ParameterComponent
{
Name = "resource",
Resource = null // This causes the NullReferenceException
}
};
// This will throw NullReferenceException when processed by ParameterCompatibleFilter
Proposed Fix
Add defensive null checking in the ParseResource
method:
protected Resource ParseResource(Resource resource)
{
if (_allowParametersResource && resource?.TypeName == KnownResourceTypes.Parameters)
{
var parameters = (Parameters)resource;
var resourceParam = parameters.Parameter?.Find(param => param.Name.Equals("resource", StringComparison.OrdinalIgnoreCase));
if (resourceParam?.Resource != null)
{
resource = resourceParam.Resource;
}
// If no resource parameter found or it's null, return the original Parameters resource
// This maintains backward compatibility while preventing the crash
}
return resource;
}
Alternative Solution
Add validation in the calling methods to handle null results from ParseResource()
:
public override void OnActionExecuting(ActionExecutingContext context)
{
// ... existing code ...
if (context.ActionArguments.TryGetValue(KnownActionParameterNames.Resource, out var parsedModel))
{
var resource = ParseResource((Resource)parsedModel);
if (resource == null)
{
throw new ResourceNotValidException("Failed to extract resource from Parameters");
}
ValidateType(resource, (string)actionModelType);
}
}
Test Cases to Add
[Fact]
public void ParseResource_WithNullParameterResource_ShouldNotThrow()
{
var parameters = new Parameters();
parameters.Parameter = new List<Parameters.ParameterComponent>
{
new Parameters.ParameterComponent { Name = "resource", Resource = null }
};
var filter = new TestParameterCompatibleFilter(allowParametersResource: true);
var result = filter.ParseResource(parameters);
Assert.NotNull(result); // Should not throw NullReferenceException
}
[Fact]
public void ParseResource_WithMissingResourceParameter_ShouldNotThrow()
{
var parameters = new Parameters();
parameters.Parameter = new List<Parameters.ParameterComponent>
{
new Parameters.ParameterComponent { Name = "otherParam", Value = new FhirString("test") }
};
var filter = new TestParameterCompatibleFilter(allowParametersResource: true);
var result = filter.ParseResource(parameters);
Assert.NotNull(result); // Should not throw NullReferenceException
}
Impact
- Severity: High - Validation operations fail with HTTP 500 errors
- Affected Operations: Resource validation endpoints that accept Parameters resources
- Client Impact: Any client sending Parameters resources without proper "resource" parameter structure
Backward Compatibility
The proposed fix maintains backward compatibility by:
- Still extracting the inner resource when properly structured Parameters are provided
- Gracefully handling malformed Parameters by returning the original Parameters resource
- Not changing the method signature or expected behavior for valid inputs
This is a defensive programming issue where the code assumes Parameters resources always contain a valid "resource" parameter. The fix should prevent crashes while maintaining existing functionality.
Copilot