cybersecurity · beginner · ~15 min
Suffix matching that respects extension boundaries and case.
Implement int has_allowed_extension(const char *name, const char *const *exts, size_t n_exts).
Return 1 if name ends with any one of the strings in exts (case-insensitive ASCII compare), else 0. Each exts[i] must start with . and contain no path separators.
const char *exts[] = {".png", ".jpg", ".jpeg"};
has_allowed_extension("avatar.png", exts, 3) -> 1
has_allowed_extension("AVATAR.PNG", exts, 3) -> 1 // case-insensitive
has_allowed_extension("doc.PDF", exts, 3) -> 0
has_allowed_extension("noext", exts, 3) -> 0
has_allowed_extension(".png", exts, 3) -> 1 // entire name is the extension
has_allowed_extension("file.png.exe", exts, 3) -> 0 // double-extension trick
Upload handlers, attachment scanners, and static-file servers all need to reject filenames they shouldn't serve. The classic check is an extension allowlist — short, defensive, and catches a lot of mischief.
Filename + array of allowed extensions + count.
1/0.
ASCII only. No path traversal handling here — that's a separate exercise.
#include <stddef.h>
int has_allowed_extension(const char *name, const char *const *exts, size_t n_exts) { /* TODO */ return 0; }
Using strstr for the extension — matches .png inside evil.png.exe. Forgetting case-insensitive compare. Trusting the extension as the only check (defence in depth: also check magic bytes).
No dot in name; extension is the entire filename; double extension; uppercase extension.
O(name_len * n_exts).
Solve this exercise in the browser editor — compile and run against the test harness, no setup required.