diff --git a/assets/src/bundles/admin/index.js b/assets/src/bundles/admin/index.js
--- a/assets/src/bundles/admin/index.js
+++ b/assets/src/bundles/admin/index.js
@@ -1,9 +1,10 @@
 /**
- * Copyright (C) 2018  The Software Heritage developers
+ * Copyright (C) 2018-2022  The Software Heritage developers
  * See the AUTHORS file at the top-level directory of this distribution
  * License: GNU Affero General Public License version 3, or any later version
  * See top-level LICENSE file for more information
  */
 
 export * from './deposit';
+export * from './mailmap';
 export * from './origin-save';
diff --git a/assets/src/bundles/admin/mailmap-form.ejs b/assets/src/bundles/admin/mailmap-form.ejs
new file mode 100644
--- /dev/null
+++ b/assets/src/bundles/admin/mailmap-form.ejs
@@ -0,0 +1,29 @@
+<%#
+  Copyright (C) 2022  The Software Heritage developers
+  See the AUTHORS file at the top-level directory of this distribution
+  License: GNU Affero General Public License version 3, or any later version
+  See top-level LICENSE file for more information
+%>
+
+<form id="swh-mailmap-form" class="text-left">
+  <div class="form-group">
+    <label for="swh-mailmap-from-email">Email address</label>
+    <input type="email" class="form-control" id="swh-mailmap-from-email" value="<%= email %>"
+           <% if (updateForm) { %> readonly <% } %> required>
+  </div>
+  <div class="form-group">
+    <label for="swh-mailmap-display-name">Display name</label>
+    <input class="form-control" id="swh-mailmap-display-name" value="<%= displayName %>"
+           placeholder="John Doe <jdoe@example.org>" required>
+  </div>
+  <div class="custom-control custom-checkbox">
+    <input class="custom-control-input" type="checkbox" value=""
+           id="swh-mailmap-display-name-activated"
+           <% if (displayNameActivated) { %> checked <% } %>>
+    <label class="custom-control-label pt-0"
+           for="swh-mailmap-display-name-activated">Activate display name</label>
+  </div>
+  <div class="d-flex justify-content-center">
+    <input id="swh-mailmap-form-submit" type="submit" value="<%= buttonText %>">
+  </div>
+</form>
\ No newline at end of file
diff --git a/assets/src/bundles/admin/mailmap.js b/assets/src/bundles/admin/mailmap.js
new file mode 100644
--- /dev/null
+++ b/assets/src/bundles/admin/mailmap.js
@@ -0,0 +1,159 @@
+/**
+ * Copyright (C) 2022  The Software Heritage developers
+ * See the AUTHORS file at the top-level directory of this distribution
+ * License: GNU Affero General Public License version 3, or any later version
+ * See top-level LICENSE file for more information
+ */
+
+import {csrfPost, handleFetchError} from 'utils/functions';
+
+import mailmapFormTemplate from './mailmap-form.ejs';
+
+let mailmapsTable;
+
+export function mailmapForm(buttonText, email = '', displayName = '',
+                            displayNameActivated = false, update = false) {
+  return mailmapFormTemplate({
+    buttonText: buttonText,
+    email: email,
+    displayName: displayName,
+    displayNameActivated: displayNameActivated,
+    updateForm: update
+  });
+}
+
+function getMailmapDataFromForm() {
+  return {
+    'from_email': $('#swh-mailmap-from-email').val(),
+    'display_name': $('#swh-mailmap-display-name').val(),
+    'display_name_activated': $('#swh-mailmap-display-name-activated').prop('checked')
+  };
+}
+
+function processMailmapForm(formTitle, formHtml, formApiUrl) {
+  swh.webapp.showModalHtml(formTitle, formHtml);
+  $(`#swh-mailmap-form`).on('submit', async event => {
+    event.preventDefault();
+    event.stopPropagation();
+    const postData = getMailmapDataFromForm();
+    try {
+      const response = await csrfPost(
+        formApiUrl, {'Content-Type': 'application/json'}, JSON.stringify(postData)
+      );
+      $('#swh-web-modal-html').modal('hide');
+      handleFetchError(response);
+      mailmapsTable.draw();
+    } catch (response) {
+      const error = await response.text();
+      swh.webapp.showModalMessage('Error', error);
+    }
+  });
+}
+
+export function addNewMailmap() {
+  const mailmapFormHtml = mailmapForm('Add mailmap');
+  processMailmapForm('Add new mailmap', mailmapFormHtml, Urls.profile_mailmap_add());
+}
+
+export function updateMailmap(mailmapId) {
+  let mailmapData;
+  const rows = mailmapsTable.rows().data();
+  for (let i = 0; i < rows.length; ++i) {
+    const row = rows[i];
+    if (row.id === mailmapId) {
+      mailmapData = row;
+      break;
+    }
+  }
+  const mailmapFormHtml = mailmapForm('Update mailmap', mailmapData.from_email,
+                                      mailmapData.display_name,
+                                      mailmapData.display_name_activated, true);
+  processMailmapForm('Update existing mailmap', mailmapFormHtml, Urls.profile_mailmap_update());
+}
+
+const mdiCheckBold = '<i class="mdi mdi-check-bold" aria-hidden="true"></i>';
+const mdiCloseThick = '<i class="mdi mdi-close-thick" aria-hidden="true"></i>';
+
+export function initMailmapUI() {
+  $(document).ready(() => {
+    mailmapsTable = $('#swh-mailmaps-table')
+       .on('error.dt', (e, settings, techNote, message) => {
+         $('#swh-mailmaps-list-error').text(
+           'An error occurred while retrieving the mailmaps list');
+         console.log(message);
+       })
+       .DataTable({
+         serverSide: true,
+         ajax: Urls.profile_mailmap_list_datatables(),
+         columns: [
+           {
+             data: 'from_email',
+             name: 'from_email',
+             render: $.fn.dataTable.render.text()
+           },
+           {
+             data: 'from_email_verified',
+             name: 'from_email_verified',
+             render: (data, type, row) => {
+               return data ? mdiCheckBold : mdiCloseThick;
+             },
+             className: 'dt-center'
+           },
+           {
+             data: 'display_name',
+             name: 'display_name',
+             render: $.fn.dataTable.render.text()
+           },
+           {
+             data: 'display_name_activated',
+             name: 'display_name_activated',
+             render: (data, type, row) => {
+               return data ? mdiCheckBold : mdiCloseThick;
+             },
+             className: 'dt-center'
+           },
+           {
+             data: 'last_update_date',
+             name: 'last_update_date',
+             render: (data, type, row) => {
+               if (type === 'display') {
+                 const date = new Date(data);
+                 return date.toLocaleString();
+               }
+               return data;
+             }
+           },
+           {
+             render: (data, type, row) => {
+               const lastUpdateDate = new Date(row.last_update_date);
+               const lastProcessingDate = new Date(row.mailmap_last_processing_date);
+               if (!lastProcessingDate || lastProcessingDate < lastUpdateDate) {
+                 return mdiCloseThick;
+               } else {
+                 return mdiCheckBold;
+               }
+             },
+             className: 'dt-center',
+             orderable: false
+           },
+           {
+             render: (data, type, row) => {
+               const html =
+                `<button class="btn btn-default"
+                         onclick="swh.admin.updateMailmap(${row.id})">
+                  Edit
+                </button>`;
+               return html;
+             },
+             orderable: false
+           }
+
+         ],
+         ordering: true,
+         searching: true,
+         searchDelay: 1000,
+         scrollY: '50vh',
+         scrollCollapse: true
+       });
+  });
+}
diff --git a/cypress/integration/mailmap.spec.js b/cypress/integration/mailmap.spec.js
new file mode 100644
--- /dev/null
+++ b/cypress/integration/mailmap.spec.js
@@ -0,0 +1,226 @@
+/**
+ * Copyright (C) 2022  The Software Heritage developers
+ * See the AUTHORS file at the top-level directory of this distribution
+ * License: GNU Affero General Public License version 3, or any later version
+ * See top-level LICENSE file for more information
+ */
+
+const $ = Cypress.$;
+
+function fillFormAndSubmitMailmap(fromEmail, displayName, activated) {
+  if (fromEmail) {
+    cy.get('#swh-mailmap-from-email')
+      .clear()
+      .type(fromEmail, {delay: 0, force: true});
+  }
+
+  if (displayName) {
+    cy.get('#swh-mailmap-display-name')
+      .clear()
+      .type(displayName, {delay: 0, force: true});
+  }
+
+  if (activated) {
+    cy.get('#swh-mailmap-display-name-activated')
+      .check({force: true});
+  } else {
+    cy.get('#swh-mailmap-display-name-activated')
+      .uncheck({force: true});
+  }
+
+  cy.get('#swh-mailmap-form-submit')
+    .click();
+}
+
+function addNewMailmap(fromEmail, displayName, activated) {
+  cy.get('#swh-add-new-mailmap')
+    .click();
+
+  fillFormAndSubmitMailmap(fromEmail, displayName, activated);
+}
+
+function updateMailmap(fromEmail, displayName, activated) {
+  cy.contains('Edit')
+    .click();
+
+  fillFormAndSubmitMailmap(fromEmail, displayName, activated);
+}
+
+function checkMailmapRow(fromEmail, displayName, activated,
+                         processed = false, row = 1, nbRows = 1) {
+  cy.get('tbody tr').then(rows => {
+    assert.equal(rows.length, 1);
+    const cells = rows[0].cells;
+    assert.equal($(cells[0]).text(), fromEmail);
+    assert.include($(cells[1]).html(), 'mdi-check-bold');
+    assert.equal($(cells[2]).text(), displayName);
+    assert.include($(cells[3]).html(), activated ? 'mdi-check-bold' : 'mdi-close-thick');
+    assert.notEqual($(cells[4]).text(), '');
+    assert.include($(cells[5]).html(), processed ? 'mdi-check-bold' : 'mdi-close-thick');
+  });
+}
+
+describe('Test mailmap administration', function() {
+
+  before(function() {
+    this.url = this.Urls.admin_mailmap();
+  });
+
+  beforeEach(function() {
+    cy.task('db:user_mailmap:delete');
+    cy.intercept('POST', this.Urls.profile_mailmap_add())
+      .as('mailmapAdd');
+    cy.intercept('POST', this.Urls.profile_mailmap_update())
+      .as('mailmapUpdate');
+    cy.intercept(`${this.Urls.profile_mailmap_list_datatables()}**`)
+      .as('mailmapList');
+  });
+
+  it('should not display mailmap admin link in sidebar when anonymous', function() {
+    cy.visit(this.url);
+    cy.get('.swh-mailmap-admin-item')
+      .should('not.exist');
+  });
+
+  it('should not display mailmap admin link when connected as unprivileged user', function() {
+    cy.userLogin();
+    cy.visit(this.url);
+
+    cy.get('.swh-mailmap-admin-item')
+      .should('not.exist');
+
+  });
+
+  it('should display mailmap admin link in sidebar when connected as privileged user', function() {
+    cy.mailmapAdminLogin();
+    cy.visit(this.url);
+
+    cy.get('.swh-mailmap-admin-item')
+      .should('exist');
+  });
+
+  it('should not create a new mailmap when input data are empty', function() {
+    cy.mailmapAdminLogin();
+    cy.visit(this.url);
+
+    addNewMailmap('', '', true);
+
+    cy.get('#swh-mailmap-form :invalid').should('exist');
+
+    cy.get('#swh-mailmap-form')
+      .should('be.visible');
+
+  });
+
+  it('should not create a new mailmap when from email is invalid', function() {
+    cy.mailmapAdminLogin();
+    cy.visit(this.url);
+
+    addNewMailmap('invalid_email', 'display name', true);
+
+    cy.get('#swh-mailmap-form :invalid').should('exist');
+
+    cy.get('#swh-mailmap-form')
+      .should('be.visible');
+  });
+
+  it('should create a new mailmap when input data are valid', function() {
+    cy.mailmapAdminLogin();
+    cy.visit(this.url);
+
+    const fromEmail = 'user@example.org';
+    const displayName = 'New user display name';
+    addNewMailmap(fromEmail, displayName, true);
+    cy.wait('@mailmapAdd');
+
+    cy.get('#swh-mailmap-form :invalid').should('not.exist');
+
+    // ensure table redraw before next check
+    cy.contains(fromEmail);
+
+    cy.get('#swh-mailmap-form')
+      .should('not.be.visible');
+
+    checkMailmapRow(fromEmail, displayName, true);
+
+  });
+
+  it('should not create a new mailmap for an email already mapped', function() {
+    cy.mailmapAdminLogin();
+    cy.visit(this.url);
+
+    const fromEmail = 'user@example.org';
+    const displayName = 'New user display name';
+    addNewMailmap(fromEmail, displayName, true);
+    cy.wait('@mailmapAdd');
+
+    addNewMailmap(fromEmail, displayName, true);
+    cy.wait('@mailmapAdd');
+
+    cy.get('#swh-mailmap-form')
+      .should('not.be.visible');
+
+    cy.contains('Error')
+      .should('be.visible');
+
+    checkMailmapRow(fromEmail, displayName, true);
+
+  });
+
+  it('should update a mailmap', function() {
+    cy.mailmapAdminLogin();
+    cy.visit(this.url);
+
+    const fromEmail = 'user@example.org';
+    const displayName = 'New display name';
+    addNewMailmap(fromEmail, displayName, false);
+    cy.wait('@mailmapAdd');
+
+    cy.get('#swh-mailmap-form :invalid').should('not.exist');
+
+    // ensure table redraw before next check
+    cy.contains(fromEmail);
+
+    cy.get('#swh-mailmap-form')
+      .should('not.be.visible');
+
+    checkMailmapRow(fromEmail, displayName, false);
+
+    const newDisplayName = 'Updated display name';
+    updateMailmap('', newDisplayName, true);
+    cy.wait('@mailmapUpdate');
+
+    cy.get('#swh-mailmap-form :invalid').should('not.exist');
+
+    // ensure table redraw before next check
+    cy.contains(fromEmail);
+
+    cy.get('#swh-mailmap-form')
+      .should('not.be.visible');
+
+    checkMailmapRow(fromEmail, newDisplayName, true);
+
+  });
+
+  it('should indicate when a mailmap has been processed', function() {
+    cy.mailmapAdminLogin();
+    cy.visit(this.url);
+
+    const fromEmail = 'user@example.org';
+    const displayName = 'New user display name';
+    addNewMailmap(fromEmail, displayName, true);
+    cy.wait('@mailmapAdd');
+
+    // ensure table redraw before next check
+    cy.contains(fromEmail);
+
+    checkMailmapRow(fromEmail, displayName, true, false);
+
+    cy.task('db:user_mailmap:mark_processed');
+
+    cy.visit(this.url);
+    checkMailmapRow(fromEmail, displayName, true, true);
+
+  });
+
+});
diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js
--- a/cypress/plugins/index.js
+++ b/cypress/plugins/index.js
@@ -7,6 +7,7 @@
 
 const axios = require('axios');
 const fs = require('fs');
