cybersecurity · intermediate · ~15 min · safe pentest lab
Canonicalize attacker-supplied identifiers before comparing them, and bounds-check the container and every pointer inside it before dereferencing — the core discipline behind safe API surface enumeration.
During authorized API reconnaissance you scrape a list of endpoint paths from logs, a swagger dump, and crawler output. Many are duplicates that only look distinct: /api/v1/users and /api/v1/users/ are the same resource, differing by one trailing slash. To size the real attack surface you must count how many distinct endpoints there really are.
The asset is a fixed, in-harness array of path strings — never a live host, socket, or filesystem. The threat is a miscount that either understates the surface or, worse, a memory-safety bug: the paths list is attacker-influenced, so it may be NULL, carry a negative or zero count, or contain NULL entries. The insecure assumption to avoid is "every pointer is a valid, non-empty C string".
Implement count_unique_paths. Two paths are considered equal when they match after stripping at most one trailing '/'. Only one slash is removed, so /x/ and /x// remain distinct. Deny by default: validate paths and n before use, and skip any NULL entry rather than dereferencing it.
For an array sample = { "/api/v1/users", "/api/v1/users/", "/admin" }:
count_unique_paths(sample, 3) -> 2
n == 0 or n < 0 -> 0paths == NULL -> 0NULL element is skipped, not counted"/" collapses to the empty string, equal to ""paths: a pointer to an array of n C-string pointers (const char *const *). Individual elements may be NULL. n: the element count (int), which may be 0 or negative.
An int: the number of distinct paths, where two paths are equal after stripping at most one trailing '/'. NULL entries are ignored.
n may be any int (including 0 and negative). paths may be NULL. Any element may be NULL. Each non-NULL element is a NUL-terminated C string. Do not allocate; O(n^2) comparison is acceptable. Must be memory-safe under ASan/UBSan.
int count_unique_paths(const char *const *paths, int n)
{
/* TODO: count distinct paths (two are equal after stripping at most ONE
trailing '/'). This insecure stub blindly trusts n and returns it
without bounds checks or deduplication. Replace it. */
return n;
}
Returning n without deduplicating; dereferencing paths or paths[i] before NULL-checking; stripping ALL trailing slashes instead of exactly one; treating a path with a trailing slash as always distinct from its slash-less twin; using signed length underflow when a string is empty.
n == 0 and n < 0 both return 0; paths == NULL returns 0; a NULL element is skipped; "/" normalizes to "" and equals ""; only ONE trailing slash is stripped so "/x/" != "/x//"; exact duplicates collapse to a single count.
Solve this exercise in the browser editor — compile and run against the test harness, no setup required.