Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

initial commit. xss, lfi, csrf, and 404 so far

  • Loading branch information...
commit bcb770d613dda528c01aa1d11924e6124f2619a1 0 parents
@contra contra authored
Showing with 3,531 additions and 0 deletions.
  1. +20 −0 LICENSE
  2. +60 −0 README.md
  3. +722 −0 fusker-log.txt
  4. +13 −0 index.html
  5. +3 −0  lib/config.js
  6. +87 −0 lib/fusker.js
  7. +21 −0 lib/mods/404.js
  8. +17 −0 lib/mods/csrf.js
  9. +7 −0 lib/mods/lfi.js
  10. 0  lib/mods/sqli.js
  11. +11 −0 lib/mods/xss.js
  12. 0  lib/util.js
  13. +1 −0  node_modules/validator/.gitignore
  14. +20 −0 node_modules/validator/LICENSE
  15. +169 −0 node_modules/validator/README.md
  16. +14 −0 node_modules/validator/index.html
  17. +1 −0  node_modules/validator/index.js
  18. +291 −0 node_modules/validator/lib/entities.js
  19. +89 −0 node_modules/validator/lib/filter.js
  20. +15 −0 node_modules/validator/lib/index.js
  21. +230 −0 node_modules/validator/lib/validator.js
  22. +198 −0 node_modules/validator/lib/xss.js
  23. +27 −0 node_modules/validator/package.json
  24. +4 −0 node_modules/validator/test.js
  25. +134 −0 node_modules/validator/test/filter.test.js
  26. +454 −0 node_modules/validator/test/validator.test.js
  27. +23 −0 node_modules/validator/validator-min.js
  28. +838 −0 node_modules/validator/validator.js
  29. +45 −0 package.json
  30. +17 −0 test.js
