How can I block loading remote content in DotNetBrowser? - wpf

I'm using the DotNetBrowser in my WPF emailing application to display the emails content. I'd like to block every remote content and remote images. I use this email privacy tester to check if I can correctly block the remote content.
I checked the documentation of the DotNetBrowser and I found that it's possible to disable couple of things using the BrowserPreferences setting. I disabled everything with the following piece of code:
BrowserPreferences pref = new BrowserPreferences {
ImagesEnabled = false,
JavaScriptEnabled = false,
PluginsEnabled = false,
WebAudioEnabled = false,
ApplicationCacheEnabled = false,
LocalStorageEnabled = false,
AllowDisplayingInsecureContent = false,
AllowRunningInsecureContent = false,
...
};
wpfBrowserView.Browser.Preferences = pref;
wpfBrowserView.Browser.LoadHTML(myHtml);
But this blocks just couple of the possible harmful contents. Then I set a custom LoadHandler, where I could prevent couple of more cases:
MyBrowserLoadHandler loadHandler = new MyBrowserLoadHandler();
loadHandler.Load += args => {
// just don't allow to load the content
};
wpfBrowserView.Browser.LoadHandler = loadHandler;
This is not enough, because it still fails two of them (the Link Prefetch and the CSS link tag).
I don't want to do a static analysis on the email's html to handle those cases as well, so I'm searching for an easier way to do it. For example in the Android's WebView, it's just two methods to call (setBlockNetworkLoads(true) and setBlockNetworkImage(true)) and it does the whole thing. Is there a solution like this in DotNetBrowser?

Finally I found the solution. I dropped out all of my tries what I posted above, and tried another way. The DotNetBrowser has a resource handler and you can set what kind of resources you'd like to allow to load and what not. Here is my code:
var network = wpfBrowserView.Browser.Context.NetworkService;
var resourceHandler = new MyResourceHandler();
resourceHandler.Load += args => {
ResourceType resource = args.Parameters.ResourceType;
switch (resource) {
case ResourceType.PREFETCH:
case ResourceType.IMAGE:
case ResourceType.MEDIA:
case ResourceType.OBJECT:
case ResourceType.STYLESHEET:
case ResourceType.FONT_RESOURCE:
case ResourceType.SUB_RESOURCE:
return false;
default:
// allow to load for the others
return true;
}
};
network.ResourceHandler = resourceHandler;
And the custom resource handler:
public class ResourceLoadEventArgs {
public ResourceParams Parameters { get; set; }
}
public delegate bool ResourceLoadHandler(ResourceLoadEventArgs args);
public class MyResourceHandler : ResourceHandler {
public event ResourceLoadHandler Load;
public bool CanLoadResource(ResourceParams parameters) {
return Load?.Invoke(new ResourceLoadEventArgs { Parameters = parameters }) ?? true;
}
}
So, adding this piece of code before loading the html into the browser view, it causes to pass every test in the email privacy tester. Then you can put a button for the user to load the remote content, and when the user clicks on it, you can allow every resource.

Related

Nancy request tracing does not show anything

