1. ホーム
  2. c#

[解決済み] 構造体を比較するときに、なぜこのアサートはフォーマット例外を投げるのですか?

2023-02-12 18:59:45

質問

2つのオブジェクトが等しいことを証明したい。 System.Drawing.Size 構造体の等価性を主張しようとしていますが、期待される主張の失敗ではなく、フォーマット例外が発生します。

[TestMethod]
public void AssertStructs()
{
    var struct1 = new Size(0, 0);
    var struct2 = new Size(1, 1);

    //This throws a format exception, "System.FormatException: Input string was not in a correct format."
    Assert.AreEqual(struct1, struct2, "Failed. Expected {0}, actually it is {1}", struct1, struct2); 

    //This assert fails properly, "Failed. Expected {Width=0, Height=0}, actually it is {Width=1, Height=1}".
    Assert.AreEqual(struct1, struct2, "Failed. Expected " + struct1 + ", actually it is " + struct2); 
}

これは意図された動作なのでしょうか?私はここで何か間違ったことをしているのでしょうか?

どのように解決するのですか?

わかりました。そう、これはバグなのです。

問題は、2つのレベルの string.Format が行われていることです。

第一 のレベルの書式は、次のようなものです。

string template  = string.Format("Expected: {0}; Actual: {1}; Message: {2}",
                                 expected, actual, message);

次に string.Format を、指定されたパラメータと一緒に使います。

string finalMessage = string.Format(template, parameters);

(明らかに文化が提供されていますし いくつかの のようなサニタイズがあります...しかし、十分ではありません)。

文字列に変換された後、期待値と実際の値そのものが中括弧で終わっていなければ、これは問題ないように見えます。 Size . 例えば、最初のサイズは以下のように変換されます。

{Width=0, Height=0}

ということで、2段目の書式は以下のような感じです。

string.Format("Expected: {Width=0, Height=0}; Actual: {Width=1, Height=1 }; " +
              "Message = Failed expected {0} actually is {1}", struct1, struct2);

...で、これが失敗している。痛そう。

実際、期待される部分と実際の部分に私たちのパラメータを使うようにフォーマットを騙すことで、本当に簡単にこれを証明することができます。

var x = "{0}";
var y = "{1}";
Assert.AreEqual<object>(x, y, "What a surprise!", "foo", "bar");

という結果になります。

Assert.AreEqual failed. Expected:<foo>. Actual:<bar>. What a surprise!

明らかに壊れています。 foo を期待していなかったし、実際の値も bar !

基本的に、これは SQL インジェクション攻撃のようなものですが、より恐ろしくない文脈で string.Format .

回避策として string.Format を使うことができます。これによって、実際の値/期待される値でフォーマットした結果に対して、第2レベルのフォーマットが実行されるのを避けることができます。