CVE-2021-27291: Inefficient Regular Expression Complexity (CWE-1333)
value: In pygments 1.1+, fixed in 2.7.4, the lexers used to parse programming languages rely heavily on regular expressions. Some of the regular expressions have exponential or cubic worst-case complexity and are vulnerable to ReDoS. By crafting malicious input, an attacker can cause a denial of service.
CWE description: The product uses a regular expression with an inefficient, possibly exponential worst-case computational complexity that consumes excessive CPU cycles.
CVSS v3 base score: 7.5
Published: 2021-03-17T13:15Z
Repository: https://github.com/pygments/pygments
Language: Python
Files changed: CHANGES, pygments/lexers/archetype.py, pygments/lexers/factor.py, pygments/lexers/jvm.py, pygments/lexers/matlab.py, pygments/lexers/objective.py, pygments/lexers/templates.py, pygments/lexers/varnish.py
Commit date: 2021-01-11 10:04:50 +0100
Commit message: Fix several exponential/cubic complexity regexes found by Ben Caller/Doyensec

Diff: diff --git a/CHANGES b/CHANGES
index a6d84e17..084d4930 100644
--- a/CHANGES
+++ b/CHANGES
@@ -36,12 +36,15 @@ Version 2.7.4
 - Fix backtracking string regexes in JavaScript/TypeScript, Modula2
   and many other lexers (#1637)
 - Limit recursion with nesting Ruby heredocs (#1638)
 - Fix a few inefficient regexes for guessing lexers
 - Fix the raw token lexer handling of Unicode (#1616)
-- Revert a private API change in the HTML formatter (#1655) -- please note that private APIs remain subject to change!
+- Revert a private API change in the HTML formatter (#1655) -- 
+  please note that private APIs remain subject to change!
 - Add Dracula theme style (#1636)
+- Fix several exponential/cubic-complexity regexes found by
+  Ben Caller/Doyensec (#1675)
 
 Thanks to Google's OSS-Fuzz project for finding many of these bugs.
 
 
 Version 2.7.3
diff --git a/pygments/lexers/archetype.py b/pygments/lexers/archetype.py
index 65046613..26f5ea8c 100644
--- a/pygments/lexers/archetype.py
+++ b/pygments/lexers/archetype.py
@@ -56,11 +56,11 @@ class AtomsLexer(RegexLexer):
             (r'\d{2}:\d{2}:\d{2}(\.\d+)?([+-]\d{4}|Z)?', Literal.Date),
             # ISO 8601 duration
             (r'P((\d*(\.\d+)?[YyMmWwDd]){1,3}(T(\d*(\.\d+)?[HhMmSs]){,3})?|'
              r'T(\d*(\.\d+)?[HhMmSs]){,3})', Literal.Date),
             (r'[+-]?(\d+\.\d*|\.\d+|\d+)[eE][+-]?\d+', Number.Float),
-            (r'[+-]?(\d+)*\.\d+%?', Number.Float),
+            (r'[+-]?\d*\.\d+%?', Number.Float),
             (r'0x[0-9a-fA-F]+', Number.Hex),
             (r'[+-]?\d+%?', Number.Integer),
         ],
         'values': [
             include('ordered_values'),
diff --git a/pygments/lexers/factor.py b/pygments/lexers/factor.py
index be7b30df..9200547f 100644
--- a/pygments/lexers/factor.py
+++ b/pygments/lexers/factor.py
@@ -263,11 +263,11 @@ class FactorLexer(RegexLexer):
 
             # vocab.private
             (r'(?:<PRIVATE|PRIVATE>)\s', Keyword.Namespace),
 
             # strings
-            (r'"""\s+(?:.|\n)*?\s+"""', String),
+            (r'"""\s(?:.|\n)*?\s"""', String),
             (r'"(?:\\\\|\\"|[^"])*"', String),
             (r'\S+"\s+(?:\\\\|\\"|[^"])*"', String),
             (r'CHAR:\s+(?:\\[\\abfnrstv]|[^\\]\S*)\s', String.Char),
 
             # comments
@@ -320,11 +320,11 @@ class FactorLexer(RegexLexer):
             (r'\S+', Name.Variable),
         ],
         'slots': [
             (r'\s+', Text),
             (r';\s', Keyword, '#pop'),
-            (r'(\{\s+)(\S+)(\s+[^}]+\s+\}\s)',
+            (r'(\{\s+)(\S+)(\s[^}]+\s\}\s)',
              bygroups(Text, Name.Variable, Text)),
             (r'\S+', Name.Variable),
         ],
         'vocabs': [
             (r'\s+', Text),
diff --git a/pygments/lexers/jvm.py b/pygments/lexers/jvm.py
index 62dfd45e..9a9397c2 100644
--- a/pygments/lexers/jvm.py
+++ b/pygments/lexers/jvm.py
@@ -979,11 +979,10 @@ class CeylonLexer(RegexLexer):
             (r'(class|interface|object|alias)(\s+)',
              bygroups(Keyword.Declaration, Text), 'class'),
             (r'(import)(\s+)', bygroups(Keyword.Namespace, Text), 'import'),
             (r'"(\\\\|\\[^\\]|[^"\\])*"', String),
             (r"'\\.'|'[^\\]'|'\\\{#[0-9a-fA-F]{4}\}'", String.Char),
-            (r'".*``.*``.*"', String.Interpol),
             (r'(\.)([a-z_]\w*)',
              bygroups(Operator, Name.Attribute)),
             (r'[a-zA-Z_]\w*:', Name.Label),
             (r'[a-zA-Z_]\w*', Name),
             (r'[~^*!%&\[\](){}<>|+=:;,./?-]', Operator),
diff --git a/pygments/lexers/matlab.py b/pygments/lexers/matlab.py
index 4823c6a7..57884862 100644
--- a/pygments/lexers/matlab.py
+++ b/pygments/lexers/matlab.py
@@ -135,11 +135,11 @@ class MatlabLexer(RegexLexer):
             (r'^\s*%\}', Comment.Multiline, '#pop'),
             (r'^.*\n', Comment.Multiline),
             (r'.', Comment.Multiline),
         ],
         'deffunc': [
-            (r'(\s*)(?:(.+)(\s*)(=)(\s*))?(.+)(\()(.*)(\))(\s*)',
+            (r'(\s*)(?:(\S+)(\s*)(=)(\s*))?(.+)(\()(.*)(\))(\s*)',
              bygroups(Whitespace, Text, Whitespace, Punctuation,
                       Whitespace, Name.Function, Punctuation, Text,
                       Punctuation, Whitespace), '#pop'),
             # function with no args
             (r'(\s*)([a-zA-Z_]\w*)', bygroups(Text, Name.Function), '#pop'),
@@ -636,11 +636,11 @@ class OctaveLexer(RegexLexer):
         ],
         'string': [
             (r"[^']*'", String, '#pop'),
         ],
         'deffunc': [
-            (r'(\s*)(?:(.+)(\s*)(=)(\s*))?(.+)(\()(.*)(\))(\s*)',
+            (r'(\s*)(?:(\S+)(\s*)(=)(\s*))?(.+)(\()(.*)(\))(\s*)',
              bygroups(Whitespace, Text, Whitespace, Punctuation,
                       Whitespace, Name.Function, Punctuation, Text,
                       Punctuation, Whitespace), '#pop'),
             # function with no args
             (r'(\s*)([a-zA-Z_]\w*)', bygroups(Text, Name.Function), '#pop'),
@@ -708,11 +708,11 @@ class ScilabLexer(RegexLexer):
         'string': [
             (r"[^']*'", String, '#pop'),
             (r'.', String, '#pop'),
         ],
         'deffunc': [
-            (r'(\s*)(?:(.+)(\s*)(=)(\s*))?(.+)(\()(.*)(\))(\s*)',
+            (r'(\s*)(?:(\S+)(\s*)(=)(\s*))?(.+)(\()(.*)(\))(\s*)',
              bygroups(Whitespace, Text, Whitespace, Punctuation,
                       Whitespace, Name.Function, Punctuation, Text,
                       Punctuation, Whitespace), '#pop'),
             # function with no args
             (r'(\s*)([a-zA-Z_]\w*)', bygroups(Text, Name.Function), '#pop'),
diff --git a/pygments/lexers/objective.py b/pygments/lexers/objective.py
index 34e4062f..38ac9bb0 100644
--- a/pygments/lexers/objective.py
+++ b/pygments/lexers/objective.py
@@ -259,15 +259,15 @@ class LogosLexer(ObjectiveCppLexer):
         'root': [
             (r'(%subclass)(\s+)', bygroups(Keyword, Text),
              'logos_classname'),
             (r'(%hook|%group)(\s+)([a-zA-Z$_][\w$]+)',
              bygroups(Keyword, Text, Name.Class)),
-            (r'(%config)(\s*\(\s*)(\w+)(\s*=\s*)(.*?)(\s*\)\s*)',
+            (r'(%config)(\s*\(\s*)(\w+)(\s*=)(.*?)(\)\s*)',
              bygroups(Keyword, Text, Name.Variable, Text, String, Text)),
             (r'(%ctor)(\s*)(\{)', bygroups(Keyword, Text, Punctuation),
              'function'),
-            (r'(%new)(\s*)(\()(\s*.*?\s*)(\))',
+            (r'(%new)(\s*)(\()(.*?)(\))',
              bygroups(Keyword, Text, Keyword, String, Keyword)),
             (r'(\s*)(%end)(\s*)', bygroups(Text, Keyword, Text)),
             inherit,
         ],
     }
diff --git a/pygments/lexers/templates.py b/pygments/lexers/templates.py
index 33c06c4c..5c3346b4 100644
--- a/pygments/lexers/templates.py
+++ b/pygments/lexers/templates.py
@@ -1403,11 +1403,11 @@ class EvoqueLexer(RegexLexer):
                       String, Punctuation)),
             # directives: evoque, overlay
             # see doc for handling first name arg: /directives/evoque/
             # + minor inconsistency: the "name" in e.g. $overlay{name=site_base}
             # should be using(PythonLexer), not passed out as String
-            (r'(\$)(evoque|overlay)(\{(%)?)(\s*[#\w\-"\'.]+[^=,%}]+?)?'
+            (r'(\$)(evoque|overlay)(\{(%)?)(\s*[#\w\-"\'.]+)?'
              r'(.*?)((?(4)%)\})',
              bygroups(Punctuation, Name.Builtin, Punctuation, None,
                       String, using(PythonLexer), Punctuation)),
             # directives: if, for, prefer, test
             (r'(\$)(\w+)(\{(%)?)(.*?)((?(4)%)\})',
diff --git a/pygments/lexers/varnish.py b/pygments/lexers/varnish.py
index 23653f7a..9d358bd7 100644
--- a/pygments/lexers/varnish.py
+++ b/pygments/lexers/varnish.py
@@ -59,11 +59,11 @@ class VCLLexer(RegexLexer):
             include('whitespace'),
             (r'(\.probe)(\s*=\s*)(\w+)(;)',
              bygroups(Name.Attribute, Operator, Name.Variable.Global, Punctuation)),
             (r'(\.probe)(\s*=\s*)(\{)',
              bygroups(Name.Attribute, Operator, Punctuation), 'probe'),
-            (r'(\.\w+\b)(\s*=\s*)([^;]*)(\s*;)',
+            (r'(\.\w+\b)(\s*=\s*)([^;\s]*)(\s*;)',
              bygroups(Name.Attribute, Operator, using(this), Punctuation)),
             (r'\{', Punctuation, '#push'),
             (r'\}', Punctuation, '#pop'),
         ],
         'statements': [

Vulnerable code: - Revert a private API change in the HTML formatter (#1655) -- please note that private APIs remain subject to change!
            (r'[+-]?(\d+)*\.\d+%?', Number.Float),
            (r'"""\s+(?:.|\n)*?\s+"""', String),
            (r'(\{\s+)(\S+)(\s+[^}]+\s+\}\s)',
            (r'".*``.*``.*"', String.Interpol),
            (r'(\s*)(?:(.+)(\s*)(=)(\s*))?(.+)(\()(.*)(\))(\s*)',
            (r'(\s*)(?:(.+)(\s*)(=)(\s*))?(.+)(\()(.*)(\))(\s*)',
            (r'(\s*)(?:(.+)(\s*)(=)(\s*))?(.+)(\()(.*)(\))(\s*)',
            (r'(%config)(\s*\(\s*)(\w+)(\s*=\s*)(.*?)(\s*\)\s*)',
            (r'(%new)(\s*)(\()(\s*.*?\s*)(\))',
            (r'(\$)(evoque|overlay)(\{(%)?)(\s*[#\w\-"\'.]+[^=,%}]+?)?'
            (r'(\.\w+\b)(\s*=\s*)([^;]*)(\s*;)',
Fixed code: - Revert a private API change in the HTML formatter (#1655) -- 
  please note that private APIs remain subject to change!
- Fix several exponential/cubic-complexity regexes found by
  Ben Caller/Doyensec (#1675)
            (r'[+-]?\d*\.\d+%?', Number.Float),
            (r'"""\s(?:.|\n)*?\s"""', String),
            (r'(\{\s+)(\S+)(\s[^}]+\s\}\s)',
            (r'(\s*)(?:(\S+)(\s*)(=)(\s*))?(.+)(\()(.*)(\))(\s*)',
            (r'(\s*)(?:(\S+)(\s*)(=)(\s*))?(.+)(\()(.*)(\))(\s*)',
            (r'(\s*)(?:(\S+)(\s*)(=)(\s*))?(.+)(\()(.*)(\))(\s*)',
            (r'(%config)(\s*\(\s*)(\w+)(\s*=)(.*?)(\)\s*)',
            (r'(%new)(\s*)(\()(.*?)(\))',
            (r'(\$)(evoque|overlay)(\{(%)?)(\s*[#\w\-"\'.]+)?'
            (r'(\.\w+\b)(\s*=\s*)([^;\s]*)(\s*;)',