using Microsoft.AspNetCore.Mvc; using System; using System.Data; using System.Data.SqlClient; using System.Threading.Tasks; using System.Web; using Microsoft.AspNetCore.Http; using subproj; using System.Data.SqlTypes; using System.Xml; public class IsDisposedBooleanField : IDisposable { private bool isDisposed; private CancellationTokenSource tok; public IsDisposedBooleanField() { tok = new CancellationTokenSource(); } public IsDisposedBooleanField(bool input) { tok = new CancellationTokenSource(); isDisposed = input; } public void Dispose() { if (!isDisposed) { isDisposed = true; tok.Dispose(); } } } // Expect 12 TAINT_ERROR for SQL injection flows. public class PulseTaintTests { [HttpGet] async static void asyncBadSqlInjection(string input) { subproj.WeatherForecast.runSqlCommandBad(input); } public async Task sendStringToSqlAsync(string input) { using (SqlCommand sqlCommand = new SqlCommand()) { using (var connection = new SqlConnection()) { sqlCommand.CommandText = "SELECT ProductId FROM Products WHERE ProductName = '" + input + "'"; sqlCommand.CommandType = CommandType.Text; sqlCommand.ExecuteReader(); } } return true; } public static async Task sendStringToSqlStaticAsync(string input) { using (SqlCommand sqlCommand = new SqlCommand()) { using (var connection = new SqlConnection()) { sqlCommand.CommandText = "SELECT ProductId FROM Products WHERE ProductName = '" + input + "'"; sqlCommand.CommandType = CommandType.Text; sqlCommand.ExecuteReader(); } } return true; } public static async Task sendStringToSqlStaticAsyncWithManyArguments( int intInput, object x, string stringInput, object y) { using (var fs = new FileStream(intInput.ToString(), FileMode.Open)) { try { x.GetHashCode(); y.GetHashCode(); } catch (Exception e) { return false; } using (var sr = new StreamReader(fs)) { using (SqlCommand sqlCommand = new SqlCommand()) { using (var connection = new SqlConnection()) { sqlCommand.CommandText = "SELECT ProductId FROM Products WHERE ProductName = '" + stringInput + "'"; sqlCommand.CommandType = CommandType.Text; sqlCommand.ExecuteReader(); } } } } return true; } [HttpGet] public async Task UserControlledInputSourceAsync(string input) { return input; } [HttpGet] public string UserControlledInputSourceNotAsync(string input) { return input; } public void CreateSqlInjectionFromUserControlledInputSourceNotAsync() { var userInput = UserControlledInputSourceNotAsync("userInput"); using (SqlCommand sqlCommand = new SqlCommand()) { sqlCommand.CommandText = "SELECT ProductId FROM Products WHERE ProductName = '" + userInput + "'"; } } public void CreateSqlInjectionFromUserControlledInputSourceAsync() { var userInput = UserControlledInputSourceAsync("userInput"); using (SqlCommand sqlCommand = new SqlCommand()) { sqlCommand.CommandText = "SELECT ProductId FROM Products WHERE ProductName = '" + userInput + "'"; } } [HttpGet] public async void UserControlledInputSourceVoid(int input) { input.ToString(); return; } public async void createBadAsyncSqlInjectionManyArguments() { var userControlledString = UserControlledInputSourceAsync(1.ToString()).Result; var xObject = new object(); var yObject = new object(); var integerInput = 4; var success = await sendStringToSqlStaticAsyncWithManyArguments(integerInput, xObject, userControlledString, yObject); } [HttpGet] public async void createBadAsyncSqlInjectionAwaitStaticAsync(string name) { var success = await sendStringToSqlStaticAsync(name); } [HttpGet] public async void createBadAsyncSqlInjectionAwaitAsync(string name) { var success = await sendStringToSqlAsync(name); } static void sqlBadConsoleReadLine() { var input = Console.ReadLine(); subproj.WeatherForecast.runSqlCommandBad(input); } [HttpPost] static void sqlBadInt(int InputParameter) { subproj.WeatherForecast.runSqlCommandBad(InputParameter.ToString()); } [HttpPost] static void sqlBadString(string InputParameter) { subproj.WeatherForecast.runSqlCommandBad(InputParameter); } [HttpPost] static void sqlParameterizedOk(int InputParameter) { subproj.WeatherForecast.runSqlCommandParameterized(InputParameter.ToString()); } [HttpPost] static void sqlStoredProcedureOk(string InputParameter) { subproj.WeatherForecast.runSqlCommandStoredProcedure(InputParameter.ToString()); } [HttpGet] public void SearchRawData(string query) { var queryPrefix = "prefix"; using (var conn = new SqlConnection("readerConnectionString")) { using (var command = new SqlCommand(queryPrefix + query)) { try { var reader = command.ExecuteReader(); while (reader.Read()) { Console.Write("Hello"); } } catch (Exception ex) { Console.Write(ex.Message); return; } } } } [HttpGet] public static void PropagateTaintArrayIterator(string[] roleIds) { foreach (var roleId in roleIds) { using var command = new SqlCommand(roleId); } } [HttpGet] public static void PropagateTaintArrayIterateByIndex(string[] roleIds) { for (int i = 0; i < roleIds.Length; i++) { using var command = new SqlCommand(roleIds[i]); } } } // Expect a thread safety violation issue public class ThreadSafety { int mBalance = 0; private readonly object balanceLock = new object(); public void deposit(int amount) { if (amount > 0) { lock (balanceLock) { mBalance += amount; } } } public int withdraw(int amount) { if (amount >= 0 && mBalance - amount >= 0) { mBalance -= amount; return mBalance; } else { return 0; } } /// /// An example with no null dereference expected. /// public static string NullDeReferenceOK() { return "abc"; } /// /// An example with null dereference error expected. /// public static string NullDeReferenceBad() { return null; } public static void TestNullDerefExpectError() { object x = new object(); try { try { Console.Write("First try catch"); throw new Exception(); } catch (Exception) { try { Console.Write("Before finally"); throw new Exception(); } catch (Exception) { x = new object(); } finally { Console.Write("Inner try catch finally"); x = null; } } finally { Console.Write("Outer try catch finally"); throw new Exception(); } Console.Write("Last instruction of outer try catch"); } catch (Exception) { x.GetHashCode(); } } public static void TestNullDerefExpectNoError() { object x = new object(); try { try { Console.Write("First try catch"); throw new Exception(); } catch (Exception) { try { Console.Write("Before finally"); throw new Exception(); } catch (Exception) { x = null; } finally { Console.Write("Inner try catch finally"); x = new object(); } } finally { Console.Write("Outer try catch finally"); throw new Exception(); } Console.Write("Last instruction of outer try catch"); } catch (Exception) { x.GetHashCode(); } } } public class MainClass { public static void Main(string[] args) { // FIXME: should close the global streams by calling p.Cleanup() // Null dereference error report expected. ThreadSafety.NullDeReferenceBad().GetHashCode(); // No null dereference error report expected. ThreadSafety.NullDeReferenceOK().GetHashCode(); } } // 18 reports expected (19 with --pulse-increase-leak-recall flag) class InferResourceLeakTests { private static byte[] myBytes = new byte[] { 10, 4 }; /// /// This was a former false positive that would occur because async methods with a using /// statement would only execute the disposal finally block if the __state field generated by /// the compiler was negative; Infer did not know that a separate initializer method would /// initialize this field to a negative value. /// public async Task AsyncUsingShouldNotReport() { var y = new object(); using (var stream = new FileStream("", FileMode.Open)) { object x = null; } return y; } /// /// Validates that even though the Dispose occurs within a finally block, the exceptional /// control flow is handled so that the analysis sees that the Dispose is not invoked. Also /// gets coverage over finally blocks ending with throw instead of endfinally. /// public static void TryFinallyThrow() { var fs = new FileStream("", FileMode.Open); var y = new FileStream("", FileMode.Open); try { Console.Write("hello"); } finally { if (fs != null) { fs.Dispose(); } throw new Exception(); } if (y != null) { y.Dispose(); } } /// /// This was a false positive that occurs when we return a class that owns IDisposable types. /// This occured because the setter method at the end of the async MoveNext() method which /// retains the new object (and therefore the underlying IDisposable type) in memory is not /// modeled. However, with the async update to the translation, this is no longer a problem. /// public async Task ReturnFileStreamTaskOK() { var stream = new FileStream("", FileMode.Open); return new TakeAndDispose(stream); } /// /// Validates that default initializations of boolean fields i.e. indicating disposal to false /// is taken into account and applied to conditional disposal. /// public static void UsingOnCustomIDisposableWithBooleanFieldOK() { using (var custom = new IsDisposedBooleanField()); } /// /// Validates that default initializations of boolean fields i.e. indicating disposal to false /// is taken into account and applied to conditional disposal. Should report because the bool /// is initialized to true, not false, and therefore disposal occurs. /// public static void UsingOnCustomIDisposableWithBooleanFieldTrueShouldReport() { using (var custom = new IsDisposedBooleanField(true)); } /// /// The "using" construct applies the generic IDisposable dispose in the bytecode, which is /// problematic when it is applied to a custom IDisposable object -- if translated directly, /// this prevents the analysis from applying the spec it has for the custom IDisposable object /// which in turn can cause false positive alerts for underlying IDisposable fields. /// public static void UsingOnCustomIDisposableOK() { Stream stream = new FileStream("MyFile", FileMode.Open); using TakeAndDispose tad = new TakeAndDispose(stream); } /// /// Tests that the model of Write for throwing an exception (thus resource not getting /// disposed) works. /// public static void UsageCanThrowShouldReport() { FileStream fs = new FileStream("MyFile.txt", FileMode.Open, FileAccess.Read); fs.Write(myBytes, 0, 1); // this line can throw an exception, preventing future disposal fs.Dispose(); } /// /// Tests that using (finally) correctly handles disposal despite possible exception. /// public static void UsageCanThrowOk() { using (var fs = new FileStream("MyFile.txt", FileMode.Open, FileAccess.Read)) { // the using properly handles the exception. fs.Write(myBytes, 0, 1); // this line can throw an exception } } /// /// Tests that an exception getting thrown causes the leak to be reported. /// public static void ThrowBetweenConstructionAndDisposeShouldReport() { var stream1 = new FileStream("MyFile.txt", FileMode.Open); throw new FileNotFoundException(); stream1.Dispose(); } /// /// False negative: the throw that occurs in another method doesn't get propagated here. /// public static void CallThrowingMethodBetweenConstructionAndDisposeShouldReport() { // this doesn't report since the fact that doesThrow() doesn't get propageted here. var stream1 = new FileStream("MyFile.txt", FileMode.Open); doesThrow(); stream1.Dispose(); } /// /// Tests that conditional exception (with a boolean value inducing it) works. /// public static void CallThrowingMethodBetweenConstructionAndDisposeShouldReport2() { var stream1 = new FileStream("MyFile.txt", FileMode.Open); doesThrowOrDispose(true, stream1); } public static void doesThrow() { throw new ArgumentException("Hi"); } public static void doesThrowOrDispose(bool b, Stream s) { if (b) { throw new ArgumentException("Hi"); } s.Dispose(); } /// /// Basic allocation/dispose -- should be not report an issue. /// public static void ConstructingOneOk() { var stream1 = new FileStream("NotAFile.bad", FileMode.Open); stream1.Dispose(); } /// /// Validates that delegation of the underlying stream to StreamReader works properly. /// public static void DelegatingToConstructorOk() { var stream1 = new FileStream("SomeFile.txt", FileMode.Open); var stream2 = new StreamReader(stream1); stream2.Dispose(); } /// /// Validates that the first allocation of the resource is correctly reported as lost. /// public static void DelegatingResourceToResourceShouldReport() { // this first filestream is lost and never disposed, this is where we should report var fs = new FileStream("SomeFile.txt", FileMode.Open); // this filestream is the stream delegated to the bufferedstream and disposed with it fs = new FileStream("SomeFile.txt", FileMode.Open); var sr = new StreamReader(fs); sr.Dispose(); } /// /// Validates that IDisposable resources which are exceptions that shouldn't be reported on /// indeed are not reported. /// public static void UsagesNotTrackedOk() { MemoryStream ms = new MemoryStream(10); ms.Write(myBytes, 0, 1); StringReader sr = new StringReader("MyString"); sr.Read(); StringWriter sw = new StringWriter(); sw.Write("Hello"); // there is no call to dispose/close, but MemoryStream has no resources, so we don't track it // If we did this with almost any other resource we should report. } /// /// Further validation of exceptions to disposing of IDisposables. /// public static void UsageNotTrackedDelegatedOk() { var stream1 = new MemoryStream(); var streamReader = new StreamReader(stream1); streamReader.Dispose(); } /// /// Validates that the exception getting thrown (preventing the dispose) is correctly interpreted. /// public static void UsageNotTrackedDelegatedShouldReport() { var stream1 = new MemoryStream(); var streamReader = new StreamReader(stream1); // potential exception here means the stream reader may not be disposed. var fs = new FileStream("MyFile.txt", FileMode.Open); fs.Write(myBytes, 0, 1); fs.Dispose(); streamReader.Dispose(); } /// /// Validates that interprocedural allocation and disposal is correctly handled. /// public static void UseMethodToCreateStreamAndDisposeOk() { var fs = CreateStreamOk(); fs.Close(); } /// /// Validates that interprocedural allocation (no disposal) is correctly reported. /// public static void UseMethodToCreateStreamAndLeakShouldReport() { var fs = CreateStreamOk(); } /// /// Validates that a returned StreamReader/underlying stream is not reported. /// public static StreamReader CreateStreamReaderAndReturnOk() { var fs = new FileStream("SomeFile.txt", FileMode.Append); var sr = new StreamReader(fs); return sr; } public static FileStream CreateStreamOk() { return new FileStream("MyFile.txt", FileMode.Create); } /// /// Validates that a user-defined IDisposable is reported. /// public static void LeakCustomDisposableShouldReport() { var tad = new TakeAndDispose(CreateStreamOk()); } /// /// Validates that delegation to user-defined IDisposable is handled. /// public static TakeAndDispose PassDisposableToCustomDisposableOk() { Stream stream = new FileStream("MyFile", FileMode.Open); return new TakeAndDispose(stream); } /// /// Validates that delegation to and disposal of a user-defined IDisposable is handled. /// public static void PassDisposableToCustomDisposableAndDisposeOk() { Stream stream = new FileStream("MyFile", FileMode.Open); TakeAndDispose tad = new TakeAndDispose(stream); tad.Dispose(); } /// /// Validates that failure to dispose of a user-defined IDisposable (with delegated resource) /// is reported. /// public static void PassDisposableToCustomDisposableAndDisposeShouldReport() { Stream stream = new FileStream("MyFile", FileMode.Open); TakeAndDispose tad = new TakeAndDispose(stream); stream.Dispose(); } /// /// Validates that delegation to user-defined IDisposable (no Dispose of underlying) is /// handled. /// public static TakeWithoutDispose PassDisposableToCustomDisposable2Ok() { Stream stream = new FileStream("MyFile", FileMode.Open); return new TakeWithoutDispose(stream); } /// /// Validates that delegation to user-defined IDisposable (no Dispose of underlying) is /// correctly handled. /// public static void PassDisposableToCustomDisposableAndDispose2ShouldReport() { Stream stream = new FileStream("MyFile", FileMode.Open); TakeWithoutDispose twd = new TakeWithoutDispose(stream); twd.Dispose(); } /// /// Validates that delegation to user-defined non-IDisposable is correctly handled. /// public static void PassDisposableToCustomClassWithDisposeAndDisposeOk() { Stream stream = new FileStream("MyFile", FileMode.Open); var tadnd = new TakeAndDisposeNotDisposable(stream); tadnd.Dispose(); } /// /// Validates that return of delegated stream to non-IDisposable is correctly handled. /// public static TakeAndDisposeNotDisposable PassDisposableToCustomClassWithDisposeOk() { Stream stream = new FileStream("MyFile", FileMode.Open); return new TakeAndDisposeNotDisposable(stream); } /// /// Should be no leak, as all resources are allocated via using. /// public static string NestedUsingWithThrownException() { using (var x = new StreamReader("")) { Console.Write("First using"); using (var y = new StreamReader("")) { Console.Write("Second using"); if (y != null) { throw new Exception(); } } } return "done"; } /// /// Validates that upward casting of stream to IDisposable is not reported on. /// public static IDisposable CastingOk() { FileStream myStream = new FileStream("MyFile", FileMode.Open); return myStream; } /// /// Validates that initialization of out variable is correctly not reported on. /// public static void OutStreamOk(out FileStream myStream) { myStream = new FileStream("myFile.txt", FileMode.OpenOrCreate); } // unknown function behavior test public static void UnknownCallIsNopReport() { // reports if the unknown functions do *not* havoc arguments. FileStream myStream = new FileStream("MyFile", FileMode.OpenOrCreate); myStream.ToString(); } private static SqlXml AddRbioProtocolSlimShouldReport(SqlXml remoteReplicas) { // only reports with flag --pulse-increase-leak-recall. XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(remoteReplicas.Value); var nodeReader = new XmlNodeReader(xmlDoc.DocumentElement); // SqlXml is unknown, so nodeReader should be leaked here. return new SqlXml(nodeReader); } } public class TakeAndDispose : IDisposable { private bool disposedValue; private Stream stream; public TakeAndDispose(Stream stream) { this.disposedValue = false; this.stream = stream; } public virtual void Dispose() { if (!disposedValue) { stream.Dispose(); disposedValue = true; } } } public class TakeWithoutDispose : IDisposable { private bool disposedValue; private Stream stream; public TakeWithoutDispose(Stream stream) { this.disposedValue = false; this.stream = stream; } public virtual void Dispose() { if (!disposedValue) { //stream.Dispose(); disposedValue = true; } } } public class TakeAndDisposeNotDisposable { private Stream stream; public TakeAndDisposeNotDisposable(Stream stream) { this.stream = stream; } public void Dispose() { stream.Dispose(); } } public class MultipleConstructors { private readonly FileStream _myStream; public MultipleConstructors(String filename, Boolean c) : this(new FileStream(filename, FileMode.Open), c) { // should report, as if c=true, we the FileStream is never stored. } public MultipleConstructors(FileStream myStream, Boolean c) { if (c) { throw new ArgumentException("Just because"); } _myStream = myStream; } public MultipleConstructors(String filename) : this(new FileStream(filename, FileMode.Open)) { // should report as the FileStream is never stored. } public MultipleConstructors(FileStream myStream) { throw new ArgumentException("Just because"); _myStream = myStream; } /// /// Although the field is indeed stored, the object goes out of scope and leaks the field. /// public void InvokeConstructorFalseBooleanShouldReport() { var x = new MultipleConstructors("test", false); } } internal class ArrayTest { public static readonly int ArraySize = 5; /// /// Should not report, stream gets closed. /// public static void straightLineCodeOk() { FileStream[] streams = new FileStream[ArraySize]; streams[0] = new FileStream("hi", FileMode.Create); streams[0].Close(); } /// /// Should not report, stream is returned in array. /// public static FileStream[] straightLineCodeReturnedOk() { FileStream[] streams = new FileStream[ArraySize]; streams[0] = new FileStream("hi", FileMode.Create); return streams; } /// /// Should not report, underlying stream gets closed. /// public static void straightLineCodeWithVariantOk() { FileStream[] streams = new FileStream[ArraySize]; FileStream stream = new FileStream("hi", FileMode.Create); streams[0] = stream; stream.Close(); } /// /// Should not report, stream gets allocated/freed in loop. /// public static void LoopingCodeOk() { FileStream[] streams = new FileStream[ArraySize]; for (int i = 0; i < ArraySize; i++) { streams[i] = new FileStream("hi", FileMode.Create); } for (int i = 0; i < ArraySize; i++) { streams[i].Close(); } } /// /// Should not report, streams allocated in loop and returned. /// public static FileStream[] LoopingCodeReturnedOk() { FileStream[] streams = new FileStream[ArraySize]; for (int i = 0; i < ArraySize; i++) { streams[i] = new FileStream("hi", FileMode.Create); } return streams; } } internal class ListTest { private readonly List _streamList = new List(); private readonly Dictionary _streamDict = new Dictionary(); public static readonly int ListSize = 5; /// /// Should not report, stream is closed (false positive -- ElementAt model) /// public static void straightLineCodeFalsePositive() { // reports as no model for ElementAt(). List streams = new List(); streams.Add(new FileStream("hi", FileMode.Create)); streams.ElementAt(0).Close(); } /// /// Should not report, stream is returned in list. /// public static List straightLineCodeReturnedOk() { List streams = new List(); streams.Add(new FileStream("hi", FileMode.Create)); return streams; } /// /// Should not report, underlying stream closed. /// public static void straightLineCodeWithVariantOk() { List streams = new List(); FileStream stream = new FileStream("hi", FileMode.Create); streams.Add(stream); stream.Close(); } /// /// Should not report, streams allocated/closed in loop (false positive -- ElementAt model). /// public static void LoopingCodeFalsePositive() { // reports as no model for ElementAt() List streams = new List(); for (int i = 0; i < ListSize; i++) { streams.Add(new FileStream("hi", FileMode.Create)); } for (int i = 0; i < ListSize; i++) { streams.ElementAt(i).Close(); } } /// /// Should not report, streams allocated/stored in loop, returned. /// /// public static List LoopingCodeReturnedOk() { List streams = new List(); for (int i = 0; i < ListSize; i++) { streams.Add(new FileStream("hi", FileMode.Create)); } return streams; } /// /// Should not report, streams added to instance collection. /// public void AddToFieldOk() { FileStream stream = new FileStream("hi", FileMode.Create); _streamList.Add(stream); FileStream stream2 = new FileStream("hi", FileMode.Create); _streamDict.Add("mykey", stream2); } }