src/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
20 / 20
100.0%
Branches:
37 / 38
97.4%
Functions:
3 / 3
100.0%

HttpAccess.h
Line Branch Exec Source
1 #ifndef httpaccess_h
2 #define httpaccess_h
3 #include <string.h>
4 #include <strings.h>
5
6 // Who may reach what on port 80 under Secured
7 // (docs/adr/0006-two-security-modes-eleven-http-routes.md). Paths are the
8 // request's path, without the query.
9
10 // The files the web UI loads, which all sit in the root. Static serving
11 // answers these alone, so the rolling codes in shades.cfg leave the device
12 // only through /backup. A path below a route, such as /backup/a.js, isn't one.
13 41 inline bool isWebAsset(const char *path) {
14
7/8
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 37 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 26 times.
✓ Branch 5 taken 11 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 26 times.
41 if(!path || path[0] != '/' || strchr(path + 1, '/') || strstr(path, "..")) return false;
15
4/4
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 23 times.
26 if(strcmp(path, "/") == 0 || strcmp(path, "/appversion") == 0) return true;
16 static const char *const types[] = {".html", ".js", ".css", ".png", ".svg", ".ico"};
17 23 const size_t len = strlen(path);
18
2/2
✓ Branch 0 taken 117 times.
✓ Branch 1 taken 16 times.
133 for(const char *type : types) {
19 117 const size_t n = strlen(type);
20
4/4
✓ Branch 0 taken 104 times.
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 97 times.
117 if(len > n + 1 && strcmp(path + len - n, type) == 0) return true;
21 }
22 16 return false;
23 }
24
25 // True for what anyone may reach without a token: the web UI's files, what
26 // logging in needs, SSDP's description and preflight requests. /ws is here
27 // because the socket checks its own token in the handshake, from the query.
28 25 inline bool isPublicRoute(const char *path, bool preflight) {
29
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 24 times.
25 if(preflight) return true;
30
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 23 times.
24 if(!path) return false;
31 static const char *const routes[] = {"/loginContext", "/login", "/upnp.xml", "/ws"};
32
2/2
✓ Branch 0 taken 86 times.
✓ Branch 1 taken 19 times.
105 for(const char *route : routes)
33
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 82 times.
86 if(strcmp(path, route) == 0) return true;
34 19 return isWebAsset(path);
35 }
36
37 // True when a socket handshake comes from a page the device served. A
38 // browser always sends Origin with a handshake, so another site's page open
39 // on the LAN can't use a token the browser holds. Clients that aren't
40 // browsers send none.
41 16 inline bool originMatchesHost(const char *origin, const char *host) {
42
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 15 times.
16 if(!origin) return true;
43 static const char *const schemes[] = {"http://", "https://"};
44
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 2 times.
20 for(const char *scheme : schemes) {
45 18 const size_t n = strlen(scheme);
46
8/8
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 10 times.
✓ Branch 5 taken 2 times.
✓ Branch 6 taken 6 times.
✓ Branch 7 taken 4 times.
18 if(strncasecmp(origin, scheme, n) == 0) return host && host[0] && strcasecmp(origin + n, host) == 0;
47 }
48 2 return false;
49 }
50 #endif
51