cybersecurity · intermediate · ~15 min · safe pentest lab
Heuristic scoring with explicit, auditable rules.
Score a URL for structural phishing smells — the cheap, auditable first layer that runs before any ML model.
Implement int phishy_score(const char *url) that adds 1 point for each rule the URL matches and returns the total (or -1 if url is NULL).
Rules, 1 point each:
@.xn--.- and a brand keyword from {paypal, apple, bank, microsoft, google, amazon} (case-insensitive).Hostname extraction: skip a leading http:// or https://, then take everything up to the next / (or end of string). With no scheme, the hostname starts at the beginning.
url: a NUL-terminated URL string the grader passes. No fetch, DNS, or network — pure string analysis.Returns int: the number of rules matched (>= 0), or -1 if url is NULL.
phishy_score("https://example.com/") -> 0
phishy_score("https://example.com@evil.io/") >= 1 (contains @)
phishy_score("https://login123.com/") >= 1 (digit run)
phishy_score("https://xn--exmple-cua.com/") >= 1 (punycode)
phishy_score("https://paypal-secure.com/") >= 1 (brand + dash)
phishy_score("https://paypal.com/") == 0 (brand, no dash)
phishy_score(NULL) == -1
- in the hostname scores 0 for rule 6.Structural smells are the cheapest first layer of any phishing detector. Get the score right and most of the work is done before any ML runs.
A NUL-terminated URL string url.
An int: total points (>=0) for matched rules, or -1 if url is NULL.
Hostname bounded at 256 chars; brand match is case-insensitive; no network.
int phishy_score(const char *url) {
/* TODO */
(void)url;
return 0;
}
Counting brand keywords in the path. Not enforcing the >= 3 in a run (instead counting total digits). Forgetting to skip the scheme.
No scheme. Hostname-only URL. URL with userinfo before @.
O(n) over the URL length.
Solve this exercise in the browser editor — compile and run against the test harness, no setup required.