After many trials, I can't seem to get Nancy's Diagnostic Tracing to show anything beyond a set of empty white boxes. It appears one box per browser session. Yet the boxes never include any information.
Per the diagnostics page I've added a diagnostics password and set
StaticConfiguration.EnableRequestTracing = true;
I expected there to be at some sort of default tracing out of the box, but just in case I made a call to
this.Context.Trace.TraceLog.WriteLog(x => x.AppendLine("What's ip?"));
in one of my modules, and confirmed said module route was getting called. Still no indication there was tracing going on.
We're running NancyFx version 1.45 in a framework 4.7.1 app. Any suggestions on why this could be happening?
Thanks
Enable tracing and diagnostics by using a custom bootstrapper.
For NancyFx v1:
NancyOptions options = new NancyOptions
{
Bootstrapper = new CustomBootstrapper()
};
app.UseNancy(options);
public class CustomBootstrapper : DefaultNancyBootstrapper
{
protected override void ApplicationStartup(TinyIoCContainer container, IPipelines pipelines)
{
base.ApplicationStartup(container, pipelines);
StaticConfiguration.DisableErrorTraces = false;
StaticConfiguration.EnableRequestTracing = true;
if (!StaticConfiguration.EnableRequestTracing)
{
DiagnosticsHook.Disable(pipelines);
}
}
protected override DiagnosticsConfiguration DiagnosticsConfiguration => new DiagnosticsConfiguration() { Enabled = true, Password = #"A2\6mVtH/XRT\p,B" };
}
For NancyFx v2:
NancyOptions options = new NancyOptions
{
Bootstrapper = new CustomBootstrapper()
};
app.UseNancy(options);
public class CustomBootstrapper : DefaultNancyBootstrapper
{
public override void Configure(INancyEnvironment environment)
{
environment.Diagnostics(
enabled: true,
password: #"A2\6mVtH/XRT\p,B");
environment.Tracing(
enabled: true,
displayErrorTraces: true);
}
}

Redirect if claim is missing in RequiresAuthentication

I'm working on setting up two-factor in my application and I'm trying to make it redirect back to the verification page if the user is logged in but not verified (I'm keeping track of if the user is verified in the sessions table which is added to ClaimsPrincipal 'IsVerified').
The problem i'm having is the example I am using from the documentation doesn't seem to be working properly:
public static class ModuleSecurity
{
public static string[] ExcludedPaths = { "/", "/login", "/login/verify", "/admin/settings", "/login/tf/setup" };
public static void RequiresAuthentication(this NancyModule module)
{
module.Before.AddItemToEndOfPipeline(RequiresAuthentication);
}
private static Response RequiresAuthentication(NancyContext context)
{
// Check if user is authenticated
if (context.CurrentUser == null)
return new Response() { StatusCode = HttpStatusCode.Unauthorized };
if (context.CurrentUser.FindFirst("RequireVerification")?.Value == "True" && context.CurrentUser.FindFirst("IsVerified")?.Value != "True" && !ExcludedPaths.Any(x => x.ToLower() == context.Request.Path.ToLower()))
return new Response().WithHeader("Location", "/login/verify").WithContentType("text/html").WithStatusCode(HttpStatusCode.SeeOther);
return null;
}
}
Putting break points in I see the "module.Before.AddItemToEndOfPipeline" is executed but it is not executing the other method I have.
Then problem was I was adding this to the BEFORE pipeline but i'm calling this.RequiresClaims after the route was triggered (so I needed the AFTER pipeline). I was able to do this by adding the extensions and using the module.AddBeforeOrExecute option.

CefSharp: Injecting custom CSS File using a custom scheme

I'm using CefSharp (47) to render a webpage from a host that I have no control over, and I want to make some additional CSS tweaks to those provided by the host.
Reading up on various topics across GitHub (https://github.com/cefsharp/CefSharp/blob/cefsharp/47/CefSharp.Example/CefSharpSchemeHandlerFactory.cs), and here (CefSharp custom SchemeHandler), I wrote a custom scheme handler accordingly:
public class CustomSchemeHandlerFactory : ISchemeHandlerFactory
{
public const string SchemeName = "custom";
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
{
Console.WriteLine(request.Url);
if (schemeName.ToLower() == SchemeName.ToLower())
{
// Do some stuff
}
return null;
}
}
I attempt to bind it in my application in the following manner:
CefSettings settings = new CefSettings();
settings.CachePath = browserCachePath;
settings.RegisterScheme(new CefCustomScheme()
{
SchemeName = CustomSchemeHandlerFactory.SchemeName,
SchemeHandlerFactory = new CustomSchemeHandlerFactory()
});
Cef.Initialize(settings);
The application then browses to the appropriate website, and uses the 'LoadingStateChanged' event to then fire off some JavaScript to inject the CSS file I want to load:
string linkText = "<link rel=\u0022stylesheet\u0022 type=\u0022text/css\u0022 href=\u0022custom://custom.css\u0022>";
var jsFunctionText = string.Format("(function() {{ $('head').append('{0}'); return true;}}) ();", linkText);
var injectionTask = await _myBrowser.GetMainFrame().EvaluateScriptAsync(jsFunctionText, null);
...which succeeds.
But my custom resource handler 'Create' event is never fired.
I can only presume that the handler isn't being registered properly, so I'd appreciate any advice/help in getting this working properly!
Thanks!

How do I make sure the UI is updated during long running processes in a WPF application?

In a WPF app that follows the MVVM pattern, I've run across a common issue where a user clicks on a button which fires an event in the ViewModel. This event should enable a "Please Wait" spinner animation, do some processing which may take a few seconds, then hide the spinner. I'm not really sure of a good pattern I can use to make sure the spinner animation always appears.
As an example, I have a login process which does the following:
Displays spinner (set property on VM to true, spinner is bound to it)
Attempt to connect to server (can take a few seconds depending on connection)
On a failure, display a failure message
On success, save off some info about the user so it's available to the rest of the app.
What I'm finding is that the spinner never actually appears. I have tried wrapping the longer-running process in a Task.Run call, but that hasn't seemed to help.
Here's an approximation of what the code looks like:
// When true, spinner should be visible
protected bool _authenticatingIsVisible = false;
public bool AuthenticatingIsVisible
{
get { return _authenticatingIsVisible; }
set
{
_authenticatingIsVisible = value;
NotifyOfPropertyChange(() => AuthenticatingIsVisible);
}
}
public void Login()
{
try
{
AuthenticationIsVisible = true;
AuthCode result = AuthCode.NoAuthenticated;
Task.Run(() => { result = _client.Authenticate() }).Wait();
AuthenticationIsVisible = false;
if (result == AuthCode.Authenticated)
{
// Bit of misc. code to set up the environment
// Another check to see if something has failed
// If it has, displays a dialog.
// ex.
var error = new Error("Something Failed", "Details Here", Answer.Ok);
var vm = new DialogViewModel() { Dialog = error };
_win.ShowDialog(vm);
return;
}
else
{
DisplayAuthMessage(result);
}
}
finally
{
AuthenticationIsVisible = false;
}
}
The proper way would be not to block the UI thread (which is what you are doing right now with .Wait()), and use AsyncAwait instead.
private Task<AuthCode> Authenticate()
{
return Task.Run<AuthCode>(()=>
{
return _client.Authenticate();
});
}
public async void Login()
{
AuthenticationIsVisible = true;
AuthCode result = await Authenticate();
AuthenticationIsVisible = false;
}

Getting the current page when receiving a toast notification (WP8.1 Silverlight, receiving WNS toast notification)

I have an event that fires when the app is live and I receive an notification CurrentChannel_PushNotificationReceived. In this function I want to find out which page is currently displayed to know if the notification should update content on the page. The question is therefore twofold, how to know which page is currently displayed and interact with the toast notification.
Update
The issue is that I cannot interact with the elements because of clash with the OS threading (Dispatcher).
Therefore using the below code it allows me to access the content of the message. But I am still not able to get the info of the current_page
_channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
_channel.PushNotificationReceived += OnPushNotificationReceived;
private void OnPushNotificationReceived(PushNotificationChannel sender, PushNotificationReceivedEventArgs args)
{
switch (args.NotificationType)
{
case PushNotificationType.Badge:
this.OnBadgeNotificationReceived(args.BadgeNotification.Content.GetXml());
break;
case PushNotificationType.Tile:
this.OnTileNotificationReceived(args.TileNotification.Content.GetXml());
break;
case PushNotificationType.Toast:
this.OnToastNotificationReceived(args.ToastNotification.Content.GetXml());
break;
case PushNotificationType.Raw:
this.OnRawNotificationReceived(args.RawNotification.Content);
break;
}
args.Cancel = true;
}
private void OnBadgeNotificationReceived(string notificationContent)
{
// Code when a badge notification is received when app is running
}
private void OnTileNotificationReceived(string notificationContent)
{
// Code when a tile notification is received when app is running
}
private void OnToastNotificationReceived(string notificationContent)
{
// Code when a toast notification is received when app is running
// Show a toast notification programatically
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(notificationContent);
var toastNotification = new ToastNotification(xmlDocument);
//toastNotification.SuppressPopup = true;
ToastNotificationManager.CreateToastNotifier().Show(toastNotification);
}
private void OnRawNotificationReceived(string notificationContent)
{
// Code when a raw notification is received when app is running
}
Question
How do I access the current page information in the different onXXXXNotificationReceived. The current snippets work but not within these functions:
var currentPage = ((PhoneApplicationFrame)Application.Current.RootVisual).Content;
var tempBool = currentPage.GetType() is BC_Menu.StartUp.SecondScreen;
or
RootFrame.CurrentSource;
My guess is it is because of the UI-thread. So how can I use the dispatcher to get the information? I have tried some solutions with the dispatcher, but I cannot await the information, and therefore it is not applicable.
System.Windows.Threading.DispatcherOperation op = App.RootFrame.Dispatcher.BeginInvoke(new Func<Uri>(() =>
{
return RootFrame.CurrentSource;
})
);
await op; //Not awaitable.
There's no reason to await the dispatcher to the UI thread. Simply dispatch to the UI thread and then perform the rest of your logic, like displaying the toast or navigating to a page, from within the UI thread...
Register the event...
var channel = await PushNotificationChannelManager.CreatePushNotificationChannelForApplicationAsync();
channel.PushNotificationReceived += Channel_PushNotificationReceived;
On the event handler, cancel displaying the notification and then dispatch to UI thread...
private void Channel_PushNotificationReceived(PushNotificationChannel sender, PushNotificationReceivedEventArgs args)
{
// Make sure you cancel displaying the toast on this thread (not on UI thread)
// since cancellation needs to be set before this thread/method returns
args.Cancel = true;
// Then dispatch to the UI thread
App.RootFrame.Dispatcher.BeginInvoke(delegate
{
var currPage = ((PhoneApplicationFrame)Application.Current.RootVisual).Content;
switch (args.NotificationType)
{
case PushNotificationType.Toast:
// TODO
break;
}
});
}
Do all of your code inside the dispatcher's delegate. All your code will be executing on the UI thread... you'll be able to navigate pages, obtain current page, etc.
Ok. Try this. Create a static property on App.xaml.cs.
public static object CurrentPageInfo { get; set; }
And assign the page type or page name to the property on 'OnNavigatedTo' method on every page.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var currentPage = ((PhoneApplicationFrame)Application.Current.RootVisual).Content;
App.CurrentPageInfo = currentPage.GetType() is BC_Menu.StartUp.SecondScreen;
}
So that you can identify the page source type on receiving notifications by accessing the App.CurrentPageInfo property. Hope it helps!

Resources