Start learning free

LESSON 163 OF 188 · BACKEND: BACKEND WITH AI

The AI Shows Too Much

Practice

An AI assistant wrote product_label, the text for the shop's shelf: GET /products/1/label answers {"label": "Mug (kitchen)"}. It put its code in a try, which is a good idea: when something fails, the route still answers. But look at what its except sends back:

@app.get("/products/<int:product_id>/label")
def product_label(product_id):
    query = "SELECT name, category FROM products WHERE id = ?"
    try:
        row = db.execute(query, (product_id,)).fetchone()
        label = row["name"] + " (" + row["category"] + ")"
        return jsonify({"label": label})
    except Exception as error:
        return jsonify({"error": str(error), "sql": query, "trace": traceback.format_exc()}), 500

That answer goes to whoever made the request. It holds the error's text, the SQL query and the stack trace: the files, line numbers and lines of code that led to the crash. A customer can't do anything with it. An attacker can: table names, column names and your code are a free map for finding the next hole.

The rule: the details go to the log, which only you read, and the visitor gets a short safe message. Here it is on another route:

    except Exception as error:
        logging.error("order %s failed: %s", order_id, error)
        return jsonify({"error": "Please try again later"}), 500

A missing product is not a server error, so it answers 404, not 500. Your rewrite must give these answers (product 8 is a Gift card with no category, a bug nobody had found yet):

GET /products/1/label   →  200  {"label": "Mug (kitchen)"}
GET /products/6/label   →  200  {"label": "Teapot (kitchen)"}
GET /products/8/label   →  500  {"error": "Something went wrong on our side"}
GET /products/99/label  →  404  {"error": "Product not found"}

The check then reads your log through /_test/log, a route it adds only for the test, and expects one line: ERROR GET /products/8/label failed: can only concatenate str (not "NoneType") to str.

Your task: rewrite product_label so any error answers only {"error": "Something went wrong on our side"} with 500 and logs the method, the path and the error, while a missing product answers 404.


Assignment
Follow the Coding Tutorial to rewrite product_label with a try that builds the label and an except that logs the details and answers a safe message.


Hint
Indentation matters: the except sits at the depth of try:, and the 404 check sits inside the try, right below the row line. Pass logging.error the text "%s %s failed: %s", then the three values in order.


STEP 1 OF 9
    Ctrl + Enter to check