UNPKG

sitecheck

Version:

Open Source web application security scanner

139 lines (126 loc) 5.78 kB
/** * @license Apache-2.0 * Copyright (C) 2016 The Sitecheck Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ "use strict"; var winston = require("winston"); var Promise = require("bluebird"); var Target = require('./target.js'); var params = require('./params.js'); var CancellationToken = require('./cancellationToken.js'); const CONSTANTS = require("./constants.js"); var Issue = require('./issue.js'); winston.remove(winston.transports.Console); winston.add(winston.transports.Console, { handleExceptions: true, humanReadableUnhandledException: true }); var targets = []; var issues = []; /** * Main function. * Starts a scan. * @param {Array} opts - An array of scan parameters. * <ul> * <li>config : path to config file.</li> * <li>url : Url to scan. Mandatory unless defined in config file.</li> * <li>checks : An array of check names. Check names must match names of js files in src/checks/**, without ".js". Mandatory unless defined in config file.</li> * <li>allPages : true to scan all pages of website. Default is false.</li> * <li>log : true to activate log to file. Default is false.</li> * <li>silent : true to prevent console logs. Default is false.</li> * <li>loglevel : sets log level. Possible values are "error", "warn", " ", "verbose", "debug", "silly". Default is "warn".</li> * </ul> * @param {function} callback - (err, data). data is an object : * { progress : float 0.0 - 1.0, * targetProgress : float 0.0 - 1.0, * issues : [array os Issue], * targets : [array of Target] } */ function scan(opts, callback) { params.gatherScanParams(opts); var scanId = ""; targets.push(new Target(params.url, CONSTANTS.TARGETTYPE.PAGE)); targets.push(new Target(params.url, CONSTANTS.TARGETTYPE.SERVER)); targets[1].isDone = true; let index = 0; var ct = new CancellationToken(); let lastIndex = 0; // check targets SERIALLY and call cb after each one. Promise.mapSeries(targets, function (target, index, length) { lastIndex = index; if (target.isDone) { return; } return checkTarget(target, ct, callback, index, length); }).then(function () { let progressData = getProgressData(lastIndex, targets.length, 0, 0); // "(targets.length, targets.length" on purpose because we're 100% done. callback(null, progressData); }).catch(function (data) { if (data instanceof Error) callback(data); else { let progressData = getProgressData(lastIndex, targets.length, 0, 0); callback(null, progressData); } }); } function checkTarget(target, cancellationToken, callback, targetIndex, targetCount) { let runningChecks = []; let checkCount = 0; let checkIndex = 0; for (let checkName of params.checks) { let fileName = params.checkMap.get(checkName); let Check = require(fileName); let check = new Check(target); if (check.targetType == target.targetType) { checkCount++; runningChecks.push( check.check(cancellationToken).then(() => { }).catch((err) => { if (err) { if (err instanceof Error) { console.log('Check "' + fileName + '" aborted.'); } else if (err instanceof Array) { for (let a of err) { if (a instanceof Issue) { issues.push(a); } } } } }).finally(() => { checkIndex++; let progressData = getProgressData(targetIndex, targetCount, checkIndex, checkCount); callback(null, progressData); })); } } // Concurrency level can be managed by request option "pool: {maxSockets: Infinity}" (https://github.com/request/request#requestoptions-callback) return Promise.all(runningChecks); } function getProgressData(targetIndex, targetCount, checkIndex, checkCount) { let curTargetProgress = 0; if (checkCount) curTargetProgress = (checkIndex) / checkCount; let overallProgress = (targetIndex + curTargetProgress) / targetCount; let progressData = { progress: overallProgress, targetProgress: curTargetProgress, targetIndex: targetIndex + 1, // Nth target we're in targetCount: targetCount, checkIndex: checkIndex, checkCount: checkCount, issues: issues } return progressData; } module.exports = { scan: scan };