dotnet/roslyn-analyzers

CA2100/SQL vulnerability analyzer runaway memory usage with big interpolated strings

andre-ss6 opened this issue · 0 comments

Analyzer

Diagnostic ID: CA2100: Review SQL queries for security vulnerabilities

Analyzer source

SDK: Built-in CA analyzers in .NET 5 SDK or later

Version: SDK 8.0.403

Describe the bug

When I try to build a project with a big interpolated string that is candidate for verification for CA2100, all of my 64GB of RAM are used by VBCSCompiler.exe (when building from VS; same thing happens when building from dotnet though) and the build never finishes (at least I didn't wait to see if it ever would).

Steps To Reproduce

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <AnalysisMode>all</AnalysisMode> <!-- If you comment out this line, build succeeds normally -->
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>
using System.Data;
using System.Data.Common;

namespace BigStringAnalyzerTest
{
    public class Program
    {
        DbConnection CreateConnection() => null;

        public async Task Main(string[] args)
        {
            using DbConnection connection = CreateConnection();
            using DbCommand command = connection.CreateCommand();
            command.Connection = connection;

            var customer = args[0].Length > 0 ? new { Id = args[0].Length } : null;
            command.CommandText = $@"
select 'address_street_type' AS table_name, ast.id, ast.customer_id, ast.description, ast.created_at, ast.created_by_email, ast.last_updated_at, ast.last_updated_by_email from public.address_street_type AS ast {(customer == null ? "" : "WHERE ast.customer_id = @customerId")}
UNION
select 'lot_occupancy_type' AS table_name, lot.id, lot.customer_id, lot.description, lot.created_at, lot.created_by_email, lot.last_updated_at, lot.last_updated_by_email from public.lot_occupancy_type AS lot {(customer == null ? "" : "WHERE lot.customer_id = @customerId")}
UNION
select 'property_purpose' AS table_name, pp.id, pp.customer_id, pp.description, pp.created_at, pp.created_by_email, pp.last_updated_at, pp.last_updated_by_email from public.property_purpose AS pp {(customer == null ? "" : "WHERE pp.customer_id = @customerId")}
UNION
select 'property_propietor_type' AS table_name, ppt.id, ppt.customer_id, ppt.description, ppt.created_at, ppt.created_by_email, ppt.last_updated_at, ppt.last_updated_by_email from public.property_propietor_type AS ppt {(customer == null ? "" : "WHERE ppt.customer_id = @customerId")}
UNION
select 'property_frontage_delimitation_type' AS table_name, pfdt.id, pfdt.customer_id, pfdt.description, pfdt.created_at, pfdt.created_by_email, pfdt.last_updated_at, pfdt.last_updated_by_email from public.property_frontage_delimitation_type AS pfdt {(customer == null ? "" : "WHERE pfdt.customer_id = @customerId")}
UNION
select 'property_regime_type' AS table_name, prt.id, prt.customer_id, prt.description, prt.created_at, prt.created_by_email, prt.last_updated_at, prt.last_updated_by_email from public.property_regime_type AS prt {(customer == null ? "" : "WHERE prt.customer_id = @customerId")}
UNION
select 'terrain_situation_type' AS table_name, tst.id, tst.customer_id, tst.description, tst.created_at, tst.created_by_email, tst.last_updated_at, tst.last_updated_by_email from public.terrain_situation_type AS tst {(customer == null ? "" : "WHERE tst.customer_id = @customerId")}
UNION
select 'terrain_topography_type' AS table_name, ttt.id, ttt.customer_id, ttt.description, ttt.created_at, ttt.created_by_email, ttt.last_updated_at, ttt.last_updated_by_email from public.terrain_topography_type AS ttt {(customer == null ? "" : "WHERE ttt.customer_id = @customerId")}
UNION
select 'terrain_pedology_type' AS table_name, tpt.id, tpt.customer_id, tpt.description, tpt.created_at, tpt.created_by_email, tpt.last_updated_at, tpt.last_updated_by_email from public.terrain_pedology_type AS tpt {(customer == null ? "" : "WHERE tpt.customer_id = @customerId")}
UNION
select 'land_tax_aliquot_calculation_type' AS table_name, ltac.id, ltac.customer_id, ltac.description, ltac.created_at, ltac.created_by_email, ltac.last_updated_at, ltac.last_updated_by_email from public.land_tax_aliquot_calculation_type AS ltac {(customer == null ? "" : "WHERE ltac.customer_id = @customerId")}
UNION
select 'property_type' AS table_name, pt.id, pt.customer_id, pt.description, pt.created_at, pt.created_by_email, pt.last_updated_at, pt.last_updated_by_email from public.property_type AS pt {(customer == null ? "" : "WHERE pt.customer_id = @customerId")}
UNION
select 'property_alignment_type' AS table_name, pat.id, pat.customer_id, pat.description, pat.created_at, pat.created_by_email, pat.last_updated_at, pat.last_updated_by_email from public.property_alignment_type AS pat {(customer == null ? "" : "WHERE pat.customer_id = @customerId")}
UNION
select 'property_situation_type' AS table_name, pst.id, pst.customer_id, pst.description, pst.created_at, pst.created_by_email, pst.last_updated_at, pst.last_updated_by_email from public.property_situation_type AS pst {(customer == null ? "" : "WHERE pst.customer_id = @customerId")}
UNION
select 'property_built_unit_situation_type' AS table_name, pbus.id, pbus.customer_id, pbus.description, pbus.created_at, pbus.created_by_email, pbus.last_updated_at, pbus.last_updated_by_email from public.property_built_unit_situation_type AS pbus {(customer == null ? "" : "WHERE pbus.customer_id = @customerId")}
UNION
select 'property_structural_material_type' AS table_name, psmt.id, psmt.customer_id, psmt.description, psmt.created_at, psmt.created_by_email, psmt.last_updated_at, psmt.last_updated_by_email from public.property_structural_material_type AS psmt {(customer == null ? "" : "WHERE psmt.customer_id = @customerId")}
UNION
select 'property_roofing_material_type' AS table_name, prmt.id, prmt.customer_id, prmt.description, prmt.created_at, prmt.created_by_email, prmt.last_updated_at, prmt.last_updated_by_email from public.property_roofing_material_type AS prmt {(customer == null ? "" : "WHERE prmt.customer_id = @customerId")}
UNION
select 'property_wall_material_type' AS table_name, pwmt.id, pwmt.customer_id, pwmt.description, pwmt.created_at, pwmt.created_by_email, pwmt.last_updated_at, pwmt.last_updated_by_email from public.property_wall_material_type AS pwmt {(customer == null ? "" : "WHERE pwmt.customer_id = @customerId")}
UNION
select 'property_cladding_material_type' AS table_name, pcladmt.id, pcladmt.customer_id, pcladmt.description, pcladmt.created_at, pcladmt.created_by_email, pcladmt.last_updated_at, pcladmt.last_updated_by_email from public.property_cladding_material_type AS pcladmt {(customer == null ? "" : "WHERE pcladmt.customer_id = @customerId")}
UNION
select 'property_facade_material_type' AS table_name, pfacmt.id, pfacmt.customer_id, pfacmt.description, pfacmt.created_at, pfacmt.created_by_email, pfacmt.last_updated_at, pfacmt.last_updated_by_email from public.property_facade_material_type AS pfacmt {(customer == null ? "" : "WHERE pfacmt.customer_id = @customerId")}
UNION
select 'property_flooring_material_type' AS table_name, pflomt.id, pflomt.customer_id, pflomt.description, pflomt.created_at, pflomt.created_by_email, pflomt.last_updated_at, pflomt.last_updated_by_email from public.property_flooring_material_type AS pflomt {(customer == null ? "" : "WHERE pflomt.customer_id = @customerId")}
UNION
select 'property_painting_material_type' AS table_name, ppmt.id, ppmt.customer_id, ppmt.description, ppmt.created_at, ppmt.created_by_email, ppmt.last_updated_at, ppmt.last_updated_by_email from public.property_painting_material_type AS ppmt {(customer == null ? "" : "WHERE ppmt.customer_id = @customerId")}
UNION
select 'property_coating_material_type' AS table_name, pcoamt.id, pcoamt.customer_id, pcoamt.description, pcoamt.created_at, pcoamt.created_by_email, pcoamt.last_updated_at, pcoamt.last_updated_by_email from public.property_coating_material_type AS pcoamt {(customer == null ? "" : "WHERE pcoamt.customer_id = @customerId")}
UNION
select 'property_frame_material_type' AS table_name, pframt.id, pframt.customer_id, pframt.description, pframt.created_at, pframt.created_by_email, pframt.last_updated_at, pframt.last_updated_by_email from public.property_frame_material_type AS pframt {(customer == null ? "" : "WHERE pframt.customer_id = @customerId")}
UNION
select 'property_construction_standard_rating' AS table_name, pcsr.id, pcsr.customer_id, pcsr.description, pcsr.created_at, pcsr.created_by_email, pcsr.last_updated_at, pcsr.last_updated_by_email from public.property_construction_standard_rating AS pcsr {(customer == null ? "" : "WHERE pcsr.customer_id = @customerId")}
UNION
select 'property_conservation_status' AS table_name, pcs.id, pcs.customer_id, pcs.description, pcs.created_at, pcs.created_by_email, pcs.last_updated_at, pcs.last_updated_by_email from public.property_conservation_status AS pcs {(customer == null ? "" : "WHERE pcs.customer_id = @customerId")}
UNION
select 'property_sanitary_installation_type' AS table_name, psit.id, psit.customer_id, psit.description, psit.created_at, psit.created_by_email, psit.last_updated_at, psit.last_updated_by_email from public.property_sanitary_installation_type AS psit {(customer == null ? "" : "WHERE psit.customer_id = @customerId")}
UNION
select 'property_electrical_installation_type' AS table_name, peit.id, peit.customer_id, peit.description, peit.created_at, peit.created_by_email, peit.last_updated_at, peit.last_updated_by_email from public.property_electrical_installation_type AS peit {(customer == null ? "" : "WHERE peit.customer_id = @customerId")}
            ";
            
            await connection.OpenAsync();
            var reader = await command.ExecuteReaderAsync();

            while (await reader.ReadAsync())
            {
                Console.WriteLine(reader.GetString("foo"));
            }
        }
    }
}

dotnet clean && dotnet build -c Release

Expected behavior

Builds normally.

Actual behavior

64GB of RAM used and build doesn't finish.