python.flask.security.injection.raw-html-concat.raw-html-format

profile photo of semgrepsemgrep
Author
unknown
Download Count*

Detected user input flowing into a manually constructed HTML string. You may be accidentally bypassing secure methods of rendering HTML by manually constructing HTML and this could create a cross-site scripting vulnerability, which could let attackers steal sensitive user data. To be sure this is safe, check that the HTML is rendered safely. Otherwise, use templates (flask.render_template) which will safely render HTML instead.

Run Locally

Run in CI

Defintion

rules:
  - id: raw-html-format
    languages:
      - python
    severity: WARNING
    message: Detected user input flowing into a manually constructed HTML string.
      You may be accidentally bypassing secure methods of rendering HTML by
      manually constructing HTML and this could create a cross-site scripting
      vulnerability, which could let attackers steal sensitive user data. To be
      sure this is safe, check that the HTML is rendered safely. Otherwise, use
      templates (`flask.render_template`) which will safely render HTML instead.
    metadata:
      cwe:
        - "CWE-79: Improper Neutralization of Input During Web Page Generation
          ('Cross-site Scripting')"
      owasp:
        - A07:2017 - Cross-Site Scripting (XSS)
        - A03:2021 - Injection
      category: security
      technology:
        - flask
      references:
        - https://flask.palletsprojects.com/en/2.0.x/security/#cross-site-scripting-xss
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      cwe2022-top25: true
      cwe2021-top25: true
      subcategory:
        - vuln
      likelihood: MEDIUM
      impact: MEDIUM
      confidence: MEDIUM
      vulnerability_class:
        - Cross-Site-Scripting (XSS)
    mode: taint
    pattern-sanitizers:
      - pattern: jinja2.escape(...)
      - pattern: flask.escape(...)
      - pattern: flask.render_template("~=/.*\.html", ...)
    pattern-sources:
      - patterns:
          - pattern-either:
              - pattern: flask.request.$ANYTHING
              - patterns:
                  - pattern-inside: |
                      @$APP.route(...)
                      def $FUNC(..., $ROUTEVAR, ...):
                        ...
                  - pattern: $ROUTEVAR
    pattern-sinks:
      - patterns:
          - pattern-either:
              - patterns:
                  - pattern-either:
                      - pattern: '"$HTMLSTR" % ...'
                      - pattern: '"$HTMLSTR".format(...)'
                      - pattern: '"$HTMLSTR" + ...'
                      - pattern: f"$HTMLSTR{...}..."
              - patterns:
                  - pattern-inside: |
                      $HTML = "$HTMLSTR"
                      ...
                  - pattern-either:
                      - pattern: $HTML % ...
                      - pattern: $HTML.format(...)
                      - pattern: $HTML + ...
          - metavariable-pattern:
              metavariable: $HTMLSTR
              language: generic
              pattern: <$TAG ...

Examples

raw-html-concat.py

import os
import flask
import hashlib

app = flask.Flask(__name__)

@app.route("/route_param/<route_param>")
def route_param(route_param):
    print("blah")
    # ruleid:raw-html-format
    return "<a href='%s'>Click me!</a>" % route_param

@app.route("/route_param_ok/<route_param>")
def route_param_ok(route_param):
    print("blah")
    # ok: raw-html-format
    return "<a href='https://example.com'>Click me!</a>"

@app.route("/route_param_format/<route_param>")
def route_param_format(route_param):
    print("blah")
    # ruleid:raw-html-format
    return "<a href='{}'>Click me!</a>".format(route_param)

@app.route("/route_param_percent_format/<route_param>")
def route_param_percent_format(route_param):
    print("blah")
    # ruleid:raw-html-format
    return "<a href='%s'>Click me!</a>" % route_param

@app.route("/get_param_inline", methods=["GET"])
def get_param_inline():
    # ruleid:raw-html-format
    return "<a href='%s'>Click me!</a>" % flask.request.args.get("param")

@app.route("/get_param_inline_concat", methods=["GET"])
def get_param_inline_concat():
    # ruleid:raw-html-format
    return "<a href='" + flask.request.args.get("param") + "'>Click me!</a>"

@app.route("/get_param_concat", methods=["GET"])
def get_param_concat():
    param = flask.request.args.get("param")
    # ruleid:raw-html-format
    return "<a href='" + param + "'>Click me!</a>"

@app.route("/get_param_format", methods=["GET"])
def get_param_format():
    param = flask.request.args.get("param")
    # ruleid:raw-html-format
    return "<a href='{}'>Click me!</a>".format(param)

@app.route("/get_param_percent_format", methods=["GET"])
def get_param_percent_format():
    param = flask.request.args.get("param")
    # ruleid:raw-html-format
    return "<a href='%s'>Click me!</a>" % (param,)

@app.route("/post_param_branch", methods=["POST"])
def post_param_branch():
    param = flask.request.form['param']
    if True:
        # ruleid:raw-html-format
        return "<a href='%s'>Click me!</a>" % param

# Real world example
@app.route('/models/<model>')
def load_model(model):
    # ruleid:raw-html-format
    htmlpage = '''
    <body style='margin : 0px; overflow: hidden;'>
        <scene-tag embedded arjs>
            <marker-tag id="memarker" type="pattern" url="../static/patterns/pattern-kanji_qr.patt" vidhandler>
                <entity model="obj: url(../static/models/{}.obj); mtl: url(../static/models/{}.mtl)"> </entity>
            </marker-tag>
        </scene-tag>
    </body>
    '''.format(model,model)
    return htmlpage

@app.route("/ok")
def ok():
    # ok: raw-html-format
    return "<a href='https://example.com'>Click me!</a>"