<commit_before>/**
 * @fileOverview options.js loads options from local storage to
 * initialize forms, and saves updates to the options.
 *
 * Available options include setting the some-trust whitelisted domains 
 * and the server the user uploads their content to when generating new
 * injectable links.
 *
 * For more information about the whitelist, read:
 * https://github.com/privly/privly-organization/wiki/whitelist
 *
 * Local Storage Bindings Used:
 *
 * - user_whitelist_csv: This is the string of text the user gives the extension
 *   to specify which servers they trust to automatically inject into the host
 *   page. This string is presented to the user every time they visit options,
 *   but the string used by the content script is user_whitelist_regexp
 *
 * - user_whitelist_regexp: This string is formatted specifically so that 
 *   privly.js can update its whitelist regexp.
 *
 * - posting_content_server_url: The content server the user will post to
 *   when generating new content.
 *
 * - privly_glyph: A consistent visual identifier to prevent spoofing.
 *   It is stored as series of hex colors stated
 *   without the leading hash sign, and separated by commas. 
 *   eg: ffffff,f0f0f0,3f3f3f
 *
 */
/* jshint undef: true, unused: true */
/* global ls */

/**
 * Saves user's setting to disable Privly button appearance
 */
function saveCheckedSetting() {

  // Don't run on Firefox
  var status = document.getElementById("button_status");
  if (status === null ) {return;}

  // reset status
  status.innerHTML = "";

  var checkedState = document.querySelector("#disableBtn").checked;
  ls.setItem("Options:DissableButton", checkedState);

  status.innerHTML = "Setting Saved."; 
}

/**
 * Restores user's setting to disable Privly button appearance
 */
function restoreCheckedSetting() {
  var btn = document.getElementById("disableBtn");
  if ( btn === null ) {return;} // Don't run on Firefox
  var stored = ls.getItem("Options:DissableButton");
  btn.checked = (stored !== undefined && stored === true);

  // Save the current setting for the Privly button appearance
  btn.addEventListener('click', saveCheckedSetting);
}

/**
 * Saves user's custom whitelist to local storage.
 */
function saveWhitelist() {
  var domains = []; // stores valid domains
  var inputs = [];
  var invalid_domains = false;
  var invalid_domain = document.getElementById('invalid_domain');
  invalid_domain.className = ''; // hide the error message
  var url_inputs = document.getElementsByClassName('whitelist_url');
  for(var i = 0; i < url_inputs.length ; i++ ){
    url_inputs[i].className = "whitelist_url form-control"; // remove error class
    if(url_inputs[i].value.length > 0) {
      inputs.push({
          // remove any leading protocol and invalid characters
          domain:  url_inputs[i].value.replace(/ /g, "")
                                      .replace(/[^a-zA-Z0-9\-:._]/g, ""),
          input: url_inputs[i] // save input for later use
      });
    }
  }

  // Each subdomain can be from 1-63 characters and may contain alphanumeric
  // characters, - and _ but may not begin or end with - or _
  // Each domain can be from 1-63 characters and may contain alphanumeric 
  // characters and - but may not begin or end with - Each top level domain may
  // be from 2 to 9 characters and may contain alpha characters
  var validateSubdomain = /^(?!\-|_)[\w\-]{1,63}/g; //subdomains
  var validateDomain = /^(?!\-)[a-zA-Z0-9\-?]{1,63}$/g; //domain
  var validateDomainAndPort = /^(?!\-)[a-zA-Z0-9\-?]{1,63}(?::\d+)?$/g; 
  var validateTLD = /^[a-zA-Z]{2,9}(?::\d+)?$/g; //top level domain
  
  //needed because js regex does not have look-behind
  var notEndInHyphenOrUnder = /[^\-_]$/g; 
  var notEndInHyphen = /[^\-]$/g;

  var domain_regexp = "";  // stores regex to match validated domains

  //iterate over entered list, split by invalid chars
  for (i = 0; i < inputs.length; i++) {
    var parts = inputs[i].domain.split(".");
    var valid_parts_count = 0;
    
    //iterate over domains, split by .
    for (var j = 0; j < parts.length; j++) {
      switch (j){
      case parts.length-1: // validate TLD or Domain if no TLD present
        if (parts.length == 1) {
          if (parts[j].match(validateDomainAndPort)) 
            valid_parts_count++;
        } else {
          if (parts[j].match(validateTLD))
            valid_parts_count++;
        } 
        break;
      case parts.length-2: // validate Domain
        if (parts[j].match(validateDomain) &&
            parts[j].match(notEndInHyphen)) {
          valid_parts_count++;
        }
        break;
      default: // validate Subdomain(s)
        if (parts[j].match(validateSubdomain) &&
            parts[j].match(notEndInHyphenOrUnder)) {
          valid_parts_count++;
        }
        break;
      }
    }

    //if all parts of domain are valid
    //append to regex for restricting domains of injected content
    if (valid_parts_count === parts.length) {
      domain_regexp += ("|" + inputs[i].domain.toLowerCase().replace(/\./g, "\\.") + "\\\/");
      inputs[i].input.className = "whitelist_url form-control";
      domains.push(inputs[i].domain);
    } else {
      inputs[i].input.className += " invalid-domain";
      invalid_domains = true;
    }
  }
  if (invalid_domains) {
      invalid_domain.className = 'show';
  }
  ls.setItem("user_whitelist_json", JSON.stringify(domains));
  ls.setItem("user_whitelist_regexp", domain_regexp);

  // Update status to let user know options were saved.
  var status = document.getElementById("status");
  status.innerHTML = "Options Saved.";
  setTimeout(function() {
    status.innerHTML = "";
  }, 750);
}


