diff --git a/ee/app/assets/javascripts/roles_and_permissions/components/create_member_role.vue b/ee/app/assets/javascripts/roles_and_permissions/components/create_member_role.vue
index de76154c06a3fd66fd3134bb773e36e22c9fb25c..ed5271d0b566ba15202f0999f945a67f129cb784 100644
--- a/ee/app/assets/javascripts/roles_and_permissions/components/create_member_role.vue
+++ b/ee/app/assets/javascripts/roles_and_permissions/components/create_member_role.vue
@@ -9,7 +9,7 @@ import {
   GlFormSelect,
   GlFormTextarea,
 } from '@gitlab/ui';
-
+import { difference, pull } from 'lodash';
 import { createAlert } from '~/alert';
 import { sprintf, s__ } from '~/locale';
 import { ACCESS_LEVEL_GUEST_INTEGER, ACCESS_LEVEL_LABELS } from '~/access_level/constants';
@@ -68,8 +68,59 @@ export default {
     refetchMemberRolesQuery() {
       return this.groupFullPath ? groupMemberRolesQuery : instanceMemberRolesQuery;
     },
+    parentPermissionsLookup() {
+      return this.availablePermissions.reduce((acc, { value, requirements }) => {
+        if (requirements) {
+          acc[value] = requirements;
+        }
+
+        return acc;
+      }, {});
+    },
+    childPermissionsLookup() {
+      return this.availablePermissions.reduce((acc, { value, requirements }) => {
+        requirements?.forEach((requirement) => {
+          // Create the array if it doesn't exist, then add the requirement to it.
+          acc[requirement] = acc[requirement] || [];
+          acc[requirement].push(value);
+        });
+
+        return acc;
+      }, {});
+    },
+  },
+  watch: {
+    permissions(newPermissions, oldPermissions) {
+      const added = difference(newPermissions, oldPermissions);
+      const removed = difference(oldPermissions, newPermissions);
+
+      added.forEach((permission) => this.selectParentPermissions(permission));
+      removed.forEach((permission) => this.deselectChildPermissions(permission));
+    },
   },
   methods: {
+    selectParentPermissions(permission) {
+      const parentPermissions = this.parentPermissionsLookup[permission];
+
+      parentPermissions?.forEach((parentPermission) => {
+        // Only select the parent permission if it's not already selected.
+        if (!this.permissions.includes(parentPermission)) {
+          this.permissions.push(parentPermission);
+          this.selectParentPermissions(parentPermission);
+        }
+      });
+    },
+    deselectChildPermissions(permission) {
+      const childPermissions = this.childPermissionsLookup[permission];
+
+      childPermissions?.forEach((childPermission) => {
+        // Only remove the child permission if it's selected.
+        if (this.permissions.includes(childPermission)) {
+          pull(this.permissions, childPermission);
+          this.deselectChildPermissions(childPermission);
+        }
+      });
+    },
     validateFields() {
       this.baseRoleValid = this.baseRole !== null;
       this.nameValid = Boolean(this.name);
diff --git a/ee/app/assets/javascripts/roles_and_permissions/graphql/member_role_permissions.query.graphql b/ee/app/assets/javascripts/roles_and_permissions/graphql/member_role_permissions.query.graphql
index 19267c015560dff1d2bd21e5c9329552e6a5c8cb..72d39f9ae22cc95ef6f32b74ab626fed9c096585 100644
--- a/ee/app/assets/javascripts/roles_and_permissions/graphql/member_role_permissions.query.graphql
+++ b/ee/app/assets/javascripts/roles_and_permissions/graphql/member_role_permissions.query.graphql
@@ -4,6 +4,7 @@ query memberRolePermissions {
       description
       name
       value
+      requirements
     }
   }
 }
diff --git a/ee/spec/features/groups/member_roles_spec.rb b/ee/spec/features/groups/member_roles_spec.rb
index 406d95608529f8a5233a20e1380eedc750d7f816..d8b72feb66286a34e1fc7d35b2030b4994135742 100644
--- a/ee/spec/features/groups/member_roles_spec.rb
+++ b/ee/spec/features/groups/member_roles_spec.rb
@@ -66,35 +66,19 @@ def created_role(name, id, access_level, permissions)
       let(:requirement) { permissions[permission][:requirements].first }
       let(:requirement_name) { requirement.to_s.humanize }
 
-      context 'when the requirement has not been met' do
-        it 'show an error message' do
-          create_role(access_level, name, [permission_name])
-
-          created_member_role = MemberRole.find_by(
-            name: name,
-            base_access_level: Gitlab::Access.options[access_level],
-            permission => true)
-
-          expect(created_member_role).to be_nil
-          expect(page).to have_content("#{requirement_name} has to be enabled in order to enable #{permission_name}")
-        end
-      end
-
-      context 'when the requirement has been met' do
-        it 'creates the custom role' do
-          create_role(access_level, name, [permission_name, requirement_name])
+      it 'creates the custom role' do
+        create_role(access_level, name, [permission_name])
 
-          created_member_role = MemberRole.find_by(
-            name: name,
-            base_access_level: Gitlab::Access.options[access_level],
-            permission => true,
-            requirement => true)
+        created_member_role = MemberRole.find_by(
+          name: name,
+          base_access_level: Gitlab::Access.options[access_level],
+          permission => true,
+          requirement => true)
 
-          expect(created_member_role).not_to be_nil
+        expect(created_member_role).not_to be_nil
 
