ua-parser 0.2.2

Rust implementation of the User Agent String Parser project
Documentation
'use strict'

var assert = require('assert')
var path = require('path')
var fs = require('fs')
var yaml = require('yamlparser')
var regexes = readYAML('../regexes.yaml')
var safe = require('safe-regex')
var refImpl = require('uap-ref-impl')

function readYAML (fileName) {
  var file = path.join(__dirname, fileName)
  var data = fs.readFileSync(file, 'utf8')
  return yaml.eval(data)
}

suite('regexes', function () {
  Object.keys(regexes).forEach(function (parser) {
    suite(parser, function () {
      regexes[parser].forEach(function(item) {
        test(item.regex, function () {
          assert.ok(safe(item.regex))
        })
      })
    })
  })

  Object.keys(regexes).forEach(function (parser) {
    suite(`no reverse lookup in ${parser}`, function () {
      regexes[parser].forEach(function(item) {
        test(item.regex, function () {
          if (/\(\?<[!=]/.test(item.regex)) {
            assert.ok(false, 'go parser does not support regex lookbehind. See https://github.com/google/re2/wiki/Syntax')
          }
          if (/\(\?[!=]/.test(item.regex)) {
            assert.ok(false, 'go parser does not support regex lookahead. See https://github.com/google/re2/wiki/Syntax')
          }
        })
      })
    })
  })

})

suite('redos', function(){
  const parse = refImpl(regexes).parse

  const tests = [
    '"a"+"a"*3500+"a"',
    '"SmartWatch"+" "*3500+"z"',
    '";A Build HuaweiA"+"4"*3500+"z"',
    '"HbbTV/0.0.0 (;LGE;"+" "*3500+"z"',
    '"HbbTV/0.0.0 (;CUS:;"+" "*3500+"z"',
    '"HbbTV/0.0.0 (;"+" "*3500+"z"',
    '"HbbTV/0.0.0 (;z;"+" "*3500+"z"',
    '"AppleWebKit/1."+"0"*10000+"!"',
    '"AppleWebKit/1.1"+" Safari"*10000+"!"',
    '"/0"+"0"*10000+"◎"',
    '"Mozilla"+"Mobile"*10000+"!"',
    '"Mozilla"+"Mobile"*10000+"!"',
    '"Mozilla"+"Mobile"*10000+"!"',
    '"Mozilla"+"Mobile"*10000+"!"',
    '"Mozilla"+"Mobile"*10000+"!"',
    '"Opera/"+"Opera Mobi"*10000+"!"',
    '"Opera/1."+"0"*10000+"!"',
    '"Mozilla"+"Android"*10000+"!"',
    '"Chrome/1.1."+"0"*10000+"!"',
    '"Chrome/1.1."+"0"*10000+"!"',
    '"MSIE 1."+"0"*10000+"!"',
    '"iPod"+"Version/1.1"*10000+"!"',
    '"iPod;ACPUAOS 0_0"+"0"*10000+"◎"',
    '"iPod;"+"CPU"*10000+"!"',
    '"iPod;"+"CPUOS 1_1"*10000+"!"',
    '"iPod;CPU"+"OS 1_1"*10000+"!"',
    '"Version/0.0"+"0"*10000+"◎\n"',
    '"HbbTV/0.0.0"+"0"*10000+"◎"',
    '"HbbTV/1.1.1 ("+";a;"*10000+"!"',
    '"HbbTV/1.1.1 ("+";a;2011"*10000+"!"',
    '"HbbTV/1.1.1 (;a;"+"2011"*10000+"!"',
    '"CPU OS 0.0"+"0"*10000+"◎"',
    '"ArcGIS.iOS-0.0"+"0"*10000+"◎"',
    '"x86_64 1.1."+"0"*10000+"!"',
    '"x86_64 1.1.1"+"Chrome"*10000+"!"',
    '" Darwin/91"+"0"*10000+"!"',
    '" Darwin/101"+"0"*10000+"!"',
    '" Darwin/111"+"0"*10000+"!"',
    '" Darwin/121"+"0"*10000+"!"',
    '" Darwin/131"+"0"*10000+"!"',
    '"iPhone"+"Mac OS X"*10000+"!"',
    '"CFNetwork/C Darwin/17.0"+"0"*10000+"◎"',
    '"CFNetwork/"+" Darwin/17.1"*10000+"!"',
    '"CFNetwork/"+" Darwin/16.1"*10000+"!"',
    '"CFNetwork/ Darwin/16."+"0"*10000+"!"',
    '"CFNetwork/8"+" Darwin/15.1"*10000+"!"',
    '"CFNetwork/8 Darwin/15."+"0"*10000+"!"',
    '"Linux 0.0"+"0"*10000+"◎"',
    '" PTST/0"+"0"*10000+"◎\n"',
    '"Mozilla"+"Mobile"*10000+"!"',
    '"; AIRIS "+" "*10000+"◎"',
    '";ASUS"+"_"*10000+"!"',
    '";Excite "+"0"*10000+"!"',
    '"; "+" "*10000+"◎"',
    '";"+"Coolpad_"*10000+"!"',
    '";TAC-"+"0"*10000+"!"',
    '"; "+" "*10000+"◎"',
    '"; Fly F30"+"0"*10000+"◎"',
    '"; FONE 0"+"0"*10000+"◎"',
    '"; "+" "*10000+"◎"',
    '"; "+" "*10000+"◎"',
    '"; "+" "*10000+"◎"',
    '";   Build HuaweiA0"+"0"*10000+"◎"',
    '"; "+" "*10000+"◎\n"',
    '"; Ideos  "+" "*10000+"◎"',
    '"; NT-0"+"0"*10000+"◎"',
    '";ImPAD"+"0"*10000+"!"',
    '"; Intex AQUA  "+" "*10000+"◎"',
    '"; IBuddy Connect "+" "*10000+"◎"',
    '"; I-Buddy  "+" "*10000+"◎"',
    '"; Karbonn "+" "*10000+"◎"',
    '"; "+" "*10000+"◎\n"',
    '"; LAVA IRIS "+" "*10000+"◎"',
    '"; VS60"+"0"*10000+"◎"',
    '"; VS60 "+" "*10000+"◎"',
    '"; PhonePad 000"+"0"*10000+"◎"',
    '"; SmartPad 000"+"0"*10000+"◎"',
    '"; meizu_ "+" "*10000+"◎"',
    '"; Cynus F5 "+" "*10000+"◎"',
    '"; NXM0"+"0"*10000+"◎"',
    '";Nokia"+"_"*10000+"!"',
    '";IM-A111"+"0"*10000+"!"',
    '";Pantech"+"0"*10000+"!"',
    '"; Polaroid MIDC0000"+"0"*10000+"◎"',
    '"; POMP  "+" "*10000+"◎"',
    '"; SAMSUNG Galaxy Note II "+" "*10000+"◎"',
    '"; SAMSUNG-"+"0"*10000+"!"',
    '"; SCH-0"+"0"*10000+"◎\n"',
    '"; SC-0"+"0"*10000+"◎"',
    '" SCH-0"+"0"*10000+"◎"',
    '"; Xperia X0"+"0"*10000+"◎"',
    '"; Sprint  "+" "*10000+"◎"',
    '"; TM-MID0"+"0"*10000+"◎"',
    '";TM-SM"+"0"*10000+"!"',
    '"; Videocon   "+" "*10000+"◎"',
    '";XOLO "+"tab"*10000+"!"',
    '"; PAD 70"+"0"*10000+"◎"',
    '";SmartTab"+"0"*10000+"!"',
    '"; v89_"+"0"*10000+"◎"',
    '"; e0000a_v0"+"0"*10000+"◎"',
    '"Windows Phone  ; "+"IEMobile/"*10000+"!"',
    '"Windows Phone  ; "+"IEMobile/"*10000+"!"',
    '"Windows Phone  ; "+"IEMobile/"*10000+"!"',
    '"Windows Phone  ;  IEMobile/ ; ARM; Touch; HTC_blocked "+" "*10000+"◎"',
    '"Windows Phone  ; "+"IEMobile/"*10000+"!"',
    '"Windows Phone  ; "+"IEMobile/"*10000+"!"',
    '"Windows Phone  ; "+"IEMobile/"*10000+"!"',
    '"Windows Phone  ; "+"IEMobile/"*10000+"!"',
    '"Windows Phone  ; "+"IEMobile/"*10000+"!"',
    '"Windows Phone  ; "+"IEMobile/"*10000+"!"',
    '"Windows Phone  ; "+"IEMobile/"*10000+"!"',
    '"Windows Phone  ; "+"IEMobile/"*10000+"!"',
    '"Windows Phone  ; "+"IEMobile/"*10000+"!"',
    '"SAMSUNG-"+"0"*10000+"!"',
    '"(Mobile; LYF/0/ ;"+";"*10000+"◎"',
    '"(Mobile; LYF/A/"+";0rv:"*10000+"!"',
    '"(Mobile; LYF/A/1;"+"rv:"*10000+"!"',
    '"(Mobile; Nokia_A_"+"; rv:"*10000+"!"',
    '"CFNetwork/"+" Darwin/1"*10000+"!"',
    '"CFNetwork/ Darwin/1"+"(Mac"*10000+"!"',
    '"Android 0.0-update1; AA-A ;  "+" "*10000+"◎\n"',
    '"Android 0.0.0; AA-A- ;  "+" "*10000+"◎"',
    '"Android 0.0.0; A- ;  "+" "*10000+"◎"',
    '"Android 0.0.0; -AA;  "+" "*10000+"◎\n"',
    '"Android 1; "+" Build"*10000+"!"',
    '"Android 1; "+" Build"*10000+"!"',
    '"foobar!/!./"+"/"*40000+"◎"',
    '"  #- regex: \'(iPad;(Version/0.0.08"+"8"*40000+"◎"',
    '"  #- regex: \'(iPad;"*8000',
    '"  #- regex: \'HbbTV/0.0.0 (; Sony;H;"+";"*40000+"◎1\n_\n"',
    '"  #- regex: \'HbbTV/1.1.1 (; Sony;"*600',
    '"Windows Phone  ; IEMobile/ ; ARM; Touch; HTC_blockedd"+"d"*40000+"◎1\n_\n)"',
    '"Windows Phone "*8000',
    '"HTC/HTC "+" "*20000+"\n"',
    '"HTC/HTC "+" "*10000+"\n"',
    '""+"Google"*5000+"! _1SLQ_2"',
    '""+"MSIE 1.1;"*10000+"! _1SLQ_2"',
    '""+"[FB"*10000+"! _1SLQ_2"',
    '""+"[FB"*10000+"! _1SLQ_2"',
    '""+"[Pinterest/"*5000+"@1 _SLQ_2"',
    '""+"Mobile;"*5000+"! _1SLQ_2"',
    '""+"FirefoxTablet browser 1."*3000+"! _1SLQ_2"',
    '""+"Opera TabletVersion/1."*3000+"! _1SLQ_2"',
    '""+"Opera/9180Version/"*5000+"! _1SLQ_2"',
    '""+"Chrome/1 MMS/11"*5000+"! _1SLQ_2"',
    '""+"PLAYSTATION 3"*5000+"! _1SLQ_2"',
    '""+"AppleWebKit1 NX/1."*5000+"! _1SLQ_2"',
    '""+"Windows Phone Edge/1."*3000+"! _1SLQ_2"',
    '""+"amarok/"*10000+"@1 _SLQ_2"',
    '""+"iPod1GSA/1."*5000+"! _1SLQ_2"',
    '""+"iPod"*10000+"! _1SLQ_2"',
    '""+"PlayBook"*10000+"! _1SLQ_2"',
    '""+"Blackberry1Version/"*5000+"! _1SLQ_2"',
    '""+"AppleWebKit/1+ "*5000+"! _1SLQ_2"',
    '""+"HbbTV/1.1.1 (;Samsung;SmartTV0000;"*2000+"! _1SLQ_2"',
    '""+"HbbTV/1.1.1 (;Samsung;SmartTV0000;"*2000+"! _1SLQ_2"',
    '""+"HbbTV/1.1.1 (; Philips;"*3000+"! _1SLQ_2"',
    '""+"HbbTV/1.1.1 (; Philips;"*3000+"! _1SLQ_2"',
    '""+"HbbTV/1.1.1 (; Philips;"*3000+"! _1SLQ_2"',
    '""+"Symbian/3"*10000+"! _1SLQ_2"',
    '""+"Symbian/3"*10000+"! _1SLQ_2"',
    '""+"BB10;1Version/"*5000+"! _1SLQ_2"',
    '""+"BlackBerry"*5000+"! _1SLQ_2"',
    '""+"(Mobile;1Gecko/1810 Firefox/"*3000+"! _1SLQ_2"',
    '""+"(Mobile;1Gecko/1811 Firefox/"*3000+"! _1SLQ_2"',
    '""+"(Mobile;1Gecko/2610 Firefox/"*3000+"! _1SLQ_2"',
    '""+"(Mobile;1Gecko/2810 Firefox/"*3000+"! _1SLQ_2"',
    '""+"(Mobile;1Gecko/3010 Firefox/"*3000+"! _1SLQ_2"',
    '""+"(Mobile;1Gecko/3210 Firefox/"*3000+"! _1SLQ_2"',
    '""+"(Mobile;1Gecko/3410 Firefox/"*3000+"! _1SLQ_2"',
    '""+"(Mobile;1Firefox/1."*5000+"! _1SLQ_2"',
    '""+"DoCoMo"*10000+"! _1SLQ_2"',
    '""+"Mozilla"*10000+"! _1SLQ_2"',
    '""+"SmartWatch("*10000+"@1 _SLQ_2"',
    '""+"Android Application  - Sony 1 "*3000+"! _1SLQ_2"',
    '""+"Android Application  - HTC HTC 1 "*3000+"! _1SLQ_2"',
    '""+"Android Application  - - 1 "*3000+"! _1SLQ_2"',
    '""+"Android 3"*10000+"! _1SLQ_2"',
    '""+";Vega"*10000+"! _1SLQ_2"',
    '""+";ALLVIEWSpeed"*5000+"! _1SLQ_2"',
    '""+";ARCHOSGAMEPAD"*5000+"! _1SLQ_2"',
    '""+";BlackBird I8"*5000+"! _1SLQ_2"',
    '""+";BlackBird "*5000+"! _1SLQ_2"',
    '""+";CatNova"*10000+"! _1SLQ_2"',
    '""+";P-1"*1000+"@1 _SLQ_2"',
    '""+";Explay_"*10000+"! _1SLQ_2"',
    '""+";IQ"*10000+"! _1SLQ_2"',
    '""+";Pixel"*10000+"! _1SLQ_2"',
    '""+";GSmart "*10000+"! _1SLQ_2"',
    '""+";imx51_"*10000+"! _1SLQ_2"',
    '""+";Haier "*10000+"! _1SLQ_2"',
    '""+"Build/HCL ME Tablet "*3000+"@1 _SLQ_2"',
    '""+";HP "*10000+"! _1SLQ_2"',
    '""+"HTC Streaming Player "*3000+"@1 _SLQ_2"',
    '""+";HYUNDAI T1"*5000+"! _1SLQ_2"',
    '""+";imobile "*10000+"! _1SLQ_2"',
    '""+";i-style"*10000+"! _1SLQ_2"',
    '""+";iOCEAN "*10000+"! _1SLQ_2"',
    '""+";NEC-"*10000+"! _1SLQ_2"',
    '""+";Pantech "*10000+"! _1SLQ_2"',
    '""+"Android 4."*5000+"! _1SLQ_2"',
    '""+";PLT0000"*10000+"! _1SLQ_2"',
    '""+";SAMSUNG "*10000+"@1 _SLQ_2"',
    '""+";GT-B1111"*10000+"@1 _SLQ_2"',
    '""+"; SAMSUNG-"*5000+"! _1SLQ_2"',
    '""+";SK-"*10000+"! _1SLQ_2"',
    '""+";ST1111"*10000+"! _1SLQ_2"',
    '""+";ST1111"*10000+"! _1SLQ_2"',
    '""+";Build/"*10000+"! _1SLQ_2"',
    '""+"TOOKY "*10000+"! _1SLQ_2"',
    '""+"TOUCHTAB"*10000+"! _1SLQ_2"',
    '""+"VERTU "*10000+"! _1SLQ_2"',
    '""+";GTablet"*10000+"! _1SLQ_2"',
    '""+"Vodafone "*10000+"! _1SLQ_2"',
    '""+"sprd-"*10000+"@1 _SLQ_2"',
    '""+"Windows Phone "*5000+"@1 _SLQ_2)"',
    '""+"Windows Phone "*5000+"@1 _SLQ_2)"',
    '""+"Windows Phone "*5000+"@1 _SLQ_2)"',
    '""+"Windows Phone "*5000+"@1 _SLQ_2)"',
    '""+"Windows Phone "*5000+"@1 _SLQ_2)"',
    '""+"Windows Phone "*5000+"@1 _SLQ_2)"',
    '""+"Windows Phone "*5000+"@1 _SLQ_2)"',
    '""+"Windows Phone "*5000+"@1 _SLQ_2)"',
    '""+"Windows Phone "*5000+"@1 _SLQ_2)"',
    '""+"Windows Phone "*5000+"@1 _SLQ_2)"',
    '""+"(Mobile; ALCATELOneTouch ; rv:"*3000+"@1 _SLQ_2"',
    '""+"(Mobile; ZTE ; rv:"*5000+"@1 _SLQ_2"',
    '""+"(Mobile; ALCATELA; rv:"*3000+"@1 _SLQ_2"',
    '""+"PlayBook"*10000+"! _1SLQ_2"',
    '""+"webOS"*10000+"! _1SLQ_2"',
    '""+"HPiPAQ"*10000+"!1 _SLQ_2"',
    '""+"HbbTV;CE-HTML;"*5000+"@1 _SLQ_2"',
    '""+"InettvBrowser/0.0 (;Sony"*3000+"@1 _SLQ_2"',
    '""+"InettvBrowser/0.0 ("*5000+"@1 _SLQ_2"',
    '""+"MSIE"*10000+"! _1SLQ_2"',
    '""+"SMART-TV; "*10000+"! _1SLQ_2"',
    '""+"SymbianOS/9.1"*5000+"! _1SLQ_2"',
    '""+"Android "*10000+"@1 _SLQ_2"',
    '""+"Android-1.1; AA-; WOWMobile "*3000+"! _1SLQ_2"',
    '""+"Android-1.1-update1; AA-;"*3000+"! _1SLQ_2"',
    '""+"Android-1.1;AA_;"*5000+"! _1SLQ_2"',
    '""+"Android-1.1;-;"*5000+"! _1SLQ_2"',
    '""+"Android 1;   Build"*3000+"! _1SLQ_2"',
    '""+"Android 1;   Build"*3000+"! _1SLQ_2"'
  ]

  function buildAttackString (attackString) {
    const uas = attackString
      .replace(/^"|"$/g, '')
      .replace(/"?\+"([^"]+)"\*(\d+)/, (m, str, i) => {
        // console.error({m, str, i})
        const size = parseInt(i, 10)
        if (!isNaN(size)) {
          return (new Array(size).fill(str).join(''))
        } else {
          return m
        }
      })
      .replace(/\+"/, '')
    return uas
  }

  tests.forEach(attackString => {
    test(attackString, function() {
      const ua = buildAttackString(attackString)
      const start = Date.now()
      const parsed = parse(ua)
      // console.log(parsed)
      const diff = Date.now() - start
      assert.ok(diff < 500)
    })
  })
})