CVE-2020-5263 (JavaScript, published 2020-04-09T16:15Z)
Description: value: auth0.js (NPM package auth0-js) greater than version 8.0.0 and before version 9.12.3 has a vulnerability. In the case of an (authentication) error, the error object returned by the library contains the original request of the user, which may include the plaintext password the user entered. If the error object is exposed or logged without modification, the application risks password exposure. This is fixed in version 9.12.3
Severity: MEDIUM (CVSS v3 base score: 4.9)
Weakness: CWE-522 \u2014 Insufficiently Protected Credentials
The product transmits or stores authentication credentials, but it uses an insecure method that is susceptible to unauthorized interception and/or retrieval.
Repository: https://github.com/auth0/auth0.js
Files changed:
src/helper/object.js
src/helper/response-handler.js
test/helper/object.test.js
test/helper/response-handler.test.js
Commit message: Merge pull request from GHSA-prfq-f66g-43mp

* Applied #1085 from auth0.js (fixes broken tests)

* Added an object helper for setting properties at a deep level

* Masking password in original error object

Vulnerable code:
  trimUserDetails: trimUserDetails

Fixed code:
/**
 * Updates the value of a property on the given object, using a deep path selector.
 * @param {object} obj The object to set the property value on
 * @param {string|array} path The path to the property that should have its value updated. e.g. 'prop1.prop2.prop3' or ['prop1', 'prop2', 'prop3']
 * @param {any} value The value to set
 */
function updatePropertyOn(obj, path, value) {
  if (typeof path === 'string') {
    path = path.split('.');
  }

  var next = path[0];

  if (obj.hasOwnProperty(next)) {
    if (path.length === 1) {
      obj[next] = value;
    } else {
      updatePropertyOn(obj[next], path.slice(1), value);
    }
  }
}

  trimUserDetails: trimUserDetails,
  updatePropertyOn: updatePropertyOn
      objectHelper.updatePropertyOn(
        errObj,
        'original.response.req._data.password',
        '*****'
      );


  describe('setPropertyValue', function() {
    it('can set a property at the first level of the object', function() {
      var obj = {
        one: 1,
        two: 2,
        three: 3
      };

      objectHelper.updatePropertyOn(obj, 'one', 'one');

      expect(obj).to.eql({
        one: 'one',
        two: 2,
        three: 3
      });
    });

    it('can set a nested property', function() {
      var obj = {
        one: {
          two: {
            three: 3
          }
        }
      };

      objectHelper.updatePropertyOn(obj, 'one.two.three', 'three');

      expect(obj).to.eql({
        one: {
          two: {
            three: 'three'
          }
        }
      });
    });

    it("does not add new values if the key doesn't already exist", function() {
      var obj = {
        one: {
          two: {
            three: 3
          }
        }
      };

      objectHelper.updatePropertyOn(obj, 'one.two.four', 4);

      expect(obj).to.eql({
        one: {
          two: {
            three: 3
          }
        }
      });
    });
  });

  it('should mask the password object in the original response object', function(done) {
    var assert_err = {
      code: 'the_error_code',
      error: 'The error description.',
      response: {
        req: {
          _data: {
            realm: 'realm',
            client_id: 'client_id',
            username: 'username',
            password: 'this is a password'
          }
        }
      }
    };

    responseHandler(function(err, data) {
      expect(data).to.be(undefined);

      expect(err).to.eql({
        original: {
          code: 'the_error_code',
          error: 'The error description.',
          response: {
            req: {
              _data: {
                realm: 'realm',
                client_id: 'client_id',
                username: 'username',
                password: '*****'
              }
            }
          }
        },
        code: 'the_error_code',
        description: 'The error description.'
      });

      done();
    })(assert_err, null);
  });

  it('should mask the password object in the data object', function(done) {
    var assert_err = {
      code: 'the_error_code',
      error: 'The error description.',
      response: {
        req: {
          _data: {
            realm: 'realm',
            client_id: 'client_id',
            username: 'username',
            password: 'this is a password'
          }
        }
      }
    };

    responseHandler(function(err, data) {
      expect(data).to.be(undefined);

      expect(err).to.eql({
        original: {
          code: 'the_error_code',
          error: 'The error description.',
          response: {
            req: {
              _data: {
                realm: 'realm',
                client_id: 'client_id',
                username: 'username',
                password: '*****'
              }
            }
          }
        },
        code: 'the_error_code',
        description: 'The error description.'
      });

      done();
    })(assert_err, null);
  });