NAME

Mojolicious::Plugin::Loco - launch local GUI via default web browser

VERSION

version 0.003

SYNOPSIS

use Mojolicious::Lite;
plugin 'Loco';

get '/' => "index";

post '/kick-me' => sub {
  my $c = shift;
  return if $c->loco->csrf_fail;
  # system q(cat /dev/urandom | fdisk);
  $c->flash(msg => 'Ok, that hurt.');
  $c->redirect_to('/');
};

post '/quit' => sub { shift->loco->quit; };

app->start;

__DATA__
@@ index.html.ep
% layout 'default';
<h1>Ready!</h1>
%= csrf_button_to "Kick Me", '/kick-me', method => 'POST';
%= csrf_button_to "Exit",    '/quit',    method => 'POST';
<p><%= $c->flash('msg')%></p>

@@ layouts/default.html.ep
<!DOCTYPE html><html><head>
%= $c->loco->jsload;    # needs to be on every page
%= content_for 'head';
</head><body>
%= content
</body>
</html>

DESCRIPTION

This plugin allows writing low-effort desktop applications using Mojolicious (even cross-platform ones if your code is sufficiently portable).

When run as a daemon listening on an explicit loopback/localhost port, the plugin causes a dedicated window to open in your default internet browser, assuming that we're in a desktop/window-manager context where an internet browser application is installed. The server will then shut down once the browser window and all descendants thereof are subsequently closed.

For applications that will be handling sensitive data or that will be making changes to the filesystem or other local machine state, please see "SECURITY CONSIDERATIONS"

This module is currently experimental; the API may change without notice.

OPTIONS

initial_wait

How many seconds to wait on server start for browser window to finish loading. Defaults to 10.

final_wait

How many seconds to wait after browser window ceases communicating before terminating (we do not rely on window.unload). Defaults to 3. Settable from "loco.conf".

Since javascript timer events from backgrounded/hidden tabs/windows are typically throttled, reducing this below 2 will most likely make the application prematurely terminate whenever the window is hidden or minimized.

entry

URI path for the entry point of your application (i.e., what to display when the brower window opens). Defaults to /.

api_path

Path prefix for URIs used by this plugin. This is where the various endpoints needed by this module live (and is also where the required javascript file(s) are served from). Defaults to /hb/; it can be pretty much anything as long as it's distinct from what the rest of your application uses. But it would be best to keep it short.

allow_other_browsers

If false (default), $c->validation->csrf_protect rejects all csrf_token values other than the one shared between the server and the initial browser launched. Requests that check for this are then blocked from all other sessions, i.e., those created by other local browsers/processes (or remote processes if the listening point is network accessible). Setting this true disables this extra protection and $c->validation->csrf_protect will behave in the default manner.

Read "SECURITY CONSIDERATIONS" if you think you might want to set this.

HELPERS

loco->conf

Get or set plugin configuration values. Only some values are actually settable.

$c->loco->conf                       # -> full hash of settings
$c->loco->conf('final_wait')         # -> 3
$c->loco->conf(final_wait => 200)    # -> $c, sets final_wait
$c->loco->conf({final_wait => 200})  # -> same

loco->csrf_fail

Performs the csrf_token validation; returns true and renders an an HTTP 400 reply if it fails, otherwise returns false. If "allow_other_browsers" is false, this includes the "same browser" check.

return if $c->loco->csrf_fail;

loco->quit

Stop the server and render a final page if necessary. This includes the csrf_token check.

loco->jsload

Loads whatever javascript needs to be in the <head> section of every page to be displayed in the browser window. You most likely want this in your default layout.

%= $c->loco->jsload;

Or you can be more elaborate

  %= $c->loco->jsload( jquery => 'https://code.jquery.com/jquery-3.3.1.min.js', begin
        .on_hb(function(h) {
          // do something on every heartbeat
	  $('#heartbeat').html(h);
        })
  % end );

Options for "jsload" include

jquery

URL to be loading jquery from. Default is to use the jquery version included in the Mojolicious distribution. Specifying an empty string '' suppresses jquery loading entirely, if you have already loaded it as part of some other package.

nofinish

Suppress the default on_finish handler.

begin

Final begin block, if provided, will be assumed to be javascript code to further configure the heartbeat object (code is preceded by $().heartbeat()), typically to add on_hb or on_finish handlers

SECURITY CONSIDERATIONS

In a typical desktop application, traffic between the user interface and the application code will be invisible to remote attackers and code running in different processes. Structuring your application as a Mojolicious server with UI provided by an internet browser using this plugin will, in some cases, expose this traffic and make additional interferences possible.

What follows is a (necessarily incomplete) listing of vulnerabilities that can be introduced.

Listening on Network-accessible Interfaces/Ports

It is stongly recommended that you not use a listening point that is network accessible, i.e., use either a loopback address, UNIX socket (if available), unless you have a reliable firewall to block incoming connection attempts from untrusted sites. Note that the listening point for Mojolicious servers is the value of -l argument passed to the daemon command, or the value of $ENV{MOJO_LISTEN} if set, or localhost:3000 by default, so listening on a network-accessible interface should not happen unless you explicitly call for it.

You should especially not use a remotely accessible listing point if you have set "allow_other_browsers".

Cross-Site Scripting

We assume the use of a modern browser that makes HTML page content and cookies unobservable from scripts running in other domains. However since the style sheets and script pages served by your application will be arbitrarily accessible cross-domain, you should ensure that they do not reveal any application secrets (e.g., the current csrf_token).

Also, your pages should not reference or include script code from untrusted sources.

Cross-Site Request Forgery

Since any webpage may contain links, buttons, or forms targeting your application, you should use $c->validation->csrf_protect on any requests that change application state, write to the local filesystem, or otherwise make use of privileges beyond those typically available in a browser scripting context.

Access From Other Local Processes

With this plugin loaded, $c->validation->csrf_protect is restricted to only allowing requests from the session created by the browser invoked by the plugin.

If, however you choose to set "allow_other_browsers" then your application is potentially vulnerable to requests from other processes (including other browsers) running on the local host, which may then make their own connections to the listening port and obtain usable sessions. This will be of special concern if, e.g.,

  • You are concurrently running instances of alternative or out-dated browsers visiting untrusted sites able to exploit cross-site scripting vulnerabiliities to obtain the contents of your application's HTML pages or cookie values.

  • There are other local users on your machine.

  • Your application runs setuid or otherwise invokes elevated privileges (which could then be used by other processes to bypass various protections).

METHODS

Mojolicious::Plugin::Loco inherits all methods from Mojolicious::Plugin and implements the following new ones.

register

$plugin->register(Mojolicious->new, entry => '/');

Register plugin in Mojolicious application.

SEE ALSO

Mojolicious, Mojolicious::Guides, https://mojolicious.org.

AUTHOR

Roger Crew <wrog@cpan.org>

COPYRIGHT AND LICENSE

This software is copyright (c) 2018 by Roger Crew.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.