/**
 * Restores select box state to saved value from local storage.
 */
function restoreWhitelist() {

  // Legacy CSV check
  var user_whitelist_csv = ls.getItem("user_whitelist_csv");<commit_msg>refactor domain validation for easier testing<commit_after>/**
 * @fileOverview options.js loads options from local storage to
 * initialize forms, and saves updates to the options.
 *
 * Available options include setting the some-trust whitelisted domains 
 * and the server the user uploads their content to when generating new
 * injectable links.
 *
 * For more information about the whitelist, read:
 * https://github.com/privly/privly-organization/wiki/whitelist
 *
 * Local Storage Bindings Used:
 *
 * - user_whitelist_csv: This is the string of text the user gives the extension
 *   to specify which servers they trust to automatically inject into the host
 *   page. This string is presented to the user every time they visit options,
 *   but the string used by the content script is user_whitelist_regexp
 *
 * - user_whitelist_regexp: This string is formatted specifically so that 
 *   privly.js can update its whitelist regexp.
 *
 * - posting_content_server_url: The content server the user will post to
 *   when generating new content.
 *
 * - privly_glyph: A consistent visual identifier to prevent spoofing.
 *   It is stored as series of hex colors stated
 *   without the leading hash sign, and separated by commas. 
 *   eg: ffffff,f0f0f0,3f3f3f
 *
 */
/* jshint undef: true, unused: true */
/* global ls */

/**
 * Saves user's setting to disable Privly button appearance
 */
function saveCheckedSetting() {

  // Don't run on Firefox
  var status = document.getElementById("button_status");
  if (status === null ) {return;}

  // reset status
  status.innerHTML = "";

  var checkedState = document.querySelector("#disableBtn").checked;
  ls.setItem("Options:DissableButton", checkedState);

  status.innerHTML = "Setting Saved."; 
}

/**
 * Restores user's setting to disable Privly button appearance
 */
function restoreCheckedSetting() {
  var btn = document.getElementById("disableBtn");
  if ( btn === null ) {return;} // Don't run on Firefox
  var stored = ls.getItem("Options:DissableButton");
  btn.checked = (stored !== undefined && stored === true);

  // Save the current setting for the Privly button appearance
  btn.addEventListener('click', saveCheckedSetting);
}

/** 
 * Validates a FQDN
 */
function checkDomain(domain) {
  // Each subdomain can be from 1-63 characters and may contain alphanumeric
  // characters, - and _ but may not begin or end with - or _
  // Each domain can be from 1-63 characters and may contain alphanumeric 
  // characters and - but may not begin or end with - Each top level domain may
  // be from 2 to 9 characters and may contain alpha characters
  var validateSubdomain = /^(?!\-|_)[\w\-]{1,63}/g; //subdomains
  var validateDomain = /^(?!\-)[a-zA-Z0-9\-?]{1,63}$/g; //domain
  var validateDomainAndPort = /^(?!\-)[a-zA-Z0-9\-?]{1,63}(?::\d+)?$/g; 
  var validateTLD = /^[a-zA-Z]{2,9}(?::\d+)?$/g; //top level domain
  
  //needed because js regex does not have look-behind
  var notEndInHyphenOrUnder = /[^\-_]$/g; 
  var notEndInHyphen = /[^\-]$/g;

  var parts = domain.split(".");
  var valid_parts_count = 0;
    
  //iterate over domains, split by .
  for (var j = 0; j < parts.length; j++) {
    switch (j){
    case parts.length-1: // validate TLD or Domain if no TLD present
      if (parts.length == 1) {
        if (parts[j].match(validateDomainAndPort)) 
          valid_parts_count++;
      } else {
        if (parts[j].match(validateTLD))
          valid_parts_count++;
      } 
      break;
    case parts.length-2: // validate Domain
      if (parts[j].match(validateDomain) &&
          parts[j].match(notEndInHyphen)) {
        valid_parts_count++;
      }
      break;
    default: // validate Subdomain(s)
      if (parts[j].match(validateSubdomain) &&
          parts[j].match(notEndInHyphenOrUnder)) {
        valid_parts_count++;
      }
      break;
    }
  }

  //if all parts of domain are valid
  //append to regex for restricting domains of injected content
  return valid_parts_count === parts.length;
}

/**
 * Saves user's custom whitelist to local storage.
 */
function saveWhitelist() {
  var inputs = []; // Stores whitelist inputs and whitelisted values
  var url_inputs = document.getElementsByClassName('whitelist_url');
  for(var i = 0; i < url_inputs.length ; i++ ){
    url_inputs[i].className = "whitelist_url form-control"; // remove error class
    if(url_inputs[i].value.length > 0) {
      inputs.push({
          // remove any leading protocol and invalid characters
          domain:  url_inputs[i].value.replace(/ /g, "")
                                      .replace(/[^a-zA-Z0-9\-:._]/g, ""),
          input: url_inputs[i] // save input for later use
      });
    }
  }

  var domains = []; // stores valid domains
  var domain_regexp = "";  // stores regex to match validated domains

  var invalid_domains = false;
  var invalid_domain = document.getElementById('invalid_domain');
  invalid_domain.className = ''; // hide the error message

  //iterate over entered list, split by invalid chars
  for (i = 0; i < inputs.length; i++) {
    if (checkDomain(inputs[i].domain)) {
      domain_regexp += ("|" + inputs[i].domain.toLowerCase().replace(/\./g, "\\.") + "\\\/");
      inputs[i].input.className = "whitelist_url form-control";
      domains.push(inputs[i].domain);
    } else {
      inputs[i].input.className += " invalid-domain";
      invalid_domains = true;
    }
  }
  if (invalid_domains) {
      invalid_domain.className = 'show';
  }
  ls.setItem("user_whitelist_json", JSON.stringify(domains));
  ls.setItem("user_whitelist_regexp", domain_regexp);

  // Update status to let user know options were saved.
  var status = document.getElementById("status");
  status.innerHTML = "Options Saved.";
  setTimeout(function() {
    status.innerHTML = "";
  }, 750);
}


/**
 * Restores select box state to saved value from local storage.
 */
function restoreWhitelist() {

  // Legacy CSV check
  var user_whitelist_csv = ls.getItem("user_whitelist_csv");
{'fraction_non_alphanumeric': 0.12750378724120517, 'fraction_numerical': 0.00706951691634405, 'mean_word_length': 3.019959404600812, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4849633', 'n_tokens_mistral': 3678, 'n_tokens_neox': 3495, 'n_words': 1091}