<commit_before>/*    Copyright 2016-2019 Firewalla INC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

'use strict'

let log = require('../net2/logger.js')(__filename, 'info');
let ip = require('ip')

var extend = require('util')._extend

const minimatch = require('minimatch')

function arraysEqual(a, b) {
  if (a === b) return true;
  if (a == null || b == null) return false;
  if (a.length != b.length) return false;

  // If you don't care about the order of the elements inside
  // the array, you should sort both arrays here.

  for (var i = 0; i < a.length; ++i) {
    if (a[i] !== b[i]) return false;
  }
  return true;
}

module.exports = class {
  constructor(rules) {
    // FIXME: ignore any rules not begin with prefix "p"
    extend(this, rules);
    this.timestamp = new Date() / 1000;
  }

  getMatchingKeys() {
    let keys = []
    for(let k in this) {
      if (k === "type" || k.startsWith("p.")) {
        keys.push(k)
      }
    }

    return keys.sort()
  }

  isEqualToException(e) {
    const thisKeys = this.getMatchingKeys()
    const thatKeys = e.getMatchingKeys()

    if(!arraysEqual(thisKeys, thatKeys)) {
      return false
    }
    for(let i in thisKeys) {
      let k = thisKeys[i]
      if (this[k] !== e[k]) {
        return false
      }
    }

    return true
  }

  isSecurityAlarm(alarm) {
    const securityAlarmTypes = [
      "ALARM_SPOOFING_DEVICE",
      "ALARM_BRO_NOTICE",
      "ALARM_INTEL",
      "ALARM_VULNERABILITY"
    ];

    return securityAlarmTypes.includes(alarm.type);
  }

  match(alarm) {

    let matched = false;
    // FIXME: exact match only for now, and only supports String
    for (var key in this) {

      if(!key.startsWith("p.") && key !== "type") {
        continue;
      }

      var val = this[key];
      if(!alarm[key]) return false;
      let val2 = alarm[key];

      if(key === "type" && val === "ALARM_INTEL" && this.isSecurityAlarm(alarm)) {
        matched = true;
        continue;
      }

      if(val.startsWith("*.")) {
        // use glob matching
        if(!minimatch(val2, val) && // NOT glob match
           val.slice(2) !== val2) { // NOT exact sub domain match
          return false
        }
      } else {
        let cidrParts = val.split("/", 2);
        if (cidrParts.length == 2) {
          let addr = cidrParts[0];
          let mask = cidrParts[1];
          if (ip.isV4Format(addr) && RegExp("^\\d+$").test(mask) && ip.isV4Format(val2)) {
            // try matching cidr subnet iff value in alarm is an ipv4 address and value in exception is a cidr notation
            if(!ip.cidrSubnet(val).contains(val2)) {
              return false;
            }
          }
        } else {
          // not a cidr subnet exception
          if(val2 !== val) return false;
        }
      }

      matched = true;
    }

    return matched;
  }
}<commit_msg>check if field of alarm is number on exception match<commit_after>/*    Copyright 2016-2019 Firewalla INC
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

'use strict'

let log = require('../net2/logger.js')(__filename, 'info');
let ip = require('ip')

var extend = require('util')._extend

const minimatch = require('minimatch')

const _ = require('lodash')

function arraysEqual(a, b) {
  if (a === b) return true;
  if (a == null || b == null) return false;
  if (a.length != b.length) return false;

  // If you don't care about the order of the elements inside
  // the array, you should sort both arrays here.

  for (var i = 0; i < a.length; ++i) {
    if (a[i] !== b[i]) return false;
  }
  return true;
}

module.exports = class {
  constructor(rules) {
    // FIXME: ignore any rules not begin with prefix "p"
    extend(this, rules);
    this.timestamp = new Date() / 1000;
  }

  getMatchingKeys() {
    let keys = []
    for(let k in this) {
      if (k === "type" || k.startsWith("p.")) {
        keys.push(k)
      }
    }

    return keys.sort()
  }

  isEqualToException(e) {
    const thisKeys = this.getMatchingKeys()
    const thatKeys = e.getMatchingKeys()

    if(!arraysEqual(thisKeys, thatKeys)) {
      return false
    }
    for(let i in thisKeys) {
      let k = thisKeys[i]
      if (this[k] !== e[k]) {
        return false
      }
    }

    return true
  }

  isSecurityAlarm(alarm) {
    const securityAlarmTypes = [
      "ALARM_SPOOFING_DEVICE",
      "ALARM_BRO_NOTICE",
      "ALARM_INTEL",
      "ALARM_VULNERABILITY"
    ];

    return securityAlarmTypes.includes(alarm.type);
  }

  match(alarm) {

    let matched = false;
    // FIXME: exact match only for now, and only supports String
    for (var key in this) {

      if(!key.startsWith("p.") && key !== "type") {
        continue;
      }

      var val = this[key];
      if(!alarm[key]) return false;
      let val2 = alarm[key];

      if(key === "type" && val === "ALARM_INTEL" && this.isSecurityAlarm(alarm)) {
        matched = true;
        continue;
      }

      if(val.startsWith("*.")) {
        // use glob matching
        if(!minimatch(val2, val) && // NOT glob match
           val.slice(2) !== val2) { // NOT exact sub domain match
          return false
        }
      } else {
        let cidrParts = val.split("/", 2);
        if (cidrParts.length == 2) {
          let addr = cidrParts[0];
          let mask = cidrParts[1];
          if (ip.isV4Format(addr) && RegExp("^\\d+$").test(mask) && ip.isV4Format(val2)) {
            // try matching cidr subnet iff value in alarm is an ipv4 address and value in exception is a cidr notation
            if(!ip.cidrSubnet(val).contains(val2)) {
              return false;
            }
          }
        } else {
          // not a cidr subnet exception

          // alarm might has field in number
          // and assume exceptions are always loaded from redis before comparing
          if (_.isNumber(val2)) {
            val = _.toNumber(val)
            if (isNaN(val)) return false;
          }
          if(val2 !== val) return false;
        }
      }

      matched = true;
    }

    return matched;
  }
}
{'fraction_non_alphanumeric': 0.12325356204177618, 'fraction_numerical': 0.008161571448333103, 'mean_word_length': 2.189236876929863, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4395229', 'n_tokens_mistral': 2316, 'n_tokens_neox': 2260, 'n_words': 721}