20 LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2011 Contra <contra@australia.edu>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
60 README.md
@@ -0,0 +1,60 @@
+**node-fusker is a library to prevent and manage a multitude of attacks in nodejs**
+
+To install node-fusker, use [npm](http://github.com/isaacs/npm):
+
+ $ npm install fusker
+
+## Example
+
+Please see [test.js](http://github.com/Contra/fusker/blob/master/test.js) for a working example and documentation
+
+## Levels of lulz (attacking the attackers)
+
+ None - Blacklists IP
+ Low - Blacklists IP, Redirects to Google
+ Fair - Blacklists IP, Redirects to Saxroll
+ High - Blacklists IP, Redirects to browser attack page (high chance of crashing the attacker)
+ Extreme - Blacklists IP, Opens infinite tabs of attack pages (will absolutely crash the attacker)
+
+## List of included detection modules
+
+ 404 - Detects 404 as a form of attack. Only use this if you have a single page application and want to punish people who like to snoop around
+ xss - Detects common XSS attacks in incoming http requests
+ sqli - Detects SQLi attempts in incoming http requests. Only an idiot would try to SQLi a node server (lol)
+ lfi - Detects common LFI attacks in incoming http requests
+
+## Extending the library
+
+Add a .js or .coffee file to the 'mods' directory of the module.
+
+Example:
+ Adding msi.js to /mods/ will allow you to fusker.detect.push('msi');
+
+## Contributors
+
+- [tprime](https://github.com/tprime) - General
+
+## LICENSE
+
+(MIT License)
+
+Copyright (c) 2011 Contra <contra@australia.edu>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
722 fusker-log.txt
@@ -0,0 +1,722 @@
+[ - ATTACK DETAILS FOR function now() { [native code] } - ]
+ --> Module: CSRF
+ --> Request: GET /index.html?wat=1%22%3E%3Cimg%20src=x%20onerror=alert%28%22XSS%22%29%3E%3Cnoscript%3E
+ --> IP: 127.0.0.1
+ --> Raw Request: { bufferSize: 0,
+ fd: 7,
+ type: 'tcp4',
+ allowHalfOpen: true,
+ _readWatcher:
+ { socket: [Circular],
+ callback: [Function: onReadable] },
+ destroyed: false,
+ readable: true,
+ _writeQueue: [],
+ _writeQueueEncoding: [],
+ _writeQueueFD: [],
+ _writeQueueCallbacks: [],
+ _writeWatcher:
+ { socket: [Circular],
+ callback: [Function: onWritable] },
+ writable: true,
+ _writeImpl: [Function],
+ _readImpl: [Function],
+ _shutdownImpl: [Function],
+ remoteAddress: '127.0.0.1',
+ remotePort: 43438,
+ server:
+ { connections: 1,
+ allowHalfOpen: true,
+ watcher: { host: [Circular], callback: [Function] },
+ _events:
+ { request: [Function],
+ connection: [Function: connectionListener] },
+ httpAllowHalfOpen: false,
+ type: 'tcp4',
+ fd: 5 },
+ ondrain: [Function],
+ _idleTimeout: 120000,
+ _idleNext:
+ { repeat: 120,
+ _idleNext: [Circular],
+ _idlePrev: [Circular],
+ callback: [Function] },
+ _idlePrev:
+ { repeat: 120,
+ _idleNext: [Circular],
+ _idlePrev: [Circular],
+ callback: [Function] },
+ _idleStart: Fri, 22 Jul 2011 08:09:46 GMT,
+ _events:
+ { timeout: [Function],
+ error: [Function],
+ close: [Function] },
+ ondata: [Function],
+ onend: [Function],
+ _httpMessage:
+ { output: [],
+ outputEncodings: [],
+ writable: true,
+ _last: false,
+ chunkedEncoding: false,
+ shouldKeepAlive: true,
+ useChunkedEncodingByDefault: true,
+ _hasBody: true,
+ _trailer: '',
+ finished: false,
+ socket: [Circular],
+ connection: [Circular],
+ _events: { finish: [Function] } } }
+[ - END ATTACK DETAILS - ]
+
+[ - ATTACK DETAILS FOR function now() { [native code] } - ]
+ --> Module: CSRF
+ --> Request: GET /favicon.ico
+ --> IP: 127.0.0.1
+ --> Raw Request: { bufferSize: 0,
+ fd: 7,
+ type: 'tcp4',
+ allowHalfOpen: true,
+ _readWatcher:
+ { socket: [Circular],
+ callback: [Function: onReadable] },
+ destroyed: false,
+ readable: true,
+ _writeQueue: [],
+ _writeQueueEncoding: [],
+ _writeQueueFD: [],
+ _writeQueueCallbacks: [],
+ _writeWatcher:
+ { socket: [Circular],
+ callback: [Function: onWritable] },
+ writable: true,
+ _writeImpl: [Function],
+ _readImpl: [Function],
+ _shutdownImpl: [Function],
+ remoteAddress: '127.0.0.1',
+ remotePort: 43438,
+ server:
+ { connections: 1,
+ allowHalfOpen: true,
+ watcher: { host: [Circular], callback: [Function] },
+ _events:
+ { request: [Function],
+ connection: [Function: connectionListener] },
+ httpAllowHalfOpen: false,
+ type: 'tcp4',
+ fd: 5 },
+ ondrain: [Function],
+ _idleTimeout: 120000,
+ _idleNext:
+ { repeat: 120,
+ _idleNext: [Circular],
+ _idlePrev: [Circular],
+ callback: [Function] },
+ _idlePrev:
+ { repeat: 120,
+ _idleNext: [Circular],
+ _idlePrev: [Circular],
+ callback: [Function] },
+ _idleStart: Fri, 22 Jul 2011 08:09:46 GMT,
+ _events:
+ { timeout: [Function],
+ error: [Function],
+ close: [Function] },
+ ondata: [Function],
+ onend: [Function],
+ _httpMessage:
+ { output: [],
+ outputEncodings: [],
+ writable: true,
+ _last: false,
+ chunkedEncoding: false,
+ shouldKeepAlive: true,
+ useChunkedEncodingByDefault: true,
+ _hasBody: true,
+ _trailer: '',
+ finished: false,
+ socket: [Circular],
+ connection: [Circular],
+ _events: { finish: [Function] } } }
+[ - END ATTACK DETAILS - ]
+
+[ - ATTACK DETAILS FOR function now() { [native code] } - ]
+ --> Module: CSRF
+ --> Request: GET /index.html?wat=1%22%3E%3Cimg%20src=x%20onerror=alert%28%22XSS%22%29%3E%3Cnoscript%3E
+ --> IP: 127.0.0.1
+ --> Raw Request: { bufferSize: 0,
+ fd: 8,
+ type: 'tcp4',
+ allowHalfOpen: true,
+ _readWatcher:
+ { socket: [Circular],
+ callback: [Function: onReadable] },
+ destroyed: false,
+ readable: true,
+ _writeQueue: [],
+ _writeQueueEncoding: [],
+ _writeQueueFD: [],
+ _writeQueueCallbacks: [],
+ _writeWatcher:
+ { socket: [Circular],
+ callback: [Function: onWritable] },
+ writable: true,
+ _writeImpl: [Function],
+ _readImpl: [Function],
+ _shutdownImpl: [Function],
+ remoteAddress: '127.0.0.1',
+ remotePort: 43448,
+ server:
+ { connections: 2,
+ allowHalfOpen: true,
+ watcher: { host: [Circular], callback: [Function] },
+ _events:
+ { request: [Function],
+ connection: [Function: connectionListener] },
+ httpAllowHalfOpen: false,
+ type: 'tcp4',
+ fd: 5 },
+ ondrain: [Function],
+ _idleTimeout: 120000,
+ _idleNext:
+ { bufferSize: 0,
+ fd: 7,
+ type: 'tcp4',
+ allowHalfOpen: true,
+ _readWatcher:
+ { socket: [Circular],
+ callback: [Function: onReadable] },
+ destroyed: false,
+ readable: true,
+ _writeQueue: [],
+ _writeQueueEncoding: [],
+ _writeQueueFD: [],
+ _writeQueueCallbacks: [],
+ _writeWatcher:
+ { socket: [Circular],
+ callback: [Function: onWritable] },
+ writable: true,
+ _writeImpl: [Function],
+ _readImpl: [Function],
+ _shutdownImpl: [Function],
+ remoteAddress: '127.0.0.1',
+ remotePort: 43438,
+ server:
+ { connections: 2,
+ allowHalfOpen: true,
+ watcher: [Object],
+ _events: [Object],
+ httpAllowHalfOpen: false,
+ type: 'tcp4',
+ fd: 5 },
+ ondrain: [Function],
+ _idleTimeout: 120000,
+ _idleNext:
+ { repeat: 120,
+ _idleNext: [Circular],
+ _idlePrev: [Circular],
+ callback: [Function] },
+ _idlePrev: [Circular],
+ _idleStart: Fri, 22 Jul 2011 08:09:46 GMT,
+ _events:
+ { timeout: [Function],
+ error: [Function],
+ close: [Function] },
+ ondata: [Function],
+ onend: [Function],
+ _httpMessage:
+ { output: [],
+ outputEncodings: [],
+ writable: true,
+ _last: false,
+ chunkedEncoding: false,
+ shouldKeepAlive: true,
+ useChunkedEncodingByDefault: true,
+ _hasBody: true,
+ _trailer: '',
+ finished: false,
+ socket: [Circular],
+ connection: [Circular],
+ _events: [Object] } },
+ _idlePrev:
+ { repeat: 120,
+ _idleNext: [Circular],
+ _idlePrev:
+ { bufferSize: 0,
+ fd: 7,
+ type: 'tcp4',
+ allowHalfOpen: true,
+ _readWatcher: [Object],
+ destroyed: false,
+ readable: true,
+ _writeQueue: [],
+ _writeQueueEncoding: [],
+ _writeQueueFD: [],
+ _writeQueueCallbacks: [],
+ _writeWatcher: [Object],
+ writable: true,
+ _writeImpl: [Function],
+ _readImpl: [Function],
+ _shutdownImpl: [Function],
+ remoteAddress: '127.0.0.1',
+ remotePort: 43438,
+ server: [Object],
+ ondrain: [Function],
+ _idleTimeout: 120000,
+ _idleNext: [Circular],
+ _idlePrev: [Circular],
+ _idleStart: Fri, 22 Jul 2011 08:09:46 GMT,
+ _events: [Object],
+ ondata: [Function],
+ onend: [Function],
+ _httpMessage: [Object] },
+ callback: [Function] },
+ _idleStart: Fri, 22 Jul 2011 08:09:53 GMT,
+ _events:
+ { timeout: [Function],
+ error: [Function],
+ close: [Function] },
+ ondata: [Function],
+ onend: [Function],
+ _httpMessage:
+ { output: [],
+ outputEncodings: [],
+ writable: true,
+ _last: false,
+ chunkedEncoding: false,
+ shouldKeepAlive: true,
+ useChunkedEncodingByDefault: true,
+ _hasBody: true,
+ _trailer: '',
+ finished: false,
+ socket: [Circular],
+ connection: [Circular],
+ _events: { finish: [Function] } } }
+[ - END ATTACK DETAILS - ]
+
+[ - ATTACK DETAILS FOR function now() { [native code] } - ]
+ --> Module: CSRF
+ --> Request: GET /favicon.ico
+ --> IP: 127.0.0.1
+ --> Raw Request: { bufferSize: 0,
+ fd: 8,
+ type: 'tcp4',
+ allowHalfOpen: true,
+ _readWatcher:
+ { socket: [Circular],
+ callback: [Function: onReadable] },
+ destroyed: false,
+ readable: true,
+ _writeQueue: [],
+ _writeQueueEncoding: [],
+ _writeQueueFD: [],
+ _writeQueueCallbacks: [],
+ _writeWatcher:
+ { socket: [Circular],
+ callback: [Function: onWritable] },
+ writable: true,
+ _writeImpl: [Function],
+ _readImpl: [Function],
+ _shutdownImpl: [Function],
+ remoteAddress: '127.0.0.1',
+ remotePort: 43448,
+ server:
+ { connections: 1,
+ allowHalfOpen: true,
+ watcher: { host: [Circular], callback: [Function] },
+ _events:
+ { request: [Function],
+ connection: [Function: connectionListener] },
+ httpAllowHalfOpen: false,
+ type: 'tcp4',
+ fd: 5 },
+ ondrain: [Function],
+ _idleTimeout: 120000,
+ _idleNext:
+ { repeat: 7,
+ _idleNext: [Circular],
+ _idlePrev: [Circular],
+ callback: [Function] },
+ _idlePrev:
+ { repeat: 7,
+ _idleNext: [Circular],
+ _idlePrev: [Circular],
+ callback: [Function] },
+ _idleStart: Fri, 22 Jul 2011 08:11:46 GMT,
+ _events:
+ { timeout: [Function],
+ error: [Function],
+ close: [Function] },
+ ondata: [Function],
+ onend: [Function],
+ _httpMessage:
+ { output: [],
+ outputEncodings: [],
+ writable: true,
+ _last: false,
+ chunkedEncoding: false,
+ shouldKeepAlive: true,
+ useChunkedEncodingByDefault: true,
+ _hasBody: true,
+ _trailer: '',
+ finished: false,
+ socket: [Circular],
+ connection: [Circular],
+ _events: { finish: [Function] } } }
+[ - END ATTACK DETAILS - ]
+
+[ - ATTACK DETAILS FOR function now() { [native code] } - ]
+ --> Module: CSRF
+ --> Request: GET /favicon.ico
+ --> IP: 127.0.0.1
+ --> Raw Request: { bufferSize: 0,
+ fd: 7,
+ type: 'tcp4',
+ allowHalfOpen: true,
+ _readWatcher:
+ { socket: [Circular],
+ callback: [Function: onReadable] },
+ destroyed: false,
+ readable: true,
+ _writeQueue: [],
+ _writeQueueEncoding: [],
+ _writeQueueFD: [],
+ _writeQueueCallbacks: [],
+ _writeWatcher:
+ { socket: [Circular],
+ callback: [Function: onWritable] },
+ writable: true,
+ _writeImpl: [Function],
+ _readImpl: [Function],
+ _shutdownImpl: [Function],
+ remoteAddress: '127.0.0.1',
+ remotePort: 34537,
+ server:
+ { connections: 1,
+ allowHalfOpen: true,
+ watcher: { host: [Circular], callback: [Function] },
+ _events:
+ { request: [Function],
+ connection: [Function: connectionListener] },
+ httpAllowHalfOpen: false,
+ type: 'tcp4',
+ fd: 5 },
+ ondrain: [Function],
+ _idleTimeout: 120000,
+ _idleNext:
+ { repeat: 120,
+ _idleNext: [Circular],
+ _idlePrev: [Circular],
+ callback: [Function] },
+ _idlePrev:
+ { repeat: 120,
+ _idleNext: [Circular],
+ _idlePrev: [Circular],
+ callback: [Function] },
+ _idleStart: Fri, 22 Jul 2011 08:13:46 GMT,
+ _events:
+ { timeout: [Function],
+ error: [Function],
+ close: [Function] },
+ ondata: [Function],
+ onend: [Function],
+ _httpMessage:
+ { output: [],
+ outputEncodings: [],
+ writable: true,
+ _last: false,
+ chunkedEncoding: false,
+ shouldKeepAlive: true,
+ useChunkedEncodingByDefault: true,
+ _hasBody: true,
+ _trailer: '',
+ finished: false,
+ socket: [Circular],
+ connection: [Circular],
+ _events: { finish: [Function] } } }
+[ - END ATTACK DETAILS - ]
+
+[ - ATTACK DETAILS FOR function now() { [native code] } - ]
+ --> Module: CSRF-1
+ --> Request: GET /index.html?wat=1%22%3E%3Cimg%20src=x%20onerror=alert%28%22XSS%22%29%3E%3Cnoscript%3E
+ --> IP: 127.0.0.1
+ --> Raw Request: { bufferSize: 0,
+ fd: 7,
+ type: 'tcp4',
+ allowHalfOpen: true,
+ _readWatcher:
+ { socket: [Circular],
+ callback: [Function: onReadable] },
+ destroyed: false,
+ readable: true,
+ _writeQueue: [],
+ _writeQueueEncoding: [],
+ _writeQueueFD: [],
+ _writeQueueCallbacks: [],
+ _writeWatcher:
+ { socket: [Circular],
+ callback: [Function: onWritable] },
+ writable: true,
+ _writeImpl: [Function],
+ _readImpl: [Function],
+ _shutdownImpl: [Function],
+ remoteAddress: '127.0.0.1',
+ remotePort: 34558,
+ server:
+ { connections: 1,
+ allowHalfOpen: true,
+ watcher: { host: [Circular], callback: [Function] },
+ _events:
+ { request: [Function],
+ connection: [Function: connectionListener] },
+ httpAllowHalfOpen: false,
+ type: 'tcp4',
+ fd: 5 },
+ ondrain: [Function],
+ _idleTimeout: 120000,
+ _idleNext:
+ { repeat: 120,
+ _idleNext: [Circular],
+ _idlePrev: [Circular],
+ callback: [Function] },
+ _idlePrev:
+ { repeat: 120,
+ _idleNext: [Circular],
+ _idlePrev: [Circular],
+ callback: [Function] },
+ _idleStart: Fri, 22 Jul 2011 08:15:26 GMT,
+ _events:
+ { timeout: [Function],
+ error: [Function],
+ close: [Function] },
+ ondata: [Function],
+ onend: [Function],
+ _httpMessage:
+ { output: [],
+ outputEncodings: [],
+ writable: true,
+ _last: false,
+ chunkedEncoding: false,
+ shouldKeepAlive: true,
+ useChunkedEncodingByDefault: true,
+ _hasBody: true,
+ _trailer: '',
+ finished: false,
+ socket: [Circular],
+ connection: [Circular],
+ _events: { finish: [Function] } } }
+[ - END ATTACK DETAILS - ]
+
+[ - ATTACK DETAILS FOR function now() { [native code] } - ]
+ --> Module: CSRF-1
+ --> Request: GET /favicon.ico
+ --> IP: 127.0.0.1
+ --> Raw Request: { bufferSize: 0,
+ fd: 7,
+ type: 'tcp4',
+ allowHalfOpen: true,
+ _readWatcher:
+ { socket: [Circular],
+ callback: [Function: onReadable] },
+ destroyed: false,
+ readable: true,
+ _writeQueue: [],
+ _writeQueueEncoding: [],
+ _writeQueueFD: [],
+ _writeQueueCallbacks: [],
+ _writeWatcher:
+ { socket: [Circular],
+ callback: [Function: onWritable] },
+ writable: true,
+ _writeImpl: [Function],
+ _readImpl: [Function],
+ _shutdownImpl: [Function],
+ remoteAddress: '127.0.0.1',
+ remotePort: 34558,
+ server:
+ { connections: 1,
+ allowHalfOpen: true,
+ watcher: { host: [Circular], callback: [Function] },
+ _events:
+ { request: [Function],
+ connection: [Function: connectionListener] },
+ httpAllowHalfOpen: false,
+ type: 'tcp4',
+ fd: 5 },
+ ondrain: [Function],
+ _idleTimeout: 120000,
+ _idleNext:
+ { repeat: 120,
+ _idleNext: [Circular],
+ _idlePrev: [Circular],
+ callback: [Function] },
+ _idlePrev:
+ { repeat: 120,
+ _idleNext: [Circular],
+ _idlePrev: [Circular],
+ callback: [Function] },
+ _idleStart: Fri, 22 Jul 2011 08:15:26 GMT,
+ _events:
+ { timeout: [Function],
+ error: [Function],
+ close: [Function] },
+ ondata: [Function],
+ onend: [Function],
+ _httpMessage:
+ { output: [],
+ outputEncodings: [],
+ writable: true,
+ _last: false,
+ chunkedEncoding: false,
+ shouldKeepAlive: true,
+ useChunkedEncodingByDefault: true,
+ _hasBody: true,
+ _trailer: '',
+ finished: false,
+ socket: [Circular],
+ connection: [Circular],
+ _events: { finish: [Function] } } }
+[ - END ATTACK DETAILS - ]
+
+[ - ATTACK DETAILS FOR function now() { [native code] } - ]
+ --> Module: CSRF-1
+ --> Request: GET /favicon.ico
+ --> IP: 127.0.0.1
+ --> Raw Request: { bufferSize: 0,
+ fd: 7,
+ type: 'tcp4',
+ allowHalfOpen: true,
+ _readWatcher:
+ { socket: [Circular],
+ callback: [Function: onReadable] },
+ destroyed: false,
+ readable: true,
+ _writeQueue: [],
+ _writeQueueEncoding: [],
+ _writeQueueFD: [],
+ _writeQueueCallbacks: [],
+ _writeWatcher:
+ { socket: [Circular],
+ callback: [Function: onWritable] },
+ writable: true,
+ _writeImpl: [Function],
+ _readImpl: [Function],
+ _shutdownImpl: [Function],
+ remoteAddress: '127.0.0.1',
+ remotePort: 54112,
+ server:
+ { connections: 1,
+ allowHalfOpen: true,
+ watcher: { host: [Circular], callback: [Function] },
+ _events:
+ { request: [Function],
+ connection: [Function: connectionListener] },
+ httpAllowHalfOpen: false,
+ type: 'tcp4',
+ fd: 5 },
+ ondrain: [Function],
+ _idleTimeout: 120000,
+ _idleNext:
+ { repeat: 120,
+ _idleNext: [Circular],
+ _idlePrev: [Circular],
+ callback: [Function] },
+ _idlePrev:
+ { repeat: 120,
+ _idleNext: [Circular],
+ _idlePrev: [Circular],
+ callback: [Function] },
+ _idleStart: Fri, 22 Jul 2011 08:17:26 GMT,
+ _events:
+ { timeout: [Function],
+ error: [Function],
+ close: [Function] },
+ ondata: [Function],
+ onend: [Function],
+ _httpMessage:
+ { output: [],
+ outputEncodings: [],
+ writable: true,
+ _last: false,
+ chunkedEncoding: false,
+ shouldKeepAlive: true,
+ useChunkedEncodingByDefault: true,
+ _hasBody: true,
+ _trailer: '',
+ finished: false,
+ socket: [Circular],
+ connection: [Circular],
+ _events: { finish: [Function] } } }
+[ - END ATTACK DETAILS - ]
+
+[ - ATTACK DETAILS FOR function now() { [native code] } - ]
+ --> Module: XSS
+ --> Request: GET /index.html?wat=1%22%3E%3Cimg+src%3Dx+onerror%3Dalert%28%22XSS%22%29%3E%3Cnoscript%3E
+ --> IP: 127.0.0.1
+ --> Raw Request: { bufferSize: 0,
+ fd: 7,
+ type: 'tcp4',
+ allowHalfOpen: true,
+ _readWatcher:
+ { socket: [Circular],
+ callback: [Function: onReadable] },
+ destroyed: false,
+ readable: true,
+ _writeQueue: [],
+ _writeQueueEncoding: [],
+ _writeQueueFD: [],
+ _writeQueueCallbacks: [],
+ _writeWatcher:
+ { socket: [Circular],
+ callback: [Function: onWritable] },
+ writable: true,
+ _writeImpl: [Function],
+ _readImpl: [Function],
+ _shutdownImpl: [Function],
+ remoteAddress: '127.0.0.1',
+ remotePort: 55205,
+ server:
+ { connections: 1,
+ allowHalfOpen: true,
+ watcher: { host: [Circular], callback: [Function] },
+ _events:
+ { request: [Function],
+ connection: [Function: connectionListener] },
+ httpAllowHalfOpen: false,
+ type: 'tcp4',
+ fd: 5 },
+ ondrain: [Function],
+ _idleTimeout: 120000,
+ _idleNext:
+ { repeat: 120,
+ _idleNext: [Circular],
+ _idlePrev: [Circular],
+ callback: [Function] },
+ _idlePrev:
+ { repeat: 120,
+ _idleNext: [Circular],
+ _idlePrev: [Circular],
+ callback: [Function] },
+ _idleStart: Fri, 22 Jul 2011 08:32:18 GMT,
+ _events:
+ { timeout: [Function],
+ error: [Function],
+ close: [Function] },
+ ondata: [Function],
+ onend: [Function],
+ _httpMessage:
+ { output: [],
+ outputEncodings: [],
+ writable: true,
+ _last: false,
+ chunkedEncoding: false,
+ shouldKeepAlive: true,
+ useChunkedEncodingByDefault: true,
+ _hasBody: true,
+ _trailer: '',
+ finished: false,
+ socket: [Circular],
+ connection: [Circular],
+ _events: { finish: [Function] } } }
+[ - END ATTACK DETAILS - ]
+
13 index.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+<title>Fusker - Test Page</title>
+</head>
+<body bgcolor="black" background="http://content.ytmnd.com/content/1/d/b/1db6ede2772ba8f5428dbff9f1f2e3be.gif">
+<br/><br/>
+<center>
+<h1>Fusker Test Page</h1><br/>
+<img src="http://content.ytmnd.com/content/1/7/c/17c9f89183c18613f48450a649e3f64c.gif">
+<embed src="http://www.youtube.com/v/pOu1BvuhtRw?autoplay=1&rel=0" type="application/x-shockwave-flash" wmode="transparent" width="1" height="1"></embed>
+</center>
+</body>
+</html>
3  lib/config.js
@@ -0,0 +1,3 @@
+exports.dir = process.cwd();
+exports.level = 'FAIR';
+exports.banlength = 1440;
87 lib/fusker.js
@@ -0,0 +1,87 @@
+var http = require('http');
+var url = require('url');
+var sys = require('sys');
+var fs = require('fs');
+var path = require('path');
+
+/* Create global fusker var */
+global.fusker = {};
+
+/* Create our different levels of lulz */
+fusker.levels = {};
+fusker.levels.NONE = 'NONE';
+fusker.levels.LOW = 'LOW';
+fusker.levels.FAIR = 'FAIR';
+fusker.levels.HIGH = 'HIGH';
+fusker.levels.EXTREME = 'EXTREME';
+
+/* Bring in any internal library files */
+fusker.util = require('./util');
+fusker.config = require('./config');
+fusker.detect = new Array();
+
+/* Creates http server on specified port and listens on it. Runs every request through modules */
+fusker.createServer = function (port) {
+ console.log('[FUSKER] Creating server on port ' + port);
+ console.log('[FUSKER] Modules: ' + fusker.detect);
+ var serv = http.createServer(function (req, res) {
+
+ //Fix file names
+ var file = url.parse(req.url).pathname;
+ if (file == '/') {
+ file = '/index.html';
+ }
+ console.log('[FUSKER] ' + req.connection.remoteAddress + ' -> ' + file);
+
+ //Run request through specified modules
+ for (var i = fusker.detect.length - 1; i >= 0; --i) {
+ var module = require('./mods/' + fusker.detect[i]);
+ module.check(req, res);
+ }
+
+ //Feed up the file
+ fs.readFile(fusker.config.dir + file, function (err, data) {
+ if (!err) {
+ res.writeHead(200);
+ res.write(data, 'utf8');
+ res.end();
+ }
+ });
+ });
+ serv.listen(port);
+ return serv;
+};
+
+/* Appends attack details to specified log file */
+fusker.logAttack = function (file, module, req) {
+ var log = fs.createWriteStream(file, {
+ 'flags': 'a'
+ });
+
+ log.write('[ - ATTACK DETAILS FOR ' + Date.now + ' - ]\r\n');
+ log.write(' --> Module: ' + module + '\r\n');
+ log.write(' --> Request: ' + req.method + ' ' + req.url + '\r\n');
+ log.write(' --> IP: ' + req.connection.remoteAddress + '\r\n');
+ log.write(' --> Raw Request: ' + sys.inspect(req.connection) + '\r\n');
+ log.write('[ - END ATTACK DETAILS - ]\r\n\r\n');
+ log.end();
+};
+
+/* Executes payload on attacker based on lulz level */
+fusker.handleAttack = function (module, req, res) {
+ var ip = req.connection.remoteAddress;
+ console.log('[FUSKER] Attack detected! Module: ' + module + ' IP: ' + ip);
+ fusker.logAttack('fusker-log.txt', module, req);
+ switch (fusker.config.level.toUpperCase()) {
+ case 'NONE':
+ break;
+ case 'LOW':
+ break;
+ case 'FAIR':
+ break;
+ case 'HIGH':
+ break;
+ case 'EXTREME':
+ break;
+ }
+};
21 lib/mods/404.js
@@ -0,0 +1,21 @@
+var fs = require('fs');
+var url = require('url');
+
+exports.check = function (req, res) {
+ var file = url.parse(req.url).pathname;
+ switch (file) {
+ case '/':
+ file = '/index.html';
+ break;
+
+ case '/favicon.ico':
+ //exit out if its the favicon, no need to 404 it
+ return;
+ }
+
+ fs.readFile(fusker.config.dir + file, function (err, data) {
+ if (err) {
+ fusker.handleAttack('404', req, res);
+ }
+ });
+};
17 lib/mods/csrf.js
@@ -0,0 +1,17 @@
+exports.check = function (req, res) {
+ var headers = req.headers;
+ /*
+ if (!headers['x-requested-with']) {
+ fusker.handleAttack('CSRF-1', req, res);
+
+ } else */
+ if (/application\/j/.test(headers.accept)) {
+ fusker.handleAttack('CSRF-2', req, res);
+
+ } else if (req.method == "POST" && headers.referer && headers.referer.indexOf(headers.host + '/') > 0) {
+ fusker.handleAttack('CSRF-3', req, res);
+
+ } else if (req.method != "GET" && req.method != "POST") {
+ fusker.handleAttack('CSRF-4', req, res);
+ }
+};
7 lib/mods/lfi.js
@@ -0,0 +1,7 @@
+var url = require('url');
+
+exports.check = function (req, res) {
+ if (req.url.indexOf('../') > -1) {
+ fusker.handleAttack('LFI', req, res);
+ }
+};
0  lib/mods/sqli.js
No changes.
11 lib/mods/xss.js
@@ -0,0 +1,11 @@
+exports.check = function (req, res) {
+ var original = unescape(unescape(req.url));
+ var xss = ['<', '>', '(', ')'];
+
+ for (var i = xss.length - 1; i >= 0; --i) {
+ if (original.indexOf(xss[i]) > -1) {
+ fusker.handleAttack('XSS', req, res);
+ return;
+ }
+ }
+};
0  lib/util.js
No changes.
1  node_modules/validator/.gitignore
@@ -0,0 +1 @@
+.DS_Store
20 node_modules/validator/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2010 Chris O'Hara <cohara87@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
169 node_modules/validator/README.md
@@ -0,0 +1,169 @@
+**node-validator is a library of string validation, filtering and sanitization methods.**
+
+To install node-validator, use [npm](http://github.com/isaacs/npm):
+
+ $ npm install validator
+
+To use the library in the browser, include `validator-min.js`
+
+## Example
+
+ var check = require('validator').check,
+ sanitize = require('validator').sanitize
+
+ //Validate
+ check('test@email.com').len(6, 64).isEmail(); //Methods are chainable
+ check('abc').isInt(); //Throws 'Invalid integer'
+ check('abc', 'Please enter a number').isInt(); //Throws 'Please enter a number'
+ check('abcdefghijklmnopzrtsuvqxyz').is(/^[a-z]+$/);
+
+ //Sanitize / Filter
+ var int = sanitize('0123').toInt(); //123
+ var bool = sanitize('true').toBoolean(); //true
+ var str = sanitize(' \s\t\r hello \n').trim(); //'hello'
+ var str = sanitize('aaaaaaaaab').ltrim('a'); //'b'
+ var str = sanitize(large_input_str).xss();
+ var str = sanitize('&lt;a&gt;').entityDecode(); //'<a>'
+
+## Web development
+
+Often it's more desirable to check or automatically sanitize parameters by name (rather than the actual string). See [this gist](https://gist.github.com/752126) for instructions on binding the library to the `request` prototype.
+
+Example `http://localhost:8080/?zip=12345&foo=1&textarea=large_string`
+
+ get('/', function (req, res) {
+ req.onValidationError(function (msg) {
+ //Redirect the user with error 'msg'
+ });
+
+ //Validate user input
+ req.check('zip', 'Please enter a valid ZIP code').len(4,5).isInt();
+ req.check('email', 'Please enter a valid email').len(6,64).isEmail();
+ req.checkHeader('referer').contains('localhost');
+
+ //Sanitize user input
+ req.sanitize('textarea').xss();
+ req.sanitize('foo').toBoolean();
+
+ //etc.
+ });
+
+## List of validation methods
+
+ is() //Alias for regex()
+ not() //Alias for notRegex()
+ isEmail()
+ isUrl() //Accepts http, https, ftp
+ isIP()
+ isAlpha()
+ isAlphanumeric()
+ isNumeric()
+ isInt() //isNumeric accepts zero padded numbers, e.g. '001', isInt doesn't
+ isLowercase()
+ isUppercase()
+ isDecimal()
+ isFloat() //Alias for isDecimal
+ notNull()
+ isNull()
+ notEmpty() //i.e. not just whitespace
+ equals(equals)
+ contains(str)
+ notContains(str)
+ regex(pattern, modifiers) //Usage: regex(/[a-z]/i) or regex('[a-z]','i')
+ notRegex(pattern, modifiers)
+ len(min, max) //max is optional
+ isDate(date)
+ in(options) //Accepts an array or string
+ notIn(options)
+
+## List of sanitization / filter methods
+
+ trim(chars) //Trim optional `chars`, default is to trim whitespace (\r\n\t\s)
+ ltrim(chars)
+ rtrim(chars)
+ ifNull(replace)
+ toFloat()
+ toInt()
+ toBoolean() //True unless str = '0', 'false', or str.length == 0
+ toBooleanStrict() //False unless str = '1' or 'true'
+ entityDecode() //Decode HTML entities
+ entityEncode()
+ xss() //Remove common XSS attack vectors from text (default)
+ xss(true) //Remove common XSS attack vectors from images
+
+## Extending the library
+
+When adding to the Validator prototype, use `this.str` to access the string and `this.error(this.msg || default_msg)` when the string is invalid
+
+ var Validator = require('validator').Validator;
+ Validator.prototype.contains = function(str) {
+ if (!~this.str.indexOf(str)) {
+ this.error(this.msg || this.str + ' does not contain ' + str);
+ }
+ return this; //Allow method chaining
+ }
+
+When adding to the Filter (sanitize) prototype, use `this.str` to access the string and `this.modify(new_str)` to update it
+
+ var Filter = require('filter').Filter;
+ Filter.prototype.removeNumbers = function() {
+ this.modify(this.str.replace(/[0-9]+/g, ''));
+ return this.str;
+ }
+
+## Error handling
+
+By default, the validation methods throw an exception when a check fails
+
+ try {
+ check('abc').notNull().isInt()
+ } catch (e) {
+ console.log(e.message); //Invalid integer
+ }
+
+To set a custom error message, set the second param of `check()`
+
+ try {
+ check('abc', 'Please enter a valid integer').notNull().isInt()
+ } catch (e) {
+ console.log(e.message); //Please enter a valid integer
+ }
+
+To attach a custom error handler, set the `error` method of the validator instance
+
+ var Validator = require('validator').Validator;
+ var v = new Validator();
+ v.error = function(msg) {
+ console.log('Fail');
+ }
+ v.check('abc').isInt(); //'Fail'
+
+## Contributors
+
+- [PING](https://github.com/PlNG) - Fixed entity encoding
+- [Dan VerWeire](https://github.com/wankdanker) - Modified the behaviour of the error handler
+
+## LICENSE
+
+(MIT License)
+
+Copyright (c) 2010 Chris O'Hara <cohara87@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
14 node_modules/validator/index.html
@@ -0,0 +1,14 @@
+<html>
+<head>
+<script type="text/javascript" src="validator-min.js"></script>
+</head>
+<body>
+<script type="text/javascript">
+try {
+ check('ad>dfds@fsdfsd.com').isEmail();
+} catch (e) {
+ console.log('ok');
+}
+</script>
+</body>
+</html>
1  node_modules/validator/index.js
@@ -0,0 +1 @@
+exports = module.exports = require('./lib');
291 node_modules/validator/lib/entities.js
@@ -0,0 +1,291 @@
+var entities = {
+ '&nbsp;': '\u00a0',
+ '&iexcl;': '\u00a1',
+ '&cent;': '\u00a2',
+ '&pound;': '\u00a3',
+ '&curren;': '\u20ac',
+ '&yen;': '\u00a5',
+ '&brvbar;': '\u0160',
+ '&sect;': '\u00a7',
+ '&uml;': '\u0161',
+ '&copy;': '\u00a9',
+ '&ordf;': '\u00aa',
+ '&laquo;': '\u00ab',
+ '&not;': '\u00ac',
+ '&shy;': '\u00ad',
+ '&reg;': '\u00ae',
+ '&macr;': '\u00af',
+ '&deg;': '\u00b0',
+ '&plusmn;': '\u00b1',
+ '&sup2;': '\u00b2',
+ '&sup3;': '\u00b3',
+ '&acute;': '\u017d',
+ '&micro;': '\u00b5',
+ '&para;': '\u00b6',
+ '&middot;': '\u00b7',
+ '&cedil;': '\u017e',
+ '&sup1;': '\u00b9',
+ '&ordm;': '\u00ba',
+ '&raquo;': '\u00bb',
+ '&frac14;': '\u0152',
+ '&frac12;': '\u0153',
+ '&frac34;': '\u0178',
+ '&iquest;': '\u00bf',
+ '&Agrave;': '\u00c0',
+ '&Aacute;': '\u00c1',
+ '&Acirc;': '\u00c2',
+ '&Atilde;': '\u00c3',
+ '&Auml;': '\u00c4',
+ '&Aring;': '\u00c5',
+ '&AElig;': '\u00c6',
+ '&Ccedil;': '\u00c7',
+ '&Egrave;': '\u00c8',
+ '&Eacute;': '\u00c9',
+ '&Ecirc;': '\u00ca',
+ '&Euml;': '\u00cb',
+ '&Igrave;': '\u00cc',
+ '&Iacute;': '\u00cd',
+ '&Icirc;': '\u00ce',
+ '&Iuml;': '\u00cf',
+ '&ETH;': '\u00d0',
+ '&Ntilde;': '\u00d1',
+ '&Ograve;': '\u00d2',
+ '&Oacute;': '\u00d3',
+ '&Ocirc;': '\u00d4',
+ '&Otilde;': '\u00d5',
+ '&Ouml;': '\u00d6',
+ '&times;': '\u00d7',
+ '&Oslash;': '\u00d8',
+ '&Ugrave;': '\u00d9',
+ '&Uacute;': '\u00da',
+ '&Ucirc;': '\u00db',
+ '&Uuml;': '\u00dc',
+ '&Yacute;': '\u00dd',
+ '&THORN;': '\u00de',
+ '&szlig;': '\u00df',
+ '&agrave;': '\u00e0',
+ '&aacute;': '\u00e1',
+ '&acirc;': '\u00e2',
+ '&atilde;': '\u00e3',
+ '&auml;': '\u00e4',
+ '&aring;': '\u00e5',
+ '&aelig;': '\u00e6',
+ '&ccedil;': '\u00e7',
+ '&egrave;': '\u00e8',
+ '&eacute;': '\u00e9',
+ '&ecirc;': '\u00ea',
+ '&euml;': '\u00eb',
+ '&igrave;': '\u00ec',
+ '&iacute;': '\u00ed',
+ '&icirc;': '\u00ee',
+ '&iuml;': '\u00ef',
+ '&eth;': '\u00f0',
+ '&ntilde;': '\u00f1',
+ '&ograve;': '\u00f2',
+ '&oacute;': '\u00f3',
+ '&ocirc;': '\u00f4',
+ '&otilde;': '\u00f5',
+ '&ouml;': '\u00f6',
+ '&divide;': '\u00f7',
+ '&oslash;': '\u00f8',
+ '&ugrave;': '\u00f9',
+ '&uacute;': '\u00fa',
+ '&ucirc;': '\u00fb',
+ '&uuml;': '\u00fc',
+ '&yacute;': '\u00fd',
+ '&thorn;': '\u00fe',
+ '&yuml;': '\u00ff',
+ '&quot;': '\u0022',
+ '&lt;': '\u003c',
+ '&gt;': '\u003e',
+ '&apos;': '\u0027',
+ '&minus;': '\u2212',
+ '&circ;': '\u02c6',
+ '&tilde;': '\u02dc',
+ '&Scaron;': '\u0160',
+ '&lsaquo;': '\u2039',
+ '&OElig;': '\u0152',
+ '&lsquo;': '\u2018',
+ '&rsquo;': '\u2019',
+ '&ldquo;': '\u201c',
+ '&rdquo;': '\u201d',
+ '&bull;': '\u2022',
+ '&ndash;': '\u2013',
+ '&mdash;': '\u2014',
+ '&trade;': '\u2122',
+ '&scaron;': '\u0161',
+ '&rsaquo;': '\u203a',
+ '&oelig;': '\u0153',
+ '&Yuml;': '\u0178',
+ '&fnof;': '\u0192',
+ '&Alpha;': '\u0391',
+ '&Beta;': '\u0392',
+ '&Gamma;': '\u0393',
+ '&Delta;': '\u0394',
+ '&Epsilon;': '\u0395',
+ '&Zeta;': '\u0396',
+ '&Eta;': '\u0397',
+ '&Theta;': '\u0398',
+ '&Iota;': '\u0399',
+ '&Kappa;': '\u039a',
+ '&Lambda;': '\u039b',
+ '&Mu;': '\u039c',
+ '&Nu;': '\u039d',
+ '&Xi;': '\u039e',
+ '&Omicron;': '\u039f',
+ '&Pi;': '\u03a0',
+ '&Rho;': '\u03a1',
+ '&Sigma;': '\u03a3',
+ '&Tau;': '\u03a4',
+ '&Upsilon;': '\u03a5',
+ '&Phi;': '\u03a6',
+ '&Chi;': '\u03a7',
+ '&Psi;': '\u03a8',
+ '&Omega;': '\u03a9',
+ '&alpha;': '\u03b1',
+ '&beta;': '\u03b2',
+ '&gamma;': '\u03b3',
+ '&delta;': '\u03b4',
+ '&epsilon;': '\u03b5',
+ '&zeta;': '\u03b6',
+ '&eta;': '\u03b7',
+ '&theta;': '\u03b8',
+ '&iota;': '\u03b9',
+ '&kappa;': '\u03ba',
+ '&lambda;': '\u03bb',
+ '&mu;': '\u03bc',
+ '&nu;': '\u03bd',
+ '&xi;': '\u03be',
+ '&omicron;': '\u03bf',
+ '&pi;': '\u03c0',
+ '&rho;': '\u03c1',
+ '&sigmaf;': '\u03c2',
+ '&sigma;': '\u03c3',
+ '&tau;': '\u03c4',
+ '&upsilon;': '\u03c5',
+ '&phi;': '\u03c6',
+ '&chi;': '\u03c7',
+ '&psi;': '\u03c8',
+ '&omega;': '\u03c9',
+ '&thetasym;': '\u03d1',
+ '&upsih;': '\u03d2',
+ '&piv;': '\u03d6',
+ '&ensp;': '\u2002',
+ '&emsp;': '\u2003',
+ '&thinsp;': '\u2009',
+ '&zwnj;': '\u200c',
+ '&zwj;': '\u200d',
+ '&lrm;': '\u200e',
+ '&rlm;': '\u200f',
+ '&sbquo;': '\u201a',
+ '&bdquo;': '\u201e',
+ '&dagger;': '\u2020',
+ '&Dagger;': '\u2021',
+ '&hellip;': '\u2026',
+ '&permil;': '\u2030',
+ '&prime;': '\u2032',
+ '&Prime;': '\u2033',
+ '&oline;': '\u203e',
+ '&frasl;': '\u2044',
+ '&euro;': '\u20ac',
+ '&image;': '\u2111',
+ '&weierp;': '\u2118',
+ '&real;': '\u211c',
+ '&alefsym;': '\u2135',
+ '&larr;': '\u2190',
+ '&uarr;': '\u2191',
+ '&rarr;': '\u2192',
+ '&darr;': '\u2193',
+ '&harr;': '\u2194',
+ '&crarr;': '\u21b5',
+ '&lArr;': '\u21d0',
+ '&uArr;': '\u21d1',
+ '&rArr;': '\u21d2',
+ '&dArr;': '\u21d3',
+ '&hArr;': '\u21d4',
+ '&forall;': '\u2200',
+ '&part;': '\u2202',
+ '&exist;': '\u2203',
+ '&empty;': '\u2205',
+ '&nabla;': '\u2207',
+ '&isin;': '\u2208',
+ '&notin;': '\u2209',
+ '&ni;': '\u220b',
+ '&prod;': '\u220f',
+ '&sum;': '\u2211',
+ '&lowast;': '\u2217',
+ '&radic;': '\u221a',
+ '&prop;': '\u221d',
+ '&infin;': '\u221e',
+ '&ang;': '\u2220',
+ '&and;': '\u2227',
+ '&or;': '\u2228',
+ '&cap;': '\u2229',
+ '&cup;': '\u222a',
+ '&int;': '\u222b',
+ '&there4;': '\u2234',
+ '&sim;': '\u223c',
+ '&cong;': '\u2245',
+ '&asymp;': '\u2248',
+ '&ne;': '\u2260',
+ '&equiv;': '\u2261',
+ '&le;': '\u2264',
+ '&ge;': '\u2265',
+ '&sub;': '\u2282',
+ '&sup;': '\u2283',
+ '&nsub;': '\u2284',
+ '&sube;': '\u2286',
+ '&supe;': '\u2287',
+ '&oplus;': '\u2295',
+ '&otimes;': '\u2297',
+ '&perp;': '\u22a5',
+ '&sdot;': '\u22c5',
+ '&lceil;': '\u2308',
+ '&rceil;': '\u2309',
+ '&lfloor;': '\u230a',
+ '&rfloor;': '\u230b',
+ '&lang;': '\u2329',
+ '&rang;': '\u232a',
+ '&loz;': '\u25ca',
+ '&spades;': '\u2660',
+ '&clubs;': '\u2663',
+ '&hearts;': '\u2665',
+ '&diams;': '\u2666'
+};
+
+exports.decode = function (str) {
+ if (!~str.indexOf('&')) return str;
+
+ //Decode literal entities
+ for (var i in entities) {
+ str = str.replace(new RegExp(i, 'g'), entities[i]);
+ }
+
+ //Decode hex entities
+ str = str.replace(/&#x(0*[0-9a-f]{2,5});?/gi, function (m, code) {
+ return String.fromCharCode(parseInt(+code, 16));
+ });
+
+ //Decode numeric entities
+ str = str.replace(/&#([0-9]{2,4});?/gi, function (m, code) {
+ return String.fromCharCode(+code);
+ });
+
+ str = str.replace(/&amp;/g, '&');
+
+ return str;
+}
+
+exports.encode = function (str) {
+ str = str.replace(/&/g, '&amp;');
+
+ //IE doesn't accept &apos;
+ str = str.replace(/'/g, '&#39;');
+
+ //Encode literal entities
+ for (var i in entities) {
+ str = str.replace(new RegExp(entities[i], 'g'), i);
+ }
+
+ return str;
+}
89 node_modules/validator/lib/filter.js
@@ -0,0 +1,89 @@
+var entities = require('./entities');
+ xss = require('./xss');
+
+var Filter = exports.Filter = function() {}
+
+var whitespace = '\\r\\n\\t\\s';
+
+Filter.prototype.modify = function(str) {
+ this.str = str;
+}
+
+//Create some aliases - may help code readability
+Filter.prototype.convert = Filter.prototype.sanitize = function(str) {
+ this.str = str;
+ return this;
+}
+
+Filter.prototype.xss = function(is_image) {
+ this.modify(xss.clean(this.str, is_image));
+ return this.str;
+}
+
+Filter.prototype.entityDecode = function() {
+ this.modify(entities.decode(this.str));
+ return this.str;
+}
+
+Filter.prototype.entityEncode = function() {
+ this.modify(entities.encode(this.str));
+ return this.str;
+}
+
+Filter.prototype.ltrim = function(chars) {
+ chars = chars || whitespace;
+ this.modify(this.str.replace(new RegExp('^['+chars+']+', 'g'), ''));
+ return this.str;
+}
+
+Filter.prototype.rtrim = function(chars) {
+ chars = chars || whitespace;
+ this.modify(this.str.replace(new RegExp('['+chars+']+$', 'g'), ''));
+ return this.str;
+}
+
+Filter.prototype.trim = function(chars) {
+ chars = chars || whitespace;
+ this.modify(this.str.replace(new RegExp('^['+chars+']+|['+chars+']+$', 'g'), ''));
+ return this.str;
+}
+
+Filter.prototype.ifNull = function(replace) {
+ if (!this.str || this.str === '') {
+ this.modify(replace);
+ }
+ return this.str;
+}
+
+Filter.prototype.toFloat = function() {
+ this.modify(parseFloat(this.str));
+ return this.str;
+}
+
+Filter.prototype.toInt = function(radix) {
+ radix = radix || 10;
+ this.modify(parseInt(this.str, radix));
+ return this.str;
+}
+
+//Any strings with length > 0 (except for '0' and 'false') are considered true,
+//all other strings are false
+Filter.prototype.toBoolean = function() {
+ if (!this.str || this.str == '0' || this.str == 'false' || this.str == '') {
+ this.modify(false);
+ } else {
+ this.modify(true);
+ }
+ return this.str;
+}
+
+//String must be equal to '1' or 'true' to be considered true, all other strings
+//are false
+Filter.prototype.toBooleanStrict = function() {
+ if (this.str == '1' || this.str == 'true') {
+ this.modify(true);
+ } else {
+ this.modify(false);
+ }
+ return this.str;
+}
15 node_modules/validator/lib/index.js
@@ -0,0 +1,15 @@
+exports.Validator = require('./validator').Validator;
+exports.Filter = require('./filter').Filter;
+
+exports.entities = require('./entities');
+
+//Quick access methods
+exports.sanitize = exports.convert = function(str) {
+ var filter = new exports.Filter();
+ return filter.sanitize(str);
+}
+
+exports.check = exports.validate = exports.assert = function(str, fail_msg) {
+ var validator = new exports.Validator();
+ return validator.check(str, fail_msg);
+}
230 node_modules/validator/lib/validator.js
@@ -0,0 +1,230 @@
+var net = require('net');
+
+var Validator = exports.Validator = function() {}
+
+Validator.prototype.check = function(str, fail_msg) {
+ this.str = str == null || (isNaN(str) && str.length == undefined) ? '' : str+'';
+ this.msg = fail_msg;
+ this._errors = [];
+ return this;
+}
+
+//Create some aliases - may help code readability
+Validator.prototype.validate = Validator.prototype.check;
+Validator.prototype.assert = Validator.prototype.check;
+
+Validator.prototype.error = function(msg) {
+ throw new Error(msg);
+ return this;
+}
+
+Validator.prototype.isEmail = function() {
+ if (!this.str.match(/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!\.)){0,61}[a-zA-Z0-9]?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9\-](?!$)){0,61}[a-zA-Z0-9]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/)) {
+ return this.error(this.msg || 'Invalid email');
+ }
+ return this;
+}
+
+Validator.prototype.isUrl = function() {
+ if (!this.str.match(/^(?:(?:ht|f)tp(?:s?)\:\/\/|~\/|\/)?(?:\w+:\w+@)?((?:(?:[-\w\d{1-3}]+\.)+(?:com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|edu|co\.uk|ac\.uk|it|fr|tv|museum|asia|local|travel|[a-z]{2}))|((\b25[0-5]\b|\b[2][0-4][0-9]\b|\b[0-1]?[0-9]?[0-9]\b)(\.(\b25[0-5]\b|\b[2][0-4][0-9]\b|\b[0-1]?[0-9]?[0-9]\b)){3}))(?::[\d]{1,5})?(?:(?:(?:\/(?:[-\w~!$+|.,=]|%[a-f\d]{2})+)+|\/)+|\?|#)?(?:(?:\?(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)(?:&(?:[-\w~!$+|.,*:]|%[a-f\d{2}])+=?(?:[-\w~!$+|.,*:=]|%[a-f\d]{2})*)*)*(?:#(?:[-\w~!$ |\/.,*:;=]|%[a-f\d]{2})*)?$/)) {
+ return this.error(this.msg || 'Invalid URL');
+ }
+ return this;
+}
+
+Validator.prototype.isIP = function() {
+ //net.isIP is in node >= 0.3.0
+ if (typeof net.isIP === 'function') {
+ if (net.isIP(this.str) === 0) {
+ return this.error(this.msg || 'Invalid IP');
+ }
+ } else {
+ if (!this.str.match(/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/)) {
+ return this.error(this.msg || 'Invalid IP');
+ }
+ }
+ return this;
+}
+
+Validator.prototype.isAlpha = function() {
+ if (!this.str.match(/^[a-zA-Z]+$/)) {
+ return this.error(this.msg || 'Invalid characters');
+ }
+ return this;
+}
+
+Validator.prototype.isAlphanumeric = function() {
+ if (!this.str.match(/^[a-zA-Z0-9]+$/)) {
+ return this.error(this.msg || 'Invalid characters');
+ }
+ return this;
+}
+
+Validator.prototype.isNumeric = function() {
+ if (!this.str.match(/^-?[0-9]+$/)) {
+ return this.error(this.msg || 'Invalid number');
+ }
+ return this;
+}
+
+Validator.prototype.isLowercase = function() {
+ if (!this.str.match(/^[a-z0-9]+$/)) {
+ return this.error(this.msg || 'Invalid characters');
+ }
+ return this;
+}
+
+Validator.prototype.isUppercase = function() {
+ if (!this.str.match(/^[A-Z0-9]+$/)) {
+ return this.error(this.msg || 'Invalid characters');
+ }
+ return this;
+}
+
+Validator.prototype.isInt = function() {
+ if (!this.str.match(/^(?:-?(?:0|[1-9][0-9]*))$/)) {
+ return this.error(this.msg || 'Invalid integer');
+ }
+ return this;
+}
+
+Validator.prototype.isDecimal = function() {
+ if (!this.str.match(/^(?:-?(?:0|[1-9][0-9]*))?(?:\.[0-9]*)?$/)) {
+ return this.error(this.msg || 'Invalid decimal');
+ }
+ return this;
+}
+
+Validator.prototype.isFloat = function() {
+ return this.isDecimal();
+}
+
+Validator.prototype.notNull = function() {
+ if (this.str === '') {
+ return this.error(this.msg || 'Invalid characters');
+ }
+ return this;
+}
+
+Validator.prototype.isNull = function() {
+ if (this.str !== '') {
+ return this.error(this.msg || 'Invalid characters');
+ }
+ return this;
+}
+
+Validator.prototype.notEmpty = function() {
+ if (this.str.match(/^[\s\t\r\n]*$/)) {
+ return this.error(this.msg || 'String is empty');
+ }
+ return this;
+}
+
+Validator.prototype.equals = function(equals) {
+ if (this.str != equals) {
+ return this.error(this.msg || 'Not equal');
+ }
+ return this;
+}
+
+Validator.prototype.contains = function(str) {
+ if (this.str.indexOf(str) === -1) {
+ return this.error(this.msg || 'Invalid characters');
+ }
+ return this;
+}
+
+Validator.prototype.notContains = function(str) {
+ if (this.str.indexOf(str) >= 0) {
+ return this.error(this.msg || 'Invalid characters');
+ }
+ return this;
+}
+
+Validator.prototype.regex = Validator.prototype.is = function(pattern, modifiers) {
+ if (typeof pattern !== 'function') {
+ pattern = new RegExp(pattern, modifiers);
+ }
+ if (! this.str.match(pattern)) {
+ return this.error(this.msg || 'Invalid characters');
+ }
+ return this;
+}
+
+Validator.prototype.notRegex = Validator.prototype.not = function(pattern, modifiers) {
+ if (typeof pattern !== 'function') {
+ pattern = new RegExp(pattern, modifiers);
+ }
+ if (this.str.match(pattern)) {
+ return this.error(this.msg || 'Invalid characters');
+ }
+ return this;
+}
+
+Validator.prototype.len = function(min, max) {
+ if (this.str.length < min) {
+ return this.error(this.msg || 'String is too small');
+ }
+ if (typeof max !== undefined && this.str.length > max) {
+ return this.error(this.msg || 'String is too large');
+ }
+ return this;
+}
+
+//Thanks to github.com/sreuter for the idea.
+Validator.prototype.isUUID = function(version) {
+ if (version == 3 || version == 'v3') {
+ pattern = /[0-9A-F]{8}-[0-9A-F]{4}-3[0-9A-F]{3}-[0-9A-F]{4}-[0-9A-F]{12}$/i;
+ } else if (version == 4 || version == 'v4') {
+ pattern = /[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}$/i;
+ } else {
+ pattern = /[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i;
+ }
+ if (!this.str.match(pattern)) {
+ return this.error(this.msg || 'Not a UUID');
+ }
+ return this;
+}
+
+Validator.prototype.isDate = function() {
+ var pattern = /^([0-1]{0,1}[0-9]{1})\/([0-3]{0,1}[0-9]{1})\/([0-9]{4})$/;
+ var result = pattern.exec(this.str);
+
+ if (!result || result.length != 4 ) {
+ return this.error(this.msg || 'Not a date');
+ }
+
+ var dt = new Date(this.str);
+
+ if ( dt.getFullYear() != parseInt(result[3])
+ || dt.getMonth() + 1 != parseInt(result[1])
+ || dt.getDate() != parseInt(result[2])
+ ) {
+ return this.error(this.msg || 'Not a date');
+ }
+
+ return this;
+}
+
+Validator.prototype.in = function(options) {
+ if (options && typeof options.indexOf === 'function') {
+ if (!~options.indexOf(this.str)) {
+ return this.error(this.msg || 'Unexpected value');
+ }
+ return this;
+ } else {
+ return this.error(this.msg || 'Invalid in() argument');
+ }
+}
+
+Validator.prototype.notIn = function(options) {
+ if (options && typeof options.indexOf === 'function') {
+ if (options.indexOf(this.str) !== -1) {
+ return this.error(this.msg || 'Unexpected value');
+ }
+ return this;
+ } else {
+ return this.error(this.msg || 'Invalid notIn() argument');
+ }
+}
+
198 node_modules/validator/lib/xss.js
@@ -0,0 +1,198 @@
+//This module is adapted from the CodeIgniter framework
+//The license is available at http://codeigniter.com/
+
+var html_entity_decode = require('./entities').decode;
+
+var never_allowed_str = {
+ 'document.cookie': '[removed]',
+ 'document.write': '[removed]',
+ '.parentNode': '[removed]',
+ '.innerHTML': '[removed]',
+ 'window.location': '[removed]',
+ '-moz-binding': '[removed]',
+ '<!--': '&lt;!--',
+ '-->': '--&gt;',
+ '<![CDATA[': '&lt;![CDATA['
+};
+
+var never_allowed_regex = {
+ 'javascript\\s*:': '[removed]',
+ 'expression\\s*(\\(|&\\#40;)': '[removed]',
+ 'vbscript\\s*:': '[removed]',
+ 'Redirect\\s+302': '[removed]'
+};
+
+var non_displayables = [
+ /%0[0-8bcef]/g, // url encoded 00-08, 11, 12, 14, 15
+ /%1[0-9a-f]/g, // url encoded 16-31
+ /[\x00-\x08]/g, // 00-08
+ /\x0b/g, /\x0c/g, // 11,12
+ /[\x0e-\x1f]/g // 14-31
+];
+
+var compact_words = [
+ 'javascript', 'expression', 'vbscript',
+ 'script', 'applet', 'alert', 'document',
+ 'write', 'cookie', 'window'
+];
+
+exports.clean = function(str, is_image) {
+
+ //Recursively clean objects and arrays
+ if (typeof str === 'array' || typeof str === 'object') {
+ for (var i in str) {
+ str[i] = exports.clean(str[i]);
+ }
+ return str;
+ }
+
+ //Remove invisible characters
+ str = remove_invisible_characters(str);
+
+ //Protect query string variables in URLs => 901119URL5918AMP18930PROTECT8198
+ str = str.replace(/\&([a-z\_0-9]+)\=([a-z\_0-9]+)/i, xss_hash() + '$1=$2');
+
+ //Validate standard character entities - add a semicolon if missing. We do this to enable
+ //the conversion of entities to ASCII later.
+ str = str.replace(/(&\#?[0-9a-z]{2,})([\x00-\x20])*;?/i, '$1;$2');
+
+ //Validate UTF16 two byte encoding (x00) - just as above, adds a semicolon if missing.
+ str = str.replace(/(&\#x?)([0-9A-F]+);?/i, '$1;$2');
+
+ //Un-protect query string variables
+ str = str.replace(xss_hash(), '&');
+
+ //Decode just in case stuff like this is submitted:
+ //<a href="http://%77%77%77%2E%67%6F%6F%67%6C%65%2E%63%6F%6D">Google</a>
+ str = decodeURIComponent(str);
+
+ //Convert character entities to ASCII - this permits our tests below to work reliably.
+ //We only convert entities that are within tags since these are the ones that will pose security problems.
+ str = str.replace(/[a-z]+=([\'\"]).*?\\1/gi, function(m, match) {
+ return m.replace(match, convert_attribute(match));
+ });
+
+ //Remove invisible characters again
+ str = remove_invisible_characters(str);
+
+ //Convert tabs to spaces
+ str = str.replace('\t', ' ');
+
+ //Captured the converted string for later comparison
+ var converted_string = str;
+
+ //Remove strings that are never allowed
+ for (var i in never_allowed_str) {
+ str = str.replace(i, never_allowed_str[i]);
+ }
+
+ //Remove regex patterns that are never allowed
+ for (var i in never_allowed_regex) {
+ str = str.replace(new RegExp(i, 'i'), never_allowed_regex[i]);
+ }
+
+ //Compact any exploded words like: j a v a s c r i p t
+ // We only want to do this when it is followed by a non-word character
+ for (var i in compact_words) {
+ var spacified = compact_words[i].split('').join('\\s*')+'\\s*';
+
+ str = str.replace(new RegExp('('+spacified+')(\\W)', 'ig'), function(m, compat, after) {
+ return compat.replace(/\s+/g, '') + after;
+ });
+ }
+
+ //Remove disallowed Javascript in links or img tags
+ do {
+ var original = str;
+
+ if (str.match(/<a/i)) {
+ str = str.replace(/<a\\s+([^>]*?)(>|$)/gi, function(m, attributes, end_tag) {
+ attributes = filter_attributes(attributes.replace('<','').replace('>',''));
+ return m.replace(attributes, attributes.replace(/href=.*?(alert\(|alert&\#40;|javascript\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\\s*,)/gi, ''));
+ });
+ }
+
+ if (str.match(/<img/i)) {
+ str = str.replace(/<img\\s+([^>]*?)(\\s?\/?>|$)/gi, function(m, attributes, end_tag) {
+ attributes = filter_attributes(attributes.replace('<','').replace('>',''));
+ return m.replace(attributes, attributes.replace(/src=.*?(alert\(|alert&\#40;|javascript\:|charset\=|window\.|document\.|\.cookie|<script|<xss|base64\\s*,)/gi, ''));
+ });
+ }
+
+ if (str.match(/script/i) || str.match(/xss/i)) {
+ str = str.replace(/<(\/*)(script|xss)(.*?)\>/gi, '[removed]');
+ }
+
+ } while(original != str);
+
+ //Remove JavaScript Event Handlers - Note: This code is a little blunt. It removes the event
+ //handler and anything up to the closing >, but it's unlikely to be a problem.
+ event_handlers = ['[^a-z_\-]on\w*'];
+
+ //Adobe Photoshop puts XML metadata into JFIF images, including namespacing,
+ //so we have to allow this for images
+ if (!is_image) {
+ event_handlers.push('xmlns');
+ }
+
+ str = str.replace(new RegExp("<([^><]+?)("+event_handlers.join('|')+")(\\s*=\\s*[^><]*)([><]*)", 'i'), '<$1$4');
+
+ //Sanitize naughty HTML elements
+ //If a tag containing any of the words in the list
+ //below is found, the tag gets converted to entities.
+ //So this: <blink>
+ //Becomes: &lt;blink&gt;
+ naughty = 'alert|applet|audio|basefont|base|behavior|bgsound|blink|body|embed|expression|form|frameset|frame|head|html|ilayer|iframe|input|isindex|layer|link|meta|object|plaintext|style|script|textarea|title|video|xml|xss';
+ str = str.replace(new RegExp('<(/*\\s*)('+naughty+')([^><]*)([><]*)', 'gi'), function(m, a, b, c, d) {
+ return '&lt;' + a + b + c + d.replace('>','&gt;').replace('<','&lt;');
+ });
+
+ //Sanitize naughty scripting elements Similar to above, only instead of looking for
+ //tags it looks for PHP and JavaScript commands that are disallowed. Rather than removing the
+ //code, it simply converts the parenthesis to entities rendering the code un-executable.
+ //For example: eval('some code')
+ //Becomes: eval&#40;'some code'&#41;
+ str = str.replace(/(alert|cmd|passthru|eval|exec|expression|system|fopen|fsockopen|file|file_get_contents|readfile|unlink)(\\s*)\((.*?)\)/gi, '$1$2&#40;$3&#41;');
+
+ //This adds a bit of extra precaution in case something got through the above filters
+ for (var i in never_allowed_str) {
+ str = str.replace(i, never_allowed_str[i]);
+ }
+ for (var i in never_allowed_regex) {
+ str = str.replace(new RegExp(i, 'i'), never_allowed_regex[i]);
+ }
+
+ //Images are handled in a special way
+ if (is_image && str !== converted_string) {
+ throw new Error('Image may contain XSS');
+ }
+
+ return str;
+}
+
+function remove_invisible_characters(str) {
+ for (var i in non_displayables) {
+ str = str.replace(non_displayables[i], '');
+ }
+ return str;
+}
+
+function xss_hash() {
+ //TODO: Create a random hash
+ return '!*$^#(@*#&';
+}
+
+function convert_attribute(str) {
+ return str.replace('>','&gt;').replace('<','&lt;').replace('\\','\\\\');
+}
+
+//Filter Attributes - filters tag attributes for consistency and safety
+function filter_attributes(str) {
+ out = '';
+
+ str.replace(/\\s*[a-z\-]+\\s*=\\s*(?:\042|\047)(?:[^\\1]*?)\\1/gi, function(m) {
+ $out += m.replace(/\/\*.*?\*\//g, '');
+ });
+
+ return out;
+}
27 node_modules/validator/package.json
@@ -0,0 +1,27 @@
+{ "name" : "validator",
+ "description" : "Data validation, filtering and sanitization for node.js",
+ "version" : "0.2.6",
+ "homepage" : "http://github.com/chriso/node-validator",
+ "keywords" : ["validator", "validation", "assert", "params", "sanitization", "xss", "entities", "sanitize", "sanitisation", "input"],
+ "author" : "Chris O'Hara <cohara87@gmail.com>",
+ "main" : "./lib",
+ "directories" : { "lib" : "./lib" },
+ "bugs" :
+ { "mail" : "",
+ "web" : "http://github.com/chriso/node-validator/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "http://github.com/chriso/node-validator.git"
+ },
+ "contributors": [
+ { "name": "PING", "github": "https://github.com/PlNG" },
+ { "name": "Dan VerWeire", "github": "https://github.com/wankdanker" }
+ ],
+ "engines" : { "node" : ">=0.2.2" },
+ "licenses" :
+ [ { "type" : "MIT"
+ , "url" : "http://github.com/chriso/node-validator/raw/master/LICENSE"
+ }
+ ]
+}
4 node_modules/validator/test.js
@@ -0,0 +1,4 @@
+
+var xss = require('./validator').xssClean;
+
+console.log(xss('I <3 this'));
134 node_modules/validator/test/filter.test.js
@@ -0,0 +1,134 @@
+var node_validator = require('../lib'),
+ Filter = new node_validator.Filter(),
+ assert = require('assert');
+
+module.exports = {
+ 'test #ifNull()': function () {
+ //Make sure sanitize returns the new string
+ assert.equal(5, Filter.sanitize('').ifNull(5));
+ assert.equal('abc', Filter.sanitize().ifNull('abc'));
+
+ //Modify Filter.modify() to automatically replace a var with the sanitized version
+ var param = '';
+ Filter.modify = function(str) {
+ this.str = str;
+ param = str;
+ }
+ Filter.sanitize(param).ifNull('foobar');
+ assert.equal('foobar', param);
+ },
+
+ 'test #toBoolean()': function () {
+ assert.equal(true, Filter.sanitize('1').toBoolean());
+ assert.equal(true, Filter.sanitize('true').toBoolean());
+ assert.equal(true, Filter.sanitize('foobar').toBoolean());
+ assert.equal(true, Filter.sanitize(5).toBoolean());
+ assert.equal(true, Filter.sanitize(' ').toBoolean());
+
+ assert.equal(false, Filter.sanitize('0').toBoolean());
+ assert.equal(false, Filter.sanitize('false').toBoolean());
+ assert.equal(false, Filter.sanitize('').toBoolean());
+ assert.equal(false, Filter.sanitize('false').toBoolean());
+ },
+
+ 'test #toBooleanStrict()': function () {
+ assert.equal(true, Filter.sanitize('1').toBooleanStrict());
+ assert.equal(true, Filter.sanitize('true').toBooleanStrict());
+
+ assert.equal(false, Filter.sanitize('foobar').toBooleanStrict());
+ assert.equal(false, Filter.sanitize(5).toBooleanStrict());
+ assert.equal(false, Filter.sanitize(' ').toBooleanStrict());
+ assert.equal(false, Filter.sanitize('0').toBooleanStrict());
+ assert.equal(false, Filter.sanitize('false').toBooleanStrict());
+ assert.equal(false, Filter.sanitize('').toBooleanStrict());
+ assert.equal(false, Filter.sanitize('false').toBooleanStrict());
+ },
+
+ 'test #trim()': function () {
+ //Test trim() with spaces
+ assert.equal('abc', Filter.sanitize(' abc').trim());
+ assert.equal('abc', Filter.sanitize('abc ').trim());
+ assert.equal('abc', Filter.sanitize(' abc ').trim());
+
+ //Test trim() with \t
+ assert.equal('abc', Filter.sanitize(' abc').trim());
+ assert.equal('abc', Filter.sanitize('abc ').trim());
+ assert.equal('abc', Filter.sanitize(' abc ').trim());
+
+ //Test trim() with a mixture of \t, \s, \r and \n
+ assert.equal('abc', Filter.sanitize(' \r\n abc\r\n ').trim());
+
+ //Test trim() with custom chars
+ assert.equal('2', Filter.sanitize('000020000').trim('0'));
+ assert.equal('202', Filter.sanitize('01000202100101').trim('01'));
+ },
+
+ 'test #ltrim()': function () {
+ //Test ltrim() with spaces
+ assert.equal('abc', Filter.sanitize(' abc').ltrim());
+ assert.equal('abc ', Filter.sanitize(' abc ').ltrim());
+
+ //Test ltrim() with \t
+ assert.equal('abc', Filter.sanitize(' abc').ltrim());
+ assert.equal('abc ', Filter.sanitize(' abc ').ltrim());
+
+ //Test ltrim() with a mixture of \t, \s, \r and \n
+ assert.equal('abc\r\n', Filter.sanitize(' \r\n abc\r\n').ltrim());
+
+ //Test ltrim() with custom chars
+ assert.equal('20', Filter.sanitize('000020').ltrim('0'));
+ assert.equal('201', Filter.sanitize('010100201').ltrim('01'));
+ },
+
+ 'test #rtrim()': function () {
+ //Test rtrim() with spaces
+ assert.equal(' abc', Filter.sanitize(' abc ').rtrim());
+ assert.equal('abc', Filter.sanitize('abc ').rtrim());
+
+ //Test rtrim() with \t
+ assert.equal(' abc', Filter.sanitize(' abc').rtrim());
+ assert.equal('abc', Filter.sanitize('abc ').rtrim());
+
+ //Test rtrim() with a mixture of \t, \s, \r and \n
+ assert.equal(' \r\n abc', Filter.sanitize(' \r\n abc\r\n ').rtrim());
+
+ //Test rtrim() with custom chars
+ assert.equal('02', Filter.sanitize('02000').rtrim('0'));
+ assert.equal('012', Filter.sanitize('01201001').rtrim('01'));
+ },
+
+ 'test #toInt()': function () {
+ assert.ok(3 === Filter.sanitize('3').toInt());
+ assert.ok(3 === Filter.sanitize(' 3 ').toInt());
+ },
+
+ 'test #toFloat()': function () {
+ assert.ok(3 === Filter.sanitize('3.').toFloat());
+ assert.ok(3 === Filter.sanitize(' 3 ').toFloat());
+ assert.ok(0 === Filter.sanitize('.0').toFloat());
+ assert.ok(13.13 === Filter.sanitize('13.13').toFloat());
+ },
+
+ 'test #entityDecode()': function () {
+ assert.equal('&', Filter.sanitize('&amp;').entityDecode());
+ assert.equal('&&', Filter.sanitize('&amp;&amp;').entityDecode());
+ assert.equal('""', Filter.sanitize('&quot;&quot;').entityDecode());
+ assert.equal('', Filter.sanitize('&curren;').entityDecode());
+ assert.equal("'", Filter.sanitize("&#39;").entityDecode());
+ assert.equal("'", Filter.sanitize("&apos;").entityDecode());
+ },
+
+ 'test #entityEncode()': function () {
+ assert.equal('&amp;', Filter.sanitize('&').entityEncode());
+ assert.equal('&amp;&amp;', Filter.sanitize('&&').entityEncode());
+ assert.equal('&#39;', Filter.sanitize("'").entityEncode());
+ assert.equal('&quot;&quot;', Filter.sanitize('""').entityEncode());
+ assert.equal('&curren;', Filter.sanitize('').entityEncode());
+ },
+
+ 'test #xss()': function () {
+ //Need more tests!
+ assert.equal('[removed] foobar', Filter.sanitize('javascript : foobar').xss());
+ assert.equal('[removed] foobar', Filter.sanitize('j a vasc ri pt: foobar').xss());
+ }
+}
454 node_modules/validator/test/validator.test.js
@@ -0,0 +1,454 @@
+var node_validator = require('../lib'),
+ Validator = new node_validator.Validator(),
+ assert = require('assert');
+
+module.exports = {
+ 'test #isEmail()': function () {
+ //Try some invalid emails
+ var invalid = [
+ 'invalidemail@',
+ 'invalid.com',
+ '@invalid.com'
+ ];
+ invalid.forEach(function(email) {
+ try {
+ Validator.check(email, 'Invalid').isEmail();
+ assert.ok(false, 'Invalid email ('+email+') passed validation');
+ } catch(e) {
+ assert.equal('Invalid', e.message);
+ }
+ });
+
+ //Now try some valid ones
+ var valid = [
+ 'foo@bar.com',
+ 'x@x.x',
+ 'foo@bar.com.au',
+ 'foo+bar@bar.com'
+ ];
+ try {
+ valid.forEach(function(email) {
+ Validator.check(email).isEmail();
+ });
+ } catch(e) {
+ assert.ok(false, 'A valid email did not pass validation');
+ }
+ },
+
+ 'test #isUrl()': function () {
+ //Try some invalid URLs
+ var invalid = [
+ 'xyz://foobar.com', //Only http, https and ftp are valid
+ 'invalid/',
+ 'invalid.x',
+ 'invalid.',
+ '.com',
+ 'http://com/',
+ 'http://300.0.0.1/'
+ ];
+ invalid.forEach(function(url) {
+ try {
+ Validator.check(url, 'Invalid').isUrl();
+ assert.ok(false, 'Invalid url ('+url+') passed validation');
+ } catch(e) {
+ assert.equal('Invalid', e.message);
+ }
+ });
+
+ //Now try some valid ones
+ var valid = [
+ 'foobar.com',
+ 'www.foobar.com',
+ 'foobar.com/',