CVE-2022-2098 (JavaScript, published 2022-06-16T10:15Z)
Description: value: Weak Password Requirements in GitHub repository kromitgmbh/titra prior to 0.78.1.
Severity: MEDIUM (CVSS v3 base score: 9.8)
Weakness: CWE-521 \u2014 Weak Password Requirements
The product does not require that users should have strong passwords, which makes it easier for attackers to compromise user accounts.
Repository: https://github.com/kromitgmbh/titra
Files changed:
imports/ui/pages/changePassword.html
imports/ui/pages/changePassword.js
imports/ui/pages/register.html
imports/ui/pages/register.js
imports/ui/pages/signIn.js
imports/ui/styles/dark.scss
imports/ui/styles/light.scss
imports/utils/frontend_helpers.js
package-lock.json
package.json
Commit message: 🔒 fixed further security vulnerabilities adding additional password validation
💄 improved light and dark theme (thanks @thomas-kromit)


Vulnerable code:
                  <input aria-label='{{t "login.current_password"}}' type="password" class="form-control" id="at-field-current-password" name="at-field-current-password" placeholder="Current Password" autocapitalize="none" autocorrect="off">
                  <input type="password" class="form-control" id="at-field-password" name="at-field-password" placeholder="{{t 'login.password'}}" autocapitalize="none" autocorrect="off">
                  <span class="help-block hide"></span>
                  <input aria-label='{{t "login.password_again"}}' type="password" class="form-control" id="at-field-password-again" name="at-field-password-again" placeholder="{{t 'login.password_again'}}" autocapitalize="none" autocorrect="off">
import { showToast } from '../../utils/frontend_helpers.js'
                  <input type="password" class="form-control" id="at-field-password" name="at-field-password" placeholder="{{t 'login.password'}}" autocapitalize="none" autocorrect="off">
                  <input aria-label='{{t "login.password_again"}}' type="password" class="form-control" id="at-field-password-again" name="at-field-password-again" placeholder="{{t 'login.password_again'}}" autocapitalize="none" autocorrect="off">
import { validateEmail, validatePassword } from '../../utils/frontend_helpers.js' 
        if (error) {
          templateInstance.$('.notification').text(t('login.email_unknown'))
          document.querySelector('.notification').classList.remove('d-none')
        } else {
          templateInstance.$('.notification').text(t('login.reset_password_mail'))
          document.querySelector('.notification').classList.remove('d-none')
          templateInstance.$('#at-field-email').removeClass('is-invalid')
          templateInstance.$('#at-field-password').removeClass('is-invalid')
        }




