Flask check: send_file() with a file handle

A Flask check to detect use of send_file() without appropriate args
by Grayson Hardaway, Engineering @ r2c

When we started writing checks for Flask, I wasn’t sure why this one was on our list. After some Googling, I found this discussion on GitHub that explained why: as of version 0.12, Flask no longer infers the mimetype of file-like objects and throws a ValueError.

This check detects the use of open(filename, 'r') passed to flask.send_file() without the appropriate keyword args — either mimetype or attachment_filename. The keyword arguments prevent a ValueError from being thrown under these conditions.

For example, the following snippet,

import flask
app = flask.Flask(__name__)
@app.route("/send_file")
def send_file():
    f = open("test.db", 'r')
    rv = flask.send_file(f)

Has the following stack trace:

[2019-10-30 10:25:14,695] ERROR in app: Exception on /send_file [GET]
    [cut for brevity...]
    File "example.py", line 5, in send_file
        rv = flask.send_file(f)
    File "/usr/local/lib/python3.7/site-packages/flask/helpers.py", line 593, in send_file
        "Unable to infer MIME-type because no filename is available. "ValueError: Unable to infer MIME-type because no filename is available. Please set either attachment_filename, pass a filepath to filename_or_fp or set your own MIME-type via mimetype.

For the curious, the ValueError is raised here in the source: https://github.com/pallets/flask/blob/980168d08498c00a14ab0f687ffac8cc50edb326/src/flask/helpers.py#L600.

Examples

This check detects the following cases:

# flask.send_file case
flask.send_file(open("file.txt", 'r'))

# With keyword args
flask.send_file(open("file.txt", 'r'), conditional=False)

# from flask import send_file case
flask.send_file(open("file.txt", 'r'))

# Variable resolution
fin = open("file.txt", 'r')
flask.send_file(fin)

This check considers the following cases acceptable:

# String argument for arg0
flask.send_file("/tmp/file.txt")

# Has a mimetype
flask.send_file(open("file.txt", 'r'), mimetype="text/plain")

# Has a attachment_filename
fin = open("file.txt", 'r')
flask.send_file(fin, as_attachment=True, attachment_filename="file.txt")

This check is available in Bento by default as of version 0.6. Bento is a free program analysis tool focused on finding bugs that matter to you.

References