Start learning free

LESSON 163 OF 173 · BACKEND: BACKEND WITH AI

The AI Shows Too Much

Practice

An AI assistant wrote productLabel, 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 catch sends back:

@GetMapping("/products/{productId}/label")
Object productLabel(@PathVariable int productId, HttpServletRequest request) {
    String query = "SELECT name, category FROM products WHERE id = ?";
    try {
        Map<String, Object> row = db.queryForList(query, productId).get(0);
        String label = row.get("name").toString() + " (" + row.get("category").toString() + ")";
        return Map.of("label", label);
    } catch (Exception error) {
        StringWriter trace = new StringWriter();
        error.printStackTrace(new PrintWriter(trace));
        return ResponseEntity.status(500).body(Map.of("error", error.toString(), "sql", query, "trace", trace.toString()));
    }
}

That answer goes to whoever made the request. It holds the error's text, the SQL query and the stack trace: the classes, line numbers and methods 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:

    } catch (Exception error) {
        log.error("order {} failed: {}", orderId, error.getMessage());
        return ResponseEntity.status(500).body(Map.of("error", "Please try again later"));
    }

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: Cannot invoke "Object.toString()" because the return value of "java.util.Map.get(Object)" is null.

Your task: rewrite productLabel 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 productLabel with a try that builds the label and a catch that logs the details and answers a safe message.


Hint
The catch line also closes the try: } catch (Exception error) {. The 404 check sits inside the try, right below the rows line, so rows.get(0) never runs on an empty list. Pass log.error the text "{} {} failed: {}", then the three values in order.


STEP 1 OF 10
    Ctrl + Enter to check