cybersecurity · intermediate · ~15 min · safe pentest lab

Bounds-Safe HTTP Request-Line Parser

Validate the length of every attacker-controlled field against its destination capacity BEFORE copying, and deny-by-default on any malformed or oversized input, so that parsing hostile HTTP request lines can never overflow a fixed buffer.

Challenge

The attack surface

Every HTTP server begins by parsing the request line — the very first line a client sends, of the form METHOD SP PATH SP VERSION (for example GET /index.html HTTP/1.1). This string is fully attacker-controlled: an attacker on the network chooses its length, its delimiters, and its contents. A parser that trusts the input — copying the method or path into a fixed buffer without checking whether it fits — is the classic source of stack buffer overflows and, from there, remote code execution.

In this lab the harness owns two fixed destination buffers of caps mcap and pcap. Your job is to split the request line into its method and path fields without ever writing past those caps, NUL-terminating each field. This is a pure string operation over an in-harness buffer — there is no network, no socket, and no live target.

Your task

Implement:

int parse_request_line(const char *line, char *method, size_t mcap, char *path, size_t pcap);

Parse line as exactly three space-separated fields. Copy the first field into method (NUL-terminated, at most mcap bytes including the NUL) and the second field into path (at most pcap bytes including the NUL). Return 0 on success. Return -1 — writing nothing — if the line is malformed or if either field would not fit.

Edge cases to reject

  • A NULL line, or a cap of 0 (no room even for the terminator).
  • A missing second space (no version field), an empty method, or an empty path (e.g. two spaces in a row).
  • A method or path longer than its buffer allows: deny, do not truncate silently.

Example

Given the fixed line req:

parse_request_line(req, method, mcap, path, pcap) -> 0   // method="GET", path="/index.html"

Input format

line: a C string holding an HTTP request line, or NULL. method: caller-owned buffer of capacity mcap bytes. path: caller-owned buffer of capacity pcap bytes. mcap, pcap: size_t capacities that include space for the NUL terminator.

Output format

Returns int 0 on success, having written a NUL-terminated method (<= mcap bytes total) and path (<= pcap bytes total). Returns -1 on any malformed input or any field that would not fit, writing nothing to the buffers.

Constraints

C11, freestanding logic (harness supplies all includes). No dynamic allocation, no I/O, no global state. A field 'fits' only if its length plus one NUL byte is <= its cap. Treat exactly one ASCII space (0x20) as the field delimiter; the line must contain a non-empty method, a non-empty path, and a non-empty version separated by single spaces. Never read or write outside the provided buffers.

Starter code

int parse_request_line(const char *line, char *method, size_t mcap,
                       char *path, size_t pcap) {
    /* TODO: bounds-safely split "METHOD SP PATH SP VERSION".
       Validate each field's length against its cap BEFORE copying,
       and return -1 (writing nothing) on any malformed or oversized input.
       This stub accepts nothing yet, so well-formed lines fail. */
    (void)line; (void)method; (void)mcap; (void)path; (void)pcap;
    return -1;
}

Common mistakes

Using strcpy/strcat or an unchecked loop that copies until the delimiter, overflowing the buffer; checking the length AFTER copying instead of before; using > cap instead of > cap - 1 and clobbering the byte reserved for the NUL; forgetting to reject mcap==0 or pcap==0 and writing a NUL to a zero-length buffer; truncating an oversized field and returning 0 instead of rejecting it; forgetting to require the version field so 'GET /x' is wrongly accepted; dereferencing a NULL line before checking it.

Edge cases to handle

NULL line pointer; mcap or pcap equal to 0 (no room for a terminator); a line with no space at all; a line with only one space (missing version field); an empty method (leading space); an empty path (two consecutive spaces); an empty version (trailing space with nothing after it); a method or path exactly one byte too long for its cap; a method or path that fits exactly (length == cap - 1).

Background lessons

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