├── .gitignore ├── LICENSE ├── README.md ├── app.js ├── index.js ├── package.json └── test └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | .vscode -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 John H Horton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Stories in Ready](https://badge.waffle.io/Johnhhorton/node-nmap.png?label=ready&title=Ready)](https://waffle.io/Johnhhorton/node-nmap) 2 | # Node-NMAP 3 | 4 | [![Join the chat at https://gitter.im/Johnhhorton/node-nmap](https://badges.gitter.im/Johnhhorton/node-nmap.svg)](https://gitter.im/Johnhhorton/node-nmap?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 5 | NPM package enabling your [NodeJs] application to interface with the features of [NMAP]. This package requires that [NMAP] is installed and available to the running node application. 6 | 7 | UPDATE 4.0.0 8 | * Changed the code base from TypeScript to pure ES6 9 | * Removed TypeScript and TS types 10 | * Added additional port service information to output if available (-sV) 11 | * BREAKING - Changed export method to flat object, upgrade instructions below. 12 | 13 | Upgrade instructions: 14 | 15 | ```javascript 16 | //Previous usage 3.0.4 and below 17 | const nmap = require('node-nmap'); 18 | nmap.nodenmap.nmapLocation = "nmap"; //default 19 | let quickscan = new nmap.nodenmap.QuickScan('127.0.0.1 google.com'); 20 | 21 | /*4.0.0+ usage simply removes a layer of object nesting. 22 | * simply remove 'nodenmap' 23 | */ 24 | const nmap = require('node-nmap'); 25 | nmap.nmapLocation = 'nmap'; //default 26 | let quickscan = new nmap.QuickScan('127.0.0.1 google.com'); 27 | 28 | ``` 29 | 30 | 31 | UPDATE 3.0.4 32 | * Added extra error handling to detect if NMAP cannot be found a default or passed location. 33 | 34 | UPDATE 3.0.3: 35 | * Added NMAP determined Vendor when a MAC address is provided. Credit: [tbwiss](https://github.com/tbwiss) 36 | 37 | UPDATE v3: A lot of changes have come in this update: 38 | * Breaking change: All scan classes are now capitalized. 39 | * Added `scan.scanTimeout` to limit long running scans 40 | * Added `scan.scanTime` representing the duration of the scan 41 | * Added `scan.cancelScan()` to kill a running scan 42 | * Removed `autoDiscover` scan type until method of determining useful interfaces found 43 | * Bugfix: Now remove listeners for SIGINT when a scan is complete. 44 | * Added a Queued version of each scan allowing for a higher level of feedback and control over the scanning process. 45 | * Building against the latest version of NMAP (v7) 46 | 47 | UPDATE v2: I have rewritten the module in TypeScript. the .d.ts file is located at /node_modules/node-nmap/index.d.ts. 48 | As a part of this update, there is an additional mapping for the namespace/module, as well as a requirement to use `new` for each scan. 49 | 50 | Request: While `NmapScan()` will accept valid NMAP arguments, the XML to JSON conversion is only checking for specific things. If there is a common or useful NMAP feature that you would like to see included, please submit an issue and I will work it in. 51 | 52 | ## Installation 53 | `npm install node-nmap` 54 | 55 | ## Scan Types 56 | * `NmapScan` - This is the core of the package and runs the NMAP command. 57 | * `QuickScan` - Scans supplied hosts without portscan(-sP). Use for a quick discovery. 58 | * `OsAndPortScan` - Scans for open ports as well as NMAP gathered OS information. 59 | * `QueuedNmapScan` - Queued version for greater control 60 | * `QueuedQuickScan` - Queued version for greater control 61 | * `QueuedOsAndPortScan` - Queued version for greater control 62 | 63 | ## Scan instance variables, methods, and events 64 | 65 | * `scanResults` : Array of host objects - contains the results of the scan. 66 | * `scanTime` : number in ms - duration of scan. 67 | * `scanTimeout` : number in ms - scan will cancel if timeout is reached. 68 | * `startScan()` - begins the NMAP scan. 69 | * `cancelScan()` - kills the NMAP process. 70 | * `'complete'` : event - returns array of host objects 71 | * `'error'` : event - returns string with error information 72 | 73 | ## Queued scans instance variables, methods, and events 74 | 75 | * `scanTime` : number in ms - collective duration of all scans. 76 | * `currentScan` - reference to the current scan object if needed 77 | * `runActiononError` : boolean(default:false) - run the supplied action function when an error is encountered. 78 | * `saveErrorsToResults` : boolean(default:false) - save error data to the results array 79 | * `singleScanTimeout` : number in ms - timeout value to be supplied to each single scan. 80 | * `saveNotFoundToResults` : boolean(default:false) - save host not found error object to results array 81 | * `startRunScan()` - begins processing the entire queue without removing scanned hosts. 82 | * `startShiftScan()` - begins processing entire queue while removing scanned hosts. 83 | * `pause()` - pauses the queue processing (take affect between scans.). 84 | * `resume()` - resumes processing the queue. 85 | * `next(count)` - processes the next `count` queued items. Default 1. 86 | * `shift(count)` - processes the next `count` queued items while removing them from the queue. Default 1. 87 | * `results()` - returns Array of current scan result Host objects. 88 | * `shiftResults()` - returns the first item of the results objects and removes it from the results list. 89 | * `index()` - returns the current index of the queue processing 90 | * `percentComplete()` - returns the percentage completion through the processing queue. 91 | * `'complete'` : event - triggers when entire queue has been processed. Returns results Array. 92 | * `'error'` : event - triggers when an error is encountered. Returns error object. 93 | 94 | ## Usage 95 | 96 | NmapScan is the core function of the package. It emits two events: `'complete'` and `'error'`. Both of these events return data. All methods are easy to set up. Simply define a variable as one of the methods, and that variable will become a new instance of NmapScan with appropriately set commands. All input accepts either a space separated string, or an array of strings to make it easier to work with a complex set of hosts. All methods return an array of JSON objects containing information on each host. Any key without information provided from NMAP is filled as `null`. 97 | 98 | The return structure is: 99 | 100 | ```javascript 101 | [ 102 | { 103 | "hostname":"theHostname", 104 | "ip":"127.0.0.1", 105 | "mac":null, 106 | "openPorts":[ 107 | { 108 | "port":80, 109 | "service":"http" 110 | },... 111 | ], 112 | "osNmap":null, //note that osNmap is not guaranteed to be correct. 113 | },...] 114 | ``` 115 | ### Examples 116 | 117 | ```javascript 118 | var nmap = require('node-nmap'); 119 | 120 | nmap.nmapLocation = "nmap"; //default 121 | 122 | // Accepts array or comma separated string of NMAP acceptable hosts 123 | var quickscan = new nmap.QuickScan('127.0.0.1 google.com'); 124 | 125 | quickscan.on('complete', function(data){ 126 | console.log(data); 127 | }); 128 | 129 | quickscan.on('error', function(error){ 130 | console.log(error); 131 | }); 132 | 133 | quickscan.startScan(); 134 | // returns 135 | // [ 136 | // { 137 | // "hostname":"localhost", 138 | // "ip":"127.0.0.1", 139 | // "mac":null, 140 | // "openPorts":[ 141 | 142 | // ], 143 | // "osNmap":null 144 | // }, 145 | // { 146 | // "hostname":"google.com", 147 | // "ip":"74.125.21.113", 148 | // "mac":null, 149 | // "openPorts":[ 150 | 151 | // ], 152 | // "osNmap":null 153 | // } 154 | // ] 155 | 156 | 157 | // Accepts array or comma separated string for custom nmap commands in the second argument. 158 | var nmapscan = new nmap.NmapScan('127.0.0.1 google.com', '-sn'); 159 | 160 | nmapscan.on('complete',function(data){ 161 | console.log(data); 162 | }); 163 | nmapscan.on('error', function(error){ 164 | console.log(error); 165 | }); 166 | 167 | nmapscan.startScan(); 168 | 169 | // returns 170 | // [ 171 | // { 172 | // "hostname":"localhost", 173 | // "ip":"127.0.0.1", 174 | // "mac":null, 175 | // "openPorts":[ 176 | 177 | // ], 178 | // "osNmap":null 179 | // }, 180 | // { 181 | // "hostname":"google.com", 182 | // "ip":"74.125.21.113", 183 | // "mac":null, 184 | // "openPorts":[ 185 | 186 | // ], 187 | // "osNmap":null 188 | // } 189 | // ] 190 | var osandports = new nmap.OsAndPortScan('google.com'); 191 | 192 | osandports.on('complete',function(data){ 193 | console.log(data); 194 | }); 195 | osandports.on('error', function(error){ 196 | console.log(error); 197 | }); 198 | 199 | osandports.startScan(); 200 | 201 | // returns 202 | // [ 203 | // { 204 | // "hostname":"google.com", 205 | // "ip":"74.125.21.113", 206 | // "mac":null, 207 | // "openPorts":[ 208 | // { 209 | // "port":80, 210 | // "service":"http" 211 | // }, 212 | // { 213 | // "port":443, 214 | // "service":"https" 215 | // } 216 | // ], 217 | // "osNmap":"OpenBSD 4.3" 218 | // } 219 | // ] 220 | 221 | ``` 222 | 223 | 224 | ## Queued Scans 225 | 226 | Queued scanning was implemented to give higher level of control over the scanning process. 227 | While there are advantages, using the Queued scanning method does produce time overhead as a new instance 228 | of NMAP is created for each host. It may be useful to use Queued scans in the event that you are running 229 | a lengthy set of long running scans on each host. It would be recommended to perform a quickscan, before 230 | supplying the found hosts to a queued scanning process for longer running scans. 231 | 232 | ### Example 233 | ```javascript 234 | //the actionFunction gets run each time a scan on a host is complete 235 | function actionFunction(data){ 236 | console.log(data); 237 | console.log("Percentage complete" + scan.percentComplete()); 238 | } 239 | var scan = new nmap.QueuedOsAndPortScan("google.com 192.168.0.1-10", actionFunction); 240 | 241 | scan.on('complete', function(data){ 242 | console.log(data); 243 | console.log("total scan time" + scan.scanTime); 244 | }); 245 | 246 | scan.on('error', function(error){ 247 | console.log(error); 248 | }); 249 | 250 | scan.startRunScan(); //processes entire queue 251 | ``` 252 | 253 | Please open an issue if you have any questions, concerns, bugs, or critiques. 254 | 255 | [NMAP]: 256 | [NPM]: 257 | [NodeJs]: 258 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var nmap = require('./index'); 2 | var scan = new nmap.NmapScan("google.com", "-sV"); 3 | //scan.runActionOnError = true; 4 | //scan.saveErrorsToResults =true; 5 | console.log('scan starting'); 6 | scan.on('complete', function (data) { 7 | console.log(JSON.stringify(data, null, 4)); 8 | console.log("total scan time" + scan.scanTime); 9 | }); 10 | 11 | scan.on('error', function (data) { 12 | console.log(JSON.stringify(data,null, 2)); 13 | console.log("total scan time" + scan.scanTime); 14 | }); 15 | 16 | scan.startScan(); 17 | //# sourceMappingURL=app.js.map -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NodeJS <-> NMAP interface 3 | * Author: John Horton 4 | * Purpose: Create an interface for NodeJS applications to make use of NMAP installed on the local system. 5 | */ 6 | 7 | const child_process = require('child_process'); 8 | const execSync = child_process.execSync; 9 | const exec = child_process.exec; 10 | const spawn = child_process.spawn; 11 | const fs = require('fs'); 12 | const EventEmitter = require('events').EventEmitter; 13 | const os = require('os'); 14 | const Queue = require('queued-up'); 15 | const xml2js = require('xml2js'); 16 | 17 | 18 | /** 19 | * 20 | * @param {*} xmlInput 21 | * @param {*} onFailure 22 | * @returns {host[]} - Array of hosts 23 | */ 24 | function convertRawJsonToScanResults(xmlInput) { 25 | let tempHostList = []; 26 | 27 | if (!xmlInput.nmaprun.host) { 28 | //onFailure("There was a problem with the supplied NMAP XML"); 29 | return tempHostList; 30 | }; 31 | 32 | xmlInput = xmlInput.nmaprun.host; 33 | 34 | tempHostList = xmlInput.map((host) => { 35 | const newHost = { 36 | hostname: null, 37 | ip: null, 38 | mac: null, 39 | openPorts: null, 40 | osNmap: null 41 | } 42 | 43 | //Get hostname 44 | if (host.hostnames && host.hostnames[0] !== "\r\n" && host.hostnames[0] !== "\n") { 45 | newHost.hostname = host.hostnames[0].hostname[0].$.name 46 | } 47 | 48 | //get addresses 49 | host.address.forEach((address) => { 50 | const addressType = address.$.addrtype 51 | const addressAdress = address.$.addr 52 | const addressVendor = address.$.vendor 53 | 54 | if (addressType === 'ipv4') { 55 | newHost.ip = addressAdress 56 | } else if (addressType === 'mac') { 57 | newHost.mac = addressAdress 58 | newHost.vendor = addressVendor 59 | } 60 | }) 61 | 62 | //get ports 63 | if (host.ports && host.ports[0].port) { 64 | const portList = host.ports[0].port 65 | 66 | const openPorts = portList.filter((port) => { 67 | return (port.state[0].$.state === 'open') 68 | }) 69 | 70 | newHost.openPorts = openPorts.map((portItem) => { 71 | // console.log(JSON.stringify(portItem, null, 4)) 72 | 73 | const port = parseInt(portItem.$.portid) 74 | const protocol = portItem.$.protocol 75 | 76 | if (portItem.service) { 77 | const service = portItem.service[0].$.name 78 | const tunnel = portItem.service[0].$.tunnel 79 | const method = portItem.service[0].$.method 80 | const product = portItem.service[0].$.tunnel 81 | } 82 | 83 | let portObject = {} 84 | if(port) portObject.port = port 85 | if(protocol) portObject.protocol = protocol 86 | 87 | if (portItem.service) { 88 | if(service) portObject.service = service 89 | if(tunnel) portObject.tunnel = tunnel 90 | if(method) portObject.method = method 91 | if(product) portObject.product = product 92 | } 93 | 94 | return portObject 95 | }) 96 | } 97 | 98 | if (host.os && host.os[0].osmatch && host.os[0].osmatch[0].$.name) { 99 | newHost.osNmap = host.os[0].osmatch[0].$.name 100 | } 101 | return newHost 102 | }) 103 | 104 | return tempHostList; 105 | } 106 | 107 | 108 | class NmapScan extends EventEmitter { 109 | constructor(range, inputArguments) { 110 | super(); 111 | this.command = []; 112 | this.nmapoutputXML = ""; 113 | this.timer; 114 | this.range = []; 115 | this.arguments = ['-oX', '-']; 116 | this.rawData = ''; 117 | this.rawJSON; 118 | this.child; 119 | this.cancelled = false; 120 | this.scanTime = 0; 121 | this.error = null; 122 | this.scanResults; 123 | this.scanTimeout = 0; 124 | this.commandConstructor(range, inputArguments); 125 | this.initializeChildProcess(); 126 | } 127 | 128 | startTimer() { 129 | this.timer = setInterval(() => { 130 | this.scanTime += 10; 131 | if (this.scanTime >= this.scanTimeout && this.scanTimeout !== 0) { 132 | this.killChild(); 133 | } 134 | }, 10); 135 | } 136 | 137 | stopTimer() { 138 | clearInterval(this.timer); 139 | } 140 | 141 | commandConstructor(range, additionalArguments) { 142 | if (additionalArguments) { 143 | if (!Array.isArray(additionalArguments)) { 144 | additionalArguments = additionalArguments.split(' '); 145 | } 146 | this.command = this.arguments.concat(additionalArguments); 147 | } else { 148 | this.command = this.arguments; 149 | } 150 | 151 | if (!Array.isArray(range)) { 152 | range = range.split(' '); 153 | } 154 | this.range = range; 155 | this.command = this.command.concat(this.range); 156 | } 157 | 158 | killChild() { 159 | this.cancelled = true; 160 | if (this.child) { 161 | this.child.kill(); 162 | 163 | } 164 | } 165 | 166 | initializeChildProcess() { 167 | this.startTimer(); 168 | this.child = spawn(nmap.nmapLocation, this.command); 169 | process.on('SIGINT', this.killChild); 170 | process.on('uncaughtException', this.killChild); 171 | process.on('exit', this.killChild); 172 | this.child.stdout.on("data", (data) => { 173 | if (data.indexOf("percent") > -1) { 174 | // console.log(data.toString()); 175 | } else { 176 | this.rawData += data; 177 | } 178 | 179 | }); 180 | 181 | this.child.on('error', (err) => { 182 | this.killChild(); 183 | if (err.code === 'ENOENT') { 184 | this.emit('error', 'NMAP not found at command location: ' + nmap.nmapLocation) 185 | } else { 186 | this.emit('error', err.Error) 187 | } 188 | }) 189 | 190 | this.child.stderr.on("data", (err) => { 191 | this.error = err.toString(); 192 | }); 193 | 194 | this.child.on("close", () => { 195 | process.removeListener('SIGINT', this.killChild); 196 | process.removeListener('uncaughtException', this.killChild); 197 | process.removeListener('exit', this.killChild); 198 | 199 | if (this.error) { 200 | this.stopTimer(); 201 | this.emit('error', this.error); 202 | } else if (this.cancelled === true) { 203 | this.stopTimer(); 204 | this.emit('error', "Over scan timeout " + this.scanTimeout); 205 | } else { 206 | this.rawDataHandler(this.rawData); 207 | } 208 | }); 209 | } 210 | 211 | startScan() { 212 | this.child.stdin.end(); 213 | } 214 | 215 | cancelScan() { 216 | this.killChild(); 217 | this.emit('error', "Scan cancelled"); 218 | } 219 | 220 | scanComplete(results) { 221 | this.scanResults = results; 222 | this.stopTimer(); 223 | this.emit('complete', this.scanResults); 224 | } 225 | 226 | rawDataHandler(data) { 227 | let results; 228 | //turn NMAP's xml output into a json object 229 | xml2js.parseString(data, (err, result) => { 230 | if (err) { 231 | this.stopTimer(); 232 | this.emit('error', "Error converting XML to JSON in xml2js: " + err); 233 | } else { 234 | this.rawJSON = result; 235 | results = convertRawJsonToScanResults(this.rawJSON, (err) => { 236 | this.emit('error', "Error converting raw json to cleans can results: " + err + ": " + this.rawJSON); 237 | }); 238 | this.scanComplete(results); 239 | } 240 | }); 241 | } 242 | } 243 | 244 | 245 | class QuickScan extends NmapScan { 246 | constructor(range) { 247 | super(range, '-sP'); 248 | } 249 | } 250 | class OsAndPortScan extends NmapScan { 251 | constructor(range) { 252 | super(range, '-O'); 253 | } 254 | } 255 | 256 | 257 | class QueuedScan extends EventEmitter { 258 | 259 | constructor(scanClass, range, args, action = () => {}) { 260 | super(); 261 | this.scanResults = []; 262 | this.scanTime = 0; 263 | this.currentScan; 264 | this.runActionOnError = false; 265 | this.saveErrorsToResults = false; 266 | this.singleScanTimeout = 0; 267 | this.saveNotFoundToResults = false; 268 | 269 | this._queue = new Queue((host) => { 270 | 271 | if (args !== null) { 272 | this.currentScan = new scanClass(host, args); 273 | } else { 274 | this.currentScan = new scanClass(host); 275 | } 276 | if (this.singleScanTimeout !== 0) { 277 | this.currentScan.scanTimeout = this.singleScanTimeout; 278 | } 279 | 280 | this.currentScan.on('complete', (data) => { 281 | this.scanTime += this.currentScan.scanTime; 282 | if (data[0]) { 283 | data[0].scanTime = this.currentScan.scanTime; 284 | this.scanResults = this.scanResults.concat(data); 285 | } else if (this.saveNotFoundToResults) { 286 | data[0] = { 287 | error: "Host not found", 288 | scanTime: this.currentScan.scanTime 289 | } 290 | this.scanResults = this.scanResults.concat(data); 291 | 292 | } 293 | action(data); 294 | this._queue.done(); 295 | }); 296 | 297 | this.currentScan.on('error', (err) => { 298 | this.scanTime += this.currentScan.scanTime; 299 | 300 | let data = { 301 | error: err, 302 | scanTime: this.currentScan.scanTime 303 | } 304 | 305 | 306 | if (this.saveErrorsToResults) { 307 | this.scanResults = this.scanResults.concat(data); 308 | } 309 | if (this.runActionOnError) { 310 | action(data); 311 | } 312 | 313 | this._queue.done(); 314 | }); 315 | 316 | this.currentScan.startScan(); 317 | }); 318 | 319 | this._queue.add(this.rangeFormatter(range)); 320 | 321 | this._queue.on('complete', () => { 322 | this.emit('complete', this.scanResults); 323 | 324 | }); 325 | } 326 | 327 | rangeFormatter(range) { 328 | let outputRange = []; 329 | if (!Array.isArray(range)) { 330 | range = range.split(' '); 331 | } 332 | 333 | for (let i = 0; i < range.length; i++) { 334 | let input = range[i]; 335 | let temprange = range[i]; 336 | if (countCharacterOccurence(input, ".") === 3 && 337 | input.match(new RegExp("-", "g")) !== null && 338 | !input.match(/^[a-zA-Z]+$/) && 339 | input.match(new RegExp("-", "g")).length === 1 340 | ) { 341 | let firstIP = input.slice(0, input.indexOf("-")); 342 | let network; 343 | let lastNumber = input.slice(input.indexOf("-") + 1); 344 | let firstNumber; 345 | let newRange = []; 346 | for (let j = firstIP.length - 1; j > -1; j--) { 347 | if (firstIP.charAt(j) === ".") { 348 | firstNumber = firstIP.slice(j + 1); 349 | network = firstIP.slice(0, j + 1); 350 | break; 351 | } 352 | } 353 | for (let iter = firstNumber; iter <= lastNumber; iter++) { 354 | newRange.push(network + iter); 355 | } 356 | //replace the range/host string with array 357 | temprange = newRange; 358 | } 359 | outputRange = outputRange.concat(temprange); 360 | } 361 | 362 | function countCharacterOccurence(input, character) { 363 | let num = 0; 364 | for (let k = 0; k < input.length; k++) { 365 | if (input.charAt(k) === character) { 366 | num++; 367 | } 368 | } 369 | return num; 370 | } 371 | return outputRange; 372 | } 373 | 374 | startRunScan(index = 0) { 375 | this.scanResults = []; 376 | this._queue.run(0); 377 | } 378 | 379 | startShiftScan() { 380 | this.scanResults = []; 381 | this._queue.shiftRun(); 382 | } 383 | 384 | pause() { 385 | this._queue.pause(); 386 | } 387 | 388 | resume() { 389 | this._queue.resume(); 390 | } 391 | 392 | next(iterations = 1) { 393 | return this._queue.next(iterations); 394 | } 395 | 396 | shift(iterations = 1) { 397 | return this._queue.shift(iterations); 398 | } 399 | 400 | results() { 401 | return this.scanResults; 402 | } 403 | 404 | shiftResults() { 405 | this._queue.shiftResults(); 406 | return this.scanResults.shift(); 407 | } 408 | 409 | index() { 410 | return this._queue.index(); 411 | } 412 | 413 | queue(newQueue) { 414 | 415 | if (Array.isArray(newQueue)) { 416 | return this._queue.queue(newQueue); 417 | 418 | } else { 419 | return this._queue.queue(); 420 | } 421 | } 422 | 423 | percentComplete() { 424 | return Math.round(((this._queue.index() + 1) / this._queue.queue().length) * 100); 425 | } 426 | } 427 | 428 | class QueuedNmapScan extends QueuedScan { 429 | constructor(range, additionalArguments, actionFunction = () => {}) { 430 | super(NmapScan, range, additionalArguments, actionFunction); 431 | } 432 | } 433 | 434 | class QueuedQuickScan extends QueuedScan { 435 | constructor(range, actionFunction = () => {}) { 436 | super(QuickScan, range, null, actionFunction); 437 | } 438 | } 439 | 440 | class QueuedOsAndPortScan extends QueuedScan { 441 | constructor(range, actionFunction = () => {}) { 442 | super(OsAndPortScan, range, null, actionFunction); 443 | } 444 | } 445 | 446 | let nmap = { 447 | nmapLocation: "nmap", 448 | NmapScan, 449 | QuickScan, 450 | OsAndPortScan, 451 | QueuedScan, 452 | QueuedNmapScan, 453 | QueuedQuickScan, 454 | QueuedOsAndPortScan 455 | } 456 | 457 | module.exports = nmap; 458 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-nmap", 3 | "version": "4.0.0", 4 | "description": "Interfaces with locally installed NMAP", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "./node_modules/.bin/mocha --reporter spec", 8 | "app": "node app.js" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/Johnhhorton/node-nmap.git" 13 | }, 14 | "keywords": [ 15 | "nmap", 16 | "network", 17 | "scan" 18 | ], 19 | "author": "John H Horton", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/Johnhhorton/node-nmap/issues" 23 | }, 24 | "licenses": [{ 25 | "type": "MIT", 26 | "url": "https://github.com/brentertz/scapegoat/blob/master/LICENSE-MIT" 27 | }], 28 | "homepage": "https://github.com/Johnhhorton/node-nmap#readme", 29 | "dependencies": { 30 | "queued-up": "^2.0.2", 31 | "xml2js": "^0.4.15" 32 | }, 33 | "devDependencies": { 34 | "chai": "^3.4.0", 35 | "mocha": "^2.3.3" 36 | } 37 | } -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var should = require('chai').should(), 2 | assert = require('assert'), 3 | expect = require('chai').expect, 4 | nmap = require('../index'); 5 | 6 | 7 | describe('NmapScan', function() { 8 | 9 | it('runs NMAP', function(done) { 10 | 11 | this.timeout(10000); 12 | var scan = new nmap.NmapScan("google.com"); 13 | scan.on('complete', function(data) { 14 | expect(data).to.be.instanceOf(Array); 15 | expect(data).to.not.be.empty; 16 | expect(data[0]).to.include.keys('hostname', 'ip', 'mac', 'openPorts', 'osNmap'); 17 | done(); 18 | }); 19 | scan.startScan(); 20 | }); 21 | 22 | it('accepts space separated command', function(done) { 23 | 24 | this.timeout(10000); 25 | var scan = new nmap.NmapScan("-sn 127.0.0.1"); 26 | scan.on('complete', function(data) { 27 | 28 | expect(data).to.be.instanceOf(Array); 29 | expect(data).to.not.be.empty; 30 | expect(data[0]).to.include.keys('hostname', 'ip', 'mac', 'openPorts', 'osNmap'); 31 | done(); 32 | }); 33 | scan.startScan(); 34 | }); 35 | 36 | it('accepts multiple hosts', function(done) { 37 | 38 | this.timeout(10000); 39 | var scan = new nmap.NmapScan("-sn 127.0.0.1 google.com"); 40 | scan.on('complete', function(data) { 41 | 42 | expect(data).to.be.instanceOf(Array); 43 | expect(data).to.not.be.empty; 44 | expect(data[1]).to.include.keys('hostname', 'ip', 'mac', 'openPorts', 'osNmap'); 45 | done(); 46 | }); 47 | scan.startScan(); 48 | }); 49 | it('returns failure data for bad requests', function(done) { 50 | 51 | this.timeout(10000); 52 | var scan = new nmap.NmapScan("127.0.0."); 53 | scan.on('error', function(err) { 54 | expect(err).to.be.a('string'); 55 | done(); 56 | }); 57 | scan.startScan(); 58 | }); 59 | 60 | }); 61 | 62 | describe('quickScan', function() { 63 | 64 | it('scans range of hosts', function(done) { 65 | 66 | this.timeout(10000); 67 | var scan = new nmap.QuickScan("127.0.0.1 google.com"); 68 | scan.on('complete', function(data) { 69 | 70 | expect(data).to.be.instanceOf(Array); 71 | expect(data).to.not.be.empty; 72 | expect(data[1]).to.include.keys('hostname', 'ip', 'mac', 'openPorts', 'osNmap'); 73 | done(); 74 | 75 | }); 76 | scan.startScan(); 77 | }); 78 | it('returns failure data for bad requests', function(done) { 79 | 80 | this.timeout(10000); 81 | var scan = new nmap.QuickScan("127.0.0."); 82 | scan.on('error', function(err) { 83 | expect(err).to.be.a('string'); 84 | done(); 85 | }); 86 | scan.startScan(); 87 | 88 | }); 89 | 90 | }); 91 | 92 | describe('osAndPortScan', function() { 93 | 94 | it('scans hosts for open ports and OS data', function(done) { 95 | 96 | this.timeout(20000); 97 | var scan = new nmap.OsAndPortScan("google.com"); 98 | scan.on('complete', function(data) { 99 | expect(data).to.be.instanceOf(Array); 100 | expect(data).to.not.be.empty; 101 | expect(data[0]).to.include.keys('hostname', 'ip', 'mac', 'openPorts', 'osNmap'); 102 | expect(data[0].openPorts).to.be.instanceOf(Array); 103 | expect(data[0].openPorts[0].port).to.exist; 104 | done(); 105 | 106 | }); 107 | scan.startScan(); 108 | }); 109 | it('returns failure data for bad requests', function(done) { 110 | 111 | this.timeout(10000); 112 | var scan = new nmap.OsAndPortScan("127.0.0."); 113 | scan.on('error', function(err) { 114 | expect(err).to.be.a('string'); 115 | done(); 116 | }); 117 | scan.startScan(); 118 | 119 | }); 120 | 121 | }); --------------------------------------------------------------------------------