public string GetSingleCustomQueryResultRow(int startingFrom) { string result = string.Empty; string generatedPayload = PayloadDetails.Payload; if (PayloadDetails.Params != null && PayloadDetails.Params.Count() > 0) { foreach (var param in PayloadDetails.Params) { generatedPayload = generatedPayload.Replace("{" + param.Position + "}", PayloadHelpers.GetData(param.Name, this)); } } if (PayloadDetails.ExpectedResultType == Enums.ExpectedResultType.Multiple) { generatedPayload = string.Format(PayloadHelpers.GetSingleResultLimiter(PayloadDetails.Dbms), generatedPayload, startingFrom); } string query = QueryHelper.CreateQuery(Url, ExploitDetails.Exploit, generatedPayload); string pageHtml = QueryRunner.GetPageHtml(query, UseProxy ? ProxyDetails : null); result = HtmlHelpers.GetAnswerFromHtml(pageHtml, query, ExploitDetails, DetailedExceptions); //@TODO: strip scripts if (!string.IsNullOrEmpty(MappingFile) && !string.IsNullOrEmpty(result)) { XmlHelpers.SaveToMappingFile(MappingFile, PayloadDetails, result, this, (this.ExploitDetails != null) ? this.ExploitDetails.Dbms : string.Empty); } return(result); }
private static object[] ConstructEventWithSource <T>(SourceSystem sourceSystem) where T : IMessage { var payload = PayloadHelpers.Construct <T>(); PayloadHelpers.SetField(payload, "source_", sourceSystem); // the name of field is found with decompiler return(new object[] { payload }); }
public void Should_not_process_payload_if_it_is_disabled() { var eventValidator = new Mock <IEventValidator>(); eventValidator.Setup(m => m.IsEnabled(It.IsAny <PriceBandUpdated>())).Returns(false); var handler = new PriceBandEventHandler(Mock.Of <IDatabaseCommands>(), eventValidator.Object, Mock.Of <IMessagingLogger>(), Mock.Of <IConfiguration>()); var priceBandUpdated = PayloadHelpers.Construct <PriceBandUpdated>(); var result = handler.HandleAsync(priceBandUpdated, Guid.Empty.ToString()).Result; Assert.Equal(MessageHandlerResult.Success, result); eventValidator.Verify(m => m.IsValidPayload(It.IsAny <PriceBandUpdated>()), Times.Never); }
public void Should_register_error_with_model() { // arrange var payload = PayloadHelpers.Construct <AccountUpdated>(); var dbCommands = new Mock <IDatabaseCommands>(); var validator = new Mock <IEventValidator>(); validator.Setup(m => m.IsAllowedEvent(It.IsAny <object>())).Returns(true); validator.Setup(m => m.IsValidPayload(It.IsAny <object>())).Returns(false); var messagingLogger = new Mock <IMessagingLogger>(); var handler = new AccountUpdatedEventHandler(dbCommands.Object, validator.Object, messagingLogger.Object); // act var result = handler.HandleAsync(payload, "1").Result; // assert Assert.Equal(MessageHandlerResult.Fatal, result); messagingLogger.Verify(m => m.ReceivedInvalidModel(It.IsAny <string>(), It.IsAny <object>(), It.IsAny <string>()), Times.Once); }
public string GetSingleCustomQueryResultRow(int startingFrom) { string results = string.Empty; StringBuilder sbResult = new StringBuilder(); string generatedPayload = PayloadDetails.Payload; if (PayloadDetails.Params != null && PayloadDetails.Params.Count() > 0) { foreach (var param in PayloadDetails.Params) { generatedPayload = generatedPayload.Replace("{" + param.Position + "}", PayloadHelpers.GetData(param.Name, this)); } } StringBuilder sbCurExploit = new StringBuilder(); int columnIndexCounter = 0; string generatedPayloadWithLimit = string.Empty; for (int j = 0; j < _nrCols; j++) { if (PayloadDetails.ExpectedResultType == Enums.ExpectedResultType.Multiple) { generatedPayloadWithLimit = string.Format(PayloadHelpers.GetSingleResultLimiter(PayloadDetails.Dbms), generatedPayload, startingFrom + j); } if (_visibleColumnIndexes.Contains(j)) { /* * sbCurExploit.AppendFormat(GeneralPayloads.UnionBasedSelectCountedResultWrapper, _visibleColumnIndexes[columnIndexCounter], * (PayloadDetails.ExpectedResultType == Enums.ExpectedResultType.Multiple) ? generatedPayloadWithLimit : generatedPayload); */ sbCurExploit.Append(GeneralPayloads.UnionBasedSelectCountedResultWrapperPart1); sbCurExploit.Append(UrlHelpers.HexEncodeValue(string.Format(GeneralPayloads.UnionBasedSelectCountedResultWrapperPart2, _visibleColumnIndexes[columnIndexCounter]))); sbCurExploit.AppendFormat(GeneralPayloads.UnionBasedSelectCountedResultWrapperPart3, (PayloadDetails.ExpectedResultType == Enums.ExpectedResultType.Multiple) ? generatedPayloadWithLimit : generatedPayload); columnIndexCounter++; } else { sbCurExploit.AppendFormat(j.ToString()); } if (j < _nrCols - 1) { sbCurExploit.Append(","); } } string query = QueryHelper.CreateQuery(Url, ExploitDetails.Exploit, sbCurExploit.ToString()); string pageHtml = QueryRunner.GetPageHtml(query, UseProxy ? ProxyDetails : null); IList <string> resultsBatch = HtmlHelpers.GetMultipleAnswersFromHtml(pageHtml, query, ExploitDetails, DetailedExceptions); string actualValue = string.Empty; int separatorIndex = 0; int columnIndex = 0; string columnIndexString = ""; IList <int> columnsProcessed = new List <int>(); foreach (string singleResult in resultsBatch) { //@TODO: strip scripts separatorIndex = singleResult.IndexOf(GeneralPayloads.UnionBasedResultSeparator); if (separatorIndex != -1) { columnIndexString = singleResult.Substring(0, separatorIndex); if (!int.TryParse(columnIndexString, out columnIndex)) { continue; } if (columnsProcessed.Contains(columnIndex)) { continue; } else { columnsProcessed.Add(columnIndex); } actualValue = singleResult.Substring(separatorIndex + GeneralPayloads.UnionBasedResultSeparator.Length); if (!string.IsNullOrEmpty(MappingFile)) { XmlHelpers.SaveToMappingFile(MappingFile, PayloadDetails, actualValue, this, (this.ExploitDetails != null) ? this.ExploitDetails.Dbms : string.Empty); } sbResult.Append(actualValue); sbResult.Append(Environment.NewLine); } if (columnsProcessed.Count == _visibleColumnIndexes.Count) { break; } } return(sbResult.ToString()); }
public int GetTotalNoOfCustomQueryResultRows() { if (_nrCols == 0 || _nrVisibleCols == 0 || _visibleColumnIndexes.Count() == 0) { if (!TestIfVulnerable()) { throw new SqlInjException("Given script is not injectable using current injection strategy"); } } int count = 0; string generatedpayload = string.Empty; if (PayloadDetails == null) { return(0); } if (string.IsNullOrEmpty(PayloadDetails.Payload)) { return(0); } if (PayloadDetails.ExpectedResultType == Enums.ExpectedResultType.Single) { return(1); } generatedpayload = PayloadDetails.Payload; if (PayloadDetails.Params != null && PayloadDetails.Params.Count() > 0) { foreach (var param in PayloadDetails.Params) { generatedpayload = generatedpayload.Replace("{" + param.Position + "}", PayloadHelpers.GetData(param.Name, this)); } } generatedpayload = string.Format(GeneralPayloads.QueryResultCount, generatedpayload); StringBuilder sbCurExploit = new StringBuilder(); sbCurExploit.AppendFormat(GeneralPayloads.UnionBasedSelectResultWrapper, generatedpayload); if (_nrCols > 1) { sbCurExploit.Append(","); } for (int j = 1; j < _nrCols; j++) { sbCurExploit.Append(j.ToString()); if (j < _nrCols - 1) { sbCurExploit.Append(","); } } string query = QueryHelper.CreateQuery(Url, ExploitDetails.Exploit, sbCurExploit.ToString()); string pageHtml = QueryRunner.GetPageHtml(query, UseProxy ? ProxyDetails : null); var result = HtmlHelpers.GetAnswerFromHtml(pageHtml, query, ExploitDetails, DetailedExceptions); int.TryParse(result, out count); return(count); }
public int GetTotalNoOfCustomQueryResultRows() { int count = 0; string generatedpayload = string.Empty; if (PayloadDetails == null) { return(0); } if (string.IsNullOrEmpty(PayloadDetails.Payload)) { return(0); } if (PayloadDetails.ExpectedResultType == Enums.ExpectedResultType.Single) { return(1); } generatedpayload = PayloadDetails.Payload; if (PayloadDetails.Params != null && PayloadDetails.Params.Count() > 0) { foreach (var param in PayloadDetails.Params) { generatedpayload = generatedpayload.Replace("{" + param.Position + "}", PayloadHelpers.GetData(param.Name, this)); } } generatedpayload = /*UrlHelpers.HexEncodeValue(*/ string.Format(GeneralPayloads.QueryResultCount, generatedpayload);//); string query = QueryHelper.CreateQuery(Url, ExploitDetails.Exploit, generatedpayload); string pageHtml = QueryRunner.GetPageHtml(query, UseProxy ? ProxyDetails : null); string countString = HtmlHelpers.GetAnswerFromHtml(pageHtml, query, ExploitDetails, DetailedExceptions); int.TryParse(countString, out count); return(count); }
private static void ExecuteInternal(BrokeredMessage message, out BrokeredMessage response) { // We must set the out parameter no matter what, so do that first. response = new BrokeredMessage(); // We'll create the app domain in the outer scope so we can unload it when we are finished (if it was created). AppDomain sandboxDomain = null; try { var requestId = (int)message.Properties["RequestId"]; // Set correlation id of response message using the correlation ID of the request message. response.CorrelationId = message.CorrelationId; response.Properties["RequestId"] = requestId; // The request will also load the associated route, so we'll use that feature // to reduce the number of SQL calls we make. var requestTask = TraceUtility.TraceTime("Get Load Request Task", () => Program.RequestRepository.GetRequestByIdAsync(requestId)); TraceUtility.TraceTime("Load Request", () => requestTask.Wait()); var request = requestTask.Result; var route = request.Route; var routeSettings = route.RouteSettings; // Trace the incoming request URI. Trace.TraceInformation("Trace 'Request Uri' - {0}", request.Uri); try { var ev = new Evidence(); ev.AddHostEvidence(new Zone(SecurityZone.Internet)); var assemblyType = typeof(ExecutionSandbox); var assemblyPath = Path.GetDirectoryName(assemblyType.Assembly.Location); var sandboxPermissionSet = TraceUtility.TraceTime("Create Sandbox Permission Set", () => SecurityManager.GetStandardSandbox(ev)); // Exit with an error code if for some reason we can't get the sandbox permission set. if (sandboxPermissionSet == null) { throw new EntryPointException("Unable to load the sandbox environment, please contact Subroute.io for help with this error."); } TraceUtility.TraceTime("Reconfigure Appropriate Permission Sets", () => { // Remove access to UI components since we are in a headless environment. sandboxPermissionSet.RemovePermission(typeof(UIPermission)); // Remove access to the File System Dialog since we are headless. sandboxPermissionSet.RemovePermission(typeof(FileDialogPermission)); // Add the ability to use reflection for invocation and serialization. sandboxPermissionSet.AddPermission(new ReflectionPermission(PermissionState.Unrestricted)); // Add the ability to make web requests. sandboxPermissionSet.AddPermission(new WebPermission(PermissionState.Unrestricted)); // Add the ability to use the XmlSerializer and the DataContractSerializer. sandboxPermissionSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.SerializationFormatter)); }); // We'll create a new folder to hold an empty config file we create, and by // doing this, it prevents the user from gaining access to our configuration // file and the settings within, such as connection strings, infrastructure // and other sensitive information we don't want them to have. Plus it will // allow us to change any configuration settings that are specific to their // application domain, such as default settings and other infrastructure. // We must ensure that we have at least the root configuration XML tag in // the configuration file we create or various dependencies will fail // such as XmlSerializer and DataContractSerializer. var tempDirectory = Path.GetTempPath(); var userConfigDirectory = Path.Combine(tempDirectory, route.Uri); var userConfigFilePath = Path.Combine(userConfigDirectory, "app.config"); TraceUtility.TraceTime("Create Sandbox Directory", () => Directory.CreateDirectory(userConfigDirectory)); var configFile = TraceUtility.TraceTime("Generate App.Config File", () => routeSettings.Aggregate(@"<?xml version=""1.0"" encoding=""utf-8"" ?><configuration><appSettings>", (current, setting) => current + $"<add key=\"{setting.Name}\" value=\"{setting.Value}\" />{Environment.NewLine}", result => $"{result}</appSettings></configuration>")); TraceUtility.TraceTime("Write App.Config to Disk", () => File.WriteAllText(userConfigFilePath, configFile)); // We'll add one last permission to allow the user access to their own private folder. TraceUtility.TraceTime("Add Permission to Read App.Config File", () => sandboxPermissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read, new[] { userConfigDirectory }))); TraceUtility.TraceTime("Create AppDomain", () => { var appDomainSetup = new AppDomainSetup { ApplicationBase = assemblyPath, ConfigurationFile = userConfigFilePath }; sandboxDomain = AppDomain.CreateDomain("Sandboxed", ev, appDomainSetup, sandboxPermissionSet); }); // The ExecutionSandbox is a MarshalByRef type that allows us to dynamically // load their assemblies via a byte array and execute methods inside of // their app domain from out full-trust app domain. It's the bridge that // cross the app domain boundary. var executionSandbox = TraceUtility.TraceTime("Create ExecutionSandbox Instance", () => (ExecutionSandbox)sandboxDomain.CreateInstance( assemblyType.Assembly.FullName, assemblyType.FullName, false, BindingFlags.Public | BindingFlags.Instance, null, null, null, null) .Unwrap()); // Build the ExecutionRequest object that represents the incoming request // which holds the payload, headers, method, etc. The class is serialized // so it can cross the app domain boundary. So it's serialized in our // full-trust host app domain, and deserialized and reinstantiated in // the sandbox app domain. var uri = new Uri(request.Uri, UriKind.Absolute); var executionRequest = TraceUtility.TraceTime("Create RouteRequest Instance", () => new RouteRequest(uri, request.Method) { IpAddress = request.IpAddress, Headers = HeaderHelpers.DeserializeHeaders(request.RequestHeaders), Body = request.RequestPayload }); try { // The ExecutionSandbox we'll attempt to locate the best method to execute // based on the incoming request method (GET, POST, DELETE, etc.) and // will pass the ExecutionRequest we created above. In return, we receive // an instance of ExecutionResponse that has been serialized like the request // and deserialized in our full-trust host domain. var executionResponse = TraceUtility.TraceTime("Load and Execute Route", () => executionSandbox.Execute(route.Assembly, executionRequest)); // We'll use the data that comes back from the response to fill out the // remaineder of the database request record which will return the status // code, message, payload, and headers. Then we update the database. TraceUtility.TraceTime("Update Request Record", () => { request.CompletedOn = DateTimeOffset.UtcNow; request.StatusCode = (int)executionResponse.StatusCode; request.StatusMessage = executionResponse.StatusMessage; request.ResponsePayload = executionResponse.Body; request.ResponseHeaders = Common.RouteResponse.SerializeHeaders(executionResponse.Headers); Program.RequestRepository.UpdateRequestAsync(request).Wait(); }); // We'll pass back a small bit of data indiciating to the subscribers of // the response topic listening for our specific correlation ID that indicates // the route code was executed successfully and to handle it as such. response.Properties["Result"] = (int)ExecutionResult.Success; response.Properties["Message"] = "Completed Successfully"; } catch (TargetInvocationException invokationException) { // These exceptions can occur when we encounter a permission exception where // the user doesn't have permission to execute a particular block of code. var securityException = invokationException.InnerException as SecurityException; if (securityException != null) { throw new RoutePermissionException(GetPermissionErrorMessage(securityException), invokationException); } // Check for BadRequestException, we need to wrap it with the core exception. // These exceptions can occur when query string parsing fails, and since the // user's code doesn't have access to the core exceptions, we'll need to wrap // it instead manually. var badRequestException = invokationException.InnerException as Common.BadRequestException; if (badRequestException != null) { throw new Core.Exceptions.BadRequestException(badRequestException.Message, badRequestException); } // Otherwise it is most likely a custom user exception. throw new CodeException(invokationException.InnerException?.Message ?? "Route raised a custom exception.", invokationException.InnerException); } catch (EntryPointException entryPointException) { // These exceptions occur when an entry point could not be located. // Since we don't have a reference to core in the common library. // We'll instead wrap this exception in a core // exception to apply a status code. throw new RouteEntryPointException(entryPointException.Message, entryPointException); } catch (SecurityException securityException) { // These exceptions can occur when we encounter a permission exception where // the user doesn't have permission to execute a particular block of code. throw new RoutePermissionException(GetPermissionErrorMessage(securityException), securityException); } catch (Common.BadRequestException badRequestException) { // These exceptions can occur when query string parsing fails, and since the // user's code doesn't have access to the core exceptions, we'll need to wrap // it instead manually. throw new Core.Exceptions.BadRequestException(badRequestException.Message, badRequestException); } catch (Exception routeException) { // These are all other exceptions that occur during the execution of // a route. These exceptions are raised by the users code. throw new RouteException(routeException.Message, routeException); } } catch (Exception appDomainException) { // This exception relates to exceptions configuring the AppDomain and we'll still notify the // user, we just won't give them specific information that could reveal our infrastructure // unless an IStatusCodeException was thrown, meaning it's a public exception. var statusCode = 500; var statusMessage = "An unexpected exception has occurred. Please contact Subroute.io regarding this error."; var statusCodeException = appDomainException as IStatusCodeException; string stackTrace = null; if (statusCodeException != null) { statusCode = (int)statusCodeException.StatusCode; statusMessage = appDomainException.Message; if (appDomainException is CodeException) { stackTrace = appDomainException.ToString(); } } request.CompletedOn = DateTimeOffset.UtcNow; request.StatusCode = statusCode; request.ResponsePayload = PayloadHelpers.CreateErrorPayload(statusMessage, stackTrace); request.ResponseHeaders = HeaderHelpers.GetDefaultHeaders(); Program.RequestRepository.UpdateRequestAsync(request).Wait(); response.Properties["Result"] = (int)ExecutionResult.Failed; response.Properties["Message"] = appDomainException.Message; } } catch (Exception fatalException) { // These exceptions are absolutely fatal. We'll have to notify the waiting thread // via the service bus message, because we're unable to load a related request. response.Properties["Result"] = (int)ExecutionResult.Fatal; response.Properties["Message"] = fatalException.Message; } finally { // Unload the users app domain to recover all memory used by it. if (sandboxDomain != null) { TraceUtility.TraceTime("Unload AppDomain", () => AppDomain.Unload(sandboxDomain)); } } }
private static async Task ExecuteInternalAsync(BrokeredMessage message, ICollector <BrokeredMessage> response) { // We'll always need a response message, so create it now and populate it below. var responseMessage = new BrokeredMessage(); // We'll create the app domain in the outer scope so we can unload it when we are finished (if it was created). AppDomain sandboxDomain = null; try { var requestId = (int)message.Properties["RequestId"]; // Set correlation id of response message using the correlation ID of the request message. responseMessage.CorrelationId = message.CorrelationId; responseMessage.Properties["RequestId"] = requestId; // The request will also load the associated route, so we'll use that feature // to reduce the number of SQL calls we make. var request = await Program.RequestRepository.GetRequestByIdAsync(requestId).TraceTimeAsync("Load Request"); var route = request.Route; var routeSettings = route.RouteSettings.ToArray(); var routePackages = route.RoutePackages.ToArray(); // Trace the incoming request URI. Trace.TraceInformation("Trace 'Request Uri' - {0}", request.Uri); try { var ev = new Evidence(); ev.AddHostEvidence(new Zone(SecurityZone.Internet)); var assemblyType = typeof(ExecutionSandbox); var assemblyPath = Path.GetDirectoryName(assemblyType.Assembly.Location); var sandboxPermissionSet = TraceUtility.TraceTime("Create Sandbox Permission Set", () => SecurityManager.GetStandardSandbox(ev)); // Exit with an error code if for some reason we can't get the sandbox permission set. if (sandboxPermissionSet == null) { throw new EntryPointException("Unable to load the sandbox environment, please contact Subroute.io for help with this error."); } // We'll create a new folder to hold an empty config file we create, and by // doing this, it prevents the user from gaining access to our configuration // file and the settings within, such as connection strings, infrastructure // and other sensitive information we don't want them to have. Plus it will // allow us to change any configuration settings that are specific to their // application domain, such as default settings and other infrastructure. // We must ensure that we have at least the root configuration XML tag in // the configuration file we create or various dependencies will fail // such as XmlSerializer and DataContractSerializer. var directories = TraceUtility.TraceTime("Setup Filesystem", () => SetupFilesystem(route, routeSettings)); TraceUtility.TraceTime("Reconfigure Appropriate Permission Sets", () => { // Remove access to UI components since we are in a headless environment. sandboxPermissionSet.RemovePermission(typeof(UIPermission)); // Remove access to the File System Dialog since we are headless. sandboxPermissionSet.RemovePermission(typeof(FileDialogPermission)); // Add the ability to use reflection for invocation and serialization. sandboxPermissionSet.AddPermission(new ReflectionPermission(PermissionState.Unrestricted)); // Add the ability to make web requests. sandboxPermissionSet.AddPermission(new WebPermission(PermissionState.Unrestricted)); // Add the ability to use the XmlSerializer and the DataContractSerializer. // Also allows unmanaged code to be executed for drawing operations such as image resize and formatting. sandboxPermissionSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.SerializationFormatter | SecurityPermissionFlag.UnmanagedCode)); // Add permission to access the nuget package directory so that assemblies can be loaded. sandboxPermissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read, Settings.NugetPackageDirectory)); // Add permission to read execution temp directory. sandboxPermissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read, new[] { directories.RootDirectory, @"D:\GitHub\subroute.io\Subroute.Container\App_code\" })); }); TraceUtility.TraceTime("Create AppDomain", () => { var appDomainSetup = new AppDomainSetup { ApplicationBase = assemblyPath, ConfigurationFile = directories.ConfigFile }; sandboxDomain = AppDomain.CreateDomain("Sandboxed", ev, appDomainSetup, sandboxPermissionSet); }); // The ExecutionSandbox is a MarshalByRef type that allows us to dynamically // load their assemblies via a byte array and execute methods inside of // their app domain from our full-trust app domain. It's the bridge that // crosses the app domain boundary. var executionSandbox = TraceUtility.TraceTime("Create ExecutionSandbox Instance", () => (ExecutionSandbox)sandboxDomain.CreateInstance( assemblyType.Assembly.FullName, assemblyType.FullName, false, BindingFlags.Public | BindingFlags.Instance, null, null, null, null) .Unwrap()); // Prepare packages by locating the proper assemblies for the current framework and ensure // they have been downloaded to the packages folder and return their paths. executionSandbox.SetReferences(await PreparePackagesAsync(routePackages).TraceTimeAsync("Prepare Packages")); // To properly load assemblies into the dynamic partial trust assembly, we have to override // the AssemblyResolve method which is only called when an assembly load attempt is made // and fails. We can't use a closure here because to do that, the entire class ExecutionMethods // would have to be serailized across the app domain boundry. So we'll add a string array property // to the ExecutionSandbox class so we can access the references from in the app domain boundry. // Just remember this event is executed inside the partial trust domain. sandboxDomain.AssemblyResolve += (sender, args) => { var name = new AssemblyName(args.Name); var path = ExecutionSandbox.References.FirstOrDefault(r => Path.GetFileNameWithoutExtension(r) == name.Name); return(path == null ? null : Assembly.LoadFrom(path)); }; // Build the ExecutionRequest object that represents the incoming request // which holds the payload, headers, method, etc. The class is serialized // so it can cross the app domain boundary. So it's serialized in our // full-trust host app domain, and deserialized and reinstantiated in // the sandbox app domain. var uri = new Uri(request.Uri, UriKind.Absolute); var executionRequest = TraceUtility.TraceTime("Create RouteRequest Instance", () => new RouteRequest(uri, request.Method) { IpAddress = request.IpAddress, Headers = HeaderHelpers.DeserializeHeaders(request.RequestHeaders), Body = request.RequestPayload }); try { // The ExecutionSandbox we'll attempt to locate the best method to execute // based on the incoming request method (GET, POST, DELETE, etc.) and // will pass the ExecutionRequest we created above. In return, we receive // an instance of ExecutionResponse that has been serialized like the request // and deserialized in our full-trust host domain. var executionResponse = TraceUtility.TraceTime("Load and Execute Request", () => executionSandbox.Execute(route.Assembly, executionRequest)); // We'll use the data that comes back from the response to fill out the // remainder of the database request record which will return the status // code, message, payload, and headers. Then we update the database. request.CompletedOn = DateTimeOffset.UtcNow; request.StatusCode = (int)executionResponse.StatusCode; request.StatusMessage = executionResponse.StatusMessage; request.ResponsePayload = executionResponse.Body; request.ResponseHeaders = RouteResponse.SerializeHeaders(executionResponse.Headers); await Program.RequestRepository.UpdateRequestAsync(request).TraceTimeAsync("Update Request Record"); // We'll pass back a small bit of data indiciating to the subscribers of // the response topic listening for our specific correlation ID that indicates // the route code was executed successfully and to handle it as such. responseMessage.Properties["Result"] = (int)ExecutionResult.Success; responseMessage.Properties["Message"] = "Completed Successfully"; // Create the response message and send it on its way. response.Add(responseMessage); } catch (TargetInvocationException invokationException) { // These exceptions can occur when we encounter a permission exception where // the user doesn't have permission to execute a particular block of code. if (invokationException.InnerException is SecurityException securityException) { throw new RoutePermissionException(GetPermissionErrorMessage(securityException), invokationException); } // Check for BadRequestException, we need to wrap it with the core exception. // These exceptions can occur when query string parsing fails, and since the // user's code doesn't have access to the core exceptions, we'll need to wrap // it instead manually. if (invokationException.InnerException is BadRequestException badRequestException) { throw new Core.Exceptions.BadRequestException(badRequestException.Message, badRequestException); } // Otherwise it is most likely a custom user exception. throw new CodeException(invokationException.InnerException?.Message ?? "Route raised a custom exception.", invokationException.InnerException); } catch (EntryPointException entryPointException) { // These exceptions occur when an entry point could not be located. // Since we don't have a reference to core in the common library. // We'll instead wrap this exception in a core // exception to apply a status code. throw new RouteEntryPointException(entryPointException.Message, entryPointException); } catch (SecurityException securityException) { // These exceptions can occur when we encounter a permission exception where // the user doesn't have permission to execute a particular block of code. throw new RoutePermissionException(GetPermissionErrorMessage(securityException), securityException); } catch (BadRequestException badRequestException) { // These exceptions can occur when query string parsing fails, and since the // user's code doesn't have access to the core exceptions, we'll need to wrap // it instead manually. throw new Core.Exceptions.BadRequestException(badRequestException.Message, badRequestException); } catch (AggregateException asyncException) // Captures async and task exceptions. { // These exceptions occur when an entry point could not be located. // Since we don't have a reference to core in the common library. // We'll instead wrap this exception in a core // exception to apply a status code. if (asyncException.InnerException is EntryPointException entryPointException) { throw new RouteEntryPointException(entryPointException.Message, entryPointException); } // These exceptions can occur when we encounter a permission exception where // the user doesn't have permission to execute a particular block of code. if (asyncException.InnerException is SecurityException securityException) { throw new RoutePermissionException(GetPermissionErrorMessage(securityException), securityException); } // These exceptions can occur when query string parsing fails, and since the // user's code doesn't have access to the core exceptions, we'll need to wrap // it instead manually. if (asyncException.InnerException is SecurityException badRequestException) { throw new Core.Exceptions.BadRequestException(badRequestException.Message, badRequestException); } // These are all other exceptions that occur during the execution of // a route. These exceptions are raised by the users code. throw new RouteException(asyncException.InnerException?.Message ?? asyncException.Message, asyncException.InnerException); } catch (Exception routeException) { // These are all other exceptions that occur during the execution of // a route. These exceptions are raised by the users code. throw new RouteException(routeException.Message, routeException); } } catch (Exception appDomainException) { // This exception relates to exceptions configuring the AppDomain and we'll still notify the // user, we just won't give them specific information that could reveal our infrastructure // unless an IStatusCodeException was thrown, meaning it's a public exception. var statusCode = 500; var statusMessage = "An unexpected exception has occurred. Please contact Subroute.io regarding this error."; var statusCodeException = appDomainException as IStatusCodeException; string stackTrace = null; if (statusCodeException != null) { statusCode = (int)statusCodeException.StatusCode; statusMessage = appDomainException.Message; if (appDomainException is CodeException) { stackTrace = appDomainException.ToString(); } } request.CompletedOn = DateTimeOffset.UtcNow; request.StatusCode = statusCode; request.ResponsePayload = PayloadHelpers.CreateErrorPayload(statusMessage, stackTrace); request.ResponseHeaders = HeaderHelpers.GetDefaultHeaders(); await Program.RequestRepository.UpdateRequestAsync(request).TraceTimeAsync("Update Request Record (Error)"); responseMessage.Properties["Result"] = (int)ExecutionResult.Failed; responseMessage.Properties["Message"] = appDomainException.Message; // Create the response message and send it on its way. response.Add(responseMessage); } } catch (Exception fatalException) { // These exceptions are absolutely fatal. We'll have to notify the waiting thread // via the service bus message, because we're unable to load a related request. responseMessage.Properties["Result"] = (int)ExecutionResult.Fatal; responseMessage.Properties["Message"] = fatalException.Message; // Create the response message and send it on its way. response.Add(responseMessage); } finally { // Unload the users app domain to recover all memory used by it. if (sandboxDomain != null) { TraceUtility.TraceTime("Unload AppDomain", () => AppDomain.Unload(sandboxDomain)); } } }