+const sqlite3 = require('sqlite3').verbose();
 
 async function httpGet(url) {
   const response = await axios.get(url);
@@ -32,6 +33,10 @@
   };
 };
 
+function getDatabase() {
+  return new sqlite3.Database('./swh-web-test.sqlite3');
+}
+
 module.exports = (on, config) => {
   require('@cypress/code-coverage/task')(on, config);
   // produce JSON files prior launching browser in order to dynamically generate tests
@@ -124,6 +129,23 @@
         global.swhTestsData = swhTestsData;
       }
       return global.swhTestsData;
+    },
+    'db:user_mailmap:delete': () => {
+      const db = getDatabase();
+      db.serialize(function() {
+        db.run('DELETE FROM user_mailmap');
+        db.run('DELETE FROM user_mailmap_event');
+      });
+      db.close();
+      return true;
+    },
+    'db:user_mailmap:mark_processed': () => {
+      const db = getDatabase();
+      db.serialize(function() {
+        db.run('UPDATE user_mailmap SET mailmap_last_processing_date=datetime("now", "+1 hour")');
+      });
+      db.close();
+      return true;
     }
   });
   return config;
diff --git a/cypress/support/index.js b/cypress/support/index.js
--- a/cypress/support/index.js
+++ b/cypress/support/index.js
@@ -84,6 +84,9 @@
     body: ''
   }).as('swhCoverageWidget');
 }
