/**
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 utils = module.exports;
const { startsWith, cloneDeep } = require('lodash');
const { CallContext, Signature } = require('../../assess/models');
const SessionEvent = require('../../assess/response-scanning/cookies/events');
const VALID_ENCODINGS = ['utf8', 'ascii', 'utf16'];

/**
 * Checks encoding and returns if it is one of the
 * `VALID_ENCODINGS`, why? no f'n idea
 *
 * @param {string} encoding
 * @return {Boolean}
 */
utils.isValidEncoding = function(encoding) {
  return VALID_ENCODINGS.includes(encoding);
};

/**
 * Checks the options when storing a cookie,
 * and checks if `isSecure` is `false`
 *
 * @param {Object} options passed from server.constructor.prototype.state
 * @return {Boolean}
 */
utils.secureFlagMissing = function(options) {
  return utils.vulnerableOption({ options, key: 'isSecure' });
};

/**
 * Checks the options when storing a cookie,
 * and checks if `isHttpOnly` is `false`
 *
 * @param {Object} options passed from h.constructor.prototype.state
 */
utils.httpOnly = function(options) {
  return utils.vulnerableOption({ options, key: 'isHttpOnly' });
};

/**
 * Generic function to check an option and if it is `false`
 * @param {Object} params
 * @param {Object} params.options options passed to a cookie method
 * @param {string} params.key value in options to check
 * @return {Boolean}
 */
utils.vulnerableOption = function({ options, key }) {
  // eslint-disable-next-line no-prototype-builtins
  return options.hasOwnProperty(key) && options[key] !== true;
};

/**
 * Create a context for the session event.
 *
 * @param {*}      obj    - The object being applied.
 * @param {*[]}    args   - The arguments to the function.
 * @param {*}      result - The result of the function called.
 * @param {string} source - Source "type" of the event ('P', 'P0', etc).
 */
utils.createSessionContext = function(obj, args, result, source, stackOpts) {
  const mask = '[HIDDEN]';
  let opts, idx;
  // use source type to find the arg which has options and attempt to sanitize its password field
  // lodash lets me skip the if (sourceType) check since it'll return false if passed trash
  if (startsWith(source, 'P') && source.length === 2) {
    idx = parseInt(source[1]);
    opts = cloneDeep(args[idx]);
  }

  if (!opts) {
    return CallContext.create({
      obj,
      args,
      result,
      stackOpts
    });
  }

  if (opts.password) {
    opts.password = mask;
  }

  args[idx] = opts;

  return CallContext.create({ obj, args, result });
};

/**
 * Create an event given the session's state being set.
 *
 * @param  {string}      module  - Name of the module (h|server)
 * @param  {string}      method  - Method name for what triggered the event.
 * @param  {CallContext} ctxt    - Context for the call
 * @param  {string}      source  - Source "type" of the event ('P', 'P0', etc)
 * @param  {options}     options - The options which set the flag we're going off of.
 * @return {SessionEvent}
 */
utils.createSessionEvent = function(module, method, ctxt, source, options) {
  const signature = new Signature({
    moduleName: module,
    methodName: method,
    isModule: true
  });

  return new SessionEvent({
    context: ctxt,
    signature,
    source,
    options,
    framework: 'hapi'
  });
};
