sitecheck
Version:
Open Source web application security scanner
158 lines (138 loc) • 6.36 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.
*/
"use strict";
var fs = require("fs");
var valid_url = require("valid-url");
var winston = require("winston");
const url = require('url');
function Params() {
// common options
this.allPages = false; // if true crawls the entire site. If false, just one page.
// log level
this.loglevel = "warn";
this.silent = false;
this.logFile = undefined;
// check list
this.checks = [];
// connection credentials
this.loginPage = "/login";
this.user = "root";
this.password = "";
// map checkName -> js file path
this.checkMap = new Map();
/**
* Build scan parameters from default params + config file + "manual" params
* @param {Array} params - 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", "info", "verbose", "debug", "silly". Default is "warn".</li>
* </ul>
*/
this.gatherScanParams = function (params) {
var opts = require('rc')("sitecheck", this, params);
// make sure "checks" field is an array
if (typeof opts.checks === 'string' || opts.checks instanceof String) {
opts.checks = [opts.checks];
} else {
this.checks = opts.checks;
}
if (this.checks.constructor !== Array) {
throw new Error("No checks. At least one check must be set to start scan. Operation canceled.");
}
// checks
if (!this.checks || this.checks.length < 1) {
throw new Error("No checks. At least one check must be set to start scan. Operation canceled.");
}
// check url
this.url = opts.url;
if (!this.url) {
throw new Error("No Url. An url must be set. Operation canceled.");
}
if (!valid_url.isWebUri(this.url)) {
throw new Error("Invalid Url \"" + this.url + "\". Please submit a valid url. Operation canceled.");
}
var uri = new url.parse(this.url);
// log system
this.loglevel = opts.loglevel;
var possibleLogLevels = ["error", "warn", "info", "verbose", "debug", "silly"];
var foundLogLevel = false;
for (let i = 0; i < possibleLogLevels.length; i++) {
if (this.loglevel === possibleLogLevels[i]) {
winston.level = possibleLogLevels[i];
foundLogLevel = true;
}
}
if (!foundLogLevel) {
winston.level = "debug";
winston.warn("Incorrect log level \"" + this.loglevel + "\" specified. Log level was set to 'debug'.");
this.loglevel = winston.level;
}
if (opts.log) {
var logdir = './log/';
if (!fs.existsSync(logdir)) {
fs.mkdirSync(logdir);
}
var moment = require("moment");
var logfilename = logdir + uri.hostname.replace(/\./, "_") + "_" + moment.utc().format('YYMMDDHHmmss') + ".log";
winston.add(winston.transports.File, {
filename: logfilename, handleExceptions: true, humanReadableUnhandledException: true, level: winston.level
});
this.logFile = logfilename;
}
this.silent = opts.silent;
/* istanbul ignore next */ // complex and useless to test
if (this.silent) {
winston.remove(winston.transports.Console);
}
// verify check names
var verified_checks = [];
for (let i in opts.checks) {
/* istanbul ignore else */
if (opts.checks.hasOwnProperty(i)) {
let name = opts.checks[i];
let name_filtered = name.replace(/[^a-zA-Z0-9_]/g, '');
if (name_filtered !== name) {
winston.warn("Invalid check name '" + name + "'. Check names can only contain [a-zA-Z0-9_] characters. Skipped.");
}
else {
let found = false;
var check_paths = ["checks/server/", "checks/page/", "checks/"];
for (let j in check_paths) if (!found && check_paths.hasOwnProperty(j)) {
var p = check_paths[j];
var fullpath = __dirname + "/" + p + "check_" + name + ".js";
if (!found && fs.existsSync(fullpath)) {
verified_checks.push(name);
this.checkMap.set(name, fullpath);
found = true;
}
}
if (!found) {
winston.warn("Invalid check name '" + name + "'. Could not find 'check_" + name + ".js'. Skipped.");
}
}
}
}
this.checks = verified_checks;
};
}
var p = new Params();
module.exports = p;