+Cypress.Commands.add('mailmapAdminLogin', () => {
+  return loginUser('mailmap-admin', 'mailmap-admin');
+});
 
 before(function() {
 
diff --git a/package.json b/package.json
--- a/package.json
+++ b/package.json
@@ -145,6 +145,7 @@
     "schema-utils": "^4.0.0",
     "script-loader": "^0.7.2",
     "spdx-expression-parse": "^3.0.1",
+    "sqlite3": "^5.0.2",
     "style-loader": "^3.3.1",
     "stylelint": "^14.5.3",
     "stylelint-config-standard": "^25.0.0",
diff --git a/swh/web/admin/mailmap.py b/swh/web/admin/mailmap.py
new file mode 100644
--- /dev/null
+++ b/swh/web/admin/mailmap.py
@@ -0,0 +1,16 @@
+# Copyright (C) 2022  The Software Heritage developers
+# See the AUTHORS file at the top-level directory of this distribution
+# License: GNU Affero General Public License version 3, or any later version
+# See top-level LICENSE file for more information
+
+from django.contrib.auth.decorators import permission_required
+from django.shortcuts import render
+
+from swh.web.admin.adminurls import admin_route
+from swh.web.auth.utils import MAILMAP_ADMIN_PERMISSION
+
+
+@admin_route(r"mailmap/", view_name="admin-mailmap")
+@permission_required(MAILMAP_ADMIN_PERMISSION)
+def _admin_mailmap(request):
+    return render(request, "admin/mailmap.html")
diff --git a/swh/web/admin/urls.py b/swh/web/admin/urls.py
--- a/swh/web/admin/urls.py
+++ b/swh/web/admin/urls.py
@@ -9,6 +9,7 @@
 
 from swh.web.admin.adminurls import AdminUrls
 import swh.web.admin.deposit  # noqa
+import swh.web.admin.mailmap  # noqa
 import swh.web.admin.origin_save  # noqa
 from swh.web.config import is_feature_enabled
 
diff --git a/swh/web/auth/mailmap.py b/swh/web/auth/mailmap.py
--- a/swh/web/auth/mailmap.py
+++ b/swh/web/auth/mailmap.py
@@ -4,15 +4,18 @@
 # See top-level LICENSE file for more information
 
 import json
+from typing import Any, Dict
 
 from django.conf.urls import url
+from django.core.paginator import Paginator
 from django.db import IntegrityError
 from django.db.models import Q
+from django.http.request import HttpRequest
 from django.http.response import (
     HttpResponse,
     HttpResponseBadRequest,
-    HttpResponseForbidden,
     HttpResponseNotFound,
+    JsonResponse,
 )
 from rest_framework import serializers
 from rest_framework.decorators import api_view
@@ -20,7 +23,11 @@
 from rest_framework.response import Response
 
 from swh.web.auth.models import UserMailmap, UserMailmapEvent
-from swh.web.auth.utils import MAILMAP_PERMISSION
+from swh.web.auth.utils import (
+    MAILMAP_ADMIN_PERMISSION,
+    MAILMAP_PERMISSION,
+    any_permission_required,
+)
 
 
 class UserMailmapSerializer(serializers.ModelSerializer):
@@ -30,18 +37,20 @@
 
 
 @api_view(["GET"])
+@any_permission_required(MAILMAP_PERMISSION, MAILMAP_ADMIN_PERMISSION)
 def profile_list_mailmap(request: Request) -> HttpResponse:
-    if not request.user.has_perm(MAILMAP_PERMISSION):
-        return HttpResponseForbidden()
+    mailmap_admin = request.user.has_perm(MAILMAP_ADMIN_PERMISSION)
 
-    mms = UserMailmap.objects.filter(user_id=str(request.user.id),).all()
+    mms = UserMailmap.objects.filter(
+        user_id=None if mailmap_admin else str(request.user.id)
+    ).all()
     return Response(UserMailmapSerializer(mms, many=True).data)
 
 
 @api_view(["POST"])
+@any_permission_required(MAILMAP_PERMISSION, MAILMAP_ADMIN_PERMISSION)
 def profile_add_mailmap(request: Request) -> HttpResponse:
-    if not request.user.has_perm(MAILMAP_PERMISSION):
-        return HttpResponseForbidden()
+    mailmap_admin = request.user.has_perm(MAILMAP_ADMIN_PERMISSION)
 
     event = UserMailmapEvent.objects.create(
         user_id=str(request.user.id),
@@ -51,29 +60,46 @@
 
     from_email = request.data.pop("from_email", None)
     if not from_email:
-        return HttpResponseBadRequest("'from_email' must be provided and non-empty.")
+        return HttpResponseBadRequest(
+            "'from_email' must be provided and non-empty.", content_type="text/plain"
+        )
+
+    user_id = None if mailmap_admin else str(request.user.id)
+
+    from_email_verified = request.data.pop("from_email_verified", False)
+    if mailmap_admin:
+        # consider email verified when mailmap is added by admin
+        from_email_verified = True
 
     try:
         UserMailmap.objects.create(
-            user_id=str(request.user.id), from_email=from_email, **request.data
+            user_id=user_id,
+            from_email=from_email,
+            from_email_verified=from_email_verified,
+            **request.data,
         )
     except IntegrityError as e:
-        if "user_mailmap_from_email_key" in e.args[0]:
-            return HttpResponseBadRequest("This 'from_email' already exists.")
+        if (
+            "user_mailmap_from_email_key" in e.args[0]
+            or "user_mailmap.from_email" in e.args[0]
+        ):
+            return HttpResponseBadRequest(
+                "This 'from_email' already exists.", content_type="text/plain"
+            )
         else:
             raise
 
     event.successful = True
     event.save()
 
-    mm = UserMailmap.objects.get(user_id=str(request.user.id), from_email=from_email)
+    mm = UserMailmap.objects.get(user_id=user_id, from_email=from_email)
     return Response(UserMailmapSerializer(mm).data)
 
 
 @api_view(["POST"])
+@any_permission_required(MAILMAP_PERMISSION, MAILMAP_ADMIN_PERMISSION)
 def profile_update_mailmap(request: Request) -> HttpResponse:
-    if not request.user.has_perm(MAILMAP_PERMISSION):
-        return HttpResponseForbidden()
+    mailmap_admin = request.user.has_perm(MAILMAP_ADMIN_PERMISSION)
 
     event = UserMailmapEvent.objects.create(
         user_id=str(request.user.id),
@@ -83,18 +109,20 @@
 
     from_email = request.data.pop("from_email", None)
     if not from_email:
-        return HttpResponseBadRequest("'from_email' must be provided and non-empty.")
+        return HttpResponseBadRequest(
+            "'from_email' must be provided and non-empty.", content_type="text/plain"
+        )
 
-    user_id = str(request.user.id)
+    user_id = None if mailmap_admin else str(request.user.id)
 
     try:
         to_update = (
-            UserMailmap.objects.filter(Q(user_id__isnull=True) | Q(user_id=user_id))
+            UserMailmap.objects.filter(user_id=user_id)
             .filter(from_email=from_email)
             .get()
         )
     except UserMailmap.DoesNotExist:
-        return HttpResponseNotFound()
+        return HttpResponseNotFound("'from_email' cannot be found in mailmaps.")
 
     for attr, value in request.data.items():
         setattr(to_update, attr, value)
@@ -108,6 +136,50 @@
     return Response(UserMailmapSerializer(mm).data)
 
 
+@any_permission_required(MAILMAP_PERMISSION, MAILMAP_ADMIN_PERMISSION)
+def profile_list_mailmap_datatables(request: HttpRequest) -> HttpResponse:
+    mailmap_admin = request.user.has_perm(MAILMAP_ADMIN_PERMISSION)
+
+    mailmaps = UserMailmap.objects.filter(
+        user_id=None if mailmap_admin else str(request.user.id)
+    )
+
+    search_value = request.GET.get("search[value]", "")
+
+    column_order = request.GET.get("order[0][column]")
+    field_order = request.GET.get(f"columns[{column_order}][name]", "from_email")
+    order_dir = request.GET.get("order[0][dir]", "asc")
+    if order_dir == "desc":
+        field_order = "-" + field_order
+
+    mailmaps = mailmaps.order_by(field_order)
+
+    table_data: Dict[str, Any] = {}
+    table_data["draw"] = int(request.GET.get("draw", 1))
+    table_data["recordsTotal"] = mailmaps.count()
+
+    length = int(request.GET.get("length", 10))
+    page = int(request.GET.get("start", 0)) / length + 1
+
+    if search_value:
+        mailmaps = mailmaps.filter(
+            Q(from_email__icontains=search_value)
+            | Q(display_name__icontains=search_value)
+        )
+
+    table_data["recordsFiltered"] = mailmaps.count()
+
+    paginator = Paginator(mailmaps, length)
+
+    mailmaps_data = [
+        UserMailmapSerializer(mm).data for mm in paginator.page(int(page)).object_list
+    ]
+
+    table_data["data"] = mailmaps_data
+
+    return JsonResponse(table_data)
+
+
 urlpatterns = [
     url(r"^profile/mailmap/list/$", profile_list_mailmap, name="profile-mailmap-list",),
     url(r"^profile/mailmap/add/$", profile_add_mailmap, name="profile-mailmap-add",),
@@ -116,4 +188,9 @@
         profile_update_mailmap,
         name="profile-mailmap-update",
     ),
+    url(
+        r"^profile/mailmap/list/datatables/$",
+        profile_list_mailmap_datatables,
+        name="profile-mailmap-list-datatables",
+    ),
 ]
diff --git a/swh/web/auth/migrations/0006_fix_mailmap_admin_user_id.py b/swh/web/auth/migrations/0006_fix_mailmap_admin_user_id.py
new file mode 100644
--- /dev/null
+++ b/swh/web/auth/migrations/0006_fix_mailmap_admin_user_id.py
@@ -0,0 +1,41 @@
+# Copyright (C) 2022  The Software Heritage developers
+# See the AUTHORS file at the top-level directory of this distribution
+# License: GNU Affero General Public License version 3, or any later version
+# See top-level LICENSE file for more information
+
+import datetime
+
+from django.db import migrations
+
+
+def _set_first_mailmaps_as_edited_by_admin(apps, schema_editor):
+    """First mailmaps in production database have been created by a user
+    with "swh.web.mailmap" permission because no "swh.web.admin.mailmap"
+    permission existed at the time.
+
+    So change user_id to None to indicate these mailmaps have been created
+    by a mailmap administrator.
+    """
+    UserMailmap = apps.get_model("swh_web_auth", "UserMailmap")
+
+    for mailmap in UserMailmap.objects.filter(
+        last_update_date__lte=datetime.datetime(
+            2022, 2, 12
+        )  # first mailmaps added on 2022/2/11 in production
+    ):
+        if mailmap.user_id is not None:
+            mailmap.user_id = None
+            mailmap.save()
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("swh_web_auth", "0005_usermailmapevent"),
+    ]
+
+    operations = [
+        migrations.RunPython(
+            _set_first_mailmaps_as_edited_by_admin, migrations.RunPython.noop
+        ),
+    ]
diff --git a/swh/web/auth/utils.py b/swh/web/auth/utils.py
--- a/swh/web/auth/utils.py
+++ b/swh/web/auth/utils.py
@@ -11,8 +11,11 @@
 from cryptography.hazmat.primitives import hashes
 from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
 
+from django.contrib.auth.decorators import user_passes_test
 from django.http.request import HttpRequest
 
+from swh.web.common.exc import ForbiddenExc
+
 OIDC_SWH_WEB_CLIENT_ID = "swh-web"
 
 SWH_AMBASSADOR_PERMISSION = "swh.ambassador"
@@ -20,6 +23,7 @@
 ADMIN_LIST_DEPOSIT_PERMISSION = "swh.web.admin.list_deposits"
 MAILMAP_PERMISSION = "swh.web.mailmap"
 ADD_FORGE_MODERATOR_PERMISSION = "swh.web.add_forge_now.moderator"
+MAILMAP_ADMIN_PERMISSION = "swh.web.admin.mailmap"
 
 
 def _get_fernet(password: bytes, salt: bytes) -> Fernet:
@@ -96,3 +100,16 @@
     return user.is_authenticated and (
         user.is_staff or any([user.has_perm(perm) for perm in permissions])
     )
+
+
+def any_permission_required(*perms):
+    """View decorator granting access to it if user has at least one
+    permission among those passed as parameters.
+    """
+
+    def check_perms(user):
+        if any(user.has_perm(perm) for perm in perms):
+            return True
+        raise ForbiddenExc
+
+    return user_passes_test(check_perms)
diff --git a/swh/web/common/utils.py b/swh/web/common/utils.py
--- a/swh/web/common/utils.py
+++ b/swh/web/common/utils.py
@@ -30,6 +30,7 @@
 from swh.web.auth.utils import (
     ADD_FORGE_MODERATOR_PERMISSION,
     ADMIN_LIST_DEPOSIT_PERMISSION,
+    MAILMAP_ADMIN_PERMISSION,
 )
 from swh.web.common.exc import BadInputExc
 from swh.web.common.typing import QueryParameters
@@ -316,6 +317,7 @@
         "ADMIN_LIST_DEPOSIT_PERMISSION": ADMIN_LIST_DEPOSIT_PERMISSION,
         "ADD_FORGE_MODERATOR_PERMISSION": ADD_FORGE_MODERATOR_PERMISSION,
         "FEATURES": get_config()["features"],
+        "MAILMAP_ADMIN_PERMISSION": MAILMAP_ADMIN_PERMISSION,
     }
 
 
diff --git a/swh/web/templates/admin/mailmap.html b/swh/web/templates/admin/mailmap.html
new file mode 100644
--- /dev/null
+++ b/swh/web/templates/admin/mailmap.html
@@ -0,0 +1,53 @@
+{% extends "layout.html" %}
+
+{% comment %}
+Copyright (C) 2022  The Software Heritage developers
+See the AUTHORS file at the top-level directory of this distribution
+License: GNU Affero General Public License version 3, or any later version
+See top-level LICENSE file for more information
+{% endcomment %}
+
+{% load swh_templatetags %}
+{% load render_bundle from webpack_loader %}
+
+{% block header %}
+{{ block.super }}
+{% render_bundle 'admin' %}
+{% endblock %}
+
+{% block title %} Mailmap administration &ndash; Software Heritage archive {% endblock %}
+
+{% block navbar-content %}
+<h4>Mailmap administration</h4>
+{% endblock %}
+
+{% block content %}
+  <p class="mt-3">
+    This interface enables to manage author display names in the archive based
+    on their emails.
+  </p>
+  <div class="float-right">
+    <button class="btn btn-default" id="swh-add-new-mailmap" onclick="swh.admin.addNewMailmap()">
+      Add new mailmap
+    </button>
+  </div>
+  <div style="padding-top: 3rem;">
+    <table id="swh-mailmaps-table" class="table swh-table swh-table-striped" width="100%">
+      <thead>
+        <tr>
+          <th title="Email identifying author in the archive">Email</th>
+          <th title="Is email verified ?">Verified</th>
+          <th title="New display name for the author in the archive">Display name</th>
+          <th title="Should display name be used in the archive ?">Activated</th>
+          <th title="Date when that mailmap was last updated">Last update</th>
+          <th title="Is last mailmap update processed and effective in the archive ?">Effective</th>
+          <th></th>
+        </tr>
+      </thead>
+    </table>
+    <p id="swh-mailmaps-list-error"></p>
+  </div>
+  <script>
+    swh.admin.initMailmapUI();
+  </script>
+{% endblock content %}
diff --git a/swh/web/templates/layout.html b/swh/web/templates/layout.html
--- a/swh/web/templates/layout.html
+++ b/swh/web/templates/layout.html
@@ -244,12 +244,20 @@
                 {% endif %}
               {% endif %}
               {% if user.is_staff or ADMIN_LIST_DEPOSIT_PERMISSION in user.get_all_permissions %}
-              <li class="nav-item swh-deposit-admin-item" title="Deposit administration">
-                <a href="{% url 'admin-deposit' %}" class="nav-link swh-deposit-admin-link">
-                  <i style="color: #fecd1b;" class="nav-icon mdi mdi-24px mdi-folder-upload"></i>
-                  <p>Deposit</p>
-                </a>
-              </li>
+                <li class="nav-item swh-deposit-admin-item" title="Deposit administration">
+                  <a href="{% url 'admin-deposit' %}" class="nav-link swh-deposit-admin-link">
+                    <i style="color: #fecd1b;" class="nav-icon mdi mdi-24px mdi-folder-upload"></i>
+                    <p>Deposit</p>
+                  </a>
+                </li>
+              {% endif %}
+              {% if MAILMAP_ADMIN_PERMISSION in user.get_all_permissions %}
+                <li class="nav-item swh-mailmap-admin-item" title="Mailmap administration">
+                  <a href="{% url 'admin-mailmap' %}" class="nav-link swh-mailmap-admin-link">
+                    <i style="color: #fecd1b;" class="nav-icon mdi mdi-24px mdi-email"></i>
+                    <p>Mailmap</p>
+                  </a>
+                </li>
               {% endif %}
             {% endif %}
           </ul>
diff --git a/swh/web/tests/auth/test_mailmap.py b/swh/web/tests/auth/test_mailmap.py
--- a/swh/web/tests/auth/test_mailmap.py
+++ b/swh/web/tests/auth/test_mailmap.py
@@ -16,21 +16,14 @@
 
 from swh.model.model import Person
 from swh.web.auth.models import UserMailmap, UserMailmapEvent
-from swh.web.auth.utils import MAILMAP_PERMISSION
 from swh.web.common.utils import reverse
 from swh.web.tests.utils import (
     check_api_post_response,
     check_http_get_response,
-    create_django_permission,
+    check_http_post_response,
 )
 
 
-@pytest.fixture
-def mailmap_user(regular_user):
-    regular_user.user_permissions.add(create_django_permission(MAILMAP_PERMISSION))
-    return regular_user
-
-
 @pytest.mark.django_db(transaction=True)
 @pytest.mark.parametrize("view_name", ["profile-mailmap-add", "profile-mailmap-update"])
 def test_mailmap_endpoints_anonymous_user(api_client, view_name):
@@ -39,172 +32,332 @@
 
 
 @pytest.mark.django_db(transaction=True)
-def test_mailmap_endpoints_user_with_permission(api_client, mailmap_user):
-    api_client.force_login(mailmap_user)
+def test_mailmap_endpoints_user_with_permission(
+    api_client, mailmap_user, mailmap_admin
+):
 
-    request_data = {"from_email": "bar@example.org", "display_name": "bar"}
+    for user, name in ((mailmap_user, "bar"), (mailmap_admin, "baz")):
 
-    for view_name in ("profile-mailmap-add", "profile-mailmap-update"):
-        url = reverse(view_name)
-        check_api_post_response(
-            api_client, url, data=request_data, status_code=200,
-        )
+        UserMailmapEvent.objects.all().delete()
 
-    # FIXME: use check_api_get_responses; currently this crashes without
-    # content_type="application/json"
-    resp = check_http_get_response(
-        api_client,
-        reverse("profile-mailmap-list"),
-        status_code=200,
-        content_type="application/json",
-    ).data
-    assert len(resp) == 1
-    assert resp[0]["from_email"] == "bar@example.org"
-    assert resp[0]["display_name"] == "bar"
-
-    events = UserMailmapEvent.objects.order_by("timestamp").all()
-    assert len(events) == 2
-    assert events[0].request_type == "add"
-    assert json.loads(events[0].request) == request_data
-    assert events[1].request_type == "update"
-    assert json.loads(events[1].request) == request_data
+        api_client.force_login(user)
+
+        request_data = {"from_email": f"{name}@example.org", "display_name": name}
+
+        for view_name in ("profile-mailmap-add", "profile-mailmap-update"):
+            url = reverse(view_name)
+            check_api_post_response(
+                api_client, url, data=request_data, status_code=200,
+            )
+
+        # FIXME: use check_api_get_responses; currently this crashes without
+        # content_type="application/json"
+        resp = check_http_get_response(
+            api_client,
+            reverse("profile-mailmap-list"),
+            status_code=200,
+            content_type="application/json",
+        ).data
+        assert len(resp) == 1
+        assert resp[0]["from_email"] == f"{name}@example.org"
+        assert resp[0]["display_name"] == name
+
+        events = UserMailmapEvent.objects.order_by("timestamp").all()
+        assert len(events) == 2
+        assert events[0].request_type == "add"
+        assert json.loads(events[0].request) == request_data
+        assert events[1].request_type == "update"
+        assert json.loads(events[1].request) == request_data
 
 
 @pytest.mark.django_db(transaction=True)
-def test_mailmap_add_duplicate(api_client, mailmap_user):
-    api_client.force_login(mailmap_user)
+def test_mailmap_add_duplicate(api_client, mailmap_user, mailmap_admin):
 
-    check_api_post_response(
-        api_client,
-        reverse("profile-mailmap-add"),
-        data={"from_email": "foo@example.org", "display_name": "bar"},
-        status_code=200,
-    )
-    check_api_post_response(
-        api_client,
-        reverse("profile-mailmap-add"),
-        data={"from_email": "foo@example.org", "display_name": "baz"},
-        status_code=400,
-    )
+    for user, name in ((mailmap_user, "foo"), (mailmap_admin, "bar")):
+
+        api_client.force_login(user)
+
+        check_api_post_response(
+            api_client,
+            reverse("profile-mailmap-add"),
+            data={"from_email": f"{name}@example.org", "display_name": name},
+            status_code=200,
+        )
+        check_api_post_response(
+            api_client,
+            reverse("profile-mailmap-add"),
+            data={"from_email": f"{name}@example.org", "display_name": name},
+            status_code=400,
+        )
 
 
 @pytest.mark.django_db(transaction=True)
-def test_mailmap_add_full(api_client, mailmap_user):
-    api_client.force_login(mailmap_user)
-
-    request_data = {
-        "from_email": "foo@example.org",
-        "from_email_verified": True,
-        "from_email_verification_request_date": "2021-02-07T14:04:15Z",
-        "display_name": "bar",
-        "display_name_activated": True,
-        "to_email": "bar@example.org",
-        "to_email_verified": True,
-        "to_email_verification_request_date": "2021-02-07T15:54:59Z",
-    }
+def test_mailmap_add_full(api_client, mailmap_user, mailmap_admin):
 
-    check_api_post_response(
-        api_client, reverse("profile-mailmap-add"), data=request_data, status_code=200,
-    )
+    for user, name in ((mailmap_user, "foo"), (mailmap_admin, "bar")):
 
-    resp = check_http_get_response(
-        api_client,
-        reverse("profile-mailmap-list"),
-        status_code=200,
-        content_type="application/json",
-    ).data
-    assert len(resp) == 1
-    assert resp[0].items() >= request_data.items()
+        api_client.force_login(user)
+
+        UserMailmapEvent.objects.all().delete()
+
+        request_data = {
+            "from_email": f"{name}@example.org",
+            "from_email_verified": True,
+            "from_email_verification_request_date": "2021-02-07T14:04:15Z",
+            "display_name": name,
+            "display_name_activated": True,
+            "to_email": "baz@example.org",
+            "to_email_verified": True,
+            "to_email_verification_request_date": "2021-02-07T15:54:59Z",
+        }
+
+        check_api_post_response(
+            api_client,
+            reverse("profile-mailmap-add"),
+            data=request_data,
+            status_code=200,
+        )
+
+        resp = check_http_get_response(
+            api_client,
+            reverse("profile-mailmap-list"),
+            status_code=200,
+            content_type="application/json",
+        ).data
+        assert len(resp) == 1
+        assert resp[0].items() >= request_data.items()
 
-    events = UserMailmapEvent.objects.all()
-    assert len(events) == 1
-    assert events[0].request_type == "add"
-    assert json.loads(events[0].request) == request_data
-    assert events[0].successful
+        events = UserMailmapEvent.objects.all()
+        assert len(events) == 1
+        assert events[0].request_type == "add"
+        assert json.loads(events[0].request) == request_data
+        assert events[0].successful
 
 
 @pytest.mark.django_db(transaction=True)
-def test_mailmap_endpoints_error_response(api_client, mailmap_user):
-    api_client.force_login(mailmap_user)
+def test_mailmap_endpoints_error_response(api_client, mailmap_user, mailmap_admin):
+
+    for user in (mailmap_user, mailmap_admin):
+
+        api_client.force_login(user)
 
-    url = reverse("profile-mailmap-add")
-    resp = check_api_post_response(api_client, url, status_code=400)
-    assert b"from_email" in resp.content
+        UserMailmapEvent.objects.all().delete()
 
-    url = reverse("profile-mailmap-update")
-    resp = check_api_post_response(api_client, url, status_code=400)
-    assert b"from_email" in resp.content
+        url = reverse("profile-mailmap-add")
+        resp = check_api_post_response(api_client, url, status_code=400)
+        assert b"from_email" in resp.content
 
-    events = UserMailmapEvent.objects.order_by("timestamp").all()
-    assert len(events) == 2
+        url = reverse("profile-mailmap-update")
+        resp = check_api_post_response(api_client, url, status_code=400)
+        assert b"from_email" in resp.content
 
-    assert events[0].request_type == "add"
-    assert json.loads(events[0].request) == {}
-    assert not events[0].successful
+        events = UserMailmapEvent.objects.order_by("timestamp").all()
+        assert len(events) == 2
 
-    assert events[1].request_type == "update"
-    assert json.loads(events[1].request) == {}
-    assert not events[1].successful
+        assert events[0].request_type == "add"
+        assert json.loads(events[0].request) == {}
+        assert not events[0].successful
+
+        assert events[1].request_type == "update"
+        assert json.loads(events[1].request) == {}
+        assert not events[1].successful
 
 
 @pytest.mark.django_db(transaction=True)
-def test_mailmap_update(api_client, mailmap_user):
-    api_client.force_login(mailmap_user)
+def test_mailmap_update(api_client, mailmap_user, mailmap_admin):
 
-    before_add = datetime.datetime.now(tz=datetime.timezone.utc)
+    for user, name in ((mailmap_user, "foo"), (mailmap_admin, "bar")):
 
-    check_api_post_response(
-        api_client,
-        reverse("profile-mailmap-add"),
-        data={"from_email": "orig1@example.org", "display_name": "Display Name 1"},
-        status_code=200,
-    )
-    check_api_post_response(
-        api_client,
-        reverse("profile-mailmap-add"),
-        data={"from_email": "orig2@example.org", "display_name": "Display Name 2"},
-        status_code=200,
-    )
-    after_add = datetime.datetime.now(tz=datetime.timezone.utc)
+        api_client.force_login(user)
+
+        UserMailmapEvent.objects.all().delete()
+
+        before_add = datetime.datetime.now(tz=datetime.timezone.utc)
+
+        check_api_post_response(
+            api_client,
+            reverse("profile-mailmap-add"),
+            data={
+                "from_email": f"{name}1@example.org",
+                "display_name": "Display Name 1",
+            },
+            status_code=200,
+        )
+        check_api_post_response(
+            api_client,
+            reverse("profile-mailmap-add"),
+            data={
+                "from_email": f"{name}2@example.org",
+                "display_name": "Display Name 2",
+            },
+            status_code=200,
+        )
+        after_add = datetime.datetime.now(tz=datetime.timezone.utc)
+
+        user_id = None if user == mailmap_admin else str(user.id)
+
+        mailmaps = list(
+            UserMailmap.objects.filter(user_id=user_id).order_by("from_email").all()
+        )
+        assert len(mailmaps) == 2, mailmaps
+
+        assert mailmaps[0].from_email == f"{name}1@example.org", mailmaps
+        assert mailmaps[0].display_name == "Display Name 1", mailmaps
+        assert before_add <= mailmaps[0].last_update_date <= after_add
+
+        assert mailmaps[1].from_email == f"{name}2@example.org", mailmaps
+        assert mailmaps[1].display_name == "Display Name 2", mailmaps
+        assert before_add <= mailmaps[0].last_update_date <= after_add
+
+        before_update = datetime.datetime.now(tz=datetime.timezone.utc)
+
+        check_api_post_response(
+            api_client,
+            reverse("profile-mailmap-update"),
+            data={
+                "from_email": f"{name}1@example.org",
+                "display_name": "Display Name 1b",
+            },
+            status_code=200,
+        )
+
+        after_update = datetime.datetime.now(tz=datetime.timezone.utc)
 
-    mailmaps = list(UserMailmap.objects.order_by("from_email").all())
-    assert len(mailmaps) == 2, mailmaps
+        mailmaps = list(
+            UserMailmap.objects.filter(user_id=user_id).order_by("from_email").all()
+        )
+        assert len(mailmaps) == 2, mailmaps
 
-    assert mailmaps[0].from_email == "orig1@example.org", mailmaps
-    assert mailmaps[0].display_name == "Display Name 1", mailmaps
-    assert before_add <= mailmaps[0].last_update_date <= after_add
+        assert mailmaps[0].from_email == f"{name}1@example.org", mailmaps
+        assert mailmaps[0].display_name == "Display Name 1b", mailmaps
+        assert before_update <= mailmaps[0].last_update_date <= after_update
 
-    assert mailmaps[1].from_email == "orig2@example.org", mailmaps
-    assert mailmaps[1].display_name == "Display Name 2", mailmaps
-    assert before_add <= mailmaps[0].last_update_date <= after_add
+        assert mailmaps[1].from_email == f"{name}2@example.org", mailmaps
+        assert mailmaps[1].display_name == "Display Name 2", mailmaps
+        assert before_add <= mailmaps[1].last_update_date <= after_add
 
-    before_update = datetime.datetime.now(tz=datetime.timezone.utc)
+        events = UserMailmapEvent.objects.order_by("timestamp").all()
+        assert len(events) == 3
+        assert events[0].request_type == "add"
+        assert events[1].request_type == "add"
+        assert events[2].request_type == "update"
 
+
+@pytest.mark.django_db(transaction=True)
+def test_mailmap_update_from_email_not_found(api_client, mailmap_admin):
+    api_client.force_login(mailmap_admin)
     check_api_post_response(
         api_client,
         reverse("profile-mailmap-update"),
-        data={"from_email": "orig1@example.org", "display_name": "Display Name 1b"},
-        status_code=200,
+        data={"from_email": "invalid@example.org", "display_name": "Display Name",},
+        status_code=404,
     )
 
-    after_update = datetime.datetime.now(tz=datetime.timezone.utc)
 
-    mailmaps = list(UserMailmap.objects.order_by("from_email").all())
-    assert len(mailmaps) == 2, mailmaps
+NB_MAILMAPS = 20
+MM_PER_PAGE = 10
+
+
+def _create_mailmaps(client):
+    mailmaps = []
+    for i in range(NB_MAILMAPS):
+        resp = check_http_post_response(
+            client,
+            reverse("profile-mailmap-add"),
+            data={
+                "from_email": f"user{i:02d}@example.org",
+                "display_name": f"User {i:02d}",
+            },
+            status_code=200,
+        )
+        mailmaps.append(json.loads(resp.content))
+    return mailmaps
+
+
+@pytest.mark.django_db(transaction=True, reset_sequences=True)
+def test_mailmap_list_datatables_no_parameters(client, mailmap_admin):
+    client.force_login(mailmap_admin)
+    mailmaps = _create_mailmaps(client)
+
+    url = reverse("profile-mailmap-list-datatables")
+
+    resp = check_http_get_response(client, url, status_code=200)
+    mailmap_data = json.loads(resp.content)
+
+    assert mailmap_data["recordsTotal"] == NB_MAILMAPS
+    assert mailmap_data["recordsFiltered"] == NB_MAILMAPS
+
+    # mailmaps sorted by ascending from_email by default
+    for i in range(10):
+        assert mailmap_data["data"][i]["from_email"] == mailmaps[i]["from_email"]
+
+
+@pytest.mark.django_db(transaction=True, reset_sequences=True)
+@pytest.mark.parametrize("sort_direction", ["asc", "desc"])
+def test_mailmap_list_datatables_ordering(client, mailmap_admin, sort_direction):
+    client.force_login(mailmap_admin)
+    mailmaps = _create_mailmaps(client)
+    mailmaps_sorted = list(sorted(mailmaps, key=lambda d: d["display_name"]))
+    all_display_names = [mm["display_name"] for mm in mailmaps_sorted]
+    if sort_direction == "desc":
+        all_display_names = list(reversed(all_display_names))
+
+    for i in range(NB_MAILMAPS // MM_PER_PAGE):
+        url = reverse(
+            "profile-mailmap-list-datatables",
+            query_params={
+                "draw": i,
+                "length": MM_PER_PAGE,
+                "start": i * MM_PER_PAGE,
+                "order[0][column]": 2,
+                "order[0][dir]": sort_direction,
+                "columns[2][name]": "display_name",
+            },
+        )
+
+        resp = check_http_get_response(client, url, status_code=200)
+        data = json.loads(resp.content)
+
+        assert data["draw"] == i
+        assert data["recordsFiltered"] == NB_MAILMAPS
+        assert data["recordsTotal"] == NB_MAILMAPS
+        assert len(data["data"]) == MM_PER_PAGE
+
+        display_names = [mm["display_name"] for mm in data["data"]]
+
+        expected_display_names = all_display_names[
+            i * MM_PER_PAGE : (i + 1) * MM_PER_PAGE
+        ]
+        assert display_names == expected_display_names
+
+
+@pytest.mark.django_db(transaction=True, reset_sequences=True)
+def test_mailmap_list_datatables_search(client, mailmap_admin):
+    client.force_login(mailmap_admin)
+    _create_mailmaps(client)
+
+    search_value = "user1"
+
+    url = reverse(
+        "profile-mailmap-list-datatables",
+        query_params={
+            "draw": 1,
+            "length": MM_PER_PAGE,
+            "start": 0,
+            "search[value]": search_value,
+        },
+    )
 
-    assert mailmaps[0].from_email == "orig1@example.org", mailmaps
-    assert mailmaps[0].display_name == "Display Name 1b", mailmaps
-    assert before_update <= mailmaps[0].last_update_date <= after_update
+    resp = check_http_get_response(client, url, status_code=200)
+    data = json.loads(resp.content)
 
-    assert mailmaps[1].from_email == "orig2@example.org", mailmaps
-    assert mailmaps[1].display_name == "Display Name 2", mailmaps
-    assert before_add <= mailmaps[1].last_update_date <= after_add
+    assert data["draw"] == 1
+    assert data["recordsFiltered"] == MM_PER_PAGE
+    assert data["recordsTotal"] == NB_MAILMAPS
+    assert len(data["data"]) == MM_PER_PAGE
 
-    events = UserMailmapEvent.objects.order_by("timestamp").all()
-    assert len(events) == 3
-    assert events[0].request_type == "add"
-    assert events[1].request_type == "add"
-    assert events[2].request_type == "update"
+    for mailmap in data["data"]:
+        assert search_value in mailmap["from_email"]
 
 
 def populate_mailmap():
diff --git a/swh/web/tests/auth/test_migrations.py b/swh/web/tests/auth/test_migrations.py
new file mode 100644
--- /dev/null
+++ b/swh/web/tests/auth/test_migrations.py
@@ -0,0 +1,38 @@
+# Copyright (C) 2022 The Software Heritage developers
+# See the AUTHORS file at the top-level directory of this distribution
+# License: GNU General Public License version 3, or any later version
+# See top-level LICENSE file for more information
+
+from datetime import datetime
+
+APP_NAME = "swh_web_auth"
+
+MIGRATION_0005 = "0005_usermailmapevent"
+MIGRATION_0006 = "0006_fix_mailmap_admin_user_id"
+
+
+def test_fix_mailmap_admin_user_id(migrator):
+    state = migrator.apply_tested_migration((APP_NAME, MIGRATION_0005))
+    UserMailmap = state.apps.get_model(APP_NAME, "UserMailmap")
+
+    user_id = "45"
+
+    UserMailmap.objects.create(
+        user_id=user_id,
+        from_email="user@example.org",
+        from_email_verified=True,
+        display_name="New display name",
+    )
+
+    UserMailmap.objects.filter(user_id=user_id).update(
+        last_update_date=datetime(2022, 2, 11, 14, 16, 13, 614000)
+    )
+
+    assert UserMailmap.objects.filter(user_id=user_id).count() == 1
+    assert UserMailmap.objects.filter(user_id=None).count() == 0
+
+    state = migrator.apply_tested_migration((APP_NAME, MIGRATION_0006))
+    UserMailmap = state.apps.get_model(APP_NAME, "UserMailmap")
+
+    assert UserMailmap.objects.filter(user_id=user_id).count() == 0
+    assert UserMailmap.objects.filter(user_id=None).count() == 1
diff --git a/swh/web/tests/conftest.py b/swh/web/tests/conftest.py
--- a/swh/web/tests/conftest.py
+++ b/swh/web/tests/conftest.py
@@ -36,7 +36,12 @@
 from swh.storage.algos.origin import origin_get_latest_visit_status
 from swh.storage.algos.revisions_walker import get_revisions_walker
 from swh.storage.algos.snapshot import snapshot_get_all_branches, snapshot_get_latest
-from swh.web.auth.utils import ADD_FORGE_MODERATOR_PERMISSION, OIDC_SWH_WEB_CLIENT_ID
+from swh.web.auth.utils import (
+    ADD_FORGE_MODERATOR_PERMISSION,
+    MAILMAP_ADMIN_PERMISSION,
+    MAILMAP_PERMISSION,
+    OIDC_SWH_WEB_CLIENT_ID,
+)
 from swh.web.common import converters
 from swh.web.common.origin_save import get_scheduler_load_task_types
 from swh.web.common.typing import OriginVisitInfo
@@ -1224,3 +1229,19 @@
         create_django_permission(ADD_FORGE_MODERATOR_PERMISSION)
     )
     return moderator
+
+
+@pytest.fixture
+def mailmap_admin():
+    mailmap_admin = User.objects.create_user(username="mailmap-admin", password="")
+    mailmap_admin.user_permissions.add(
+        create_django_permission(MAILMAP_ADMIN_PERMISSION)
+    )
+    return mailmap_admin
+
+
+@pytest.fixture
+def mailmap_user():
+    mailmap_user = User.objects.create_user(username="mailmap-user", password="")
+    mailmap_user.user_permissions.add(create_django_permission(MAILMAP_PERMISSION))
+    return mailmap_user
diff --git a/swh/web/tests/create_test_users.py b/swh/web/tests/create_test_users.py
--- a/swh/web/tests/create_test_users.py
+++ b/swh/web/tests/create_test_users.py
@@ -11,6 +11,7 @@
     ADD_FORGE_MODERATOR_PERMISSION,
     ADMIN_LIST_DEPOSIT_PERMISSION,
     SWH_AMBASSADOR_PERMISSION,
+    MAILMAP_ADMIN_PERMISSION,
 )
 from swh.web.tests.utils import create_django_permission
 
