Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Update the HTML safeing mechanism so that it does the entire Markdown output all at once. In this way it is better able to cope with block markup that spans multiple paragraphs. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
382f3731ee65dfd0ac5a9a39e725d6af |
User & Date: | drh 2020-06-02 17:38:24.703 |
Context
2020-06-02
| ||
18:55 | Documentation improvements on the HTML safer. Only apply safe-html to Forum posts for the moment. ... (check-in: 03ce4e70b6 user: drh tags: trunk) | |
17:38 | Update the HTML safeing mechanism so that it does the entire Markdown output all at once. In this way it is better able to cope with block markup that spans multiple paragraphs. ... (check-in: 382f3731ee user: drh tags: trunk) | |
15:14 | Only use safe-html markdown rendering for the Forum. This is a temporary measure to get most things working again while we devise improvements to the safe-html mechanism. ... (check-in: f4e6efb2da user: drh tags: trunk) | |
Changes
Changes to src/browse.c.
︙ | ︙ | |||
388 389 390 391 392 393 394 | @ sandbox="allow-same-origin" @ onload="this.height=this.contentDocument.documentElement.scrollHeight;"> @ </iframe> }else{ Blob content; const char *zMime = mimetype_from_name(zName); content_get(rid, &content); | | | 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 | @ sandbox="allow-same-origin" @ onload="this.height=this.contentDocument.documentElement.scrollHeight;"> @ </iframe> }else{ Blob content; const char *zMime = mimetype_from_name(zName); content_get(rid, &content); wiki_render_by_mimetype(&content, zMime, 0); } } } db_finalize(&q); style_footer(); } |
︙ | ︙ |
Changes to src/event.c.
︙ | ︙ | |||
506 507 508 509 510 511 512 | wiki_convert(&com, 0, WIKI_INLINE|WIKI_NOBADLINKS); @ </td></tr></table> @ </blockquote> @ <p><b>Page content preview:</b><p> @ <blockquote> blob_init(&event, 0, 0); blob_append(&event, zBody, -1); | | | 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 | wiki_convert(&com, 0, WIKI_INLINE|WIKI_NOBADLINKS); @ </td></tr></table> @ </blockquote> @ <p><b>Page content preview:</b><p> @ <blockquote> blob_init(&event, 0, 0); blob_append(&event, zBody, -1); wiki_render_by_mimetype(&event, zMimetype, 0); @ </blockquote><hr /> blob_reset(&event); } for(n=2, z=zBody; z[0]; z++){ if( z[0]=='\n' ) n++; } if( n<20 ) n = 20; |
︙ | ︙ |
Changes to src/fileedit.c.
︙ | ︙ | |||
974 975 976 977 978 979 980 | break; } case FE_RENDER_HTML_INLINE:{ CX("%b",pContent); break; } case FE_RENDER_WIKI: | | | 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 | break; } case FE_RENDER_HTML_INLINE:{ CX("%b",pContent); break; } case FE_RENDER_WIKI: wiki_render_by_mimetype(pContent, zMime, 0); break; default:{ const char *zExt = strrchr(zFilename,'.'); const char *zContent = blob_str(pContent); if(FE_PREVIEW_LINE_NUMBERS & flags){ output_text_with_line_numbers(zContent, "on"); }else if(zExt && zExt[1]){ |
︙ | ︙ |
Changes to src/forum.c.
︙ | ︙ | |||
338 339 340 341 342 343 344 | if( bScroll ){ @ <div class='forumPostBody'> }else{ @ <div class='forumPostFullBody'> } blob_init(&x, 0, 0); blob_append(&x, zContent, -1); | < | | 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 | if( bScroll ){ @ <div class='forumPostBody'> }else{ @ <div class='forumPostFullBody'> } blob_init(&x, 0, 0); blob_append(&x, zContent, -1); wiki_render_by_mimetype(&x, zMimetype, WIKI_SAFE); blob_reset(&x); @ </div> }else{ @ <i>Deleted</i> } if( zClass ){ @ </div> |
︙ | ︙ |
Changes to src/info.c.
︙ | ︙ | |||
1041 1042 1043 1044 1045 1046 1047 | @ </form> @ </blockquote> } @ <div class="section">Content</div> blob_init(&wiki, pWiki->zWiki, -1); | | | 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 | @ </form> @ </blockquote> } @ <div class="section">Content</div> blob_init(&wiki, pWiki->zWiki, -1); wiki_render_by_mimetype(&wiki, pWiki->zMimetype, 0); blob_reset(&wiki); manifest_destroy(pWiki); style_footer(); } /* ** Find an check-in based on query parameter zParam and parse its |
︙ | ︙ | |||
2360 2361 2362 2363 2364 2365 2366 | } if( descOnly ){ style_submenu_element("Content", "%R/artifact/%s", zUuid); }else{ @ <hr /> content_get(rid, &content); if( renderAsWiki ){ | | | 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 | } if( descOnly ){ style_submenu_element("Content", "%R/artifact/%s", zUuid); }else{ @ <hr /> content_get(rid, &content); if( renderAsWiki ){ wiki_render_by_mimetype(&content, zMime, 0); }else if( renderAsHtml ){ @ <iframe src="%R/raw/%s(zUuid)" @ width="100%%" frameborder="0" marginwidth="0" marginheight="0" @ sandbox="allow-same-origin" id="ifm1"> @ </iframe> @ <script nonce="%h(style_nonce())"> @ document.getElementById("ifm1").addEventListener("load", |
︙ | ︙ |
Changes to src/markdown_html.c.
︙ | ︙ | |||
43 44 45 46 47 48 49 | * The empty string in the second argument leads to a syntax error * when the macro is not used with a string literal. Unfortunately * the error is not overly explicit. */ /* BLOB_APPEND_BLOB -- append blob contents to another */ #define BLOB_APPEND_BLOB(dest, src) \ | | | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | * The empty string in the second argument leads to a syntax error * when the macro is not used with a string literal. Unfortunately * the error is not overly explicit. */ /* BLOB_APPEND_BLOB -- append blob contents to another */ #define BLOB_APPEND_BLOB(dest, src) \ blob_append((dest), blob_buffer(src), blob_size(src)) /* HTML escapes ** ** html_escape() converts < to <, > to >, and & to &. ** html_quote() goes further and converts " into " and ' in '. */ |
︙ | ︙ | |||
145 146 147 148 149 150 151 | && sqlite3_strnicmp("</h1>", &data[size-5],5)==0 ){ int nTag = html_tag_length(data); blob_append(title, data+nTag, size - nTag - 5); return; } INTER_BLOCK(ob); | | | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | && sqlite3_strnicmp("</h1>", &data[size-5],5)==0 ){ int nTag = html_tag_length(data); blob_append(title, data+nTag, size - nTag - 5); return; } INTER_BLOCK(ob); blob_append(ob, data, size); BLOB_APPEND_LITERAL(ob, "\n"); } static void html_blockcode(struct Blob *ob, struct Blob *text, void *opaque){ INTER_BLOCK(ob); BLOB_APPEND_LITERAL(ob, "<pre><code>"); html_escape(ob, blob_buffer(text), blob_size(text)); |
︙ | ︙ | |||
213 214 215 216 217 218 219 | int flags, void *opaque ){ char *text_data = blob_buffer(text); size_t text_size = blob_size(text); while( text_size>0 && text_data[text_size-1]=='\n' ) text_size--; BLOB_APPEND_LITERAL(ob, "<li>"); | | | 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | int flags, void *opaque ){ char *text_data = blob_buffer(text); size_t text_size = blob_size(text); while( text_size>0 && text_data[text_size-1]=='\n' ) text_size--; BLOB_APPEND_LITERAL(ob, "<li>"); blob_append(ob, text_data, text_size); BLOB_APPEND_LITERAL(ob, "</li>\n"); } static void html_paragraph(struct Blob *ob, struct Blob *text, void *opaque){ INTER_BLOCK(ob); BLOB_APPEND_LITERAL(ob, "<p>"); BLOB_APPEND_BLOB(ob, text); |
︙ | ︙ |
Changes to src/wiki.c.
︙ | ︙ | |||
186 187 188 189 190 191 192 193 | /* ** Render wiki text according to its mimetype. ** ** text/x-fossil-wiki Fossil wiki ** text/x-markdown Markdown ** anything else... Plain text */ | > > | | > | 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | /* ** Render wiki text according to its mimetype. ** ** text/x-fossil-wiki Fossil wiki ** text/x-markdown Markdown ** anything else... Plain text ** ** If zMimetype is a null pointer, then use "text/x-fossil-wiki". */ void wiki_render_by_mimetype(Blob *pWiki, const char *zMimetype, int flags){ if( zMimetype==0 || fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ wiki_convert(pWiki, 0, flags); }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ Blob tail = BLOB_INITIALIZER; markdown_to_html(pWiki, 0, &tail); if( flags & WIKI_SAFE ) safe_html(&tail); @ %s(blob_str(&tail)) blob_reset(&tail); }else{ @ <pre class='textPlain'> @ %h(blob_str(pWiki)) @ </pre> } |
︙ | ︙ | |||
218 219 220 221 222 223 224 | if( fTxt ){ style_submenu_element("Formatted", "%R/md_rules"); }else{ style_submenu_element("Plain-Text", "%R/md_rules?txt=1"); } blob_init(&x, builtin_text("markdown.md"), -1); blob_materialize(&x); | | | | 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 | if( fTxt ){ style_submenu_element("Formatted", "%R/md_rules"); }else{ style_submenu_element("Plain-Text", "%R/md_rules?txt=1"); } blob_init(&x, builtin_text("markdown.md"), -1); blob_materialize(&x); wiki_render_by_mimetype(&x, fTxt ? "text/plain" : "text/x-markdown", 0); blob_reset(&x); style_footer(); } /* ** WEBPAGE: wiki_rules ** ** Show a summary of the wiki formatting rules. */ void wiki_rules_page(void){ Blob x; int fTxt = P("txt")!=0; style_header("Wiki Formatting Rules"); if( fTxt ){ style_submenu_element("Formatted", "%R/wiki_rules"); }else{ style_submenu_element("Plain-Text", "%R/wiki_rules?txt=1"); } blob_init(&x, builtin_text("wiki.wiki"), -1); blob_materialize(&x); wiki_render_by_mimetype(&x, fTxt ? "text/plain" : "text/x-fossil-wiki", 0); blob_reset(&x); style_footer(); } /* ** WEBPAGE: markup_help ** |
︙ | ︙ | |||
556 557 558 559 560 561 562 | if( !noSubmenu ){ wiki_standard_submenu(submenuFlags); } if( zBody[0]==0 ){ @ <i>This page has been deleted</i> }else{ blob_init(&wiki, zBody, -1); | | | 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 | if( !noSubmenu ){ wiki_standard_submenu(submenuFlags); } if( zBody[0]==0 ){ @ <i>This page has been deleted</i> }else{ blob_init(&wiki, zBody, -1); wiki_render_by_mimetype(&wiki, zMimetype, WIKI_SAFE); blob_reset(&wiki); } attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>"); manifest_destroy(pWiki); style_footer(); } |
︙ | ︙ | |||
743 744 745 746 747 748 749 | blob_zero(&wiki); while( fossil_isspace(zBody[0]) ) zBody++; blob_append(&wiki, zBody, -1); if( P("preview")!=0 ){ havePreview = 1; if( zBody[0] ){ @ Preview:<hr /> | | | 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 | blob_zero(&wiki); while( fossil_isspace(zBody[0]) ) zBody++; blob_append(&wiki, zBody, -1); if( P("preview")!=0 ){ havePreview = 1; if( zBody[0] ){ @ Preview:<hr /> wiki_render_by_mimetype(&wiki, zMimetype, WIKI_SAFE); @ <hr /> blob_reset(&wiki); } } for(n=2, z=zBody; z[0]; z++){ if( z[0]=='\n' ) n++; } |
︙ | ︙ | |||
1005 1006 1007 1008 1009 1010 1011 | @ <p class="generalError">Error: Incorrect security code.</p> } if( P("preview")!=0 ){ Blob preview; blob_zero(&preview); appendRemark(&preview, zMimetype); @ Preview:<hr /> | | | 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 | @ <p class="generalError">Error: Incorrect security code.</p> } if( P("preview")!=0 ){ Blob preview; blob_zero(&preview); appendRemark(&preview, zMimetype); @ Preview:<hr /> wiki_render_by_mimetype(&preview, zMimetype, WIKI_SAFE); @ <hr /> blob_reset(&preview); } zUser = PD("u", g.zLogin); form_begin(0, "%R/wikiappend"); login_insert_csrf_secret(); @ <input type="hidden" name="name" value="%h(zPageName)" /> |
︙ | ︙ |
Changes to src/wikiformat.c.
︙ | ︙ | |||
29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #define WIKI_INLINE 0x002 /* Do not surround with <p>..</p> */ #define WIKI_NOBLOCK 0x004 /* No block markup of any kind */ #define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */ #define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */ #define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */ #define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */ #define WIKI_MARKDOWNLINKS 0x080 /* Resolve hyperlinks as in markdown */ #endif /* ** These are the only markup attributes allowed. */ enum allowed_attr_t { | > | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | #define WIKI_INLINE 0x002 /* Do not surround with <p>..</p> */ #define WIKI_NOBLOCK 0x004 /* No block markup of any kind */ #define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */ #define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */ #define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */ #define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */ #define WIKI_MARKDOWNLINKS 0x080 /* Resolve hyperlinks as in markdown */ #define WIKI_SAFE 0x100 /* Make the result safe for embedding */ #endif /* ** These are the only markup attributes allowed. */ enum allowed_attr_t { |
︙ | ︙ | |||
1742 1743 1744 1745 1746 1747 1748 | */ void wiki_convert(Blob *pIn, Blob *pOut, int flags){ Renderer renderer; memset(&renderer, 0, sizeof(renderer)); renderer.renderFlags = flags; renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags; | < < < | 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 | */ void wiki_convert(Blob *pIn, Blob *pOut, int flags){ Renderer renderer; memset(&renderer, 0, sizeof(renderer)); renderer.renderFlags = flags; renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags; if( flags & WIKI_INLINE ){ renderer.wantAutoParagraph = 0; }else{ renderer.wantAutoParagraph = 1; } if( wikiUsesHtml() ){ renderer.state |= WIKI_HTMLONLY; |
︙ | ︙ | |||
1816 1817 1818 1819 1820 1821 1822 1823 | ** Options: ** ** --safe Do "safe-html" rendering. */ void test_markdown_render(void){ Blob in, out; int i; db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); | > | < < > | 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 | ** Options: ** ** --safe Do "safe-html" rendering. */ void test_markdown_render(void){ Blob in, out; int i; int bSafe = 0; db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); bSafe = find_option("safe",0,0)!=0; verify_all_options(); for(i=2; i<g.argc; i++){ blob_zero(&out); blob_read_from_file(&in, g.argv[i], ExtFILE); if( g.argc>3 ){ fossil_print("<!------ %h ------->\n", g.argv[i]); } markdown_to_html(&in, 0, &out); if( bSafe ) safe_html(&out); blob_write_to_file(&out, "-"); blob_reset(&in); blob_reset(&out); } } /* |
︙ | ︙ | |||
2507 2508 2509 2510 2511 2512 2513 | e = p->aStack[--p->n]; if( e==eEnd || (aMarkup[e].iType & MUTYPE_Nested)!=0 ){ blob_appendf(pBlob, "</%s>", aMarkup[e].zName); } }while( e!=eEnd && p->n>0 ); } | < < < < < < < < < < | 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 | e = p->aStack[--p->n]; if( e==eEnd || (aMarkup[e].iType & MUTYPE_Nested)!=0 ){ blob_appendf(pBlob, "</%s>", aMarkup[e].zName); } }while( e!=eEnd && p->n>0 ); } /* ** Append HTML text to a Blob object. ** ** If safe-html is enabled then the appended text is modified ** changed in the following ways: ** ** 1. Omit any elements that are not on the AllowedMarkup list. |
︙ | ︙ | |||
2551 2552 2553 2554 2555 2556 2557 | void safe_html_append(Blob *pBlob, char *zHtml, int nHtml){ char cLast; int i, j, n; HtmlTagStack s; ParsedMarkup markup; if( nHtml<=0 ) return; | < < < < | 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 | void safe_html_append(Blob *pBlob, char *zHtml, int nHtml){ char cLast; int i, j, n; HtmlTagStack s; ParsedMarkup markup; if( nHtml<=0 ) return; cLast = zHtml[nHtml]; zHtml[nHtml] = 0; html_tagstack_init(&s); i = 0; while( i<nHtml ){ if( zHtml[i]=='<' ){ |
︙ | ︙ | |||
2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 | }else{ i = j + n; } parseMarkup(&markup, zHtml+j); if( markup.iCode==MARKUP_INVALID ){ blob_appendf(pBlob, "<span class='error'><%.*s></span>", n-2, zHtml+j+1); }else{ if( markup.endTag ){ html_tagstack_pop(&s, pBlob, markup.iCode); }else{ renderMarkup(pBlob, &markup); html_tagstack_push(&s, markup.iCode); } } unparseMarkup(&markup); } html_tagstack_pop(&s, pBlob, 0); html_tagstack_clear(&s); zHtml[nHtml] = cLast; } /* ** COMMAND: test-safe-html ** ** Usage: %fossil test-safe-html FILE ... ** ** Read files named on the command-line. Send the text of each file ** through safe_html_append() and then write the result on ** standard output. */ void test_safe_html_cmd(void){ int i; Blob x; | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < | < | | | | 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 | }else{ i = j + n; } parseMarkup(&markup, zHtml+j); if( markup.iCode==MARKUP_INVALID ){ blob_appendf(pBlob, "<span class='error'><%.*s></span>", n-2, zHtml+j+1); }else if( (markup.iType & MUTYPE_Nested)==0 || markup.iCode==MARKUP_P ){ renderMarkup(pBlob, &markup); }else{ if( markup.endTag ){ html_tagstack_pop(&s, pBlob, markup.iCode); }else{ renderMarkup(pBlob, &markup); html_tagstack_push(&s, markup.iCode); } } unparseMarkup(&markup); } html_tagstack_pop(&s, pBlob, 0); html_tagstack_clear(&s); zHtml[nHtml] = cLast; } /* ** The input blob consists of HTML. Convert it into "safe HTML". Safe ** HTML has no potentially disruptive elements (ex: <script>, <style>) ** and it is embeddable, meaning that it won't close any outer elements ** from the script in which it is embedded, nor will it leave any open ** elements to affect the tail of the outer script. */ void safe_html(Blob *in){ Blob out; char *z = blob_str(in); int n = blob_size(in); int k; blob_init(&out, 0, 0); while( fossil_isspace(z[0]) ){ z++; n--; } for(k=n-1; k>5 && fossil_isspace(z[k]); k--){} if( fossil_strnicmp(z, "<div",4)==0 && !fossil_isalpha(z[4]) && fossil_strnicmp(z+k-5, "</div>",6)==0 ){ /* The input contains an outer <div>...</div>. Preserve the ** full scope of that <div>. */ int m = html_tag_length(z); k -= 5; blob_append(&out, z, m); safe_html_append(&out, z+m, k-m); blob_append(&out, z+k, n-k); }else{ safe_html_append(&out, z, n); } blob_reset(in); *in = out; } /* ** COMMAND: test-safe-html ** ** Usage: %fossil test-safe-html FILE ... ** ** Read files named on the command-line. Send the text of each file ** through safe_html_append() and then write the result on ** standard output. */ void test_safe_html_cmd(void){ int i; Blob x; for(i=2; i<g.argc; i++){ char *z; int n; blob_read_from_file(&x, g.argv[i], ExtFILE); blob_terminate(&x); safe_html(&x); z = blob_str(&x); n = blob_size(&x); while( n>0 && (z[n-1]=='\n' || z[n-1]=='\r') ) n--; fossil_print("%.*s\n", n, z); blob_reset(&x); } } |