cybersecurity · beginner · ~15 min

Validate a filename against an extension allowlist

Suffix matching that respects extension boundaries and case.

Challenge

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.

Examples

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

Why this matters

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.

Input format

Filename + array of allowed extensions + count.

Output format

1/0.

Constraints

ASCII only. No path traversal handling here — that's a separate exercise.

Starter code

#include <stddef.h>
int has_allowed_extension(const char *name, const char *const *exts, size_t n_exts) { /* TODO */ return 0; }

Common mistakes

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).

Edge cases to handle

No dot in name; extension is the entire filename; double extension; uppercase extension.

Complexity

O(name_len * n_exts).

Background lessons

Up next

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