@@ -24,12 +25,17 @@
         "ambassador@example.org",
         [SWH_AMBASSADOR_PERMISSION],
     ),
-    "deposit": ("deposit", "deposit@example.org", [ADMIN_LIST_DEPOSIT_PERMISSION],),
+    "deposit": ("deposit", "deposit@example.org", [ADMIN_LIST_DEPOSIT_PERMISSION]),
     "add-forge-moderator": (
         "add-forge-moderator",
         "moderator@example.org",
         [ADD_FORGE_MODERATOR_PERMISSION],
     ),
+    "mailmap-admin": (
+        "mailmap-admin",
+        "mailmap-admin@example.org",
+        [MAILMAP_ADMIN_PERMISSION],
+    ),
 }
 
 
diff --git a/swh/web/tests/utils.py b/swh/web/tests/utils.py
--- a/swh/web/tests/utils.py
+++ b/swh/web/tests/utils.py
@@ -228,7 +228,9 @@
     app_label = ".".join(perm_splitted[:-1])
     perm_name = perm_splitted[-1]
     content_type = ContentType.objects.create(
-        id=1000 + ContentType.objects.count(), app_label=app_label, model="dummy"
+        id=1000 + ContentType.objects.count(),
+        app_label=app_label,
+        model=perm_splitted[-1],
     )
 
     return Permission.objects.create(
diff --git a/yarn.lock b/yarn.lock
--- a/yarn.lock
+++ b/yarn.lock
@@ -1885,6 +1885,11 @@
   resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41"
   integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==
 
+ansi-regex@^2.0.0:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
+  integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
+
 ansi-regex@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
@@ -1940,6 +1945,11 @@
   dependencies:
     default-require-extensions "^3.0.0"
 
+aproba@^1.0.3:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
+  integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==
+
 "aproba@^1.0.3 || ^2.0.0":
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
@@ -1963,6 +1973,14 @@
     delegates "^1.0.0"
     readable-stream "^3.6.0"
 
+are-we-there-yet@~1.1.2:
+  version "1.1.7"
+  resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146"
+  integrity sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==
+  dependencies:
+    delegates "^1.0.0"
+    readable-stream "^2.0.6"
+
 argparse@^1.0.7:
   version "1.0.10"
   resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
@@ -2261,6 +2279,13 @@
   resolved "https://registry.yarnpkg.com/blob-util/-/blob-util-2.0.2.tgz#3b4e3c281111bb7f11128518006cdc60b403a1eb"
   integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==
 
+block-stream@*:
+  version "0.0.9"
+  resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a"
+  integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=
+  dependencies:
+    inherits "~2.0.0"
+
 bluebird@^3.7.2:
   version "3.7.2"
   resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f"
@@ -2889,6 +2914,11 @@
   resolved "https://registry.yarnpkg.com/chosen-js/-/chosen-js-1.8.7.tgz#9bfa5597f5081d602ff4ae904af9aef33265bb1d"
   integrity sha512-eVdrZJ2U5ISdObkgsi0od5vIJdLwq1P1Xa/Vj/mgxkMZf14DlgobfB6nrlFi3kW4kkvKLsKk4NDqZj1MU1DCpw==
 
+chownr@^1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b"
+  integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==
+
 chownr@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
@@ -3022,6 +3052,11 @@
   resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
   integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
 
+code-point-at@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
+  integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
+
 codemirror@^5.65.1:
   version "5.65.1"
   resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.65.1.tgz#5988a812c974c467f964bcc1a00c944e373de502"
@@ -3209,7 +3244,7 @@
   resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336"
   integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==
 
-console-control-strings@^1.0.0, console-control-strings@^1.1.0:
+console-control-strings@^1.0.0, console-control-strings@^1.1.0, console-control-strings@~1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
   integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=
@@ -4122,7 +4157,7 @@
   dependencies:
     ms "2.1.2"
 
-debug@^3.1.0, debug@^3.1.1, debug@^3.2.7:
+debug@^3.1.0, debug@^3.1.1, debug@^3.2.6, debug@^3.2.7:
   version "3.2.7"
   resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
   integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
@@ -4294,6 +4329,11 @@
   resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
   integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
 
+detect-libc@^1.0.2:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
+  integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
+
 detect-node@^2.0.4:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
@@ -5437,6 +5477,13 @@
     jsonfile "^6.0.1"
     universalify "^2.0.0"
 
+fs-minipass@^1.2.7:
+  version "1.2.7"
+  resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7"
+  integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==
+  dependencies:
+    minipass "^2.6.0"
+
 fs-minipass@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
@@ -5459,6 +5506,16 @@
   resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
   integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
 
+fstream@^1.0.0, fstream@^1.0.12:
+  version "1.0.12"
+  resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045"
+  integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==
+  dependencies:
+    graceful-fs "^4.1.2"
+    inherits "~2.0.0"
+    mkdirp ">=0.5 0"
+    rimraf "2"
+
 fsu@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/fsu/-/fsu-1.1.1.tgz#bd36d3579907c59d85b257a75b836aa9e0c31834"
@@ -5509,6 +5566,20 @@
     strip-ansi "^6.0.1"
     wide-align "^1.1.2"
 
+gauge@~2.7.3:
+  version "2.7.4"
+  resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7"
+  integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=
+  dependencies:
+    aproba "^1.0.3"
+    console-control-strings "^1.0.0"
+    has-unicode "^2.0.0"
+    object-assign "^4.1.0"
+    signal-exit "^3.0.0"
+    string-width "^1.0.1"
+    strip-ansi "^3.0.1"
+    wide-align "^1.1.0"
+
 gaze@^1.0.0:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a"
@@ -5831,7 +5902,7 @@
   dependencies:
     has-symbols "^1.0.2"
 
-has-unicode@^2.0.1:
+has-unicode@^2.0.0, has-unicode@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
   integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=
@@ -6302,7 +6373,7 @@
   resolved "https://registry.yarnpkg.com/icheck-bootstrap/-/icheck-bootstrap-3.0.1.tgz#60c9c9a71524e1d9dd5bd05167a62fef05cc3a1b"
   integrity sha512-Rj3SybdcMcayhsP4IJ+hmCNgCKclaFcs/5zwCuLXH1WMo468NegjhZVxbSNKhEjJjnwc4gKETogUmPYSQ9lEZQ==
 
-iconv-lite@0.4.24:
+iconv-lite@0.4.24, iconv-lite@^0.4.4:
   version "0.4.24"
   resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
   integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
@@ -6336,6 +6407,13 @@
   resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
   integrity sha1-SMptcvbGo68Aqa1K5odr44ieKwk=
 
+ignore-walk@^3.0.1:
+  version "3.0.4"
+  resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.4.tgz#c9a09f69b7c7b479a5d74ac1a3c0d4236d2a6335"
+  integrity sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==
+  dependencies:
+    minimatch "^3.0.4"
+
 ignore@^5.1.1, ignore@^5.1.4, ignore@^5.1.8, ignore@^5.2.0:
   version "5.2.0"
   resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
@@ -6415,7 +6493,7 @@
     once "^1.3.0"
     wrappy "1"
 
-inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4:
+inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3, inherits@~2.0.4:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
   integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -6614,6 +6692,13 @@
   resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
   integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
 
+is-fullwidth-code-point@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
+  integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
+  dependencies:
+    number-is-nan "^1.0.0"
+
 is-fullwidth-code-point@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
@@ -7786,6 +7871,14 @@
   dependencies:
     minipass "^3.0.0"
 
+minipass@^2.6.0, minipass@^2.9.0:
+  version "2.9.0"
+  resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6"
+  integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==
+  dependencies:
+    safe-buffer "^5.1.2"
+    yallist "^3.0.0"
+
 minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3:
   version "3.1.6"
   resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee"
@@ -7793,6 +7886,13 @@
   dependencies:
     yallist "^4.0.0"
 
+minizlib@^1.3.3:
+  version "1.3.3"
+  resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d"
+  integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==
+  dependencies:
+    minipass "^2.9.0"
+
 minizlib@^2.0.0, minizlib@^2.1.1:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
@@ -7806,7 +7906,7 @@
   resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
   integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
 
-mkdirp@^0.5.5, mkdirp@~0.5.1:
+"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.1:
   version "0.5.5"
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
   integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
@@ -7973,7 +8073,7 @@
   resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
   integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==
 
-nanoid@3.3.1, nanoid@^3.2.0, nanoid@^3.3.1:
+nanoid@3.3.1, nanoid@^3.3.1:
   version "3.3.1"
   resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35"
   integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==
@@ -7988,6 +8088,15 @@
   resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
   integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
 
+needle@^2.2.1:
+  version "2.9.1"
+  resolved "https://registry.yarnpkg.com/needle/-/needle-2.9.1.tgz#22d1dffbe3490c2b83e301f7709b6736cd8f2684"
+  integrity sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==
+  dependencies:
+    debug "^3.2.6"
+    iconv-lite "^0.4.4"
+    sax "^1.2.4"
+
 negotiator@0.6.2, negotiator@^0.6.2:
   version "0.6.2"
   resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
@@ -8010,11 +8119,34 @@
   dependencies:
     lower-case "^1.1.1"
 
+node-addon-api@^3.0.0:
+  version "3.2.1"
+  resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161"
+  integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==
+
 node-forge@^1.2.0:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.2.1.tgz#82794919071ef2eb5c509293325cec8afd0fd53c"
   integrity sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w==
 
+node-gyp@3.x:
+  version "3.8.0"
+  resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c"
+  integrity sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==
+  dependencies:
+    fstream "^1.0.0"
+    glob "^7.0.3"
+    graceful-fs "^4.1.2"
+    mkdirp "^0.5.0"
+    nopt "2 || 3"
+    npmlog "0 || 1 || 2 || 3 || 4"
+    osenv "0"
+    request "^2.87.0"
+    rimraf "2"
+    semver "~5.3.0"
+    tar "^2.0.0"
+    which "1"
+
 node-gyp@^8.4.1:
   version "8.4.1"
   resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937"
@@ -8031,6 +8163,22 @@
     tar "^6.1.2"
     which "^2.0.2"
 
+node-pre-gyp@^0.11.0:
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz#db1f33215272f692cd38f03238e3e9b47c5dd054"
+  integrity sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==
+  dependencies:
+    detect-libc "^1.0.2"
+    mkdirp "^0.5.1"
+    needle "^2.2.1"
+    nopt "^4.0.1"
+    npm-packlist "^1.1.6"
+    npmlog "^4.0.2"
+    rc "^1.2.7"
+    rimraf "^2.6.1"
+    semver "^5.3.0"
+    tar "^4"
+
 node-preload@^0.2.1:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/node-preload/-/node-preload-0.2.1.tgz#c03043bb327f417a18fee7ab7ee57b408a144301"
@@ -8080,6 +8228,21 @@
     undefsafe "^2.0.5"
     update-notifier "^5.1.0"
 
+"nopt@2 || 3":
+  version "3.0.6"
+  resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
+  integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k=
+  dependencies:
+    abbrev "1"
+
+nopt@^4.0.1:
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48"
+  integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==
+  dependencies:
+    abbrev "1"
+    osenv "^0.1.4"
+
 nopt@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88"
@@ -8144,6 +8307,27 @@
     jsdom "^16.6.0"
     marked "^4.0.10"
 
+npm-bundled@^1.0.1:
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.2.tgz#944c78789bd739035b70baa2ca5cc32b8d860bc1"
+  integrity sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==
+  dependencies:
+    npm-normalize-package-bin "^1.0.1"
+
+npm-normalize-package-bin@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2"
+  integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==
+
+npm-packlist@^1.1.6:
+  version "1.4.8"
+  resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e"
+  integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==
+  dependencies:
+    ignore-walk "^3.0.1"
+    npm-bundled "^1.0.1"
+    npm-normalize-package-bin "^1.0.1"
+
 npm-run-path@^4.0.0, npm-run-path@^4.0.1:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
@@ -8151,6 +8335,16 @@
   dependencies:
     path-key "^3.0.0"
 
+"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.2:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
+  integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
+  dependencies:
+    are-we-there-yet "~1.1.2"
+    console-control-strings "~1.1.0"
+    gauge "~2.7.3"
+    set-blocking "~2.0.0"
+
 npmlog@^5.0.0:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0"
@@ -8178,6 +8372,11 @@
   dependencies:
     boolbase "^1.0.0"
 
+number-is-nan@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
+  integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
+
 nwsapi@^2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"
@@ -8221,7 +8420,7 @@
   resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
   integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
 
-object-assign@^4.0.1, object-assign@^4.1.1:
+object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
   integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
@@ -8356,6 +8555,24 @@
   resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
   integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=
 
+os-homedir@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
+  integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
+
+os-tmpdir@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+  integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
+
+osenv@0, osenv@^0.1.4:
+  version "0.1.5"
+  resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
+  integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
+  dependencies:
+    os-homedir "^1.0.0"
+    os-tmpdir "^1.0.0"
+
 ospath@^1.2.2:
   version "1.2.2"
   resolved "https://registry.yarnpkg.com/ospath/-/ospath-1.2.2.tgz#1276639774a3f8ef2572f7fe4280e0ea4550c07b"
@@ -9215,7 +9432,7 @@
   resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa"
   integrity sha1-DD0L6u2KAclm2Xh793goElKpeao=
 
-rc@^1.2.8:
+rc@^1.2.7, rc@^1.2.8:
   version "1.2.8"
   resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
   integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
@@ -9265,7 +9482,7 @@
     string_decoder "^1.1.1"
     util-deprecate "^1.0.1"
 
-readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@~2.3.3, readable-stream@~2.3.6:
+readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@~2.3.3, readable-stream@~2.3.6:
   version "2.3.7"
   resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
   integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
@@ -9404,7 +9621,7 @@
   dependencies:
     throttleit "^1.0.0"
 
-request@^2.88.0:
+request@^2.87.0, request@^2.88.0:
   version "2.88.2"
   resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
   integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
@@ -9538,7 +9755,7 @@
   resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3"
   integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=
 
-rimraf@^2.6.3:
+rimraf@2, rimraf@^2.6.1, rimraf@^2.6.3:
   version "2.7.1"
   resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
   integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
@@ -9595,7 +9812,7 @@
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
   integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
 
-safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0:
+safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@^5.2.1, safe-buffer@~5.2.0:
   version "5.2.1"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
   integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
@@ -9628,7 +9845,7 @@
     klona "^2.0.4"
     neo-async "^2.6.2"
 
-sax@^1.2.1:
+sax@^1.2.1, sax@^1.2.4:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
   integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
@@ -9725,7 +9942,7 @@
   dependencies:
     semver "^6.3.0"
 
-"semver@2 || 3 || 4 || 5", semver@^5.7.1:
+"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.7.1:
   version "5.7.1"
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
   integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
@@ -9752,6 +9969,11 @@
   dependencies:
     lru-cache "^6.0.0"
 
+semver@~5.3.0:
+  version "5.3.0"
+  resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
+  integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8=
+
 send@0.17.1:
   version "0.17.1"
   resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8"
@@ -9801,7 +10023,7 @@
     parseurl "~1.3.3"
     send "0.17.1"
 
-set-blocking@^2.0.0:
+set-blocking@^2.0.0, set-blocking@~2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
   integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
@@ -10091,6 +10313,16 @@
   resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
   integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
 
+sqlite3@^5.0.2:
+  version "5.0.2"
+  resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-5.0.2.tgz#00924adcc001c17686e0a6643b6cbbc2d3965083"
+  integrity sha512-1SdTNo+BVU211Xj1csWa8lV6KM0CtucDwRyA0VHl91wEH1Mgh7RxUpI4rVvG7OhHrzCSGaVyW5g8vKvlrk9DJA==
+  dependencies:
+    node-addon-api "^3.0.0"
+    node-pre-gyp "^0.11.0"
+  optionalDependencies:
+    node-gyp "3.x"
+
 sshpk@^1.14.1, sshpk@^1.7.0:
   version "1.16.1"
   resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
@@ -10199,6 +10431,15 @@
     inherits "^2.0.1"
     readable-stream "^2.0.2"
 
+string-width@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
+  integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
+  dependencies:
+    code-point-at "^1.0.0"
+    is-fullwidth-code-point "^1.0.0"
+    strip-ansi "^3.0.0"
+
 "string-width@^1.0.1 || ^2.0.0":
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
@@ -10255,6 +10496,13 @@
   dependencies:
     safe-buffer "~5.1.0"
 
+strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
+  integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
+  dependencies:
+    ansi-regex "^2.0.0"
+
 "strip-ansi@^3.0.1 || ^4.0.0", strip-ansi@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
@@ -10489,6 +10737,28 @@
   resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b"
   integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==
 
+tar@^2.0.0:
+  version "2.2.2"
+  resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.2.tgz#0ca8848562c7299b8b446ff6a4d60cdbb23edc40"
+  integrity sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==
+  dependencies:
+    block-stream "*"
+    fstream "^1.0.12"
+    inherits "2"
+
+tar@^4:
+  version "4.4.19"
+  resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3"
+  integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==
+  dependencies:
+    chownr "^1.1.4"
+    fs-minipass "^1.2.7"
+    minipass "^2.9.0"
+    minizlib "^1.3.3"
+    mkdirp "^0.5.5"
+    safe-buffer "^5.2.1"
+    yallist "^3.1.1"
+
 tar@^6.0.2, tar@^6.1.2:
   version "6.1.11"
   resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"
@@ -11365,6 +11635,13 @@
     has-tostringtag "^1.0.0"
     is-typed-array "^1.1.7"
 
+which@1, which@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
+  integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
+  dependencies:
+    isexe "^2.0.0"
+
 which@2.0.2, which@^2.0.1, which@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
@@ -11372,14 +11649,7 @@
   dependencies:
     isexe "^2.0.0"
 
-which@^1.3.1:
-  version "1.3.1"
-  resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
-  integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
-  dependencies:
-    isexe "^2.0.0"
-
-wide-align@^1.1.2:
+wide-align@^1.1.0, wide-align@^1.1.2:
   version "1.1.5"
   resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3"
   integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==
@@ -11501,6 +11771,11 @@
   resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18"
   integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==
 
+yallist@^3.0.0, yallist@^3.1.1:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
+  integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
+
 yallist@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"