Permalink
Browse files

Format ex (#44)

* FormatEx method realization

* added json test driver

* unchecked GetHashCode + minor fixes

* refactoring

* updated common submodule

* fixed test example

* added FunctionalTest

* fixed codestyle
  • Loading branch information...
frenzyigor authored and kochetkov committed May 10, 2018
1 parent 14445f7 commit eb79d62e23720a023e28cb30c80d737136014dc7
Showing with 677 additions and 191 deletions.
  1. +116 −0 sources/LibProtection.Injections/LibProtection.Injections.Tests/FunctionalTests.cs
  2. +9 −0 ...ces/LibProtection.Injections/LibProtection.Injections.Tests/LibProtection.Injections.Tests.csproj
  3. +20 −0 sources/LibProtection.Injections/LibProtection.Injections.Tests/TestCase.cs
  4. +179 −0 sources/LibProtection.Injections/LibProtection.Injections.Tests/TestCases/formatCases/example.json
  5. +65 −0 sources/LibProtection.Injections/LibProtection.Injections.Tests/TokenConverter.cs
  6. +19 −127 sources/LibProtection.Injections/LibProtection.Injections.Tests/TokenizationTests.cs
  7. +46 −0 sources/LibProtection.Injections/LibProtection.Injections/Extensions/ArrayExtensions.cs
  8. +15 −6 ...tection.Injections/LibProtection.Injections/{Formatter.cs → Formatting/ComplementaryFormatter.cs}
  9. +66 −0 sources/LibProtection.Injections/LibProtection.Injections/Formatting/FormatResult.cs
  10. +27 −29 ...s/LibProtection.Injections/LibProtection.Injections/{FormatProvider.cs → Formatting/Formatter.cs}
  11. +16 −0 sources/LibProtection.Injections/LibProtection.Injections/Formatting/Fragment.cs
  12. +0 −14 sources/LibProtection.Injections/LibProtection.Injections/Fragment.cs
  13. +37 −11 sources/LibProtection.Injections/LibProtection.Injections/LanguageService.cs
  14. +26 −0 sources/LibProtection.Injections/LibProtection.Injections/Languages/Token.cs
  15. +1 −0 sources/LibProtection.Injections/LibProtection.Injections/LibProtection.Injections.csproj
  16. +1 −1 sources/LibProtection.Injections/LibProtection.Injections/SafeString.Formattable.cs
  17. +33 −2 sources/LibProtection.Injections/LibProtection.Injections/SafeString.cs
  18. +1 −1 submodules/libprotection-common
@@ -0,0 +1,116 @@
using NUnit.Framework;
using System;
using System.Collections;
using System.Linq;
namespace LibProtection.Injections.Tests
{
[TestFixture(Category = nameof(FunctionalTests))]
public class FunctionalTests
{
public class DataPoint
{
public Type LanguageProviderType { get; private set; }
public string Result { get; private set; }
public string Format { get; private set; }
public object[] Arguments { get; private set; }
public bool IsAttack()
{
return Result == null;
}
public static DataPoint GetDataPoint<T>(string result, string format, params object[] arguments) where T : LanguageProvider
{
DataPoint point = new DataPoint();
point.LanguageProviderType = typeof(T);
point.Result = result;
point.Format = format;
point.Arguments = arguments;
return point;
}
}
private static DataPoint[] testCases = new DataPoint[]{
//Valid
DataPoint.GetDataPoint<Html>("<a href='Default.aspx' onclick='alert(\"Hello from embedded JavaScript code!\");return false'>This site&#39;s home page</a>", "<a href='{0}' onclick='alert(\"{1}\");return false'>{2}</a>", "Default.aspx", "Hello from embedded JavaScript code!", "This site's home page"),
DataPoint.GetDataPoint<JavaScript>("operationResult.innerText = 'operationResult.innerText = \\u0027Hello from internal JavaScript code!\\u0027;';", "operationResult.innerText = '{0}';", "operationResult.innerText = 'Hello from internal JavaScript code!';"),
DataPoint.GetDataPoint<Sql>("SELECT * FROM myTable WHERE id = 1 AND myColumn = 'value1'", "SELECT * FROM myTable WHERE id = {0} AND myColumn = '{1}'", 1, "value1"),
DataPoint.GetDataPoint<Url>("Assets/jsFile.js", "{0}/{1}", "Assets", "jsFile.js"),
DataPoint.GetDataPoint<FilePath>("C:\\inetpub\\playground.libprotection.org\\Assets\\textFile.txt", "C:\\inetpub\\playground.libprotection.org\\Assets\\{0}", "textFile.txt"),
//Attacks
DataPoint.GetDataPoint<Html>(null, "<a href={0} />", "<br>"),
DataPoint.GetDataPoint<JavaScript>(null, "operationResult.innerText = {0};", "' <br>"),
DataPoint.GetDataPoint<Sql>(null, "SELECT * FROM myTable WHERE id = {0}", "1 OR 1==1 --"),
DataPoint.GetDataPoint<Url>(null, "{0}/{1}", "../Asserts", "jsFile.js"),
DataPoint.GetDataPoint<FilePath>(null, "C:\\Assets\\{0}", "..\\jsFile.js"),
//safe modifier
DataPoint.GetDataPoint<Html>(":safe", ":safe"),
DataPoint.GetDataPoint<Html>(":safe&lt;br&gt;", "{0}", ":safe<br>"),
DataPoint.GetDataPoint<Html>("&lt;br&gt;xxx:safe", "{0}xxx:safe", "<br>"),
DataPoint.GetDataPoint<Html>("<br>", "{0:safe}", "<br>"),
DataPoint.GetDataPoint<Html>("<br>", "{0:SaFe}", "<br>"),
DataPoint.GetDataPoint<Html>("<br>:safe", "{0:safe}:safe", "<br>"),
DataPoint.GetDataPoint<Html>("<br>&lt;br&gt;", "{0:safe}{1}", "<br>", "<br>"),
DataPoint.GetDataPoint<Html>("&lt;br&gt;<br>&lt;br&gt;", "{0}{1:safe}{2}", "<br>", "<br>", "<br>"),
};
public static IEnumerable TestCases
{
get
{
int i = 0;
foreach(var testCase in testCases)
{
yield return new TestCaseData(testCase).SetName($"Functional test #{++i}");
}
}
}
private delegate bool TryFormatDelegate(string format, out string formatted, params object[] args);
private delegate string FormatDelegate(string format, params object[] args);
private static void GetFormatters(Type providerType, out TryFormatDelegate tryFormatDelegate, out FormatDelegate formatDelegate)
{
var methods = typeof(SafeString<>).MakeGenericType(providerType).GetMethods();
var tryFormatMethod = methods.First(x => x.Name == "TryFormat" && x.GetParameters()[0].ParameterType == typeof(string));
tryFormatDelegate = (TryFormatDelegate)Delegate.CreateDelegate(typeof(TryFormatDelegate), null, tryFormatMethod);
var formatMethod = methods.First(x => x.Name == "Format" && x.GetParameters()[0].ParameterType == typeof(string));
formatDelegate = (FormatDelegate)Delegate.CreateDelegate(typeof(FormatDelegate), null, formatMethod);
}
[Test, TestCaseSource(typeof(FunctionalTests), nameof(TestCases))]
public void FunctionalTest(DataPoint dataPoint)
{
GetFormatters(dataPoint.LanguageProviderType, out var tryFormatDelegate, out var formatDelegate);
var tryFormatResult = tryFormatDelegate(dataPoint.Format, out var tryFormatResultValue, dataPoint.Arguments);
if (dataPoint.IsAttack())
{
Assert.False(tryFormatResult);
bool failed = false;
try
{
formatDelegate(dataPoint.Format, dataPoint.Arguments);
}
catch(AttackDetectedException)
{
failed = true;
}
Assert.IsTrue(failed);
}
else{
Assert.IsTrue(tryFormatResult);
Assert.AreEqual(tryFormatResultValue, dataPoint.Result);
var formatResultValue = formatDelegate(dataPoint.Format, dataPoint.Arguments);
Assert.AreEqual(formatResultValue, dataPoint.Result);
}
}
}
}
@@ -23,4 +23,13 @@
<ProjectReference Include="..\LibProtection.Injections\LibProtection.Injections.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="formatCases\example.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestCases\formatCases\example.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
@@ -0,0 +1,20 @@
using LibProtection.Injections.Formatting;
using System;
namespace LibProtection.Injections.Tests
{
[Serializable]
internal class TestCase
{
// name of test case (should be used for NUnit's `TestCaseData(...).SetName`)
public string Name;
// name of language provider
public string LanguageProvider;
// format string template
public string Format;
// format string arguments
public string[] Arguments;
// expected format result (should be null for new test cases)
public FormatResult Result;
}
}
@@ -0,0 +1,179 @@
{
"Name":"html",
"LanguageProvider":"Html",
"Format":"<a href='{0}' onclick='alert(\"{1}\");return false'>{2}</a>",
"Arguments":[
"Default.aspx",
"Hello from embedded JavaScript code!",
"This site's home page"
],
"Result":{
"Tokens":[
{
"IsTrivial":false,
"Text":"<a",
"LanguageProvider":"Html",
"TypeName":"HtmlTokenType",
"TypeValue":"TagOpen",
"RangeLowerBound":0,
"RangeUpperBound":1
},
{
"IsTrivial":false,
"Text":"href",
"LanguageProvider":"Html",
"TypeName":"HtmlTokenType",
"TypeValue":"AttributeName",
"RangeLowerBound":3,
"RangeUpperBound":6
},
{
"IsTrivial":false,
"Text":"=",
"LanguageProvider":"Html",
"TypeName":"HtmlTokenType",
"TypeValue":"TagEquals",
"RangeLowerBound":7,
"RangeUpperBound":7
},
{
"IsTrivial":true,
"Text":"Default.aspx",
"LanguageProvider":"Url",
"TypeName":"UrlTokenType",
"TypeValue":"PathEntry",
"RangeLowerBound":9,
"RangeUpperBound":20
},
{
"IsTrivial":false,
"Text":"onclick",
"LanguageProvider":"Html",
"TypeName":"HtmlTokenType",
"TypeValue":"AttributeName",
"RangeLowerBound":23,
"RangeUpperBound":29
},
{
"IsTrivial":false,
"Text":"=",
"LanguageProvider":"Html",
"TypeName":"HtmlTokenType",
"TypeValue":"TagEquals",
"RangeLowerBound":30,
"RangeUpperBound":30
},
{
"IsTrivial":false,
"Text":"alert",
"LanguageProvider":"JavaScript",
"TypeName":"JavaScriptTokenType",
"TypeValue":"Identifier",
"RangeLowerBound":32,
"RangeUpperBound":36
},
{
"IsTrivial":false,
"Text":"(",
"LanguageProvider":"JavaScript",
"TypeName":"JavaScriptTokenType",
"TypeValue":"OpenParen",
"RangeLowerBound":37,
"RangeUpperBound":37
},
{
"IsTrivial":true,
"Text":"\"Hello from embedded JavaScript code!\"",
"LanguageProvider":"JavaScript",
"TypeName":"JavaScriptTokenType",
"TypeValue":"StringLiteral",
"RangeLowerBound":38,
"RangeUpperBound":75
},
{
"IsTrivial":false,
"Text":")",
"LanguageProvider":"JavaScript",
"TypeName":"JavaScriptTokenType",
"TypeValue":"CloseParen",
"RangeLowerBound":76,
"RangeUpperBound":76
},
{
"IsTrivial":false,
"Text":";",
"LanguageProvider":"JavaScript",
"TypeName":"JavaScriptTokenType",
"TypeValue":"SemiColon",
"RangeLowerBound":77,
"RangeUpperBound":77
},
{
"IsTrivial":false,
"Text":"return",
"LanguageProvider":"JavaScript",
"TypeName":"JavaScriptTokenType",
"TypeValue":"Return",
"RangeLowerBound":78,
"RangeUpperBound":83
},
{
"IsTrivial":false,
"Text":" ",
"LanguageProvider":"JavaScript",
"TypeName":"JavaScriptTokenType",
"TypeValue":"WhiteSpaces",
"RangeLowerBound":84,
"RangeUpperBound":84
},
{
"IsTrivial":true,
"Text":"false",
"LanguageProvider":"JavaScript",
"TypeName":"JavaScriptTokenType",
"TypeValue":"BooleanLiteral",
"RangeLowerBound":85,
"RangeUpperBound":89
},
{
"IsTrivial":false,
"Text":">",
"LanguageProvider":"Html",
"TypeName":"HtmlTokenType",
"TypeValue":"TagClose",
"RangeLowerBound":91,
"RangeUpperBound":91
},
{
"IsTrivial":true,
"Text":"This site's home page",
"LanguageProvider":"Html",
"TypeName":"HtmlTokenType",
"TypeValue":"HtmlText",
"RangeLowerBound":92,
"RangeUpperBound":112
},
{
"IsTrivial":false,
"Text":"</a",
"LanguageProvider":"Html",
"TypeName":"HtmlTokenType",
"TypeValue":"TagOpen",
"RangeLowerBound":113,
"RangeUpperBound":115
},
{
"IsTrivial":false,
"Text":">",
"LanguageProvider":"Html",
"TypeName":"HtmlTokenType",
"TypeValue":"TagClose",
"RangeLowerBound":116,
"RangeUpperBound":116
}
],
"IsAttackDetected":false,
"InjectionPointIndex":-1,
"FormattedString":"<a href='Default.aspx' onclick='alert(\"Hello from embedded JavaScript code!\");return false'>This site&#39;s home page</a>"
}
}
@@ -0,0 +1,65 @@
using Newtonsoft.Json;
using System;
namespace LibProtection.Injections.Tests
{
class TokenConverter : JsonConverter
{
public override bool CanConvert(Type objectType) => typeof(Token) == objectType;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jobject = Newtonsoft.Json.Linq.JObject.Load(reader);
var isTrivial = jobject.Value<bool>(nameof(Token.IsTrivial));
var text = jobject.Value<string>(nameof(Token.Text));
var languageProviderName = jobject.Value<string>(nameof(Token.LanguageProvider));
var tokenTypeName = jobject.Value<string>($"{nameof(Token.Type)}Name");
var tokenTypeValue = jobject.Value<string>($"{nameof(Token.Type)}Value");
var rangeLowerBound = jobject.Value<int>($"{nameof(Token.Range)}{nameof(Range.LowerBound)}");
var rangeUpperBound = jobject.Value<int>($"{nameof(Token.Range)}{nameof(Range.UpperBound)}");
var providerType = typeof(LanguageProvider).Assembly.GetType($"{typeof(LanguageProvider).Namespace}.{languageProviderName}", throwOnError: true);
var instance = typeof(Single<>).MakeGenericType(providerType).GetProperty("Instance");
var languageProvider = (LanguageProvider)instance.GetValue(null);
var type = (Enum)Enum.Parse(typeof(Token).Assembly.GetType($"{typeof(LanguageProvider).Namespace}.{tokenTypeName}", throwOnError: true), tokenTypeValue);
return new Token(languageProvider, type, rangeLowerBound, rangeUpperBound, text, isTrivial);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var token = (Token)value;
writer.WriteStartObject();
writer.WritePropertyName(nameof(Token.IsTrivial));
writer.WriteValue(token.IsTrivial);
writer.WritePropertyName(nameof(Token.Text));
writer.WriteValue(token.Text);
writer.WritePropertyName(nameof(Token.LanguageProvider));
writer.WriteValue(token.LanguageProvider.GetType().Name);
writer.WritePropertyName($"{nameof(Token.Type)}Name");
writer.WriteValue(token.Type.GetType().Name);
writer.WritePropertyName($"{nameof(Token.Type)}Value");
writer.WriteValue(token.Type.ToString());
writer.WritePropertyName($"{nameof(Token.Range)}{nameof(Range.LowerBound)}");
writer.WriteValue(token.Range.LowerBound);
writer.WritePropertyName($"{nameof(Token.Range)}{nameof(Range.UpperBound)}");
writer.WriteValue(token.Range.UpperBound);
writer.WriteEndObject();
}
}
}
Oops, something went wrong.

0 comments on commit eb79d62

Please sign in to comment.