nsfmc: Hello, i'm not sure what the protocol is for this. 

I ran into this bug from a strangely formed embed code i got from gyfcat, but i suspect others could run into it too.

the `test_safe_links` test is currently failing in master, but the test that this adds passes.

Let me know if you have any questions!
lepture: @nsfmc a test case failed.
nsfmc: Hi @lepture, I believe that test case is failing in master as well. If there is a branch I can fork off of that is building successfully I'm happy to rebase my pull request off that one. I'm afraid I don't understand well enough the changes needed to fix the link sanitization issues :(
diff --git a/mistune.py b/mistune.py
index b341cf2..67b0880 100644
--- a/mistune.py
+++ b/mistune.py
@@ -74,7 +74,7 @@ def escape(text, quote=False, smart_amp=True):
 
 def escape_link(url, **kwargs):
     """Remove dangerous URL schemes like javascript: and escape afterwards."""
-    lower_url = url.lower()
+    lower_url = url.lower().strip('\x00\x1a \n\r\t')
     for scheme in _scheme_blacklist:
         if lower_url.startswith(scheme):
             return ''
@@ -157,7 +157,7 @@ class BlockGrammar(object):
     block_html = re.compile(
         r'^ *(?:%s|%s|%s) *(?:\n{2,}|\s*$)' % (
             r'<!--[\s\S]*?-->',
-            r'<(%s)((?:%s)*?)>([\s\S]+?)<\/\1>' % (_block_tag, _valid_attr),
+            r'<(%s)((?:%s)*?)\s*>([\s\S]+?)<\/\1>' % (_block_tag, _valid_attr),
             r'<%s(?:%s)*?\s*\/?>' % (_block_tag, _valid_attr),
         )
     )
@@ -447,7 +447,7 @@ class InlineGrammar(object):
     inline_html = re.compile(
         r'^(?:%s|%s|%s)' % (
             r'<!--[\s\S]*?-->',
-            r'<(\w+%s)((?:%s)*?)>([\s\S]*?)<\/\1>' % (_valid_end, _valid_attr),
+            r'<(\w+%s)((?:%s)*?)\s*>([\s\S]*?)<\/\1>' % (_valid_end, _valid_attr),
             r'<\w+%s(?:%s)*?\s*\/?>' % (_valid_end, _valid_attr),
         )
     )

parsing html can fail to close tag correctly
noticed while debugging an issue in https://github.com/lektor/lektor/issues/241#issuecomment-231819694 i noticed that raw html handling can be broken if there is a space before the closing `>` of the opening tag. i.e.

```
In [38]: mistune.markdown('<iframe src="http://gfycat.com" ></iframe>', escape=False)
Out[38]: '<p><iframe src="http://gfycat.com" >&lt;/iframe&gt;</p>\n'
```

but

```
In [39]: mistune.markdown('<iframe src="http://gfycat.com"></iframe>', escape=False)
Out[39]: '<p><iframe src="http://gfycat.com"></iframe></p>\n'
```

in the case of the iframe, this causes the opening iframe to gobble up the rest of the page content. this seems to hold for any tag, though, e.g.
```
In [40]: mistune.markdown('<a href="http://gfycat.com" ></a>', escape=False)
Out[40]: '<p><a href="http://gfycat.com" >&lt;/a&gt;</p>\n'
```

diff --git a/tests/test_extra.py b/tests/test_extra.py
index 7318444..07c1ca3 100644
--- a/tests/test_extra.py
+++ b/tests/test_extra.py
@@ -77,6 +77,12 @@ def test_parse_inline_html():
     assert 'href' not in ret
 
 
+def test_block_html():
+    ret = mistune.markdown(
+        '<div ></div>', escape=False
+    )
+    assert '<div ></div>' in ret
+
 def test_parse_block_html():
     ret = mistune.markdown(
         '<div>**foo**</div>', parse_block_html=True, escape=False

pip install -e .[dev]
parse_log_pytest
pytest
nose pytest
apt-get update apt-get install -y gcc
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
exceptiongroup @ file:///croot/exceptiongroup_1706031385326/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
-e git+https://github.com/lepture/mistune.git@715046fe4b5f3642b24edc7fdd62ba8915228498#egg=mistune
nose==1.3.7
packaging @ file:///croot/packaging_1734472117206/work
pluggy @ file:///croot/pluggy_1733169602837/work
pytest @ file:///croot/pytest_1738938843180/work
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work

name: mistune
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - exceptiongroup=1.2.0=py39h06a4308_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - packaging=24.2=py39h06a4308_0
  - pip=25.0=py39h06a4308_0
  - pluggy=1.5.0=py39h06a4308_0
  - pytest=8.3.4=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py39h06a4308_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - nose==1.3.7
prefix: /opt/conda/envs/mistune

tests/test_extra.py::test_safe_links tests/test_extra.py::test_block_html
tests/test_extra.py::test_escape tests/test_extra.py::test_linebreak tests/test_extra.py::test_skip_style tests/test_extra.py::test_use_xhtml tests/test_extra.py::test_parse_inline_html tests/test_extra.py::test_parse_block_html tests/test_extra.py::test_trigger_more_cases tests/test_extra.py::test_not_escape_block_tags tests/test_extra.py::test_not_escape_inline_tags tests/test_extra.py::test_hard_wrap_renderer
mistune.py
mistune.py