sitecheck
Version:
Open Source web application security scanner
139 lines (126 loc) • 5.78 kB
JavaScript
/**
* @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.
*/
;
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 };