-          role = created_role(name, created_member_role.id, access_level, [permission_name, requirement_name])
-          expect(page).to have_content(role)
-        end
+        role = created_role(name, created_member_role.id, access_level, [permission_name, requirement_name])
+        expect(page).to have_content(role)
       end
     end
   end
diff --git a/ee/spec/frontend/roles_and_permissions/components/create_member_role_spec.js b/ee/spec/frontend/roles_and_permissions/components/create_member_role_spec.js
index c7531f7049cc32eadc02d6a67aecdf2c65bb9987..8108e07380aeef88115899ab58c3bb70bbc91427 100644
--- a/ee/spec/frontend/roles_and_permissions/components/create_member_role_spec.js
+++ b/ee/spec/frontend/roles_and_permissions/components/create_member_role_spec.js
@@ -1,4 +1,10 @@
-import { GlFormInput, GlFormSelect, GlFormTextarea, GlFormCheckbox } from '@gitlab/ui';
+import {
+  GlFormInput,
+  GlFormSelect,
+  GlFormTextarea,
+  GlFormCheckbox,
+  GlFormCheckboxGroup,
+} from '@gitlab/ui';
 import Vue, { nextTick } from 'vue';
 import VueApollo from 'vue-apollo';
 import { createAlert, VARIANT_DANGER } from '~/alert';
@@ -256,4 +262,72 @@ describe('CreateMemberRole', () => {
       expect(mockAlertDismiss).toHaveBeenCalledTimes(1);
     });
   });
+
+  describe('dependent permissions', () => {
+    const availablePermissions = [
+      { value: 'A' },
+      { value: 'B', requirements: ['A'] },
+      { value: 'C', requirements: ['B'] }, // Nested dependency: C -> B -> A
+      { value: 'D', requirements: ['C'] }, // Nested dependency: D -> C -> B -> A
+      { value: 'E', requirements: ['F'] }, // Circular dependency
+      { value: 'F', requirements: ['E'] }, // Circular dependency
+      { value: 'G', requirements: ['A', 'B', 'C'] }, // Multiple dependencies
+    ];
+
+    const checkPermissions = (permissions) => {
+      wrapper.findComponent(GlFormCheckboxGroup).vm.$emit('input', permissions);
+    };
+
+    const expectCheckedPermissions = (expected) => {
+      const selectedValues = wrapper
+        .findComponent(GlFormCheckboxGroup)
+        .attributes('checked')
+        .split(',')
+        .sort();
+
+      expect(selectedValues).toEqual(expected.sort());
+    };
+
+    beforeEach(() => {
+      createComponent({ availablePermissions, stubs: { GlFormCheckboxGroup: true } });
+    });
+
+    it.each`
+      permission | expected
+      ${'A'}     | ${['A']}
+      ${'B'}     | ${['A', 'B']}
+      ${'C'}     | ${['A', 'B', 'C']}
+      ${'D'}     | ${['A', 'B', 'C', 'D']}
+      ${'E'}     | ${['E', 'F']}
+      ${'F'}     | ${['E', 'F']}
+      ${'G'}     | ${['A', 'B', 'C', 'G']}
+    `('selects $expected when $permission is selected', async ({ permission, expected }) => {
+      await checkPermissions([permission]);
+
+      expectCheckedPermissions(expected);
+    });
+
+    it.each`
+      permission | expected
+      ${'A'}     | ${['E', 'F']}
+      ${'B'}     | ${['A', 'E', 'F']}
+      ${'C'}     | ${['A', 'B', 'E', 'F']}
+      ${'D'}     | ${['A', 'B', 'C', 'E', 'F', 'G']}
+      ${'E'}     | ${['A', 'B', 'C', 'D', 'G']}
+      ${'F'}     | ${['A', 'B', 'C', 'D', 'G']}
+      ${'G'}     | ${['A', 'B', 'C', 'D', 'E', 'F']}
+    `(
+      'selects $expected when all permissions are selected and $permission is unselected',
+      async ({ permission, expected }) => {
+        const allPermissions = availablePermissions.map((p) => p.value);
+        const selectedPermissions = allPermissions.filter((v) => v !== permission);
+        // Start by checking all the permissions.
+        await checkPermissions(allPermissions);
+        // Uncheck the permission by removing it from all permissions.
+        await checkPermissions(selectedPermissions);
+
+        expectCheckedPermissions(expected);
+      },
+    );
+  });
 });
diff --git a/ee/spec/frontend/roles_and_permissions/mock_data.js b/ee/spec/frontend/roles_and_permissions/mock_data.js
index 8dd553a0dddff3fe9ca619d33471a1194837381c..ba1eb8d2b9ffd5f88efc8c331cf13efa815bf2c8 100644
--- a/ee/spec/frontend/roles_and_permissions/mock_data.js
+++ b/ee/spec/frontend/roles_and_permissions/mock_data.js
@@ -1,7 +1,22 @@
 export const mockDefaultPermissions = [
-  { name: 'Permission A', description: 'Description A', value: 'READ_CODE' },
-  { name: 'Permission B', description: 'Description B', value: 'READ_VULNERABILITY' },
-  { name: 'Permission C', description: 'Description C', value: 'ADMIN_VULNERABILITY' },
+  {
+    name: 'Permission A',
+    description: 'Description A',
+    value: 'READ_CODE',
+    requirements: null,
+  },
+  {
+    name: 'Permission B',
+    description: 'Description B',
+    value: 'READ_VULNERABILITY',
+    requirements: null,
+  },
+  {
+    name: 'Permission C',
+    description: 'Description C',
+    value: 'ADMIN_VULNERABILITY',
+    requirements: null,
+  },
 ];
 
 export const mockPermissions = {