Not able to use FormattableString
Asked Answered
E

1

8

Reading this interesting article about SQL injection prevention in EF Core, I found out that now an interpolated string may result in a FormattableString.

Running this test code in .NET Core 2.2:

public static void Main()
{
    var filter = "Mark'; DROP TABLE tbl; --";

    Console.WriteLine(FromSql("SELECT * FROM tbl WHERE fld = '" + filter + "'"));
    Console.WriteLine(FromSql($"SELECT * FROM tbl WHERE fld = {filter}"));
    Console.WriteLine(FromSql(FormattableStringFactory.Create(
                                  "SELECT * FROM tbl WHERE fld = {0}", filter)));
}

private static string FromSql(string sql) => sql;

private static string FromSql(FormattableString sql)
{   
    var formatArgs = sql.GetArguments();

    for (var paramIndex = 0; paramIndex < sql.ArgumentCount; ++paramIndex)
        formatArgs[paramIndex] = "@p" + paramIndex;

    return sql.ToString();
}

is not giving what I expected:

SELECT * FROM tbl WHERE fld = 'Mark'; DROP TABLE tbl; --'
SELECT * FROM tbl WHERE fld = Mark'; DROP TABLE tbl; --
SELECT * FROM tbl WHERE fld = @p0

The second print should output like the last one.

Try this is fiddle.

What am I missing?

Echo answered 19/7, 2019 at 9:16 Comment(2)
I guess the answer is that a string literal starting with a $ does not actually construct a string of type FormattableString - I guess the compiler hasn't caught up with the library.Ningsia
@500-InternalServerError In the linked article, they use that exact syntax and it works. The called EF Core method is similar to what I wrote, you can see it here:github.com/aspnet/EntityFrameworkCore/blob/…Echo
D
3

Your second call Console.WriteLine(FromSql($"SELECT * FROM tbl WHERE fld = {filter}")); looks to be going to the FromSql(string sql) overload instead of FromSql(FormattableString sql).

Just remove the FromSql(string sql) method (and the first call to it),
and it will go as expected; see the modified Fiddle.


The compiler seems to translate var q = $"SELECT * FROM tbl WHERE fld = {filter}"; to a string.

 String q = $"SELECT * FROM tbl WHERE fld = {filter}";`  

Because of the string type, a String.Format has taken place.
From the documentation:

If an interpolated string has the type string, it's typically transformed into a String.Format method call .


Whereas explicitly specifying FormattableString as in

FormattableString q = $"SELECT * FROM tbl WHERE fld = {filter}";  

a FormattableStringFactory.Create is being used.
From the documentation:

If an interpolated string has the type IFormattable or FormattableString, the compiler generates a call to the FormattableStringFactory.Create method.

Damon answered 19/7, 2019 at 21:1 Comment(3)
Interesting, it seems that the other overload in the source code I linked in the comment above uses RawSqlString instead of string. This way probably the compiler can understand which overload to call.Echo
Mystery solved, it's written on the RawSqlString doc page: "This type enables overload resolution between the regular and interpolated FromSql()" - learn.microsoft.com/en-us/dotnet/api/…Echo
Funny thing is that now they marked that method as obsolete and replaced it with two explicit implementations: FromSqlRaw and FromSqlInterpolated.Echo

© 2022 - 2024 — McMap. All rights reserved.