Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Add the safe-html setting that determines which kinds of documents are allowed to generate unsafe HTML from Markdown. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
89b6dda99c45533358f1af425acc226f |
User & Date: | drh 2020-06-02 20:05:28 |
Context
2020-06-02
| ||
22:34 | mv: Handle the case of soft-renaming of a managed sub-directory that was already renamed at the file-system level; see forumpost/f5b13591e3 ... (check-in: 709d2f80 user: ashepilko tags: trunk) | |
20:05 | Add the safe-html setting that determines which kinds of documents are allowed to generate unsafe HTML from Markdown. ... (check-in: 89b6dda9 user: drh tags: trunk) | |
18:55 | Documentation improvements on the HTML safer. Only apply safe-html to Forum posts for the moment. ... (check-in: 03ce4e70 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 403 | @ 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); safe_html_context(DOCSRC_FILE); wiki_render_by_mimetype(&content, zMime); } } } 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 521 | 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); safe_html_context(DOCSRC_WIKI); wiki_render_by_mimetype(&event, zMimetype); @ </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 989 | break; } case FE_RENDER_HTML_INLINE:{ CX("%b",pContent); break; } case FE_RENDER_WIKI: safe_html_context(DOCSRC_FILE); wiki_render_by_mimetype(pContent, zMime); 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 353 | if( bScroll ){ @ <div class='forumPostBody'> }else{ @ <div class='forumPostFullBody'> } blob_init(&x, 0, 0); blob_append(&x, zContent, -1); safe_html_context(DOCSRC_FORUM); wiki_render_by_mimetype(&x, zMimetype); 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 1056 | @ </form> @ </blockquote> } @ <div class="section">Content</div> blob_init(&wiki, pWiki->zWiki, -1); safe_html_context(DOCSRC_WIKI); wiki_render_by_mimetype(&wiki, pWiki->zMimetype); 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 ){ | > | | 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 | } if( descOnly ){ style_submenu_element("Content", "%R/artifact/%s", zUuid); }else{ @ <hr /> content_get(rid, &content); if( renderAsWiki ){ safe_html_context(DOCSRC_FILE); wiki_render_by_mimetype(&content, zMime); }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/setup.c.
︙ | ︙ | |||
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 | @ or tag are treated specially when this feature is enabled. @ <ul> @ <li> <b>branch/</b><i>branch-name</i> @ <li> <b>checkin/</b><i>full-checkin-hash</i> @ <li> <b>tag/</b><i>tag-name</i> @ </ul> @ (Property: "wiki-about")</p> @ <hr /> onoff_attribute("Enable WYSIWYG Wiki Editing", "wysiwyg-wiki", "wysiwyg-wiki", 0, 0); @ <p>Enable what-you-see-is-what-you-get (WYSIWYG) editing of wiki pages. @ The WYSIWYG editor generates HTML instead of markup, which makes @ subsequent manual editing more difficult. @ (Property: "wysiwyg-wiki")</p> | > > > > > > > > > > > > > > > > > > > > > | 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 | @ or tag are treated specially when this feature is enabled. @ <ul> @ <li> <b>branch/</b><i>branch-name</i> @ <li> <b>checkin/</b><i>full-checkin-hash</i> @ <li> <b>tag/</b><i>tag-name</i> @ </ul> @ (Property: "wiki-about")</p> @ <hr /> entry_attribute("Allow Unsafe HTML In Markdown", 6, "safe-html", "safe-html", "", 0); @ <p>Allow "unsafe" HTML (ex: <script>, <form>, etc) to be @ generated by <a href="%R/md_rules">Markdown-formatted</a> documents. @ This setting is a string where each character indicates a "type" of @ document in which to allow unsafe HTML: @ <ul> @ <li> <b>b</b> → checked-in files, embedded documentation @ <li> <b>f</b> → forum posts @ <li> <b>t</b> → tickets @ <li> <b>w</b> → wiki pages @ </ul> @ Include letters for each type of document for which unsafe HTML should @ be allowed. For example, to allow unsafe HTML only for checked-in files, @ make this setting be just "<b>b</b>". To allow unsafe HTML anywhere except @ in forum posts, make this setting be "<b>btw</b>". The default is an @ empty string which means that Fossil never allows Markdown documents @ to generate unsafe HTML. @ (Property: "safe-html")</p> @ <hr /> @ <hr /> onoff_attribute("Enable WYSIWYG Wiki Editing", "wysiwyg-wiki", "wysiwyg-wiki", 0, 0); @ <p>Enable what-you-see-is-what-you-get (WYSIWYG) editing of wiki pages. @ The WYSIWYG editor generates HTML instead of markup, which makes @ subsequent manual editing more difficult. @ (Property: "wysiwyg-wiki")</p> |
︙ | ︙ |
Changes to src/tkt.c.
︙ | ︙ | |||
510 511 512 513 514 515 516 517 518 519 520 521 522 523 | ticket_init(); initializeVariablesFromCGI(); getAllTicketFields(); initializeVariablesFromDb(); zScript = ticket_viewpage_code(); if( P("showfields")!=0 ) showAllFields(); if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1); Th_Render(zScript); if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1); zFullName = db_text(0, "SELECT tkt_uuid FROM ticket" " WHERE tkt_uuid GLOB '%q*'", zUuid); if( zFullName ){ | > | 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 | ticket_init(); initializeVariablesFromCGI(); getAllTicketFields(); initializeVariablesFromDb(); zScript = ticket_viewpage_code(); if( P("showfields")!=0 ) showAllFields(); if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br />\n", -1); safe_html_context(DOCSRC_TICKET); Th_Render(zScript); if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1); zFullName = db_text(0, "SELECT tkt_uuid FROM ticket" " WHERE tkt_uuid GLOB '%q*'", zUuid); if( zFullName ){ |
︙ | ︙ |
Changes to src/wiki.c.
︙ | ︙ | |||
189 190 191 192 193 194 195 | ** ** 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". */ | | | | | 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | ** ** 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){ if( zMimetype==0 || fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0 ){ wiki_convert(pWiki, 0, 0); }else if( fossil_strcmp(zMimetype, "text/x-markdown")==0 ){ Blob tail = BLOB_INITIALIZER; markdown_to_html(pWiki, 0, &tail); safe_html(&tail); @ %s(blob_str(&tail)) blob_reset(&tail); }else{ @ <pre class='textPlain'> @ %h(blob_str(pWiki)) @ </pre> } |
︙ | ︙ | |||
221 222 223 224 225 226 227 | 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 257 258 | 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); safe_html_context(DOCSRC_TRUSTED); wiki_render_by_mimetype(&x, fTxt ? "text/plain" : "text/x-markdown"); 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); safe_html_context(DOCSRC_TRUSTED); wiki_render_by_mimetype(&x, fTxt ? "text/plain" : "text/x-fossil-wiki"); blob_reset(&x); style_footer(); } /* ** WEBPAGE: markup_help ** |
︙ | ︙ | |||
559 560 561 562 563 564 565 | if( !noSubmenu ){ wiki_standard_submenu(submenuFlags); } if( zBody[0]==0 ){ @ <i>This page has been deleted</i> }else{ blob_init(&wiki, zBody, -1); | > | | 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 | if( !noSubmenu ){ wiki_standard_submenu(submenuFlags); } if( zBody[0]==0 ){ @ <i>This page has been deleted</i> }else{ blob_init(&wiki, zBody, -1); safe_html_context(DOCSRC_WIKI); wiki_render_by_mimetype(&wiki, zMimetype); blob_reset(&wiki); } attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>"); manifest_destroy(pWiki); style_footer(); } |
︙ | ︙ | |||
746 747 748 749 750 751 752 | 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 /> | > | | 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 | 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 /> safe_html_context(DOCSRC_WIKI); wiki_render_by_mimetype(&wiki, zMimetype); @ <hr /> blob_reset(&wiki); } } for(n=2, z=zBody; z[0]; z++){ if( z[0]=='\n' ) n++; } |
︙ | ︙ | |||
1008 1009 1010 1011 1012 1013 1014 | @ <p class="generalError">Error: Incorrect security code.</p> } if( P("preview")!=0 ){ Blob preview; blob_zero(&preview); appendRemark(&preview, zMimetype); @ Preview:<hr /> | > | | 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 | @ <p class="generalError">Error: Incorrect security code.</p> } if( P("preview")!=0 ){ Blob preview; blob_zero(&preview); appendRemark(&preview, zMimetype); @ Preview:<hr /> safe_html_context(DOCSRC_WIKI); wiki_render_by_mimetype(&preview, zMimetype); @ <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)" /> |
︙ | ︙ | |||
1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 | ** add <html><body> tag wrappers around the output. The ** hurdle here is that the markdown converter resets its ** input blob before appending the output, which is ** different from wiki_convert() and htmlize_to_blob(), and ** precludes us simply appending the opening <html><body> ** part to the body */; }else if( fossil_strcmp(zMimetype, "text/plain")==0 ){ htmlize_to_blob(&html,zBody,i); }else{ fossil_fatal("Unsupported MIME type '%s' for wiki page '%s'.", zMimetype, pWiki->zWikiTitle ); } blob_reset(&body); | > > | 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 | ** add <html><body> tag wrappers around the output. The ** hurdle here is that the markdown converter resets its ** input blob before appending the output, which is ** different from wiki_convert() and htmlize_to_blob(), and ** precludes us simply appending the opening <html><body> ** part to the body */; safe_html_context(DOCSRC_WIKI); safe_html(&html); }else if( fossil_strcmp(zMimetype, "text/plain")==0 ){ htmlize_to_blob(&html,zBody,i); }else{ fossil_fatal("Unsupported MIME type '%s' for wiki page '%s'.", zMimetype, pWiki->zWikiTitle ); } blob_reset(&body); |
︙ | ︙ | |||
1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 | if( blob_size(&title) ){ @ <div class="section accordion">%h(blob_str(&title))</div> }else{ wiki_section_label(zPrefix, zName, mFlags); } wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags); @ <div class="accordion_panel"> convert_href_and_output(&tail); @ </div> blob_reset(&tail); blob_reset(&title); blob_reset(&markdown); }else if( fossil_strcmp(pWiki->zMimetype, "text/plain")==0 ){ wiki_section_label(zPrefix, zName, mFlags); | > > | 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 | if( blob_size(&title) ){ @ <div class="section accordion">%h(blob_str(&title))</div> }else{ wiki_section_label(zPrefix, zName, mFlags); } wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags); @ <div class="accordion_panel"> safe_html_context(DOCSRC_WIKI); safe_html(&tail); convert_href_and_output(&tail); @ </div> blob_reset(&tail); blob_reset(&title); blob_reset(&markdown); }else if( fossil_strcmp(pWiki->zMimetype, "text/plain")==0 ){ wiki_section_label(zPrefix, zName, mFlags); |
︙ | ︙ |
Changes to src/wikiformat.c.
︙ | ︙ | |||
1809 1810 1811 1812 1813 1814 1815 | ** COMMAND: test-markdown-render ** ** Usage: %fossil test-markdown-render FILE ... ** ** Render markdown in FILE as HTML on stdout. ** Options: ** | | > | | 1809 1810 1811 1812 1813 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 1840 | ** COMMAND: test-markdown-render ** ** Usage: %fossil test-markdown-render FILE ... ** ** Render markdown in FILE as HTML on stdout. ** Options: ** ** --safe Restrict the output to use only "safe" HTML */ 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); safe_html_context( bSafe ? DOCSRC_UNTRUSTED : DOCSRC_TRUSTED ); safe_html(&out); blob_write_to_file(&out, "-"); blob_reset(&in); blob_reset(&out); } } /* |
︙ | ︙ | |||
2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 | } unparseMarkup(&markup); } html_tagstack_pop(&s, pBlob, 0); html_tagstack_clear(&s); zHtml[nHtml] = cLast; } /* ** The input blob contains HTML. If safe-html is enabled, then ** convert the input into "safe HTML". The following modifications ** are made: ** ** 1. Remove any elements that are not on the AllowedMarkup list. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } unparseMarkup(&markup); } html_tagstack_pop(&s, pBlob, 0); html_tagstack_clear(&s); zHtml[nHtml] = cLast; } /* ** This local variable is true if the safe_html() function is enabled. ** In other words, this is true if the output of Markdown should be ** restricted to use only "safe" HTML. */ static int safeHtmlEnable = 1; #if INTERFACE /* ** Allowed values for the eTrust parameter to safe_html_context(). */ #define DOCSRC_FILE 1 /* Document is a checked-in file */ #define DOCSRC_FORUM 2 /* Document is a forum post */ #define DOCSRC_TICKET 3 /* Document is a ticket comment */ #define DOCSRC_WIKI 4 /* Document is a wiki page */ #define DOCSRC_TRUSTED 5 /* safe_html() is always a no-op */ #define DOCSRC_UNTRUSTED 6 /* safe_html() is always enabled */ #endif /* INTERFACE */ /* ** Specify the context in which a markdown document with potentially ** unsafe HTML will be rendered. */ void safe_html_context(int eTrust){ static const char *zSafeHtmlSetting = 0; char cPerm = 0; if( eTrust==DOCSRC_TRUSTED ){ safeHtmlEnable = 0; return; } if( eTrust==DOCSRC_UNTRUSTED ){ safeHtmlEnable = 1; return; } if( zSafeHtmlSetting==0 ){ zSafeHtmlSetting = db_get("safe-html", ""); } switch( eTrust ){ case DOCSRC_FILE: cPerm = 'b'; break; case DOCSRC_FORUM: cPerm = 'f'; break; case DOCSRC_TICKET: cPerm = 't'; break; case DOCSRC_WIKI: cPerm = 'w'; break; } safeHtmlEnable = (strchr(zSafeHtmlSetting,cPerm)==0); } /* ** The input blob contains HTML. If safe-html is enabled, then ** convert the input into "safe HTML". The following modifications ** are made: ** ** 1. Remove any elements that are not on the AllowedMarkup list. |
︙ | ︙ | |||
2601 2602 2603 2604 2605 2606 2607 | */ void safe_html(Blob *in){ Blob out; /* Holding area for the revised text during construction */ char *z; /* Original input text */ int n; /* Number of bytes in the original input text */ int k; | | | 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 | */ void safe_html(Blob *in){ Blob out; /* Holding area for the revised text during construction */ char *z; /* Original input text */ int n; /* Number of bytes in the original input text */ int k; if( safeHtmlEnable==0 ) return; z = blob_str(in); n = blob_size(in); 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]) |
︙ | ︙ |