CVE-2021-32691: Improper Authentication (CWE-287)
value: Apollos Apps is an open source platform for launching church-related apps. In Apollos Apps versions prior to 2.20.0, new user registrations are able to access anyone's account by only knowing their basic profile information (name, birthday, gender, etc). This includes all app functionality within the app, as well as any authenticated links to Rock-based webpages (such as giving and events). There is a patch in version 2.20.0. As a workaround, one can patch one's server by overriding the `create` data source method on the `People` class.
CWE description: When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.
CVSS v3 base score: 9.8
Published: 2021-06-16T22:15Z
Repository: https://github.com/ApollosProject/apollos-apps
Language: JavaScript
Files changed: packages/apollos-data-connector-rock/src/auth/__tests__/index.tests.js, packages/apollos-data-connector-rock/src/people/__tests__/__snapshots__/data-source.tests.js.snap, packages/apollos-data-connector-rock/src/people/data-source.js
Commit date: 2021-06-14 07:55:07 -0400
Commit message: fix: turns off automerging new people (#1989)

* fix: turns off automerging new people

* fix

* tests and snaps
Diff: diff --git a/packages/apollos-data-connector-rock/src/auth/__tests__/index.tests.js b/packages/apollos-data-connector-rock/src/auth/__tests__/index.tests.js
index edf11916..2b5fbf7b 100644
--- a/packages/apollos-data-connector-rock/src/auth/__tests__/index.tests.js
+++ b/packages/apollos-data-connector-rock/src/auth/__tests__/index.tests.js
@@ -222,15 +222,16 @@ describe('Auth', () => {
 
       expect(result).toEqual(false);
     });
 
     it('creates user profile', async () => {
+      context.dataSources.Person.create = jest.fn(() => 35);
       const result = await context.dataSources.Auth.createUserProfile({
         email: 'isaac.hardy@newspring.cc',
       });
 
-      expect(result).toEqual('35');
+      expect(result).toEqual(35);
     });
 
     it('throws error in createUserProfile', async () => {
       try {
         await context.dataSources.Auth.createUserProfile({
@@ -274,10 +275,11 @@ describe('Auth', () => {
               }
             }
           }
         }
       `;
+      context.dataSources.Person.create = jest.fn(() => 1);
 
       const rootValue = {};
 
       const result = await graphql(schema, query, rootValue, context);
       expect(result).toMatchSnapshot();
diff --git a/packages/apollos-data-connector-rock/src/people/__tests__/__snapshots__/data-source.tests.js.snap b/packages/apollos-data-connector-rock/src/people/__tests__/__snapshots__/data-source.tests.js.snap
index 26944d28..fd5845b8 100644
--- a/packages/apollos-data-connector-rock/src/people/__tests__/__snapshots__/data-source.tests.js.snap
+++ b/packages/apollos-data-connector-rock/src/people/__tests__/__snapshots__/data-source.tests.js.snap
@@ -5,23 +5,17 @@ exports[`Person Throws an error if trying to set an invalid gender 1`] = `"Rock
 exports[`Person creates a profile 1`] = `
 Array [
   Array [
     "/People",
     Object {
-      "BirthDay": 1,
-      "BirthMonth": 2,
-      "BirthYear": 2020,
-      "FirstName": "Vincent",
-      "Gender": 1,
+      "Gender": 0,
       "IsSystem": false,
     },
   ],
 ]
 `;
 
-exports[`Person creates a profile 2`] = `Object {}`;
-
 exports[`Person gets person from aliasId: The call to fetch the alias id 1`] = `
 Array [
   Array [
     "/PersonAlias?%24filter=Id%20eq%2051&%24select=PersonId&%24top=1",
     Object {},
diff --git a/packages/apollos-data-connector-rock/src/people/data-source.js b/packages/apollos-data-connector-rock/src/people/data-source.js
index 30c588d8..e8555fd5 100644
--- a/packages/apollos-data-connector-rock/src/people/data-source.js
+++ b/packages/apollos-data-connector-rock/src/people/data-source.js
@@ -63,17 +63,22 @@ export default class Person extends RockApolloDataSource {
     }
     // Otherwise, return null.
     return null;
   };
 
-  create = (profile) => {
+  create = async (profile) => {
     const rockUpdateFields = this.mapApollosFieldsToRock(profile);
-    return this.post('/People', {
+    // auto-merge functionality is compromised
+    // we are creating a new user and patching them with profile details
+    const id = await this.post('/People', {
       Gender: 0, // required by Rock. Listed first so it can be overridden.
-      ...rockUpdateFields,
       IsSystem: false, // required by rock
     });
+    await this.patch(`/People/${id}`, {
+      ...rockUpdateFields,
+    });
+    return id;
   };
 
   mapGender = ({ gender }) => {
     // If the gender is coming from Rock (an int) map into the string value.
     if (typeof gender === 'number') {

Vulnerable code:       expect(result).toEqual('35');
      "BirthDay": 1,
      "BirthMonth": 2,
      "BirthYear": 2020,
      "FirstName": "Vincent",
      "Gender": 1,
exports[`Person creates a profile 2`] = `Object {}`;

  create = (profile) => {
    return this.post('/People', {
      ...rockUpdateFields,
Fixed code:       context.dataSources.Person.create = jest.fn(() => 35);
      expect(result).toEqual(35);
      context.dataSources.Person.create = jest.fn(() => 1);
      "Gender": 0,
  create = async (profile) => {
    // auto-merge functionality is compromised
    // we are creating a new user and patching them with profile details
    const id = await this.post('/People', {
    await this.patch(`/People/${id}`, {
      ...rockUpdateFields,
    });
    return id;