/**
Copyright: 2024 Contrast Security, Inc
  Contact: support@contrastsecurity.com
  License: Commercial

  NOTICE: This Software and the patented inventions embodied within may only be
  used as part of Contrast Security’s commercial offerings. Even though it is
  made available through public repositories, use of this Software is subject to
  the applicable End User Licensing Agreement found at
  https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
  between Contrast Security and the End User. The Software may not be reverse
  engineered, modified, repackaged, sold, redistributed or otherwise used in a
  way not consistent with the End User License Agreement.
*/
'use strict';

/**
 * Checks a user agent to see if it matches known bad bots.
 * @module lib/rules/protect/bot-check
 */

const { isString } = require('../../../util/is-string');
const partial = require('../../../util/partial');
const logger = require('../../../core/logger')('contrast:protect:bot-blocker');

const knownBots = require('./bots');

/**
 * Tester is a function that reads a rule, and returns a test function.
 * The function returned will read an input string and return `true`
 * if it matched a bot defined by the rule.
 * @param   {Object}   rule The rule used to create the test function.
 * @returns {Function}      The function for testing user agent strings.
 */
const Tester = (function(includes, startsWith) {
  return function(rule) {
    const tests = [];

    if (rule.includes) {
      tests.push(partial(includes, rule.includes));
    }

    if (rule.startsWith) {
      tests.push(partial(startsWith, rule.startsWith));
    }

    return function doChecks(string) {
      return tests.every((test) => test(string)) ? rule : false;
    };
  };
})(
  (segment, string) => string.includes(segment),
  (segment, string) => string.indexOf(segment) === 0
);

/**
 * Generate rule execution logic from the bots file.
 */
const tests = knownBots.bots
  .filter((rule) => {
    // Make sure the rules are valid.
    if (
      !(rule.hasOwnProperty('startsWith') || rule.hasOwnProperty('includes'))
    ) {
      logger.warn('Bot missing required fields %j', rule);
      return false;
    }

    return rule;
  })
  .map((rule) => Tester(rule));

/**
 * Compares an input string against a list of known bad bot signatures.
 * @param   {String}  string The string to check.
 * @returns {Boolean}        If the string matches a known bot signature.
 */
function isKnownBot(string) {
  if (!string || !isString(string)) {
    return false;
  }

  for (let i = 0; i < tests.length; i++) {
    const signature = tests[i](string);
    if (signature) {
      return signature;
    }
  }

  return false;
}

exports.isKnownBot = isKnownBot;
