diff --git a/SOURCES/CVE-2021-20095.patch b/SOURCES/CVE-2021-20095.patch
new file mode 100644
index 0000000..8b4c079
--- /dev/null
+++ b/SOURCES/CVE-2021-20095.patch
@@ -0,0 +1,128 @@
+diff --git a/babel/localedata.py b/babel/localedata.py
+index 4b6d3b6..080b723 100644
+--- a/babel/localedata.py
++++ b/babel/localedata.py
+@@ -13,6 +13,8 @@
+ """
+ 
+ import os
++import re
++import sys
+ import threading
+ from collections import MutableMapping
+ from itertools import chain
+@@ -33,6 +35,7 @@ def get_base_dir():
+ _cache = {}
+ _cache_lock = threading.RLock()
+ _dirname = os.path.join(get_base_dir(), 'locale-data')
++_windows_reserved_name_re = re.compile("^(con|prn|aux|nul|com[0-9]|lpt[0-9])$", re.I)
+ 
+ 
+ def normalize_locale(name):
+@@ -49,6 +52,22 @@ def normalize_locale(name):
+             return locale_id
+ 
+ 
++def resolve_locale_filename(name):
++    """
++    Resolve a locale identifier to a `.dat` path on disk.
++    """
++
++    # Clean up any possible relative paths.
++    name = os.path.basename(name)
++
++    # Ensure we're not left with one of the Windows reserved names.
++    if sys.platform == "win32" and _windows_reserved_name_re.match(os.path.splitext(name)[0]):
++        raise ValueError("Name %s is invalid on Windows" % name)
++
++    # Build the path.
++    return os.path.join(_dirname, '%s.dat' % name)
++
++
+ def exists(name):
+     """Check whether locale data is available for the given locale.
+ 
+@@ -60,7 +79,7 @@ def exists(name):
+         return False
+     if name in _cache:
+         return True
+-    file_found = os.path.exists(os.path.join(_dirname, '%s.dat' % name))
++    file_found = os.path.exists(resolve_locale_filename(name))
+     return True if file_found else bool(normalize_locale(name))
+ 
+ 
+@@ -102,6 +121,7 @@ def load(name, merge_inherited=True):
+     :raise `IOError`: if no locale data file is found for the given locale
+                       identifer, or one of the locales it inherits from
+     """
++    name = os.path.basename(name)
+     _cache_lock.acquire()
+     try:
+         data = _cache.get(name)
+@@ -119,7 +139,7 @@ def load(name, merge_inherited=True):
+                     else:
+                         parent = '_'.join(parts[:-1])
+                 data = load(parent).copy()
+-            filename = os.path.join(_dirname, '%s.dat' % name)
++            filename = resolve_locale_filename(name)
+             with open(filename, 'rb') as fileobj:
+                 if name != 'root' and merge_inherited:
+                     merge(data, pickle.load(fileobj))
+diff --git a/tests/test_localedata.py b/tests/test_localedata.py
+index 3599b21..173e7a3 100644
+--- a/tests/test_localedata.py
++++ b/tests/test_localedata.py
+@@ -11,12 +11,18 @@
+ # individuals. For the exact contribution history, see the revision
+ # history and logs, available at http://babel.edgewall.org/log/.
+ 
++import os
++import pickle
++import sys
++import tempfile
+ import unittest
+ import random
+ from operator import methodcaller
+ import sys
+ 
+-from babel import localedata, numbers
++import pytest
++
++from babel import localedata, Locale, UnknownLocaleError, numbers
+ 
+ class MergeResolveTestCase(unittest.TestCase):
+ 
+@@ -117,3 +123,33 @@ def test_locale_argument_acceptance():
+     assert normalized_locale == None
+     locale_exist = localedata.exists(['en_us', None])
+     assert locale_exist == False
++
++def test_locale_name_cleanup():
++    """
++    Test that locale identifiers are cleaned up to avoid directory traversal.
++    """
++    no_exist_name = os.path.join(tempfile.gettempdir(), "babel%d.dat" % random.randint(1, 99999))
++    with open(no_exist_name, "wb") as f:
++        pickle.dump({}, f)
++
++    try:
++        name = os.path.splitext(os.path.relpath(no_exist_name, localedata._dirname))[0]
++    except ValueError:
++        if sys.platform == "win32":
++            pytest.skip("unable to form relpath")
++        raise
++
++    assert not localedata.exists(name)
++    with pytest.raises(IOError):
++        localedata.load(name)
++    with pytest.raises(UnknownLocaleError):
++        Locale(name)
++
++
++@pytest.mark.skipif(sys.platform != "win32", reason="windows-only test")
++def test_reserved_locale_names():
++    for name in ("con", "aux", "nul", "prn", "com8", "lpt5"):
++        with pytest.raises(ValueError):
++            localedata.load(name)
++        with pytest.raises(ValueError):
++            Locale(name)
diff --git a/SPECS/babel.spec b/SPECS/babel.spec
index e54605a..bd86d7f 100644
--- a/SPECS/babel.spec
+++ b/SPECS/babel.spec
@@ -11,7 +11,7 @@
 
 Name:           babel
 Version:        2.5.1
-Release:        9%{?dist}
+Release:        10%{?dist}
 Summary:        Tools for internationalizing Python applications
 
 License:        BSD
@@ -23,6 +23,13 @@ Patch0:         babel-2.3.4-remove-pytz-version.patch
 # don't have capacity to ship in RHEL8
 Patch1:         Skip-tests-involving-freezegun-module-which-we-re-no.patch
 
+
+# Fix CVE-2021-20095: relative path traversal allows an attacker to load
+# arbitrary locale files on disk and execute arbitrary code
+# Resolved upstream: https://github.com/python-babel/babel/pull/782/
+# CVE bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1955615
+Patch2:         CVE-2021-20095.patch
+
 BuildArch:      noarch
 
 BuildRequires:  python2-devel
@@ -171,6 +178,10 @@ export TZ=America/New_York
 %endif  # python3
 
 %changelog
+* Wed May 12 2021 Charalampos Stratakis <cstratak@redhat.com> - 2.5.1-10
+- Fix CVE-2021-20095
+Resolves: rhbz#1955615
+
 * Thu Apr 25 2019 Tomas Orsava <torsava@redhat.com> - 2.5.1-9
 - Bumping due to problems with modular RPM upgrade path
 - Resolves: rhbz#1695587