AjaxRacer

This tutorial describes how to use the AjaxRacer tool, presented in the paper "Practical AJAX Race Detection for JavaScript Web Applications" (in Proc. ESEC/FSE 2018).

This artifact consists of:

  1. an Ubuntu 14.04.5 virtual machine (vm.ova), where AjaxRacer and all of its dependencies have already been installed,1 and
  2. the source code of AjaxRacer (GitHub).

The page Getting started describes how to run a quick experiment to check if everything works as it should.

The page Usage instructions describes how to use AjaxRacer.

The page Install instructions describes how to install AjaxRacer on a fresh Ubuntu 14.04.5 machine (i.e., how to derive the virtual machine that has already been prepared).


1The username and password of the virtual machine is "ajaxracer".

Getting started

The reader may choose to use the pre-configured virtual machine, or follow the install instructions. To use the pre-configured virtual machine vm.ova, open VirtualBox and import vm.ova via "Import Appliance..." in the File menu.

We recommend using the following VirtualBox settings for the virtual machine:

  • System → Motherboard → Base Memory: 8192 MB
  • System → Processor → Processor(s): 4 CPUs
  • Display → Screen → Video Memory: 128 MB

AjaxRacer has been installed in the directory ~/ajaxracer/ in the virtual machine. The usage instructions on this page are relative to this directory. Hence, start by opening a terminal and stepping into this directory:

cd ~/ajaxracer/

Verifying that everything works

Run AjaxRacer on one of the tests too check that AjaxRacer and its dependencies have been installed correctly by issuing following command (this will take a few minutes):

./ajaxracer.js --run-id test --url test/ajax_nondeterministic_ui/index.html

Then run the following command to serve the report using a local HTTP server (note that this command will build the web site, which takes a few minutes):

npm run report

Finally, open the URL http://localhost:3000/ in a browser to view the automatically generated report. The report should look similar to this one.

Usage instructions

Just getting started? Check Getting started.

Running AjaxRacer

Consider the web page in the directory test/ajax_nondeterministic_ui/ (see on GitHub). This example illustrates the essence of an observable AJAX race. AjaxRacer can be applied to this page by issuing the following command:

./ajaxracer.js --run-id test --url test/ajax_nondeterministic_ui/index.html

Note:

  1. The argument passed to the --run-id option is simply an identifier for the run. It merely specifies which directory that should be used for the output and does not need to be unique. In this case, the result of the run is stored in the out/test/ directory.

  2. The URL is automatically converted to http://localhost:8080/test/ajax_nondeterministic_ui/index.html as the path points to a test on the disk (AjaxRacer starts an HTTP server on port 8080 on its own).

  3. By default, AjaxRacer uses a headless instance of Google Chrome. This can be circumvented by passing the flag --no-headless. This is mostly intended for testing purposes, though.

Overview

When executing the ./ajaxracer.js command, the following happens:

  1. Phase 1 of AjaxRacer is run (described in [1]) using the script in src/phases/phase-1.js.

    This script starts a proxy server on port 8081 (using src/utils/proxy.js). The proxy server dynamically intercepts and instruments HTML and JavaScript source files (see instrumentation_server/proxy.py). The actual instrumentation is carried out by src/instrumentation/instrument-html.js and src/instrumentation/instrument-js.js.

    When the proxy server has been started, the web page is loaded in Google Chrome using the Protractor testing framework (using src/utils/protractor.js). The interaction with the web page is carried out by the Protractor test script driver/spec.js. This script waits for the web application to load, and then sends a message to the web application that will generate a trace for every event handler. This is carried out by the analysis that is injected into the web application by the instrumentation. The key components responsible for generating the traces are src/analysis/analysis.js, src/analysis/modes/observation-mode-execution.js, and src/analysis/monitors/event-monitor.js.

    Finally, the results are stored by driver/spec.js to the disk.

  2. Phase 2 of AjaxRacer is run using the script in src/phases/phase-2.js.

    This script will analyze the traces from Phase 1 to find "potentially AJAX conflicting pairs of user event handlers". For each such pair, the script runs the given pair of user event handlers in "synchronous" mode and "adverse" mode [1]. This involves starting a proxy server and loading the web application in the browser using the Protractor testing framework, similar to the way Phase 1 works.

    Unlike in Phase 1, though, the Protractor test script driver/spec.js will cause src/analysis/modes/adverse-mode-execution.js and src/analysis/modes/synchronous-mode-execution.js to run in the browser.

Viewing the results

The results of the run can be viewed in the browser by issuing the command npm run report. This command will compile the TypeScript source files in the report/ directory to JavaScript, which may take a few minutes. Afterwards, the results will be available at http://localhost:3000/.

Note that it is only necessary to run this command once: if AjaxRacer is applied to a website after this command is run, the results will automatically become available at http://localhost:3000/, if the page is reloaded.

The generated report should look similar to the one that was automatically generated for the experiments from [1]. This report is available at http://www.brics.dk/experiments/.

