CVE-2021-25991 (Ruby, published 2021-12-29T09:15Z)
Description: value: In Ifme, versions v5.0.0 to v7.32 are vulnerable against an improper access control, which makes it possible for admins to ban themselves leading to their deactivation from Ifme account and complete loss of admin access to Ifme.
Severity: MEDIUM (CVSS v3 base score: 7.3)
Weakness: NVD-CWE-Other \u2014 Other
NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
Repository: https://github.com/ifmeorg/ifme
Files changed:
app/controllers/notifications_controller.rb
app/helpers/comments_helper.rb
app/views/allies/index.html.erb
app/views/profile/index.html.erb
app/views/search/index.html.erb
client/app/widgets/Comments/__tests__/Comments.spec.jsx
client/app/widgets/Comments/index.jsx
spec/helpers/comments_helper_spec.rb
Commit message: [#2052] Fix improper access control leads to admin self-banning


Vulnerable code:
    data[:email] = User.find(data[:user_id]).email
          <div>
            <%= link_to t('common.actions.report'), new_report_path(uid: ally.uid) %>
          </div>
        <div>
          <%= link_to t('common.actions.report'), new_report_path(uid: @profile.uid) %>
        </div>
    <% if current_user.admin? %>
            <div>
              <%= link_to t('common.actions.report'), new_report_path(uid: user.uid) %>
            </div>
const comment = {
  currentUserId: 'some-uid',
};

const component = <Comments formProps={formProps} />;
  beforeEach(() => {
    jest.spyOn(axios, 'post').mockResolvedValue({
      data: { comment },
    jest.spyOn(axios, 'delete').mockResolvedValue({
      data: { id },
  it('renders correctly', () => {
    render(component);
    expect(screen.getByRole('textbox')).toBeInTheDocument();
    expect(screen.queryByRole('article')).not.toBeInTheDocument();
    expect(screen.getByRole('button', { name: 'Submit' })).toBeInTheDocument();
  });
  it('add and delete a comment', async () => {
    render(component);
    expect(screen.queryByRole('article')).not.toBeInTheDocument();
    userEvent.type(screen.getByRole('textbox'));
    userEvent.selectOptions(screen.getByRole('combobox'), 'private');
    userEvent.click(screen.getByRole('button', { name: 'Submit' }));
    await waitFor(() => expect(screen.getByRole('article')).toBeInTheDocument());
    expect(screen.getByRole('article')).toHaveTextContent('Hey');
    userEvent.click(screen.getByRole('link', { name: 'Delete' }));
    await waitFor(() => expect(screen.queryByRole('article')).not.toBeInTheDocument());
    if (currentUserUid !== uid) {

Fixed code:
    if data[:user_id]
      data[:email] = User.find(data[:user_id]).email
    end
      commentByAdmin: user.admin?,
          <% if !ally.admin? && ally != current_user %>
            <div>
              <%= link_to t('common.actions.report'), new_report_path(uid: ally.uid) %>
            </div>
          <% end %>
        <% if !@profile.admin? && @profile != current_user %>
          <div>
            <%= link_to t('common.actions.report'), new_report_path(uid: @profile.uid) %>
          </div>
        <% end %>
    <% if @profile != current_user && current_user.admin? %>
            <% if !user.admin? && user != current_user %>
              <div>
                <%= link_to t('common.actions.report'), new_report_path(uid: user.uid) %>
              </div>
            <% end %>
const getComment = ({ commentByAdmin } = {}) => ({
  currentUserUid: 'some-uid',
  commentByAdmin: Boolean(commentByAdmin),
});
  describe('when written by a non-admin user', () => {
    it('renders correctly with a report link', () => {
      render(<Comments formProps={formProps} comments={[getComment()]} />);
      expect(screen.getByRole('textbox')).toBeInTheDocument();
      expect(screen.getByRole('article')).toBeInTheDocument();
      expect(
        screen.getByRole('button', { name: 'Submit' }),
      ).toBeInTheDocument();
      expect(screen.getByRole('link', { name: 'Report' })).toBeInTheDocument();

    it('add and delete a comment', async () => {
      jest.spyOn(axios, 'post').mockResolvedValue({
        data: { comment: getComment() },
      });
      jest.spyOn(axios, 'delete').mockResolvedValue({
        data: { id },
      });
      render(<Comments formProps={formProps} />);
      expect(screen.queryByRole('article')).not.toBeInTheDocument();

      userEvent.type(screen.getByRole('textbox'));
      userEvent.selectOptions(screen.getByRole('combobox'), 'private');
      userEvent.click(screen.getByRole('button', { name: 'Submit' }));

      await waitFor(() => expect(screen.getByRole('article')).toBeInTheDocument());
      expect(screen.getByRole('article')).toHaveTextContent('Hey');

      userEvent.click(screen.getByRole('link', { name: 'Delete' }));

      await waitFor(() => expect(screen.queryByRole('article')).not.toBeInTheDocument());
  describe('when written by an admin user', () => {
    it('renders correctly without a report link', () => {
      render(
        <Comments
          formProps={formProps}
          comments={[getComment({ commentByAdmin: true })]}
        />,
      );
      expect(screen.getByRole('textbox')).toBeInTheDocument();
      expect(screen.getByRole('article')).toBeInTheDocument();
      expect(
        screen.getByRole('button', { name: 'Submit' }),
      ).toBeInTheDocument();
      expect(
        screen.queryByRole('link', { name: 'Report' }),
      ).not.toBeInTheDocument();
    });
    it('add and delete a comment', async () => {
      jest.spyOn(axios, 'post').mockResolvedValue({
        data: { comment: getComment({ commentByAdmin: true }) },
      });
      jest.spyOn(axios, 'delete').mockResolvedValue({
        data: { id },
      });
      render(<Comments formProps={formProps} />);
      expect(screen.queryByRole('article')).not.toBeInTheDocument();
      userEvent.type(screen.getByRole('textbox'));
      userEvent.selectOptions(screen.getByRole('combobox'), 'private');
      userEvent.click(screen.getByRole('button', { name: 'Submit' }));
      await waitFor(() => expect(screen.getByRole('article')).toBeInTheDocument());
      expect(screen.getByRole('article')).toHaveTextContent('Hey');
      userEvent.click(screen.getByRole('link', { name: 'Delete' }));
      await waitFor(() => expect(screen.queryByRole('article')).not.toBeInTheDocument());
    });
  commentByAdmin: Boolean,
    commentByAdmin: Boolean,
    if (currentUserUid !== uid && !commentByAdmin) {
      commentByAdmin,
              commentByAdmin,
                                                                                          commentByAdmin: false,
                                                                                          commentByAdmin: false,
                                                                                          commentByAdmin: false,
                                                                                          commentByAdmin: false,
                                                                                          commentByAdmin: false,
                                                                                          commentByAdmin: false,
                                                                                          commentByAdmin: false,
                                                                                          commentByAdmin: false,
                                                                                          commentByAdmin: false,
                                                                                          commentByAdmin: false,