├── jsparsy.config.json-sample ├── jsparsy.urls.json-sample ├── README.md └── jsparsy.js /jsparsy.config.json-sample: -------------------------------------------------------------------------------- 1 | { 2 | "proto": "http", 3 | "domain": "localhost", 4 | "login": { 5 | "enabled": false, 6 | "method": "post", 7 | "action": "/login.php", 8 | "failure": "Password is incorrect", 9 | "fields": { 10 | "username": "john", 11 | "password": "doe" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /jsparsy.urls.json-sample: -------------------------------------------------------------------------------- 1 | [ 2 | {"url": "http://localhost/", "status": 200, "depth": 1, "referer": ""}, 3 | {"url": "http://localhost/todo", "status": 200, "depth": 2, "referer": "http://localhost/"}, 4 | {"url": "http://localhost/faq", "status": 200, "depth": 2, "referer": "http://localhost/"}, 5 | {"url": "http://localhost/user/profile", "status": 200, "depth": 2, "referer": "http://localhost/"}, 6 | {"url": "http://localhost/user/bookmarks", "status": 200, "depth": 2, "referer": "http://localhost/"} 7 | ] 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jsparsy 2 | 3 | Jsparsy will scan a set of URLs for javascript runtime errors, console.log() outputs, as well as server errors. 4 | It is capable of doing a full POST or GET login prior checking the given URLs. 5 | 6 | ## Requirements 7 | 8 | * [CasperJS](http://casperjs.org/) 9 | * [PhantomJS](http://phantomjs.org/) 10 | * [crawlpy](https://github.com/cytopia/crawlpy) 11 | 12 | 13 | ## Usage 14 | 15 | ```shell 16 | casperjs jsparsy.js --config=/path/to/config.json --urls=/path/to/urls.json --server-errors=yes|no 17 | ``` 18 | 19 | ## How to configure jsparsy 20 | 21 | See [jsparsy.config.json-sample](https://github.com/cytopia/jsparsy/blob/master/jsparsy.config.json-sample). 22 | 23 | It is also possible and even recommended to use the configuration file from [crawlpy](https://github.com/cytopia/crawlpy), 24 | as you will need that anyway to generate the URL's (next section). 25 | 26 | 27 | ## Howto generate the URLs? 28 | 29 | You can use [crawlpy](https://github.com/cytopia/crawlpy) to generate the URL json file. 30 | -------------------------------------------------------------------------------- /jsparsy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: cytopia (0x695128A2) 3 | * @date: 2016-08-12 4 | * @version: v0.3 5 | * 6 | * Check given URL's for JS run-time errors, console.log and server errors. 7 | */ 8 | 9 | 10 | 11 | //------------------------------------------------------------ 12 | // Requires 13 | //------------------------------------------------------------ 14 | 15 | // CasperJS 16 | var casper = require('casper').create({ 17 | verbose: true, // Print log messages? 18 | logLevel: 'warning', // 'debug', 'info', 'warning', 'error' overwrite via --loglevel=info 19 | pageSettings: { 20 | loadImages: false, // Do not load images 21 | loadPlugins: false, // Do not load plugins like Flash, Silverlight, etc. 22 | javascriptEnabled : true, // Parse Javascript 23 | XSSAuditingEnabled: false, // Do not audit xss attempts 24 | userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4' 25 | } 26 | }); 27 | 28 | // Filesystem library 29 | var fs = require('fs'); 30 | 31 | 32 | 33 | //------------------------------------------------------------ 34 | // Command line arguments 35 | //------------------------------------------------------------ 36 | 37 | // Check configuration file cli argument 38 | if (!casper.cli.has('conf')) { 39 | casper.echo('No configuration file specified', 'ERROR'); 40 | casper.echo('Usage: runtime-errors.js --conf=/path/to/config.json --urls=/path/to/urls.json --server-errors=yes|no'); 41 | casper.done(1); 42 | casper.exit(1); 43 | //phantom.exit(1); 44 | } 45 | // Check url's file cli argument 46 | if (!casper.cli.has('urls')) { 47 | casper.echo('No url file specified', 'ERROR'); 48 | casper.echo('Usage: runtime-errors.js --conf=/path/to/config.json --urls=/path/to/urls.json --server-errors=yes|no'); 49 | casper.done(1); 50 | casper.exit(1); 51 | //phantom.exit(1); 52 | } 53 | // Check url's file cli argument 54 | if (!casper.cli.has('server-errors')) { 55 | casper.echo('Must specify --server-errors=yes|no', 'ERROR'); 56 | casper.echo('Usage: runtime-errors.js --conf=/path/to/config.json --urls=/path/to/urls.json --server-errors=yes|no'); 57 | casper.done(1); 58 | casper.exit(1); 59 | //phantom.exit(1); 60 | } 61 | 62 | // Check if the file actually exists 63 | if (!fs.exists(casper.cli.get('conf'))) { 64 | casper.echo('Config file: "' + casper.cli.get('conf') + '" does not exist', 'ERROR'); 65 | casper.echo('Usage: runtime-errors.js --conf=/path/to/config.json --urls=/path/to/urls.json --server-errors=yes|no'); 66 | casper.done(1); 67 | casper.exit(1); 68 | //phantom.exit(1); 69 | } 70 | // Check if the file actually exists 71 | if (!fs.exists(casper.cli.get('urls'))) { 72 | casper.echo('Url file: "' + casper.cli.get('urls') + '" does not exist', 'ERROR'); 73 | casper.echo('Usage: runtime-errors.js --conf=/path/to/config.json --urls=/path/to/urls.json --server-errors=yes|no'); 74 | casper.done(1); 75 | casper.exit(1); 76 | //phantom.exit(1); 77 | } 78 | // Check if server-errors is on or off 79 | if (casper.cli.get('server-errors') != 'yes' && casper.cli.get('server-errors') != 'no') { 80 | casper.echo('Must specify --server-errors=yes|no', 'ERROR'); 81 | casper.echo('Usage: runtime-errors.js --conf=/path/to/config.json --urls=/path/to/urls.json --server-errors=yes|no'); 82 | casper.done(1); 83 | casper.exit(1); 84 | //phantom.exit(1); 85 | } 86 | 87 | //------------------------------------------------------------ 88 | // Includes 89 | //------------------------------------------------------------ 90 | var config = require(casper.cli.get('conf')); 91 | var urls = require(casper.cli.get('urls')); 92 | 93 | 94 | 95 | 96 | //------------------------------------------------------------ 97 | // Init globals 98 | //------------------------------------------------------------ 99 | var base_url = config.proto + '://' + config.domain; 100 | var login_url = base_url + config.login.action; 101 | 102 | /** 103 | * Error storage 104 | */ 105 | var show_srv_err = (casper.cli.get('server-errors') == 'yes') ? true : false 106 | var errors_runtime = []; 107 | var errors_server = []; 108 | var errors_console = []; 109 | 110 | 111 | 112 | 113 | /******************************************************************************** 114 | * 115 | * E V E N T L I S T E N E R 116 | * 117 | ********************************************************************************/ 118 | 119 | 120 | /** 121 | * This function is called when any other callback has failed. 122 | * Nice self-checking stuff ;-) 123 | */ 124 | casper.on('complete.error', function(err) { 125 | this.die('Complete callback has failed: ' + err); 126 | }); 127 | 128 | 129 | /** 130 | * On Each Request Start (Initiated) 131 | */ 132 | casper.on('resource.requested', function(resource, request) { 133 | //this.echo('[-->] ' + resource.url, 'INFO'); 134 | }); 135 | 136 | 137 | /** 138 | * On reach Request End (Received) 139 | */ 140 | casper.on('resource.received', function(resource) { 141 | //this.echo('[' + resource.status + '] ' + resource.url); 142 | }); 143 | 144 | 145 | /** 146 | * On Remote Message (console.log found) 147 | */ 148 | casper.on('remote.message', function(msg) { 149 | this.echo('--------------------------------------------------------------------------------', 'ERROR'); 150 | this.echo('[CONSOLE.LOG]', 'WARNING'); 151 | this.echo('Error: ' + msg, 'WARNING'); 152 | errors_console.push(msg); 153 | }); 154 | 155 | 156 | /** 157 | * On Resource Error (Server error) 158 | */ 159 | casper.on('resource.error', function(resourceError) { 160 | if (show_srv_err) { 161 | this.echo('--------------------------------------------------------------------------------', 'ERROR'); 162 | this.echo('[SERVER ERROR]', 'WARNING'); 163 | this.echo('Error: ' + resourceError.errorString, 'WARNING'); 164 | this.echo('URL: ' + resourceError.url, 'WARNING'); 165 | this.echo('Code: ' + resourceError.errorCode, 'WARNING'); 166 | this.echo('ID: ' + resourceError.id, 'WARNING'); 167 | errors_server.push(resourceError.errorString); 168 | } 169 | }); 170 | 171 | 172 | /** 173 | * On Page Error (Client error) 174 | */ 175 | casper.on('page.error', function(msg, trace) { 176 | this.echo('--------------------------------------------------------------------------------', 'ERROR'); 177 | this.echo('[CLIENT ERROR]', 'ERROR'); 178 | this.echo('Error: ' + msg, 'ERROR'); 179 | this.echo('URL: ' + this.getCurrentUrl(), 'WARNING'); 180 | this.echo('file: ' + trace[0].file, 'WARNING'); 181 | this.echo('line: ' + trace[0].line, 'WARNING'); 182 | this.echo('function: ' + trace[0]['function'], 'WARNING'); 183 | errors_runtime.push(msg); 184 | }); 185 | 186 | 187 | 188 | /******************************************************************************** 189 | * 190 | * RUN 191 | * 192 | ********************************************************************************/ 193 | 194 | 195 | // Do a Login before continuing 196 | if (config.login.enabled) { 197 | 198 | // Initial request is to start on the login page itself 199 | // instead of the base url, as many sites require 200 | // a pre-session to be setup. 201 | casper.start(login_url); 202 | 203 | // Login 204 | casper.log('Initiating login at: ' + login_url, 'info'); 205 | casper.thenOpen(login_url, { 206 | 'method': config.login.method, 207 | 'data': config.login.fields 208 | }); 209 | 210 | // Check if Login was successful 211 | casper.then(function() { 212 | var htmlSource = this.getPageContent(); 213 | var failString = config.login.failure; 214 | 215 | // Login failed 216 | if (htmlSource.search(failString) != -1) { 217 | casper.log('Login failed', 'error'); 218 | //casper.done(1); 219 | casper.exit(1); 220 | //phantom.exit(1); 221 | } else { 222 | casper.log('Login successful', 'info'); 223 | } 224 | }) 225 | } 226 | // No Login required 227 | else { 228 | // Start on the base url 229 | casper.start(base_url); 230 | casper.log('No login required', 'info'); 231 | } 232 | 233 | 234 | 235 | /** 236 | * Iterate over all URL's 237 | */ 238 | for (var i=0; i 0) { 254 | this.echo(errors_runtime.length + ' Javascript errors found', 'WARNING'); 255 | exit_code = 1; 256 | } else { 257 | this.echo('No Javascript errors found', 'INFO'); 258 | } 259 | 260 | // Console.log found (Abort with error) 261 | if (errors_console.length > 0) { 262 | this.echo(errors_console.length + ' console.log found', 'WARNING'); 263 | } else { 264 | this.echo('No console.log found', 'INFO'); 265 | } 266 | 267 | if (show_srv_err) { 268 | // Server errors (http >= 500) [don't give a shit] 269 | if (errors_server.length > 0) { 270 | this.echo(errors_server.length + ' Server errors found', 'WARNING'); 271 | } else { 272 | this.echo('No Server errors found', 'INFO'); 273 | } 274 | } 275 | 276 | casper.exit(exit_code); 277 | }); 278 | 279 | --------------------------------------------------------------------------------