werkzeug icon indicating copy to clipboard operation
werkzeug copied to clipboard

debugger failed to open in browser

Open replabrobin opened this issue 7 months ago • 3 comments

Under specific conditions the code at tbtools line 428 raises an error during traceback rendering. This causes the browser to show a fairly meaning less message

`Internal Server Error

The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.` This was with Flask==3.1.1.

In my particular case the original error as shown in the terminal was File "<string>", line 25 lxml.etree.XMLSyntaxError: Opening and ending tag mismatch: p line 24 and story, line 25, column 10

follow the tutorial at https://docs.reportlab.com/demos/flask_reportlab/flask_reportlab/ but in the preppy template line 24 replace <p>{{ say }}>/p> with <p>{{ say }}/<p> this causes a parse error and the traceback render fails because line_idx is a bad index.

The terminal traceback continues `During handling of the above exception, another exception occurred:

Traceback (most recent call last): File "/home/robin/tmp/rlflask/.py313/lib/python3.13/site-packages/werkzeug/serving.py", line 370, in run_wsgi execute(self.server.app) ........... File "/home/robin/tmp/rlflask/.py313/lib/python3.13/site-packages/werkzeug/debug/tbtools.py", line 428, in render_html File "/home/robin/tmp/rlflask/.py313/lib/python3.13/site-packages/werkzeug/debug/tbtools.py", line 428, in render_html render_line(lines[line_idx], "current") ~~~~~^^^^^^^^^^ IndexError: list index out of range`

The traceback should have been rendered as expected

Environment: x86_64 linux 6.14.6

  • Python version: 3.13.3
  • Werkzeug version: 3.1.3

If I change the failing line in tbtools.py with if 0<=line_idx<len(lines): render_line(lines[line_idx], "current") then I do get a rendered traceback. The console cannot be entered at the lxml frame, but that is probably expected.

replabrobin avatar May 20 '25 11:05 replabrobin

Please reformat this to show the entire traceback as a code block. Also, please include a minimal reproducible example with steps to reproduce the issue.

davidism avatar May 20 '25 12:05 davidism

The minimal example

from flask import Flask
app = Flask(__name__)

def rmlfail():
	from rlextra.rml2pdf.rml2pdf import go, _invalid_rml_template
	import io
	buf = io.BytesIO()
	rml = _invalid_rml_template % ('','<para>A/<para>')
	go(rml,buf)
	return 'SUCCEEDED'

@app.route("/")
def hello_world():
	return "<p>Hello, World!</p>\n<pre>%s</pre>" % rmlfail()

The terminal output

(.py313) robin@minikat:~/devel/tflask
$ flask --app app run --debug
 * Serving Flask app 'app'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 125-191-719
127.0.0.1 - - [20/May/2025 16:29:03] "GET / HTTP/1.1" 500 -
Error on request:
Traceback (most recent call last):
  File "/home/robin/devel/tflask/.py313/lib/python3.13/site-packages/werkzeug/debug/__init__.py", line 343, in debug_application
    app_iter = self.app(environ, start_response)
  File "/home/robin/devel/tflask/.py313/lib/python3.13/site-packages/flask/app.py", line 1536, in __call__
    return self.wsgi_app(environ, start_response)
           ~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/robin/devel/tflask/.py313/lib/python3.13/site-packages/flask/app.py", line 1514, in wsgi_app
    response = self.handle_exception(e)
  File "/home/robin/devel/tflask/.py313/lib/python3.13/site-packages/flask/app.py", line 1511, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/robin/devel/tflask/.py313/lib/python3.13/site-packages/flask/app.py", line 919, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/robin/devel/tflask/.py313/lib/python3.13/site-packages/flask/app.py", line 917, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/robin/devel/tflask/.py313/lib/python3.13/site-packages/flask/app.py", line 902, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)  # type: ignore[no-any-return]
           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
  File "/home/robin/devel/tflask/app.py", line 15, in hello_world
    return "<p>Hello, World!</p>\n<pre>%s</pre>" % rmlfail()
                                                                            ~~~~^^
  File "/home/robin/devel/tflask/app.py", line 10, in rmlfail
    go(rml,buf)
    ~~^^^^^^^^^
  File "rlextra/rml2pdf/rml2pdf.py", line 7527, in go
  File "/home/robin/devel/tflask/.py313/lib/python3.13/site-packages/rlextra/radxml/xml2tt.py", line 44, in __call__
    return LxmlTT(**kwds).ttFromSrc(src)
           ~~~~~~~~~~~~~~~~~~~~~~~~^^^^^
  File "/home/robin/devel/tflask/.py313/lib/python3.13/site-packages/rlextra/radxml/lxmltt.py", line 63, in ttFromSrc
    return self.ttFromFile((io.BytesIO if isinstance(s,bytes) else io.StringIO)(s))
           ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/robin/devel/tflask/.py313/lib/python3.13/site-packages/rlextra/radxml/lxmltt.py", line 46, in ttFromFile
    tree = ET.parse(f,self.parser)
  File "src/lxml/etree.pyx", line 3590, in lxml.etree.parse
  File "src/lxml/parser.pxi", line 1975, in lxml.etree._parseDocument
  File "src/lxml/parser.pxi", line 1995, in lxml.etree._parseMemoryDocument
  File "src/lxml/parser.pxi", line 1875, in lxml.etree._parseDoc
  File "src/lxml/parser.pxi", line 1105, in lxml.etree._BaseParser._parseUnicodeDoc
  File "src/lxml/parser.pxi", line 633, in lxml.etree._ParserContext._handleParseResultDoc
  File "src/lxml/parser.pxi", line 743, in lxml.etree._handleParseResult
  File "src/lxml/parser.pxi", line 672, in lxml.etree._raiseParseError
  File "<string>", line 15