Experiments

The experiments from [1] can be run by issuing the following command (note that this takes several hours):

./run-experiments.js

This script is essentially just a wrapper around ./ajaxracer.js. For example, to run the experiments for the web page "Customize your MacBook" on https://www.apple.com/, one can issue one of the following commands:

./ajaxracer.js --run-id apple-customize-macbook --url "https://www.apple.com/shop/buy-mac/macbook?product=MNYF2LL/A&step=config"

or

./run-experiments.js --filter apple-customize-macbook

Note that AjaxRacer runs on the live versions of the web pages from [1]. As a consequence, it will not necessarily be possible to reproduce the results if some of these web pages change. As of June 2018, some web pages have been redesigned (e.g., https://www.chevronwithtechron.com/station used to look like this, http://www.fanniemae.com/portal/jsp/search.html used to look like this) and others have been updated in a way that affects the results (e.g., the tabs on https://www.apple.com/accessibility/iphone no longer use AJAX but instead redirect the user to a different web page).

End-to-end tests

AjaxRacer comes with a set of tests in the test/ directory. These tests can be run using the following command (this indirectly invokes the script test/test.js):

npm test

To run only tests that matches "ajax_nondeterministic_ui/", one can issue the following command:

npm test -- --filter "ajax_nondeterministic_ui/"

The traces that result from executing the tests will be output to the out/ directory, and will be compared to the expected traces, which are stored in expected.json files.

Debugging

The script ./debug.sh is useful for debugging. It automatically starts the proxy server and launches Google Chrome with proper flags for using the proxy server to automatically instrument whatever website is visited.

This way it is possible to debug errors in the instrumentation and the dynamic analysis using the developer toolbar in the browser.

References

[1] Practical AJAX Race Detection for JavaScript Web Applications (in Proc. ESEC/FSE 2018).

Install instructions

Instructions for installing AjaxRacer and its dependencies are available in the INSTALL.md from the GitHub repository. This page repeats these instructions for the reader's convenience.

The following install instructions have been tested on Ubuntu 14.04.5, and can be used to derive the virtual machine that has been included with the AjaxRacer artifact.

Installing dependencies

Misc. prerequisites

sudo apt update
sudo apt install curl git graphviz

Node.js v8.9.4 and NPM packages

  1. Install Node.js and the NPM package manager

    curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -
    sudo apt-get install -y nodejs
  2. Install Node.js v8.9.4 using the NPM package n

    sudo npm install -g n
    sudo n 8.9.4
  3. Verify that the Node.js v8.9.4 is installed by running

    node --version

  4. Install the following NPM packages

    sudo npm install -g http-server lite-server @angular/cli@1.4.1

Google Chrome and Protractor v5.2.0

  1. Install Google Chrome from https://www.google.com/chrome.

  2. Install Protractor using NPM

    sudo npm install -g protractor@5.2.0
    sudo webdriver-manager update

mitmproxy v0.17

  1. Install mitmproxy v0.17:

    sudo apt install python-pip python-dev libffi-dev libssl-dev libxml2-dev libxslt1-dev libjpeg8-dev zlib1g-dev
    sudo pip install -U pip
    sudo pip install mitmproxy==0.17

    Troubleshooting: If the last command fails with an error, try to reinstall Python 2.7 (this should remove some packages that will be installed with mitmproxy):

    sudo apt remove python-2.7
    sudo apt install python-2.7

    It may be necessary to run sudo apt-get install ubuntu-desktop to prevent a blank screen after rebooting.

  2. Install the mitmproxy certificate (needed for AjaxRacer to work with web applications that use SSL):

    1. Start mitmproxy by issuing the command mitmdump -p 8080.
    2. Open http://mitm.it/ in Google Chrome by issuing the following command after closing all instances of Google Chrome:

      google-chrome-stable http://mitm.it/ --proxy-server="127.0.0.1:8080"
    3. Download the mitmproxy certificate mitmproxy-ca-cert.pem for Ubuntu by clicking on the button "Other".

    4. Install the mitmproxy certificate: Open chrome://settings/certificates in Google Chrome, click on "Authorities", and import the certificate mitmproxy-ca-cert.pem. In addition, issue the following commands.

      sudo cp ~/Downloads/mitmproxy-ca-cert.pem /usr/local/share/ca-certificates/mitmproxy-ca-cert.crt
      sudo update-ca-certificates
    5. Test that the mitmproxy certificate is installed correctly by running the following command.

      google-chrome-stable https://github.com/cs-au-dk/ajaxracer --proxy-server="127.0.0.1:8080"

      If mitmproxy has not been setup properly, Google Chrome will show a warning saying "Your connection is not private".

Installing AjaxRacer

Issue the following commands to install AjaxRacer.

git clone https://github.com/cs-au-dk/ajaxracer.git
cd ajaxracer
npm install
(cd report/; npm install)

Verifying that everything works

See Getting started.