Challenge 3: Spot the SQL Injection Risk — Solution Vulnerable version: Post.where("title LIKE '%#{params[:q]}%'") Safe version: Post.where("title LIKE ?", "%#{params[:q]}%") Explanation: The vulnerable version builds the SQL string by directly interpolating params[:q] into the query text using Ruby string interpolation (#{...}) BEFORE Active Record ever sees it as a value -- by the time Active Record runs the query, params[:q] is already indistinguishable from the rest of the SQL text. If a user submitted something like ' OR '1'='1 as their search term, that text becomes part of the actual SQL statement's structure rather than being treated as a literal string to search for -- exactly the SQL injection pattern the completed SQLi course covered, where user input is allowed to change the meaning of the query itself rather than just supplying a value within it. The safe version uses ? as a placeholder and passes the interpolated string as a SEPARATE argument to .where. Active Record sends the query and the value to the database separately, so the database always treats params[:q] strictly as a literal string value to search for, never as executable SQL syntax, no matter what characters it contains. =begin Notes: - Both versions call the SAME Active Record method, .where, with a string-based condition -- this is the important, easy-to-miss detail: using Active Record does not automatically make every possible query safe. Only the placeholder form (or the hash form, Post.where(title: ...)) gets automatic parameterization; string-interpolating a value directly into the condition string bypasses that protection entirely, even inside Active Record's own query interface. - This is precisely why the chapter's tip box stresses that Active Record automates the SAFE version of SQL rather than eliminating the need to understand SQL injection at all -- the underlying risk from the SQLi course is still there if the placeholder syntax isn't used correctly. =end