The Never Ending - Change this code to not construct the URL from user-controlled data

Sonar = Enterprise Edition v2025.1 (102418)

I received the error that a url that was passed in as a parameter makes the Server-side request vulnerable to forging attacks.

Change this code to not construct the URL from user-controlled data.

Server-side requests should not be vulnerable to forging attacks roslyn.sonaranalyzer.security.cs:S5144 

1 SOURCE a user can craft an HTTP request with malicious content

public async Task<dynamic> ProxySearch([FromBody] ProxyParameters proxyParameters)

2 This invocation can propagate malicious content to its return value

 Uri uri = new Uri(proxyParameters.url);

3 This invocation can propagate malicious content to its return value

4 SINK this invocation is not safe; a malicious value can be used as argument
Navigate locations

 var response = await client.GetAsync(proxyParameters.url);

Here is my updated code that still gets the error.

[Route("api/[controller]")]
[ApiController]
public class ProxyController : ControllerBase
{
     private readonly string[] allowedSchemes = { "https" };
     private readonly string[] allowedDomains = { "trusted1.example.com", "trusted2.example.com" };


    [HttpPost()]
    public async Task<dynamic> ProxySearch([FromBody] ProxyParameters proxyParameters)
    {
        try
        {
            var clientHandler = new HttpClientHandler();            

            Uri uri = new Uri(proxyParameters.url);
            if (!allowedDomains.Contains(uri.Host) && !allowedSchemes.Contains(uri.Scheme))
            {
                return BadRequest();
            }
            
            using (var client = new HttpClient(clientHandler))
            {
                var response = await client.GetAsync(proxyParameters.url);

                response.EnsureSuccessStatusCode();

                return new
                {
                    statusCode = response.StatusCode,
                    content = await response.Content.ReadAsStringAsync()
                };
            }
        }
        catch (Exception e)
        {
            return new
            {
                error = e.Message,
                stack = e.StackTrace,
                innerError = e.InnerException?.Message ?? string.Empty
            };
        }
    }
}

I changed the code to do exactly what the fix example said, which is below.

public class ExampleController: Controller
{
    private readonly string[] allowedSchemes = { "https" };
    private readonly string[] allowedDomains = { "trusted1.example.com", "trusted2.example.com" };

    [HttpGet]
    public async Task<IActionResult> ImageFetch(string location)
    {
        Uri uri = new Uri(location);

        if (!allowedDomains.Contains(uri.Host) && !allowedSchemes.Contains(uri.Scheme))
        {
            return BadRequest();
        }

        await using Stream stream =
            await client.GetStreamAsync(location);
        var exampleImage =
            await JsonSerializer.DeserializeAsync<ExampleImage>(stream);

        return Ok(example ?? new());
    }
}

It is citing the same issue even though I remedied the issue as instructed. What are other options for this. The URL will be incoming and I can validate the scheme and the domain, but not the rest of the URL as they will be dynamic.

Hi,

What language is this?

 
Thx,
Ann

The language is C#

Hi,

Thanks! I’ll flag this for the language experts.

 
Ann

I figured out the issue. The issue is the example given by Sonar is incorrect. The uri variable should be used in the client call, not the location variable. I thought it was strange that it was still using the location variable since it is the thing that could be hacked, but I don’t have a lot of experience with Sonar so I thought maybe it was smart enough to figure it out. After making this change it passed.

await client.GetStreamAsync(uri)

Hi,

I’m glad you worked through this. Hopefully we’ll get this rule description updated quickly with your input!

 
Thx!
Ann