sebadorn.de

JavaScript source protection with NW.js

You can minify and uglify JavaScript files, but technically the source code of your distributed NW.js application is still readable. But NW.js also provides the means to compile JavaScript to a binary file and then load it as part of the application. The command line tool nwjc to create the binary file is included in the SDK version.

Assuming you have a JavaScript file js/private.js:

'use strict';

function secretFunction( foo ) {
    return foo * 4;
};

Then you can compile it like this to a file js/private.bin:

$ ./nwjs-sdk-v0.30.5-linux-x64/nwjc js/private.js js/private.bin

Internally the tool uses the V8 snapshot feature, which means the versions have to match. A binary file created with NW.js 0.30 can only be loaded by 0.30. Binary files also do not work cross-platform. For each platform it is necessary to compile its own binary file with the SDK for the same platform.

To then load the binary file in your application, it works like this:

let win = nw.Window.get();
win.evalNWBin( null, 'js/private.bin' );

let value = secretFunction( 4 ); // returns 16

Note however that the loading is per window. If you open another window in your application, the file has to be loaded there again.

Using the DevTools you can of course find the functions and variables which have been loaded from the file. The function implementation however is protected:

> String( secretFunction )
< "function secretFunction() { [native code] }"

DevTools issues

Update 2018-12-15: Since NW.js 0.34 this issue seems to be fixed. Loading binary files works even with the DevTools open.


There is an issue with loading binary files and the DevTools. Basically you cannot have the DevTools open and then load the file. There will be no error, but the contents will not be available. This is a known issue.

My temporary solution is to just close the DevTools. But just closing them right before is not enough, you also have to use a timeout before loading the file:

let win = nw.Window.get();

// Function is only available in the SDK build.
if( typeof win.closeDevTools === 'function' ) {
    win.closeDevTools();
}

setTimeout( () => {
    win.evalNWBin( null, 'js/private.bin' );
}, 500 );

But why not check first if the DevTools are open? Then you could open them again afterwards. According to the API documentation there is win.isDevToolsOpen(). But it exists only in the documentation. Using the SDK build there is de facto no such function. This too is a known issue.

Wine for Windows

I successfully used Wine 3 to compile a binary file for the Windows version of a NW.js application and then load it there. So if you are on Linux or macOS you will not need Windows for your build process. You should of course still test your application to make sure it works on all targeted platforms.


Using NW.js to communicate with a DS4 controller

DS4 green light

NW.js still provides the Chrome Apps API which has been removed from Chrome, but not ChromeOS. This will allow us to access in a platform-independant manner devices which are connected with the PC per USB.

Without this API, a 3rd party Node.js module like node-hid could be used. This will however come with platform-dependant libraries and will have to be updated or rebuild each time the Node.js version changes.

This article concentrates on sending data to the controller. However it is also possible to retrieve data like pressed buttons using the established connection. Aside from using chrome.hid there is also the Gamepad API for read-only access.

Identifying the controller

First we need a way to identify the DS4. Devices come with a vendor Id and product Id. According to the Gentoo Wiki they are as follows:

Device Vendor Id Product Id
DS4 (1st gen) hex 054C / dec 1356 hex 05C4 / dec 1476
DS4 (2nd gen) hex 054C / dec 1356 hex 09CC / dec 2508

Having tested with both devices, I can also confirm the Ids.

Get the device

For all communication with the device, we will use the chrome.hid API. First we define a filter using the vendor and product Id, and then query the available devices:

var filter = {
    filter: [
        { vendorId: 1356, productId: 1476 },
        { vendorId: 1356, productId: 2508 }
    ]
};

