Repository: mozilla/bleach

Issue:
Left angle bracket '<'  with any characters (tag or not) and no closing right angle bracket prevents remaining text from being returned
Given some text that contains a left angle bracket and no closing right angle bracket, the `bleach.clean `method returns nothing after the occurrence of the left angle bracket.

Consider the following example strings
`example_str_1 = 'random prefix text <anything any amount of suffix text'`
`example_str_2 = '<e any amount of text here is gone'`
`example_str_3 = '<it works when there is a closing>'`

Expected output
example_str_1  `random prefix text &lt;anything any amount of suffix text`
example_str_2  `&lt;e any amount of text here is gone`
example_str_3  `&lt;it works when there is a closing right bracket&gt;`

Actual output
example_str_1 `random prefix text `                             
example_str_2  _empty string_                                  
example_str_3  `&lt;it works when there is a closing right bracket&gt;` 

Python version 3.6.8
bleach 3.1.5

Discussion:

Patch:
diff --git a/bleach/html5lib_shim.py b/bleach/html5lib_shim.py
index 6fc9048..d121953 100644
--- a/bleach/html5lib_shim.py
+++ b/bleach/html5lib_shim.py
@@ -385,7 +385,17 @@ class BleachHTMLTokenizer(HTMLTokenizer):
             yield token
 
         if last_error_token:
-            yield last_error_token
+            if last_error_token["data"] == "eof-in-tag-name":
+                # Handle the case where the text being parsed ends with <
+                # followed by a series of characters. It's treated as a tag
+                # name that abruptly ends, but we should treat that like
+                # character data
+                yield {
+                    "type": TAG_TOKEN_TYPE_CHARACTERS,
+                    "data": "<" + self.currentToken["name"],
+                }
+            else:
+                yield last_error_token
 
     def consumeEntity(self, allowedChar=None, fromAttribute=False):
         # If this tokenizer is set to consume entities, then we can let the


Test patch:
diff --git a/tests/test_clean.py b/tests/test_clean.py
index b9c262a..ab11253 100644
--- a/tests/test_clean.py
+++ b/tests/test_clean.py
@@ -156,6 +156,22 @@ def test_bare_entities_get_escaped_correctly(text, expected):
     assert clean(text) == expected
 
 
+@pytest.mark.parametrize(
+    "text, expected",
+    [
+        ("x<y", "x&lt;y"),
+        ("<y", "&lt;y"),
+        ("x < y", "x &lt; y"),
+        ("<y>", "&lt;y&gt;"),
+    ],
+)
+def test_lessthan_escaping(text, expected):
+    # Tests whether < gets escaped correctly in a series of edge cases where
+    # the html5lib tokenizer hits an error because it's not the beginning of a
+    # tag.
+    assert clean(text) == expected
+
+
 @pytest.mark.parametrize(
     "text, expected",
     [