/**
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';
const _ = require('lodash');
const UrlExclusion = require('./url.js');
const { INPUT_TYPES, EXCLUSION_INPUT_TYPES } = require('../../constants.js');

const generalizedInputTypes = {
  [INPUT_TYPES.JSON_ARRAYED_VALUE]: EXCLUSION_INPUT_TYPES.BODY,
  [INPUT_TYPES.JSON_VALUE]: EXCLUSION_INPUT_TYPES.BODY,
  [INPUT_TYPES.XML_VALUE]: EXCLUSION_INPUT_TYPES.BODY,
  [INPUT_TYPES.MULTIPART_CONTENT_TYPE]: EXCLUSION_INPUT_TYPES.BODY,
  [INPUT_TYPES.MULTIPART_FIELD_NAME]: EXCLUSION_INPUT_TYPES.BODY,
  [INPUT_TYPES.MULTIPART_NAME]: EXCLUSION_INPUT_TYPES.BODY,
  [INPUT_TYPES.MULTIPART_VALUE]: EXCLUSION_INPUT_TYPES.BODY,
  [INPUT_TYPES.COOKIE_NAME]: EXCLUSION_INPUT_TYPES.COOKIE,
  [INPUT_TYPES.COOKIE_VALUE]: EXCLUSION_INPUT_TYPES.COOKIE,
  [INPUT_TYPES.HEADER]: EXCLUSION_INPUT_TYPES.HEADER,
  [INPUT_TYPES.PARAMETER_NAME]: EXCLUSION_INPUT_TYPES.PARAMETER,
  [INPUT_TYPES.PARAMETER_VALUE]: EXCLUSION_INPUT_TYPES.PARAMETER,
  [INPUT_TYPES.URL_PARAMETER]: EXCLUSION_INPUT_TYPES.PARAMETER
};
const { BODY, PARAMETER, QUERYSTRING } = EXCLUSION_INPUT_TYPES;

class InputExclusion extends UrlExclusion {
  constructor(dtm) {
    super(dtm);
    // The special nature of '*' is described in the UI form for creating these.
    // If the value is the wildcard the exclusion applies to all type values.
    this.isNamed =
      dtm.inputType !== EXCLUSION_INPUT_TYPES.BODY &&
      dtm.inputType !== EXCLUSION_INPUT_TYPES.QUERYSTRING &&
      _.trim(dtm.inputName) !== '*';

    this.inputType = InputExclusion.generalizeInputType(dtm.inputType);
    this.inputName = dtm.inputName;
  }

  shouldExclude(ruleId, type, name) {
    return this.appliesToProtectRule(ruleId) && this.appliesToInputType(type) && this.matches(name);
  }

  appliesToInputType(type) {
    return (
      this.inputType === InputExclusion.generalizeInputType(type) ||
      (this.inputType === PARAMETER && (type === BODY || type === QUERYSTRING))
    );
  }

  /**
   * Check if an input name matches this rule
   *
   * @param name String name of input
   */
  matches(name) {
    // if the input name is * or if it's a type
    // that does not support setting exclusions by name
    // (body and querystring) then it must match
    if (!this.isNamed) {
      return true;
    }
    // headers are matched case insensitive
    if (this.inputType === EXCLUSION_INPUT_TYPES.HEADER) {
      return name.toLowerCase() === this.inputName;
    }
    // otherwise we need to do an exact match
    return name === this.inputName;
  }

  /**
   * Given the source input type, will return a generic type that will match
   * those of the exclusions options. This is done because there are more input
   * types for which a rule can apply than there are exclusion type options.
   *
   * @param   {String} type The source input type
   * @returns {String}      The input type relevant to exclusions optionsa
   */
  static generalizeInputType(type) {
    return generalizedInputTypes[type] || type;
  }
}

module.exports = InputExclusion;
