all files / src/ app.js

80% Statements 44/55
50% Branches 8/16
80% Functions 4/5
83.02% Lines 44/53
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120                                                                                                                                                       
/**
 * @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 = [];
 
/**
 * 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} endCallback - (err, data). data is an array of Issue(s).
 * @param {function} progressCallback - (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, endCallback, progressCallback) {
    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();
 
    // check targets SERIALLY and call cb after each one.
    Promise.mapSeries(targets, function (target, index, length) {
        if (target.isDone)
            return;
        return checkTarget(target, ct, progressCallback, index, length);
    }).then(function () {
        endCallback();
    }).catch(function (data) {
        if (data instanceof Error) endCallback(data);
        else endCallback(null, data);
    });
}
 
function checkTarget(target, cancellationToken, progressCallback, targetIndex, targetCount) {
    let runningChecks = [];
    let checksCount = params.checks.length;
    let checksDone = 0;
    for (let checkName of params.checks) {
        let fileName = params.checkMap.get(checkName);
        let Check = require(fileName);
        let check = new Check(target);
        Eif (check.targetType == target.targetType) {
            runningChecks.push(
                check.check(cancellationToken).then(() => {
                    checksDone++;
                    let curTargetProgress = checksDone / checksCount;
                    let overallProgress = (targetIndex + checksProgress) / checksProgress;
                    let progressData = {
                        progress: targetsProgress,
                        targetProgress: curTargetProgress
                    }
                    progressCallback(progress);
                    console.log("yipikaï !");
                }).catch((err) => {
                    checksDone++;
                    Eif (err) {
                        Iif (err instanceof Error) {
                            console.log('Check "' + fileName + '" aborted.');
                        } else Eif (err instanceof Array) {
                            for (let a of err) {
                                Eif (a instanceof Issue) {
                                    let maybeFP = '';
                                    Iif (a.maybeFalsepositive) maybeFP = " (may be false positive)";
                                    console.log("Issue : " + a.ref + " : " + a.errorContent + maybeFP);
                                }
                            }
                        }
                    }
                }));
        }
    }
 
    // Concurrency level can be managed by request option "pool: {maxSockets: Infinity}" (https://github.com/request/request#requestoptions-callback)
    return Promise.all(runningChecks);
}
 
module.exports = { scan: scan };