Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Impose a limit on the depth of nesting of inline footnotes. Also add a few test cases: for depth limiting and HTML hijacking. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | markdown-footnotes |
Files: | files | file ages | folders |
SHA3-256: |
f4ff013ace5f1c557b9089133c0874b1 |
User & Date: | george 2022-02-21 04:29:46 |
Context
2022-02-21
| ||
05:14 |
Add a comment for append_footnote_upc() .
Also substitute a variable of zero value with just "0" constant.
No functional changes.
...
(check-in: ae8a3dd5 user: george tags: markdown-footnotes)
| |
04:29 | Impose a limit on the depth of nesting of inline footnotes. Also add a few test cases: for depth limiting and HTML hijacking. ... (check-in: f4ff013a user: george tags: markdown-footnotes) | |
2022-02-20
| ||
23:00 |
If there are issues with footnotes then set TH1 variable $footnotes_issues_counters
to a space separated list of integers that count for "misref", "unref" and "joins".
This eliminates the need for JavaScript for the case when a custom skin
wants to warn about issues with footnotes
in the header of a page. Also fix counting of "joins": count the number of unique labels that have multiple definitions (and not the number of such definitions). ... (check-in: 773cef5c user: george tags: markdown-footnotes) | |
Changes
Changes to src/default.css.
︙ | ︙ | |||
1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 | div.markdown ol.footnotes > li.fn-joined > sup.fn-joined::after { content: "(joined from multiple locations) "; } div.markdown ol.footnotes > li.fn-misreference { margin-top: 0.75em; margin-bottom: 0.75em; } div.markdown ol.footnotes > li.fn-misreference, div.markdown ol.footnotes > li.fn-unreferenced { color: gray; } div.markdown ol.footnotes > li.fn-misreference > span { color: red; } div.markdown ol.footnotes > li.fn-misreference > span::after { content: " (use of undefined label)."; } div.markdown ol.footnotes > li.fn-unreferenced { padding-left: 0.5em; } div.markdown ol.footnotes > li.fn-unreferenced > code { color: red; } div.markdown ol.footnotes > li.fn-unreferenced > i::after { content: " was defined but is not referenced"; } div.markdown ol.footnotes > li.fn-unreferenced > pre { color: gray; font-size: 85%; padding-left: 0.5em; margin-top: 0.25em; border-left: 2px solid red; } div.markdown > ol.footnotes > li > .fn-backrefs { margin-right: 0.5em; font-weight: bold; } div.markdown > ol.footnotes > li > .fn-backrefs > a, div.markdown sup.noteref > a { | > > > > > > > > | 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 | div.markdown ol.footnotes > li.fn-joined > sup.fn-joined::after { content: "(joined from multiple locations) "; } div.markdown ol.footnotes > li.fn-misreference { margin-top: 0.75em; margin-bottom: 0.75em; } div.markdown ol.footnotes > li.fn-toodeep > i, div.markdown ol.footnotes > li.fn-misreference, div.markdown ol.footnotes > li.fn-unreferenced { color: gray; } div.markdown ol.footnotes > li.fn-misreference > span { color: red; } div.markdown ol.footnotes > li.fn-misreference > span::after { content: " (use of undefined label)."; } div.markdown ol.footnotes > li.fn-unreferenced { padding-left: 0.5em; } div.markdown ol.footnotes > li.fn-unreferenced > code { color: red; } div.markdown ol.footnotes > li.fn-unreferenced > i::after { content: " was defined but is not referenced"; } div.markdown ol.footnotes > li.fn-toodeep > i::after { content: " depth of nesting of inline footnotes exceeded the limit"; } div.markdown ol.footnotes > li.fn-toodeep > pre, div.markdown ol.footnotes > li.fn-unreferenced > pre { color: gray; font-size: 85%; padding-left: 0.5em; margin-top: 0.25em; border-left: 2px solid red; } div.markdown ol.footnotes > li.fn-toodeep > pre { margin-left: 0.5em; } div.markdown > ol.footnotes > li > .fn-backrefs { margin-right: 0.5em; font-weight: bold; } div.markdown > ol.footnotes > li > .fn-backrefs > a, div.markdown sup.noteref > a { |
︙ | ︙ |
Changes to src/markdown.c.
︙ | ︙ | |||
2730 2731 2732 2733 2734 2735 2736 | parse_block(ob, &rndr, blob_buffer(&text), blob_size(&text)); if( (blob_size(allNotes) || rndr.notes.misref.nUsed) ){ /* Footnotes must be parsed for the correct discovery of (back)links */ Blob *notes = new_work_buffer( &rndr ); Blob *tmp = new_work_buffer( &rndr ); | | | | 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 | parse_block(ob, &rndr, blob_buffer(&text), blob_size(&text)); if( (blob_size(allNotes) || rndr.notes.misref.nUsed) ){ /* Footnotes must be parsed for the correct discovery of (back)links */ Blob *notes = new_work_buffer( &rndr ); Blob *tmp = new_work_buffer( &rndr ); int nMarks = -1, maxDepth = 5; /* inline notes may get appended to rndr.notes.all while rendering */ while(1){ struct footnote *aNotes; const int N = COUNT_FOOTNOTES( allNotes ); /* make a shallow copy of `origin` */ blob_truncate(notes,0); blob_append(notes, blob_buffer(allNotes), blob_size(allNotes)); aNotes = CAST_AS_FOOTNOTES(notes); qsort(aNotes, N, sizeof(struct footnote), cmp_footnote_sort); if( --maxDepth < 0 || nMarks == rndr.notes.nMarks ) break; nMarks = rndr.notes.nMarks; for(i=0; i<N; i++){ const int j = aNotes[i].index; struct footnote *x = CAST_AS_FOOTNOTES(allNotes) + j; assert( 0<=j && j<N ); if( x->bRndred || !x->nUsed ) continue; |
︙ | ︙ | |||
2771 2772 2773 2774 2775 2776 2777 | /* footnotes rendering */ if( rndr.make.footnote_item && rndr.make.footnotes ){ Blob *all_items = new_work_buffer(&rndr); int j = -1; for(i=0; i<COUNT_FOOTNOTES(notes); i++){ const struct footnote* x = CAST_AS_FOOTNOTES(notes) + i; | | | | | | 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 | /* footnotes rendering */ if( rndr.make.footnote_item && rndr.make.footnotes ){ Blob *all_items = new_work_buffer(&rndr); int j = -1; for(i=0; i<COUNT_FOOTNOTES(notes); i++){ const struct footnote* x = CAST_AS_FOOTNOTES(notes) + i; if( x->iMark ){ rndr.make.footnote_item(all_items, &x->text, x->iMark, x->bRndred ? x->nUsed : 0, rndr.make.opaque); j = i; } } if( rndr.notes.misref.nUsed ){ rndr.make.footnote_item(all_items, 0, -1, rndr.notes.misref.nUsed, rndr.make.opaque); g.ftntsIssues[0] += rndr.notes.misref.nUsed; } while( ++j < COUNT_FOOTNOTES(notes) ){ const struct footnote* x = CAST_AS_FOOTNOTES(notes) + j; assert( !x->iMark ); assert( !x->bRndred ); assert( (&x->id) + 1 == &x->text ); /* see html_footnote_item() */ assert( (&x->upc)- 1 == &x->text ); rndr.make.footnote_item(all_items,&x->text,x->iMark,0,rndr.make.opaque); g.ftntsIssues[1]++; } rndr.make.footnotes(ob, all_items, rndr.make.opaque); release_work_buffer(&rndr, all_items); } release_work_buffer(&rndr, notes); } |
︙ | ︙ |
Changes to src/markdown_html.c.
︙ | ︙ | |||
466 467 468 469 470 471 472 | BLOB_APPEND_URI(ob, ctx); blob_appendf(ob,"#misref%s-%c'>%c</a>", unique,c, c); } if( i < nUsed ) BLOB_APPEND_LITERAL(ob," …"); } BLOB_APPEND_LITERAL(ob,"</sup>\n<span>Misreference</span>"); | | | > > > > > | | > | | < < < | > > | > | | 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 | BLOB_APPEND_URI(ob, ctx); blob_appendf(ob,"#misref%s-%c'>%c</a>", unique,c, c); } if( i < nUsed ) BLOB_APPEND_LITERAL(ob," …"); } BLOB_APPEND_LITERAL(ob,"</sup>\n<span>Misreference</span>"); }else if( iMark > 0 ){ /* a regular footnote */ char pos[24]; int bJoin = 0; /* make.footnote_item() invocations should pass args accordingly */ const struct Blob * upc = text+1; #define _joined_footnote_indicator "<ul class='fn-joined'>" #define _jfi_sz (sizeof(_joined_footnote_indicator)-1) assert( text ); assert( blob_size(text) ); memset(pos,0,24); sprintf(pos, "%s-%i", unique, iMark); blob_appendf(ob, "<li id='footnote%s' class='", pos); if( nUsed ){ if( blob_size(text)>=_jfi_sz && !memcmp(blob_buffer(text),_joined_footnote_indicator,_jfi_sz)){ bJoin = 1; BLOB_APPEND_LITERAL(ob, "fn-joined "); } append_footnote_upc(ob, upc, 0); }else{ BLOB_APPEND_LITERAL(ob, "fn-toodeep "); } if( nUsed <= 1 ){ BLOB_APPEND_LITERAL(ob, "fn-monoref'><sup class='fn-backrefs'>"); blob_appendf(ob,"<a id='footnote%s-a' href='", pos); BLOB_APPEND_URI(ob, ctx); blob_appendf(ob,"#noteref%s-a'>^</a>", pos); }else{ int i; BLOB_APPEND_LITERAL(ob, "fn-polyref'><sup class='fn-backrefs'>^"); |
︙ | ︙ | |||
510 511 512 513 514 515 516 | BLOB_APPEND_URI(ob, ctx); blob_appendf(ob,"#noteref%s-%s'>%s</a>", pos,l.c, l.c); } if( i < nUsed ) BLOB_APPEND_LITERAL(ob," …"); } BLOB_APPEND_LITERAL(ob,"</sup>\n"); append_footnote_upc(ob, upc, 1); | | | > > > > > > | 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 | BLOB_APPEND_URI(ob, ctx); blob_appendf(ob,"#noteref%s-%s'>%s</a>", pos,l.c, l.c); } if( i < nUsed ) BLOB_APPEND_LITERAL(ob," …"); } BLOB_APPEND_LITERAL(ob,"</sup>\n"); append_footnote_upc(ob, upc, 1); if( bJoin ){ BLOB_APPEND_LITERAL(ob,"<sup class='fn-joined'></sup><ul>"); blob_append(ob,blob_buffer(text)+_jfi_sz,blob_size(text)-_jfi_sz); }else if( nUsed ){ BLOB_APPEND_BLOB(ob, text); }else{ BLOB_APPEND_LITERAL(ob,"<i></i>\n" "<pre><code class='language-markdown'>"); html_escape(ob, blob_buffer(text), blob_size(text)); BLOB_APPEND_LITERAL(ob,"</code></pre>"); } #undef _joined_footnote_indicator #undef _jfi_sz }else{ /* a footnote was defined but wasn't used */ /* make.footnote_item() invocations should pass args accordingly */ const struct Blob * id = text-1; assert( !nUsed ); assert( text ); assert( blob_size(text) ); assert( blob_size(id) ); BLOB_APPEND_LITERAL(ob,"<li class='fn-unreferenced'>\n[^ <code>"); html_escape(ob, blob_buffer(id), blob_size(id)); BLOB_APPEND_LITERAL(ob, "</code> ]<i></i>\n" "<pre><code class='language-markdown'>"); |
︙ | ︙ |
Changes to test/markdown-test3.md.
︙ | ︙ | |||
121 122 123 124 125 126 127 128 129 130 131 132 133 134 | in the stylesheet.[^nostyle] If a footnote consists just of a valid userclass token then this token is not interpreted as such, instead it is emitted as plain text. (^ .bare.classlist.inside.inline.footnote: )[^bare1] [^bare2] ## Footnotes [branch]: /timeline?r=markdown-footnotes&nowiki [^ 1]: Footnotes is a Fossil' extention of Markdown. Your other tools may have limited support for these. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 | in the stylesheet.[^nostyle] If a footnote consists just of a valid userclass token then this token is not interpreted as such, instead it is emitted as plain text. (^ .bare.classlist.inside.inline.footnote: )[^bare1] [^bare2] <html> Click <a href="?a=B"e='&nonASCII=😂&script=<script>alert('Broken!');</script>"> here</a> and <a href='?a=B"e="&nonASCII=😂&script=<script>alert("Broken!");</script>'> here</a> to test escaping of REQUEST_URI in the generated footnote markers. </html> A depth of nesting must be limited. (^ A long chain of nested inline footnotes... (^ is a rather unusual thing... (^ and requires extra CPU cycles for processing. (^ Theoretically speaking O(n<sup>2</sup>). (^ Thus it is worth dismissing those footnotes... (^ that are nested deeper than on a certain level. (^ A particular value for that limit... (^ is hard-coded in src/markdown.c ... (^ in function `markdown()` ... (^ in variable named `maxDepth`. (^ For the time being, its value is **5** ) ) ) ) ) ) ) ) ) ) ) ## Footnotes [branch]: /timeline?r=markdown-footnotes&nowiki [^ 1]: Footnotes is a Fossil' extention of Markdown. Your other tools may have limited support for these. |
︙ | ︙ | |||
175 176 177 178 179 180 181 | .at.the.2nd.line.of.labeled.footnote.definition: [^nostyle]: .unused.classes: In that case text of the footnote just looks like as if no special processing occured. | > > > > > > > | 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | .at.the.2nd.line.of.labeled.footnote.definition: [^nostyle]: .unused.classes: In that case text of the footnote just looks like as if no special processing occured. [^ <script>alert("You have been pwned!");</script> ]: Labels are escaped [^ <textarea>"Last words here...' ]: <textarea>Content is also escaped</textarea> |