//++++ [AdvancedCustomization] ++++ ///////////////////////////////////////////////////////
.internal-autofill-selected {
    appearance:             menulist-button;
    background-color:       $success !important;
    background-image:       none !important;
  border-left:              1px solid $dark;
  border-right:             1px solid $dark;
  border-bottom:            1px solid $dark;
    fill: #FFFFFF;
    fill: $grey !important;
    stroke: $grey !important;
    fill: $grey !important;
    fill: $dark-form !important;
    stroke: $dark !important;
    fill: $body-color !important;
    fill: $body-color !important;
    fill: $lightgrey !important;
$primary: #009688;
$secondary: #f7f7f7;
$success: #009688;
$info: #b2dfdb;
$warning: #ffc107;
$danger: #ff5722;
$light: #BDBDBD;
$dark: #455A64;
    color: #fff !important;
    border-color: #009688 !important; 
    color: #fff!important;
    border-color: #006e63!important;
    color: #000 !important;
    color: #fff !important;
  color: #495057;
  background-color: $secondary;
  border-left: 1px solid #dee2e6;
  border-right: 1px solid #dee2e6;
  border-bottom: 1px solid #dee2e6;
  background: $secondary !important;
  background: rgba(0,112,101,0.05) !important;
  background: rgba(0, 150, 136, 0.5) !important;
    color: #7b7b7b;
    background-color: #ffffff;
    border-color: #e8e8e8;
  border-color: #ced4da !important;
  color: #212529 !important;
  background-color: #ececec !important;
    color: #000000 !important;
    background-color: $secondary !important;
    border-color: $light !important;
    color: #000000 !important;
    background-color: #eee !important;
    border-color: $light !important;
    --dt-border-color: #d1d8dd;
    --dt-light-bg: #f5f7fa;
    --dt-text-color: #000000;
    --dt-text-light: #dfe2e5;
    --dt-cell-bg: #fff;
    --dt-header-cell-bg: var(--dt-cell-bg);
  color: #495057;
  background-color: #fff;
  border-color: #dee2e6 #dee2e6 #fff;
  color: #495057;
  background-color: #fff;
  border-color: #dee2e6 #dee2e6 #fff;
  background-color: $secondary;  
  background-color: $secondary;
  color: $dark;
  background-color: $primary !important;
  background-color: $primary !important;
  background-color: $primary !important;
  background-color: $primary !important;
  background-color: $primary !important;
  background-color: $primary !important;
  background-color: $primary !important;
  background-color: $primary !important;
  background-color: $primary !important;
  background-color: $primary !important;
  background: $primary !important;
  fill: #212529;

  } if (enoughRegex.test(pwd) === false) {
  } if (strongRegex.test(pwd)) {
  } if (mediumRegex.test(pwd)) {
  "version": "0.77.2",
      "version": "2.1.2",
      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.1.2.tgz",
      "integrity": "sha512-hoyByceqwKirw7w3Z7gnIIZC3Wx3J484Y3L/cMpXFbr7d9ZQj2mODrirNzcJa+SM3UlpWXYvKV4RlRpFXlWgXg==",
        "@jridgewell/trace-mapping": "^0.3.0"
      "version": "7.17.10",
      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.17.10.tgz",
      "integrity": "sha512-GZt/TCsG70Ms19gfZO1tM4CVnXsPgEPBCpJu+Qz3L0LUDsY5nZqFZglIoPC1kIYOtNBZlrnFT+klg12vFGZXrw==",
      "version": "7.18.2",
      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.2.tgz",
      "integrity": "sha512-A8pri1YJiC5UnkdrWcmfZTJTV85b4UXTAfImGmCfYmax4TR9Cw8sDS0MOk++Gp2mE/BefVJ5nwy5yzqNJbP/DQ==",
        "@babel/parser": "^7.18.0",
        "@babel/traverse": "^7.18.2",
        "@babel/types": "^7.18.2",
      "version": "7.18.4",
      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.4.tgz",
      "integrity": "sha512-FDge0dFazETFcxGw/EXzOkN8uJp0PC7Qbm+Pe9T+av2zlBpOgunFHkQPPn+eRuClU73JF+98D531UgayY89tow==",
      "version": "7.18.2",
      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.2.tgz",
      "integrity": "sha512-9eNwoeovJ6KH9zcCNnENY7DMFwTU9JdGCFtqNLfUAqtUHRCOsTOqWoffosP8vKmNYeSBUv3yVJXjfd8ucwOjUA==",
        "@babel/parser": "^7.18.0",
        "@babel/types": "^7.18.2",
      "version": "4.20.3",
      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.3.tgz",
      "integrity": "sha512-NBhymBQl1zM0Y5dQT/O+xiLP9/rzOIQdKM/eMJBAq7yBgaB6krIYLGejrwVYnSHZdqjscB1SPuAjHwxjvN6Wdg==",
        "caniuse-lite": "^1.0.30001332",
        "electron-to-chromium": "^1.4.118",
        "node-releases": "^2.0.3",
      "version": "1.0.30001344",
      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001344.tgz",
      "integrity": "sha512-0ZFjnlCaXNOAYcV7i+TtdKBp0L/3XEU2MF/x6Du1lrh+SRX4IfzIVL4HNJg5pB2PmFb8rszIGyOvsZnqqRoc2g==",
      "version": "1.4.141",
      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.141.tgz",
      "integrity": "sha512-mfBcbqc0qc6RlxrsIgLG2wCqkiPAjEezHxGTu7p3dHHFOurH4EjS9rFZndX5axC8264rI1Pcbw8uQP39oZckeA==",
      "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
  "version": "0.78.0",
    "@babel/core": "^7.18.2",

Fixed code:
                  <input aria-label='{{t "login.current_password"}}' type="password" class="form-control" id="at-field-current-password" name="at-field-current-password" placeholder="Current Password" autocapitalize="none" autocorrect="off" maxlength="50">
                  <input type="password" class="form-control" id="at-field-password" name="at-field-password" placeholder="{{t 'login.password'}}" autocapitalize="none" autocorrect="off" maxlength="50" minlength="8">
                  <div class="js-password-feedback invalid-feedback hide"></div>
                  <input aria-label='{{t "login.password_again"}}' type="password" class="form-control" id="at-field-password-again" name="at-field-password-again" placeholder="{{t 'login.password_again'}}" autocapitalize="none" autocorrect="off"  maxlength="50" minlength="8">
import { showToast, validatePassword } from '../../utils/frontend_helpers.js'
      const passwordValidation = validatePassword(templateInstance.$('#at-field-password').val())
      if (!passwordValidation.valid) {
        templateInstance.$('#at-field-password').addClass('is-invalid')
        templateInstance.$('#at-field-password-again').addClass('is-invalid')
        templateInstance.$('.notification').text(passwordValidation.message)
        document.querySelector('.notification').classList.toggle('d-none')
        return
      }
      const passwordValidation = validatePassword(templateInstance.$('#at-field-password').val())
      if (!passwordValidation.valid) {
        templateInstance.$('#at-field-password').addClass('is-invalid')
        templateInstance.$('#at-field-password-again').addClass('is-invalid')
        templateInstance.$('.notification').text(passwordValidation.message)
        document.querySelector('.notification').classList.toggle('d-none')
        return
      }
  'keyup #at-field-password': (event, templateInstance) => {
    event.preventDefault()
    const validetedPW = validatePassword(templateInstance.$('#at-field-password').val())
    templateInstance.$('.js-password-feedback').text(validetedPW.message)
    if (validetedPW.valid) {
      templateInstance.$('#at-field-password').removeClass('is-invalid')
      templateInstance.$('#at-field-password-again').removeClass('is-invalid')
      templateInstance.$('.js-password-feedback').removeClass('invalid-feedback')
      templateInstance.$('.js-password-feedback').addClass('valid-feedback')
      templateInstance.$('.js-password-feedback').removeClass('hide')
    } else {
      templateInstance.$('#at-field-password').addClass('is-invalid')
      templateInstance.$('.js-password-feedback').removeClass('valid-feedback')
      templateInstance.$('.js-password-feedback').addClass('invalid-feedback')
      templateInstance.$('.js-password-feedback').removeClass('hide')
      templateInstance.$('.js-password-feedback').addClass('d-block')
    }
  },
                  <input type="password" class="form-control" id="at-field-password" name="at-field-password" placeholder="{{t 'login.password'}}" autocapitalize="none" autocorrect="off"  maxlength="50" minlength="8">
                  <input aria-label='{{t "login.password_again"}}' type="password" class="form-control" id="at-field-password-again" name="at-field-password-again" placeholder="{{t 'login.password_again'}}" autocapitalize="none" autocorrect="off"  maxlength="50" minlength="8">
import { validateEmail, validatePassword } from '../../utils/frontend_helpers.js'
        templateInstance.$('.notification').text(t('login.reset_password_mail'))
        document.querySelector('.notification').classList.remove('d-none')
        templateInstance.$('#at-field-email').removeClass('is-invalid')
        templateInstance.$('#at-field-password').removeClass('is-invalid')

$none:                                  none;
//++++ [AdvancedCustomization] ++++ ///////////////////////////////////////////////////////
::selection {
  color:                    $success !important;
  background:               $lightgrey !important;
}
::-moz-selection {
  color:                    $success !important;
  background:               $lightgrey !important;
}
.nav-tabs {
    border-bottom:          0px solid #dee2e6 !important;
.dt-scrollable {
    border-top:             0px solid var(--dt-border-color) !important;
}
.dt-scrollable__no-data {
    border:                 0px !important;
}

  border-left:              1px solid $dark !important;
  border-right:             1px solid $dark !important;
  border-bottom:            1px solid $dark !important;
  border-top:               1px solid $dark !important;
  border-top-right-radius:  0.25rem !important;
  border-bottom-right-radius:  0.25rem !important;
  border-bottom-left-radius:  0.25rem !important;




.fc .fc-scrollgrid-section-header.fc-scrollgrid-section-sticky > * {
    top: 0;
    background: none !important;
}

    fill:                 #FFFFFF;
    fill:                   $grey !important;
    stroke:                 $grey !important;
    fill:                   $grey !important;
    fill:                   $dark-form !important;
    stroke:                 $dark !important;
    fill:                   $body-color !important;
    fill:                   $body-color !important;
    fill:                   $lightgrey !important;
$primary:             #009688;
$secondary:           #f7f7f7;
$success:             #009688;
$info:                #b2dfdb;
$warning:             #ffc107;
$danger:              #ff5722;
$light:               #BDBDBD;
$dark:                #455A64;
    color:            #fff !important;
    border-color:     #009688 !important; 
    color:            #fff!important;
    border-color:     #006e63!important;
    color:            #000 !important;
    color:            #fff !important;
  color:              #495057;
  background-color:     $secondary;
  border-left:          1px solid #dee2e6;
  border-right:         1px solid #dee2e6;
  border-bottom:        1px solid #dee2e6;
  background:           $secondary !important;
  background:         rgba(0,112,101,0.05) !important;
  background:         rgba(0, 150, 136, 0.5) !important;
    color:              #7b7b7b;
    background-color:   #ffffff;
    border-color:       #e8e8e8;
  border-color:         #ced4da !important;
  color:                #212529 !important;
  background-color:     #ececec !important;
    color:              #000000 !important;
    background-color:     $secondary !important;
    border-color:         $light !important;
    color:                #000000 !important;
    background-color:     #eee !important;
    border-color:           $light !important;
    --dt-border-color:     #d1d8dd;
    --dt-light-bg:         #f5f7fa;
    --dt-text-color:       #000000;
    --dt-text-light:       #dfe2e5;
    --dt-cell-bg:          #fff;
    --dt-header-cell-bg:     var(--dt-cell-bg);
  color:                    #495057;
  background-color:         #fff;
  border-color:             #dee2e6 #dee2e6 #fff;
  
}
.nav-tabs {
    border-bottom: 0px solid #dee2e6 !important;
}
.dt-scrollable {
    //border-top: 0px solid #d1d8dd;
    border-top: 0px solid var(--dt-border-color) !important;
}
.dt-scrollable__no-data {
border: 0px !important;
  color:                    #495057;
  background-color:         #fff;
  border-color:             #dee2e6 #dee2e6 #fff;
  background-color:           $secondary;  
  background-color:           $secondary;
  color:                      $dark;
  background-color:           $primary !important;
  background-color:           $primary !important;
  background-color:           $primary !important;
  background-color:           $primary !important;
  background-color:           $primary !important;
  background-color:           $primary !important;
  background-color:           $primary !important;
  background-color:           $primary !important;
  background-color:           $primary !important;
  background-color:           $primary !important;
  background:                 $primary !important;
  fill:                     #212529;
}
.tab-borders {
    background-color:       #f7f7f7 !important;
    border-left:            1px solid #dee2e6 !important;
    border-right:           1px solid #dee2e6 !important;
    border-bottom:          1px solid #dee2e6 !important;
    border-top:             1px solid #dee2e6 !important;
    border-top-right-radius:            0.25rem !important;
    border-bottom-right-radius:         0.25rem !important;
    border-bottom-left-radius:          0.25rem !important;
  }
  if (enoughRegex.test(pwd) === false) {
  }
  if (strongRegex.test(pwd)) {
  }
  if (mediumRegex.test(pwd)) {
  if (pwd.length > 50) {
    return { valid: false, message: t('login.password_insufficient') }
  }
  "version": "0.78.0",
      "version": "2.2.0",
      "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
      "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
        "@jridgewell/gen-mapping": "^0.1.0",
        "@jridgewell/trace-mapping": "^0.3.9"
      "version": "7.18.5",
      "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.5.tgz",
      "integrity": "sha512-BxhE40PVCBxVEJsSBhB6UWyAuqJRxGsAw8BdHMJ3AKGydcwuWW4kOO3HmqBQAdcq/OP+/DlTVxLvsCzRTnZuGg==",
      "version": "7.18.5",
      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.5.tgz",
      "integrity": "sha512-MGY8vg3DxMnctw0LdvSEojOsumc70g0t18gNyUdAZqB1Rpd1Bqo/svHGvt+UJ6JcGX+DIekGFDxxIWofBxLCnQ==",
        "@babel/parser": "^7.18.5",
        "@babel/traverse": "^7.18.5",
        "@babel/types": "^7.18.4",
      "version": "7.18.5",
      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.5.tgz",
      "integrity": "sha512-YZWVaglMiplo7v8f1oMQ5ZPQr0vn7HPeZXxXWsxXJRjGVrzUFn9OxFQl1sb5wzfootjA/yChhW84BV+383FSOw==",
      "version": "7.18.5",
      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.5.tgz",
      "integrity": "sha512-aKXj1KT66sBj0vVzk6rEeAO6Z9aiiQ68wfDgge3nHhA/my6xMM/7HGQUNumKZaoa2qUPQ5whJG9aAifsxUKfLA==",
        "@babel/parser": "^7.18.5",
        "@babel/types": "^7.18.4",
    "@jridgewell/gen-mapping": {
      "version": "0.1.1",
      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
      "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
      "dev": true,
      "requires": {
        "@jridgewell/set-array": "^1.0.0",
        "@jridgewell/sourcemap-codec": "^1.4.10"
      }
    },
      "version": "4.20.4",
      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.4.tgz",
      "integrity": "sha512-ok1d+1WpnU24XYN7oC3QWgTyMhY/avPJ/r9T00xxvUOIparA/gc+UPUMaod3i+G6s+nI2nUb9xZ5k794uIwShw==",
        "caniuse-lite": "^1.0.30001349",
        "electron-to-chromium": "^1.4.147",
        "node-releases": "^2.0.5",
      "version": "1.0.30001354",
      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001354.tgz",
      "integrity": "sha512-mImKeCkyGDAHNywYFA4bqnLAzTUvVkqPvhY4DV47X+Gl2c5Z8c3KNETnXp14GQt11LvxE8AwjzGxJ+rsikiOzg==",
      "version": "1.4.156",
      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.156.tgz",
      "integrity": "sha512-/Wj5NC7E0wHaMCdqxWz9B0lv7CcycDTiHyXCtbbu3pXM9TV2AOp8BtMqkVuqvJNdEvltBG6LxT2Q+BxY4LUCIA==",
      "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
  "version": "0.78.1",
    "@babel/core": "^7.18.5",