python.flask.caching.query-string.flask-cache-query-string

profile photo of semgrepsemgrep
Author
1,200
Download Count*

Flask-caching doesn't cache query strings by default. You have to use query_string=True. Also you shouldn't cache verbs that can mutate state.

Run Locally

Run in CI

Defintion

rules:
  - id: flask-cache-query-string
    patterns:
      - pattern-either:
          - pattern: |
              @app.route("...")
              @cache.cached(...)
              def $HANDLER(...):
                ...
                request.args.get(...)
          - pattern: |
              @app.route("...", methods=[..., "POST", ...])
              @cache.cached(...)
              def $HANDLER(...):
                ...
          - pattern: |
              @app.route("...", methods=[..., "PUT", ...])
              @cache.cached(...)
              def $HANDLER(...):
                ...
          - pattern: |
              @app.route("...", methods=[..., "DELETE", ...])
              @cache.cached(...)
              def $HANDLER(...):
                ...
          - pattern: |
              @app.route("...", methods=[..., "PATCH", ...])
              @cache.cached(...)
              def $HANDLER(...):
                ...
      - pattern-not: |
          @app.route("...")
          @cache.cached(..., query_string=True)
          def $HANDLER(...):
            ...
            request.args.get(...)
    message: Flask-caching doesn't cache query strings by default. You have to use
      `query_string=True`. Also you shouldn't cache verbs that can mutate state.
    severity: WARNING
    languages:
      - python
    metadata:
      category: caching
      technology:
        - flask
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]

Examples

query-string.py

from flask_caching import Cache
from flask import Flask

app = Flask(__name__)
cache = Cache(config={"CACHE_TYPE": "simple"})

# ruleid:flask-cache-query-string
@app.route("/api/pack/<pack_id>")
@cache.cached(timeout=None)  # cache until restart or manual invalidation
def get_pack(pack_id: str) -> ApiResponse:
    expand_qs = request.args.get("expand_rules")
    expand_rules = expand_qs != None
    pack = registry_controller.get_pack(pack_id, expand_rules=expand_rules)
    if pack is not None:
        return jsonify(pack)
    else:
        raise NotFound

# ok:flask-cache-query-string
@app.route("/api/pack/<pack_id>")
@cache.cached(timeout=10, query_string=True)
@login_exempt
def get_pack_but_caches_qs(pack_id: str) -> ApiResponse:
    expand_qs = request.args.get("expand_rules")
    expand_rules = expand_qs != None
    pack = registry_controller.get_pack(pack_id, expand_rules=expand_rules)
    if pack is not None:
        return jsonify(pack)
    else:
        raise NotFound

# ok:flask-cache-query-string
@app.route("/api/pack/<pack_id>")
@cache.cached(timeout=None)  # cache until restart or manual invalidation
@login_exempt
def get_pack_no_query_string(pack_id: str) -> ApiResponse:
    pack = registry_controller.get_pack(pack_id)
    if pack is not None:
        return jsonify(pack)
    else:
        raise NotFound

# ruleid:flask-cache-query-string
@app.route("/api/pack/<pack_id>", methods=["POST"])
@cache.cached(timeout=None)  # cache until restart or manual invalidation
@login_exempt
def get_pack_modify_verb(pack_id: str) -> ApiResponse:
    pack = registry_controller.get_pack(pack_id)
    if pack is not None:
        return jsonify(pack)
    else:
        raise NotFound

# ruleid:flask-cache-query-string
@app.route("/api/pack/<pack_id>", methods=["POST", "PUT"])
@cache.cached(timeout=None)  # cache until restart or manual invalidation
@login_exempt
def get_pack_multiple_modify_verb(pack_id: str) -> ApiResponse:
    pack = registry_controller.get_pack(pack_id)
    if pack is not None:
        return jsonify(pack)
    else:
        raise NotFound

# ok:flask-cache-query-string
@app.route("/api/pack/<pack_id>", methods=["GET"])
@cache.cached(timeout=None)  # cache until restart or manual invalidation
@login_exempt
def get_pack_multiple_modify_verb(pack_id: str) -> ApiResponse:
    pack = registry_controller.get_pack(pack_id)
    if pack is not None:
        return jsonify(pack)
    else:
        raise NotFound