A trust boundary is a point where one component accepts facts from another component with different authority. A request can propose a ticket number; it cannot establish that the caller owns that ticket. A database can return a stored comment; storage does not establish that the comment is safe to interpret as HTML. Begin by naming the source, the sink and the decision at each crossing.
This course uses small local Python functions and real in-memory SQLite queries. It runs no browser, login provider or public service. Failing tests expose specific violated contracts. Passing those tests gives evidence about these fixtures, not a guarantee that an application is secure.
Validation asks whether a value belongs to the application's accepted domain. The lab's ticket identifier is an ASCII decimal string from "1" to "9999", without leading zeroes. Its comment is a Python string of 1–80 Unicode code points without NUL. These are product choices, not universal identifier or name rules. Reject invalid input with ValueError; silently repairing " 7" or accepting True changes the contract.
Encoding asks how accepted data should be represented at a particular sink. A comment such as Mina & Jo <3 is valid text. Removing punctuation would damage it. In the fixed HTML text position between <p> and </p>, html.escape preserves its meaning while representing angle brackets and ampersands as character references.
from html import escape
comment = 'Mina & Jo <3'
print('<p>' + escape(comment, quote=True) + '</p>')The result is <p>Mina & Jo <3</p>. This transformation is not SQL parameter binding, JavaScript string encoding, URL scheme validation or an authorization check. Escaping a value for a quoted HTML attribute also does not make an arbitrary URL scheme acceptable. Prefer a framework's context-aware template autoescaping in an actual service; the fixed wrapper makes this exercise's sink explicit.
Store original text when the data model calls for text, and encode when producing the output representation. Encoding an already encoded value can display &lt; where a reader expected <. Conversely, unescaping input before rendering can reintroduce syntax. In this lab render_comment always receives original text, so a literal "<" becomes "&lt;" in the HTML source and remains that literal text on display.
Work through three cases before coding: a valid ticket with punctuation, a Unicode digit that resembles an ASCII digit, and a comment of exactly 80 code points. Python's isdecimal() alone accepts more scripts than the ticket grammar permits. Python's string length counts code points, not UTF-8 bytes or user-perceived grapheme clusters; uploads later use byte limits.
The build separates parse_ticket from render_comment so neither operation accidentally grants the other's authority. The first bug hunt weakens the identifier alphabet; the second bypasses the output transformation. Test rejected input as carefully as happy paths, then explain which downstream decisions still need their own checks.