File Coverage

hoedown/src/escape.c
Criterion Covered Total %
statement 25 38 65.7
branch 22 32 68.7
condition n/a
subroutine n/a
pod n/a
total 47 70 67.1


line stmt bran cond sub pod time code
1             #include "escape.h"
2              
3             #include <assert.h>
4             #include <stdio.h>
5             #include <string.h>
6              
7             #define ESCAPE_GROW_FACTOR(x) (((x) * 12) / 10) /* this is very scientific, yes */
8              
9             /*
10             * The following characters will not be escaped:
11             *
12             * -_.+!*'(),%#@?=;:/,+&$ alphanum
13              *
14              * Note that this character set is the addition of:
15              *
16              * - The characters which are safe to be in an URL
17              * - The characters which are *not* safe to be in
18              * an URL because they are RESERVED characters.
19              *
20              * We asume (lazily) that any RESERVED char that
21              * appears inside an URL is actually meant to
22              * have its native function (i.e. as an URL
23              * component/separator) and hence needs no escaping.
24              *
25              * There are two exceptions: the chacters & (amp)
26              * and ' (single quote) do not appear in the table.
27             * They are meant to appear in the URL as components,
28             * yet they require special HTML-entity escaping
29             * to generate valid HTML markup.
30             *
31             * All other characters will be escaped to %XX.
32             *
33             */
34             static const char HREF_SAFE[] = {
35             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
36             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
37             0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
38             1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
39             1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
40             1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
41             0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
42             1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
43             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
44             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
45             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
46             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
47             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
48             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
49             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
50             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
51             };
52            
53             void
54 1           hoedown_escape_href(hoedown_buffer *ob, const uint8_t *src, size_t size)
55             {
56             static const char hex_chars[] = "0123456789ABCDEF";
57             size_t i = 0, org;
58             char hex_str[3];
59            
60 1           hex_str[0] = '%';
61            
62 1 50         while (i < size) {
63             org = i;
64 15 100         while (i < size && HREF_SAFE[src[i]] != 0)
    50          
65 14           i++;
66            
67 1 50         if (i > org) {
68 1 50         if (org == 0) {
69 1 50         if (i >= size) {
70 1           hoedown_buffer_put(ob, src, size);
71 1           return;
72             }
73            
74 0           hoedown_buffer_grow(ob, ESCAPE_GROW_FACTOR(size));
75             }
76            
77 0           hoedown_buffer_put(ob, src + org, i - org);
78             }
79            
80             /* escaping */
81 0 0         if (i >= size)
82             break;
83            
84 0           switch (src[i]) {
85             /* amp appears all the time in URLs, but needs
86             * HTML-entity escaping to be inside an href */
87             case '&':
88 0           HOEDOWN_BUFPUTSL(ob, "&amp;");
89 0           break;
90            
91             /* the single quote is a valid URL character
92             * according to the standard; it needs HTML
93             * entity escaping too */
94             case '\'':
95 0           HOEDOWN_BUFPUTSL(ob, "&#x27;");
96 0           break;
97            
98             /* the space can be escaped to %20 or a plus
99             * sign. we're going with the generic escape
100             * for now. the plus thing is more commonly seen
101             * when building GET strings */
102             #if 0
103             case ' ':
104             hoedown_buffer_putc(ob, '+');
105             break;
106             #endif
107              
108             /* every other character goes with a %XX escaping */
109             default:
110 0           hex_str[1] = hex_chars[(src[i] >> 4) & 0xF];
111 0           hex_str[2] = hex_chars[src[i] & 0xF];
112 0           hoedown_buffer_put(ob, hex_str, 3);
113             }
114              
115 0           i++;
116             }
117             }
118              
119             /**
120              * According to the OWASP rules:
121              *
122              * & --> &amp;
123              * < --> &lt;
124              * > --> &gt;
125              * " --> &quot;
126             * ' --> &#x27; &apos; is not recommended
127             * / --> &#x2F; forward slash is included as it helps end an HTML entity
128             *
129             */
130             static const char HTML_ESCAPE_TABLE[] = {
131             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
132             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
133             0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 4,
134             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0,
135             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
136             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
137             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
138             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
139             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
140             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
141             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
142             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
143             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
144             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
145             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
146             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
147             };
148            
149             static const char *HTML_ESCAPES[] = {
150             "",
151             "&quot;",
152             "&amp;",
153             "&#39;",
154                     "&#47;",
155                     "&lt;",
156                     "&gt;"
157             };
158              
159             void
160 18           hoedown_escape_html(hoedown_buffer *ob, const uint8_t *src, size_t size, int secure)
161             {
162             size_t i = 0, org, esc = 0;
163              
164 20 50         while (i < size) {
165             org = i;
166 90 100         while (i < size && (esc = HTML_ESCAPE_TABLE[src[i]]) == 0)
    100          
167 70           i++;
168              
169 20 100         if (i > org) {
170 19 100         if (org == 0) {
171 18 100         if (i >= size) {
172 17           hoedown_buffer_put(ob, src, size);
173 17           return;
174             }
175              
176 1           hoedown_buffer_grow(ob, ESCAPE_GROW_FACTOR(size));
177             }
178              
179 2           hoedown_buffer_put(ob, src + org, i - org);
180             }
181              
182             /* escaping */
183 3 100         if (i >= size)
184             break;
185            
186             /* The forward slash is only escaped in secure mode */
187 2 50         if (src[i] == '/' && !secure) {
    50          
188 2           hoedown_buffer_putc(ob, '/');
189             } else {
190 0           hoedown_buffer_puts(ob, HTML_ESCAPES[esc]);
191             }
192            
193 2           i++;
194             }
195             }
196