cybersecurity · intermediate · ~25 min

HTML-escape user-provided text

Per-character expansion with bounded output.

Challenge

HTML-escape user text so it renders as literal characters instead of being interpreted as markup — the primary defense against XSS.

Task

Implement int html_escape(const char *in, char *out, size_t cap) that copies in into out, replacing each HTML-significant character with its entity:

char replacement
& &
< &lt;
> &gt;
" &quot;
' &#39;

All other characters are copied unchanged. Always NUL-terminate out.

Input

  • in: a NUL-terminated ASCII string the grader provides.
  • out: the destination buffer.
  • cap: the capacity of out in bytes (cap >= 1).

Output

Returns the number of bytes written to out, excluding the terminating NUL. Returns -1 if the escaped result (plus its NUL) would not fit in cap.

Example

html_escape("plain", out, 256)        ->   5,   out = "plain"
html_escape("<b>&'\"a", out, 256)      ->   >0,  out = "&lt;b&gt;&amp;&#39;&quot;a"
html_escape("&&&&&", out, 5)          ->   -1   (won't fit)
html_escape("", out, 256)            ->   0,   out = ""

Edge cases

  • Empty input: returns 0, writes just the NUL.
  • Input with no special characters: copied verbatim.
  • A replacement that won't fit mid-escape: return -1.

Rules

  • Do not allocate (no malloc). Reserve one byte for the NUL when checking capacity.

Why this matters

HTML escaping is the primary defense against XSS. Hand-rolling the escape teaches you exactly which characters carry meaning in HTML.

Input format

A NUL-terminated ASCII string in, a destination out, and its capacity cap (>= 1).

Output format

Bytes written excluding the NUL; -1 if the result would not fit. out is always NUL-terminated.

Constraints

in is raw input string; out receives escaped HTML with capacity cap.

Starter code

#include <stddef.h>
int html_escape(const char *in, char *out, size_t cap) { /* TODO */ return -1; }

Common mistakes

Buffer overflow when replacement entity is longer than single character.

Edge cases to handle

cap too small for escaped entities, string with no entities, empty string.

Complexity

O(n).

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.