CVE-2020-5263 (published 2020-04-09T16:15Z)

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 v2 base score: 4.0 (AV:N/AC:L/Au:S/C:P/I:N/A:N)
CVSS v3 base score: 4.9, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:N)

Weakness information:
cwe_id: CWE-522
cwe_name: Insufficiently Protected Credentials
description: The product transmits or stores authentication credentials, but it uses an insecure method that is susceptible to unauthorized interception and/or retrieval.
extended_description: 
            
            
            
         
         

Fix information:
repo_url: https://github.com/auth0/auth0.js
commit_details: repo_url: https://github.com/auth0/auth0.js
author: Steve Hobbs
author_date: 2020-04-09 09:22:45+01:00
msg: 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
repository: repo_url: https://github.com/auth0/auth0.js
repo_name: auth0/auth0.js
description: Auth0 headless browser sdk
repo_language: JavaScript
owner: auth0
file_changes: filename: object.js
change_type: ModificationType.MODIFY
diff: @@ -168,6 +168,28 @@ function trimUserDetails(options) {
   return trimMultiple(options, ['username', 'email', 'phoneNumber']);
 }
 
+/**
+ * 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);
+    }
+  }
+}
+
 export default {
   toSnakeCase: toSnakeCase,
   toCamelCase: toCamelCase,
@@ -178,5 +200,6 @@ export default {
   extend: extend,
   getOriginFromUrl: getOriginFromUrl,
   getLocationFromUrl: getLocationFromUrl,
-  trimUserDetails: trimUserDetails
+  trimUserDetails: trimUserDetails,
+  updatePropertyOn: updatePropertyOn
 };

programming_language: JavaScript
filename: response-handler.js
change_type: ModificationType.MODIFY
diff: @@ -27,6 +27,12 @@ function wrapCallback(cb, options) {
         original: err
       };
 
+      objectHelper.updatePropertyOn(
+        errObj,
+        'original.response.req._data.password',
+        '*****'
+      );
+
       if (err.response && err.response.statusCode) {
         errObj.statusCode = err.response.statusCode;
       }

programming_language: JavaScript
filename: object.test.js
change_type: ModificationType.MODIFY
diff: @@ -707,4 +707,62 @@ describe('helpers', function() {
       });
     });
   });
+
+  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
+          }
+        }
+      });
+    });
+  });
 });

programming_language: JavaScript
filename: response-handler.test.js
change_type: ModificationType.MODIFY
diff: @@ -241,4 +241,88 @@ describe('helpers responseHandler', function() {
       { keepOriginalCasing: true }
     )(null, assert_data);
   });
+
+  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);
+  });
 });

programming_language: JavaScript