id: js-libraries-detect

info:
  name: Common JS Libraries - Detection
  author: adamparsons,cbadke,ChetGan,ErikOwen,jacalynli,geeknik
  severity: info
  description: Checks a target web app for inclusion of common JavaScript libraries
  metadata:
    max-request: 1
  tags: headless,tech,js,discovery

headless:
  - steps:
      - action: navigate
        args:
          url: "{{BaseURL}}"

      - action: waitload

      - action: script
        name: fingerprintAxios
        args:
          code: |
            () => {
              //check for axios
              if (!window.axios) {
                return ""
              }

              try {
                // check for version
                // only works on some websites
                return window.axios.VERSION
              } catch (e) {}

              return "Version not found"
            }

      - action: script
        name: fingerprintBootstrap
        args:
          code: |
            () => {
              try {
                // if not using jQuery
                return bootstrap.Tooltip.VERSION || ""
              } catch (e) {}

              try {
                // if using jQuery
                return $.fn.tooltip.Constructor.VERSION || ""
              } catch (e) {}

              return ""
            }

      - action: script
        name: fingerprintJQuery
        args:
          code: |
            () => {
              let version = "";
              try {
                if(window.jQuery) {
                  version = jQuery.fn.jquery;
                }
                if(window.$) {
                  version = $.fn.jquery;
                }
                version = version.replace(".min", "");
                version = version.replace(".slim", "");
                return version;
              } catch (e) {}

              return "";
            }

      - action: script
        name: fingerprintLodash
        args:
          code: |
            () => {
              try {
                return _.VERSION || "";
              } catch (e) {}
              return "";
            }

      - action: script
        name: fingerprintMomentJs
        args:
          code: |
            () => {
              try {
                return moment.version || "";
              } catch (e) {}
              return "";
            }

      - action: script
        name: fingerprintReact
        args:
          code: |
            () => {
              try {
                return window.React.version || "";
              } catch (e) {}
              return "";
            }

      - action: script
        name: fingerprintReactDOM
        args:
          code: |
            () => {
              try {
                if (window.ReactDOM) {
                  return window.React.version || "";
                }
              } catch (e) {}
              return "";
            }

      - action: script
        name: fingerprintAngular
        args:
          code: |
            () => {

              try {
                // Angular Version 1
                return angular.version.full
              } catch (e) {}

              try {
                // Angular Version 2+
                return getAllAngularRootElements()[0].attributes["ng-version"].value
              } catch (e) {}

              return ""
            }

      - action: script
        name: fingerprintBackboneJs
        args:
          code: |
            () => {

              try {
                return window.Backbone.VERSION || ""
              } catch (e) {}
              return ""
            }

      - action: script
        name: fingerprintEmberJs
        args:
          code: |
            () => {
              try {
                return Ember.VERSION || ""
              } catch (e) {}
              return "";
            }

      - action: script
        name: fingerprintVue
        args:
          code: |
            () => {

              //method 1 (simple)
              try {
                return Vue.version
              } catch (e) {}

              //method 2 (checks if Nuxt exists)
              try {
                const nuxtDetected = Boolean(window.__NUXT__ || window.$nuxt)
                if (nuxtDetected) {
                    let Vue
                }
                if (window.$nuxt) {
                    Vue = window.$nuxt.$root.constructor
                }
                return Vue.version
              } catch (e) {}

              //method 3 (go through all elements)
              try {
                const all = document.querySelectorAll('*')
                let flag
                for (let i = 0; i < all.length; i++) {
                    if (all[i].__vue__) {
                        flag = all[i]
                        break
                    }
                }
                if (flag) {
                    let Vue = Object.getPrototypeOf(flag.__vue__).constructor
                    while (Vue.super) {
                        Vue = Vue.super
                    }
                    return Vue.version
                }
                return ""
              } catch (e) {}
              return ""
            }

      - action: script
        name: fingerprintDojoJs
        args:
          code: |
            () => {
              try {
                return ([dojo.version.major, dojo.version.minor, dojo.version.patch].join("."))
              } catch (e) {}
              return ""
            }

      - action: script
        name: fingerprintDomPurify
        args:
          code: |
            () => {
              try {
                return DOMPurify.version || ""
              } catch (e) {}
              return ""
            }

      - action: script
        name: fingerprintModernizr
        args:
          code: |
            () => {
              try {
                return Modernizr._version || ""
              } catch (e) {}
              return ""
            }

      - action: script
        name: fingerprintD3
        args:
          code: |
            () => {
              try {
                return d3.version || "";
              } catch (e) {}
              return "";
            }

      - action: script
        name: fingerprintThreeJs
        args:
          code: |
            () => {
              try {
                return THREE.REVISION || "";
              } catch (e) {}
              return "";
            }

      - action: script
        name: fingerprintChartJs
        args:
          code: |
            () => {
              try {
                return Chart.version || "";
              } catch (e) {}
              return "";
            }

      - action: script
        name: fingerprintSlick
        args:
          code: |
            () => {
              try {
                // Assuming Slick Carousel is used as a jQuery plugin
                return $.fn.slick.version || "";
              } catch (e) {}
              return "";
            }

      - action: script
        name: fingerprintSelect2
        args:
          code: |
            () => {
              try {
                // Assuming Select2 is used as a jQuery plugin
                return $.fn.select2.version || "";
              } catch (e) {}
              return "";
            }

      - action: script
        name: fingerprintHandlebars
        args:
          code: |
            () => {
              try {
                return Handlebars.VERSION || "";
              } catch (e) {}
              return "";
            }

      - action: script
        name: fingerprintUnderscore
        args:
          code: |
            () => {
              try {
                return window._.VERSION || "";
              } catch (e) {}
              return "";
            }

      - action: script
        name: fingerprintYUI
        args:
          code: |
            () => {
              try {
                return YUI.version || "";
              } catch (e) {}
              return "";
            }

      - action: script
        name: fingerprintKnockout
        args:
          code: |
            () => {
              try {
                return window.ko?.version || "";
              } catch (e) {
                return "";
              }
            }

      - action: script
        name: fingerprintLeaflet
        args:
          code: |
            () => {
              try {
                return L.version || "";
              } catch (e) {}
              return "";
            }

      - action: script
        name: fingerprintPaperJS
        args:
          code: |
            () => {
              try {
                return paper.version || "";
              } catch (e) {}
              return "";
            }

      - action: script
        name: fingerprintFabricJS
        args:
          code: |
            () => {
              try {
                return window.fabric?.version || "";
              } catch (e) {
                return "";
              }
            }

      - action: script
        name: fingerprintP5JS
        args:
          code: |
            () => {
              try {
                return p5.VERSION || "";
              } catch (e) {}
              return "";
            }

      - action: script
        name: fingerprintNextJS
        args:
          code: |
            () => {
              try {
                return next.version || "";
              } catch (e) {}
              return "";
            }

    matchers-condition: or
    matchers:
      - type: dsl
        dsl:
          - len(fingerprintAxios) > 0
          - len(fingerprintBootstrap) > 0
          - len(fingerprintJQuery) > 0
          - len(fingerprintLodash) > 0
          - len(fingerprintMomentJs) > 0
          - len(fingerprintReact) > 0
          - len(fingerprintReactDOM) > 0
          - len(fingerprintAngular) > 0
          - len(fingerprintBackboneJs) > 0
          - len(fingerprintEmberJs) > 0
          - len(fingerprintVue) > 0
          - len(fingerprintDojoJs) > 0
          - len(fingerprintDomPurify) > 0
          - len(fingerprintModernizr) > 0
          - len(fingerprintD3) > 0
          - len(fingerprintThreeJs) > 0
          - len(fingerprintChartJs) > 0
          - len(fingerprintSlick) > 0
          - len(fingerprintSelect2) > 0
          - len(fingerprintHandlebars) > 0
          - len(fingerprintUnderscore) > 0
          - len(fingerprintYUI) > 0
          - len(fingerprintKnockout) > 0
          - len(fingerprintLeaflet) > 0
          - len(fingerprintPaperJS) > 0
          - len(fingerprintFabricJS) > 0
          - len(fingerprintP5JS) > 0
          - len(fingerprintNextJS) > 0

    extractors:
      - name: axios
        type: regex
        part: fingerprintAxios
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: bootstrap
        type: regex
        part: fingerprintBootstrap
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: jquery
        type: regex
        part: fingerprintJQuery
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: lodash
        type: regex
        part: fingerprintLodash
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: moment
        type: regex
        part: fingerprintMomentJs
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: react
        type: regex
        part: fingerprintReact
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: reactdom
        type: regex
        part: fingerprintReactDOM
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: angular
        type: regex
        part: fingerprintAngular
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: backbone
        type: regex
        part: fingerprintBackboneJs
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: emberjs
        type: regex
        part: fingerprintEmberJs
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: vuejs
        type: regex
        part: fingerprintVue
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: dojo
        type: regex
        part: fingerprintDojoJs
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: dompurify
        type: regex
        part: fingerprintDomPurify
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: modernizr
        type: regex
        part: fingerprintModernizr
        regex:
          - ^(0|[1-9]\d*)(?:\.(0|[1-9]\d*))?(?:\.(0|[1-9]\d*))?(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: d3
        type: regex
        part: fingerprintD3
        regex:
          - "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$"

      - name: threejs
        type: regex
        part: fingerprintThreeJs
        regex:
          - "^(0|[1-9]\\d*)$"

      - name: chartjs
        type: regex
        part: fingerprintChartJs
        regex:
          - "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$"

      - name: slick
        type: regex
        part: fingerprintSlick
        regex:
          - "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$"

      - name: select2
        type: regex
        part: fingerprintSelect2
        regex:
          - "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)$"

      - name: handlebars
        type: regex
        part: fingerprintHandlebars
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: underscore
        type: regex
        part: fingerprintUnderscore
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: yui
        type: regex
        part: fingerprintYUI
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: knockout
        type: regex
        part: fingerprintKnockout
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: leaflet
        type: regex
        part: fingerprintLeaflet
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: paperjs
        type: regex
        part: fingerprintPaperJS
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: fabricjs
        type: regex
        part: fingerprintFabricJS
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: p5js
        type: regex
        part: fingerprintP5JS
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$

      - name: nextjs
        type: regex
        part: fingerprintNextJS
        regex:
          - ^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
# digest: 4b0a00483046022100886c3605d3d7e8cb4a2d5e920683057633a86248365a75eed73494b0d48069f6022100e4a9d346cc616f66844f56110c0d9ce69684271de90e0b264caf7dfb036257ae:922c64590222798bb761d5b6d8e72950