chrome.hid.getDevices( filter, ( devices ) => {
    // Error handling.
    if( chrome.runtime.lastError ) {
        console.error( chrome.runtime.lastError );
        return;
    }
    if( !devices ) {
        return;
    }

    var device = devices[0];
    // Next: Connect to the device.
};
Read more

Android file transfer over MTP is a nightmare

Update, 30.09.2017: After trying it again today, I found none of the problems listed below anymore. It's fast and painless. So that's good!


It was bad before, but after the upgrade to Ubuntu 16.04 – which may or may not be related, it probably is – it went from bad to downright painful. Connecting device and PC works fine. What does not is …

  • It is slow. Opening the SD card directory with my music takes several seconds.
  • After deleting files the amount of available space is not updated. I free space for new files but get a warning telling me there isn't enough free space. At least I have the option to try and copy the files anyway, ignoring the warning.
  • The new problem: After just one transfer, I cannot access the device per MTP anymore. No content is listed for the music directory anymore. Seemingly trying to load the contents it ultimately fails to do so. Unplugging does not help. Restarting the PC does not help. Restarting the device does not help. Using a different PC with macOS does not help. What helps is … using the Android file manager and deleting a file. Something is seriously broken here.

In early Android versions the device was mounted as USB mass storage. Those were the good, old days. At least they had understandable reasons for replacing it.

Nautilus FTP

An alternative to using MTP is FTP. I used the app FTPServer to start an FTP server on my device while connected to my home WiFi. In the FTPServer settings I chose wlan0 as standard interface and pointed the server root to the directory /mnt/extSdCard. Now I can use FileZilla or Nautilus (see image above) to connect to the server from my PC. The transfer speed is of course a little limited, but the file transfer itself? – works just fine.

While I'm happy to have found a good alternative to the nightmare that is MTP, I find this whole situation quite ridiculous. At some point I'd like to upgrade from my Galaxy S3. While I'm not considering iOS or Windows Phone, I'm also disappointed of Android. Hopefully the Ubuntu phones take of.


Improvements after Mozilla’s Observatory results

Mozilla made their Observatory service public, which lets you check the security of sites. A first run resulted in an F for sebadorn.de. Following some of the sug­gestions I could improve that to a B-.

1. Redirect HTTP to HTTPS

Thanks to Let’s Encrypt I already offered HTTPS, but I didn't enforce it. Now visitors to http://sebadorn.de are redirected to https://sebadorn.de. I did so by adding the following rule to my .htaccess file:

<IfModule mod_rewrite.c>
	RewriteEngine On
	RewriteCond %{HTTP_HOST} ^sebadorn\.de [NC]
	RewriteCond %{SERVER_PORT} 80
	RewriteRule ^(.*)$ https://sebadorn.de/$1 [R,L]
</IfModule>

2. Add some more headers

<IfModule mod_headers.c>
	Header always edit Set-Cookie (.*) "$1; HttpOnly; Secure"
	Header set Content-Security-Policy "frame-ancestors 'self'"
	Header set X-Content-Type-Options "nosniff"
	Header set X-Frame-Options "SAMEORIGIN"
	Header set X-XSS-Protection "1; mode=block"
</IfModule>
Set-Cookie
Cookies about to be set received additional directives: HttpOnly and Secure. HttpOnly disallows cookies being read by JavaScript and Secure enforces an HTTPS connection. (Source)
X-Content-Type-Options
Setting this header to nosniff tells browsers not to try and guess the MIME type of contents, which potentially prevents XSS attacks. (Source)
X-Frame-Options
Setting this header to SAMEORIGIN or DENY prevents other pages from displaying the site in a frame which prevents clickjacking. (Source)
X-XSS-Protection
Setting this header to 1; mode=block tells browsers to try and detect XSS attacks and in this case stop loading the page. (Source)

Remove intro/outro from an MP3 without re-encoding

I have this podcast I listen to repeatedly. But most episodes have an intro and outro part before the actual episode, which gets quite annoying. One possibility is, of course, to just use an audio editor like Audacity, cut the unwanted parts, and save the file. But this would result in the MP3 being re-encoded and losing in quality. This shouldn't be necessary since I only want to cut off some data, right?

The tool for the job is mp3splt. I wrote this little bash script:

#!/usr/bin/env bash

F_IN=$1
F_OUT=${F_IN%.mp3}
START=$2
END=EOF-$3
OUT_DIR=./nointro

# Split at "-".
FILE_SPLIT=(${F_IN//-/ })

# Remove "_" and " " characters.
TRACK=${FILE_SPLIT[0]//_/}
TRACK=${TRACK// /}

mp3splt -f -d "$OUT_DIR" "$F_IN" "$START" "$END" -o "$F_OUT"
eyeD3 --track=$TRACK "$OUT_DIR/$F_IN"

Example: ./shorten.sh 26-FacelessOldWoman.mp3 1.18 1.15.5

First, the track number will be extracted from the file name. In my case, the number is always at the beginning and separated by a minus from the title. So we split the string and remove some unwanted characters (whitespace, underscore).

In the example, the first 1min 18sec will be removed and the last 1min 15.5sec. The resulting MP3 will be saved with the same name in the directory set in OUT_DIR.

Most id3 tags will be kept – including the embedded cover –, but the track number will be overwritten by mp3splt. That is why I extracted the track number from the file name before. Now I can set it again with eyeD3.

Done.