lxml.etree.XMLSyntaxError: Opening and ending tag mismatch: para line 14 and story, line 15, column 9

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/robin/devel/tflask/.py313/lib/python3.13/site-packages/werkzeug/serving.py", line 370, in run_wsgi
    execute(self.server.app)
    ~~~~~~~^^^^^^^^^^^^^^^^^
  File "/home/robin/devel/tflask/.py313/lib/python3.13/site-packages/werkzeug/serving.py", line 333, in execute
    for data in application_iter:
                ^^^^^^^^^^^^^^^^
  File "/home/robin/devel/tflask/.py313/lib/python3.13/site-packages/werkzeug/debug/__init__.py", line 358, in debug_application
    html = tb.render_debugger_html(
    
  File "/home/robin/devel/tflask/.py313/lib/python3.13/site-packages/werkzeug/debug/tbtools.py", line 346, in render_debugger_html
    "summary": self.render_traceback_html(include_title=False),
               ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
  File "/home/robin/devel/tflask/.py313/lib/python3.13/site-packages/werkzeug/debug/tbtools.py", line 295, in render_traceback_html
    row_parts.append(f"<li{info}>{frame.render_html(mark_library)}")
                                  ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
  File "/home/robin/devel/tflask/.py313/lib/python3.13/site-packages/werkzeug/debug/tbtools.py", line 428, in render_html
    render_line(lines[line_idx], "current")
                ~~~~~^^^^^^^^^^
IndexError: list index out of range

Steps to replicate

  1. mkdir tflask && cd tflask && create app.py
  2. python313 -mvenv .py313 && source .py313/bin/activate
  3. wget https://www.reportlab.com/ftp/rlCgi-2.6.1-py3-none-any.whl wget https://www.reportlab.com/ftp/rlextra-4.4.1-py3-none-any.whl
  4. pip install flask rlCgi-2.6.1-py3-none-any.whl rlextra-4.4.1-py3-none-any.whl
  5. flask --app app run --debug
  6. visit http://127.0.0.1:5000

replabrobin avatar May 20 '25 15:05 replabrobin

Adding a breakpoint at the point of failure reveals the probable cause of this issue

self.filename=='rml2pdf.py', self.lineno==7527, len(lines)==17

the module rlextra.rml2pdf.rml2pdf is obfuscated and is just a placeholder that imports and overwrites itself with a compiled version (in this case rml2pdf_313c.py). This is purely for commercial reasons ie to avoid giving away the source code.

The bug/problem here is that there appears to be an assumption that source is available. That is not always the case.

I'm not sure exactly how one can distinguish the case when a malevolent programmer hides / obfuscates the source code because the boss doesn't want to release it. If the bug had occurred at line 10 of the obfuscated code then presumably tbtools would have been happy to report the bug as happening at line 10 of the trampoline code.

If I put in the fix I suggested ie if 0<=line_idx<len(lines): render_line(lines[line_idx], "current") then the traceback is happy to be rendered and the failing line is shown as File "rlextra/rml2pdf/rml2pdf.py", line 7527, in go.

replabrobin avatar May 21 '25 09:05 replabrobin