Fossil SCM
Impose a limit on the depth of nesting of inline footnotes. Also add a few test cases: for depth limiting and HTML hijacking.
Commit
f4ff013ace5f1c557b9089133c0874b11f21b51a8aa5ebcd4092471a5a46ae34
Parent
773cef5cf78a80e…
4 files changed
+8
+6
-6
+23
-11
+51
+8
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -1683,10 +1683,11 @@ | ||
| 1683 | 1683 | } |
| 1684 | 1684 | div.markdown ol.footnotes > li.fn-misreference { |
| 1685 | 1685 | margin-top: 0.75em; |
| 1686 | 1686 | margin-bottom: 0.75em; |
| 1687 | 1687 | } |
| 1688 | +div.markdown ol.footnotes > li.fn-toodeep > i, | |
| 1688 | 1689 | div.markdown ol.footnotes > li.fn-misreference, |
| 1689 | 1690 | div.markdown ol.footnotes > li.fn-unreferenced { |
| 1690 | 1691 | color: gray; |
| 1691 | 1692 | } |
| 1692 | 1693 | div.markdown ol.footnotes > li.fn-misreference > span { |
| @@ -1702,16 +1703,23 @@ | ||
| 1702 | 1703 | color: red; |
| 1703 | 1704 | } |
| 1704 | 1705 | div.markdown ol.footnotes > li.fn-unreferenced > i::after { |
| 1705 | 1706 | content: " was defined but is not referenced"; |
| 1706 | 1707 | } |
| 1708 | +div.markdown ol.footnotes > li.fn-toodeep > i::after { | |
| 1709 | + content: " depth of nesting of inline footnotes exceeded the limit"; | |
| 1710 | +} | |
| 1711 | +div.markdown ol.footnotes > li.fn-toodeep > pre, | |
| 1707 | 1712 | div.markdown ol.footnotes > li.fn-unreferenced > pre { |
| 1708 | 1713 | color: gray; |
| 1709 | 1714 | font-size: 85%; |
| 1710 | 1715 | padding-left: 0.5em; |
| 1711 | 1716 | margin-top: 0.25em; |
| 1712 | 1717 | border-left: 2px solid red; |
| 1718 | +} | |
| 1719 | +div.markdown ol.footnotes > li.fn-toodeep > pre { | |
| 1720 | + margin-left: 0.5em; | |
| 1713 | 1721 | } |
| 1714 | 1722 | div.markdown > ol.footnotes > li > .fn-backrefs { |
| 1715 | 1723 | margin-right: 0.5em; |
| 1716 | 1724 | font-weight: bold; |
| 1717 | 1725 | } |
| 1718 | 1726 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -1683,10 +1683,11 @@ | |
| 1683 | } |
| 1684 | div.markdown ol.footnotes > li.fn-misreference { |
| 1685 | margin-top: 0.75em; |
| 1686 | margin-bottom: 0.75em; |
| 1687 | } |
| 1688 | div.markdown ol.footnotes > li.fn-misreference, |
| 1689 | div.markdown ol.footnotes > li.fn-unreferenced { |
| 1690 | color: gray; |
| 1691 | } |
| 1692 | div.markdown ol.footnotes > li.fn-misreference > span { |
| @@ -1702,16 +1703,23 @@ | |
| 1702 | color: red; |
| 1703 | } |
| 1704 | div.markdown ol.footnotes > li.fn-unreferenced > i::after { |
| 1705 | content: " was defined but is not referenced"; |
| 1706 | } |
| 1707 | div.markdown ol.footnotes > li.fn-unreferenced > pre { |
| 1708 | color: gray; |
| 1709 | font-size: 85%; |
| 1710 | padding-left: 0.5em; |
| 1711 | margin-top: 0.25em; |
| 1712 | border-left: 2px solid red; |
| 1713 | } |
| 1714 | div.markdown > ol.footnotes > li > .fn-backrefs { |
| 1715 | margin-right: 0.5em; |
| 1716 | font-weight: bold; |
| 1717 | } |
| 1718 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -1683,10 +1683,11 @@ | |
| 1683 | } |
| 1684 | div.markdown ol.footnotes > li.fn-misreference { |
| 1685 | margin-top: 0.75em; |
| 1686 | margin-bottom: 0.75em; |
| 1687 | } |
| 1688 | div.markdown ol.footnotes > li.fn-toodeep > i, |
| 1689 | div.markdown ol.footnotes > li.fn-misreference, |
| 1690 | div.markdown ol.footnotes > li.fn-unreferenced { |
| 1691 | color: gray; |
| 1692 | } |
| 1693 | div.markdown ol.footnotes > li.fn-misreference > span { |
| @@ -1702,16 +1703,23 @@ | |
| 1703 | color: red; |
| 1704 | } |
| 1705 | div.markdown ol.footnotes > li.fn-unreferenced > i::after { |
| 1706 | content: " was defined but is not referenced"; |
| 1707 | } |
| 1708 | div.markdown ol.footnotes > li.fn-toodeep > i::after { |
| 1709 | content: " depth of nesting of inline footnotes exceeded the limit"; |
| 1710 | } |
| 1711 | div.markdown ol.footnotes > li.fn-toodeep > pre, |
| 1712 | div.markdown ol.footnotes > li.fn-unreferenced > pre { |
| 1713 | color: gray; |
| 1714 | font-size: 85%; |
| 1715 | padding-left: 0.5em; |
| 1716 | margin-top: 0.25em; |
| 1717 | border-left: 2px solid red; |
| 1718 | } |
| 1719 | div.markdown ol.footnotes > li.fn-toodeep > pre { |
| 1720 | margin-left: 0.5em; |
| 1721 | } |
| 1722 | div.markdown > ol.footnotes > li > .fn-backrefs { |
| 1723 | margin-right: 0.5em; |
| 1724 | font-weight: bold; |
| 1725 | } |
| 1726 |
+6
-6
| --- src/markdown.c | ||
| +++ src/markdown.c | ||
| @@ -2732,11 +2732,11 @@ | ||
| 2732 | 2732 | if( (blob_size(allNotes) || rndr.notes.misref.nUsed) ){ |
| 2733 | 2733 | |
| 2734 | 2734 | /* Footnotes must be parsed for the correct discovery of (back)links */ |
| 2735 | 2735 | Blob *notes = new_work_buffer( &rndr ); |
| 2736 | 2736 | Blob *tmp = new_work_buffer( &rndr ); |
| 2737 | - int nMarks = -1; | |
| 2737 | + int nMarks = -1, maxDepth = 5; | |
| 2738 | 2738 | |
| 2739 | 2739 | /* inline notes may get appended to rndr.notes.all while rendering */ |
| 2740 | 2740 | while(1){ |
| 2741 | 2741 | struct footnote *aNotes; |
| 2742 | 2742 | const int N = COUNT_FOOTNOTES( allNotes ); |
| @@ -2745,11 +2745,11 @@ | ||
| 2745 | 2745 | blob_truncate(notes,0); |
| 2746 | 2746 | blob_append(notes, blob_buffer(allNotes), blob_size(allNotes)); |
| 2747 | 2747 | aNotes = CAST_AS_FOOTNOTES(notes); |
| 2748 | 2748 | qsort(aNotes, N, sizeof(struct footnote), cmp_footnote_sort); |
| 2749 | 2749 | |
| 2750 | - if( nMarks == rndr.notes.nMarks ) break; | |
| 2750 | + if( --maxDepth < 0 || nMarks == rndr.notes.nMarks ) break; | |
| 2751 | 2751 | nMarks = rndr.notes.nMarks; |
| 2752 | 2752 | |
| 2753 | 2753 | for(i=0; i<N; i++){ |
| 2754 | 2754 | const int j = aNotes[i].index; |
| 2755 | 2755 | struct footnote *x = CAST_AS_FOOTNOTES(allNotes) + j; |
| @@ -2773,13 +2773,13 @@ | ||
| 2773 | 2773 | if( rndr.make.footnote_item && rndr.make.footnotes ){ |
| 2774 | 2774 | Blob *all_items = new_work_buffer(&rndr); |
| 2775 | 2775 | int j = -1; |
| 2776 | 2776 | for(i=0; i<COUNT_FOOTNOTES(notes); i++){ |
| 2777 | 2777 | const struct footnote* x = CAST_AS_FOOTNOTES(notes) + i; |
| 2778 | - if( x->bRndred ){ | |
| 2778 | + if( x->iMark ){ | |
| 2779 | 2779 | rndr.make.footnote_item(all_items, &x->text, x->iMark, |
| 2780 | - x->nUsed, rndr.make.opaque); | |
| 2780 | + x->bRndred ? x->nUsed : 0, rndr.make.opaque); | |
| 2781 | 2781 | j = i; |
| 2782 | 2782 | } |
| 2783 | 2783 | } |
| 2784 | 2784 | if( rndr.notes.misref.nUsed ){ |
| 2785 | 2785 | rndr.make.footnote_item(all_items, 0, -1, |
| @@ -2786,15 +2786,15 @@ | ||
| 2786 | 2786 | rndr.notes.misref.nUsed, rndr.make.opaque); |
| 2787 | 2787 | g.ftntsIssues[0] += rndr.notes.misref.nUsed; |
| 2788 | 2788 | } |
| 2789 | 2789 | while( ++j < COUNT_FOOTNOTES(notes) ){ |
| 2790 | 2790 | const struct footnote* x = CAST_AS_FOOTNOTES(notes) + j; |
| 2791 | - assert( !x->nUsed ); | |
| 2791 | + assert( !x->iMark ); | |
| 2792 | 2792 | assert( !x->bRndred ); |
| 2793 | 2793 | assert( (&x->id) + 1 == &x->text ); /* see html_footnote_item() */ |
| 2794 | 2794 | assert( (&x->upc)- 1 == &x->text ); |
| 2795 | - rndr.make.footnote_item(all_items,&x->text,0,0,rndr.make.opaque); | |
| 2795 | + rndr.make.footnote_item(all_items,&x->text,x->iMark,0,rndr.make.opaque); | |
| 2796 | 2796 | g.ftntsIssues[1]++; |
| 2797 | 2797 | } |
| 2798 | 2798 | rndr.make.footnotes(ob, all_items, rndr.make.opaque); |
| 2799 | 2799 | release_work_buffer(&rndr, all_items); |
| 2800 | 2800 | } |
| 2801 | 2801 |
| --- src/markdown.c | |
| +++ src/markdown.c | |
| @@ -2732,11 +2732,11 @@ | |
| 2732 | if( (blob_size(allNotes) || rndr.notes.misref.nUsed) ){ |
| 2733 | |
| 2734 | /* Footnotes must be parsed for the correct discovery of (back)links */ |
| 2735 | Blob *notes = new_work_buffer( &rndr ); |
| 2736 | Blob *tmp = new_work_buffer( &rndr ); |
| 2737 | int nMarks = -1; |
| 2738 | |
| 2739 | /* inline notes may get appended to rndr.notes.all while rendering */ |
| 2740 | while(1){ |
| 2741 | struct footnote *aNotes; |
| 2742 | const int N = COUNT_FOOTNOTES( allNotes ); |
| @@ -2745,11 +2745,11 @@ | |
| 2745 | blob_truncate(notes,0); |
| 2746 | blob_append(notes, blob_buffer(allNotes), blob_size(allNotes)); |
| 2747 | aNotes = CAST_AS_FOOTNOTES(notes); |
| 2748 | qsort(aNotes, N, sizeof(struct footnote), cmp_footnote_sort); |
| 2749 | |
| 2750 | if( nMarks == rndr.notes.nMarks ) break; |
| 2751 | nMarks = rndr.notes.nMarks; |
| 2752 | |
| 2753 | for(i=0; i<N; i++){ |
| 2754 | const int j = aNotes[i].index; |
| 2755 | struct footnote *x = CAST_AS_FOOTNOTES(allNotes) + j; |
| @@ -2773,13 +2773,13 @@ | |
| 2773 | if( rndr.make.footnote_item && rndr.make.footnotes ){ |
| 2774 | Blob *all_items = new_work_buffer(&rndr); |
| 2775 | int j = -1; |
| 2776 | for(i=0; i<COUNT_FOOTNOTES(notes); i++){ |
| 2777 | const struct footnote* x = CAST_AS_FOOTNOTES(notes) + i; |
| 2778 | if( x->bRndred ){ |
| 2779 | rndr.make.footnote_item(all_items, &x->text, x->iMark, |
| 2780 | x->nUsed, rndr.make.opaque); |
| 2781 | j = i; |
| 2782 | } |
| 2783 | } |
| 2784 | if( rndr.notes.misref.nUsed ){ |
| 2785 | rndr.make.footnote_item(all_items, 0, -1, |
| @@ -2786,15 +2786,15 @@ | |
| 2786 | rndr.notes.misref.nUsed, rndr.make.opaque); |
| 2787 | g.ftntsIssues[0] += rndr.notes.misref.nUsed; |
| 2788 | } |
| 2789 | while( ++j < COUNT_FOOTNOTES(notes) ){ |
| 2790 | const struct footnote* x = CAST_AS_FOOTNOTES(notes) + j; |
| 2791 | assert( !x->nUsed ); |
| 2792 | assert( !x->bRndred ); |
| 2793 | assert( (&x->id) + 1 == &x->text ); /* see html_footnote_item() */ |
| 2794 | assert( (&x->upc)- 1 == &x->text ); |
| 2795 | rndr.make.footnote_item(all_items,&x->text,0,0,rndr.make.opaque); |
| 2796 | g.ftntsIssues[1]++; |
| 2797 | } |
| 2798 | rndr.make.footnotes(ob, all_items, rndr.make.opaque); |
| 2799 | release_work_buffer(&rndr, all_items); |
| 2800 | } |
| 2801 |
| --- src/markdown.c | |
| +++ src/markdown.c | |
| @@ -2732,11 +2732,11 @@ | |
| 2732 | if( (blob_size(allNotes) || rndr.notes.misref.nUsed) ){ |
| 2733 | |
| 2734 | /* Footnotes must be parsed for the correct discovery of (back)links */ |
| 2735 | Blob *notes = new_work_buffer( &rndr ); |
| 2736 | Blob *tmp = new_work_buffer( &rndr ); |
| 2737 | int nMarks = -1, maxDepth = 5; |
| 2738 | |
| 2739 | /* inline notes may get appended to rndr.notes.all while rendering */ |
| 2740 | while(1){ |
| 2741 | struct footnote *aNotes; |
| 2742 | const int N = COUNT_FOOTNOTES( allNotes ); |
| @@ -2745,11 +2745,11 @@ | |
| 2745 | blob_truncate(notes,0); |
| 2746 | blob_append(notes, blob_buffer(allNotes), blob_size(allNotes)); |
| 2747 | aNotes = CAST_AS_FOOTNOTES(notes); |
| 2748 | qsort(aNotes, N, sizeof(struct footnote), cmp_footnote_sort); |
| 2749 | |
| 2750 | if( --maxDepth < 0 || nMarks == rndr.notes.nMarks ) break; |
| 2751 | nMarks = rndr.notes.nMarks; |
| 2752 | |
| 2753 | for(i=0; i<N; i++){ |
| 2754 | const int j = aNotes[i].index; |
| 2755 | struct footnote *x = CAST_AS_FOOTNOTES(allNotes) + j; |
| @@ -2773,13 +2773,13 @@ | |
| 2773 | if( rndr.make.footnote_item && rndr.make.footnotes ){ |
| 2774 | Blob *all_items = new_work_buffer(&rndr); |
| 2775 | int j = -1; |
| 2776 | for(i=0; i<COUNT_FOOTNOTES(notes); i++){ |
| 2777 | const struct footnote* x = CAST_AS_FOOTNOTES(notes) + i; |
| 2778 | if( x->iMark ){ |
| 2779 | rndr.make.footnote_item(all_items, &x->text, x->iMark, |
| 2780 | x->bRndred ? x->nUsed : 0, rndr.make.opaque); |
| 2781 | j = i; |
| 2782 | } |
| 2783 | } |
| 2784 | if( rndr.notes.misref.nUsed ){ |
| 2785 | rndr.make.footnote_item(all_items, 0, -1, |
| @@ -2786,15 +2786,15 @@ | |
| 2786 | rndr.notes.misref.nUsed, rndr.make.opaque); |
| 2787 | g.ftntsIssues[0] += rndr.notes.misref.nUsed; |
| 2788 | } |
| 2789 | while( ++j < COUNT_FOOTNOTES(notes) ){ |
| 2790 | const struct footnote* x = CAST_AS_FOOTNOTES(notes) + j; |
| 2791 | assert( !x->iMark ); |
| 2792 | assert( !x->bRndred ); |
| 2793 | assert( (&x->id) + 1 == &x->text ); /* see html_footnote_item() */ |
| 2794 | assert( (&x->upc)- 1 == &x->text ); |
| 2795 | rndr.make.footnote_item(all_items,&x->text,x->iMark,0,rndr.make.opaque); |
| 2796 | g.ftntsIssues[1]++; |
| 2797 | } |
| 2798 | rndr.make.footnotes(ob, all_items, rndr.make.opaque); |
| 2799 | release_work_buffer(&rndr, all_items); |
| 2800 | } |
| 2801 |
+23
-11
| --- src/markdown_html.c | ||
| +++ src/markdown_html.c | ||
| @@ -468,29 +468,35 @@ | ||
| 468 | 468 | } |
| 469 | 469 | if( i < nUsed ) BLOB_APPEND_LITERAL(ob," …"); |
| 470 | 470 | } |
| 471 | 471 | BLOB_APPEND_LITERAL(ob,"</sup>\n<span>Misreference</span>"); |
| 472 | 472 | |
| 473 | - }else if( nUsed ){ /* a regular footnote */ | |
| 473 | + }else if( iMark > 0 ){ /* a regular footnote */ | |
| 474 | 474 | char pos[24]; |
| 475 | - const char *join = ""; | |
| 475 | + int bJoin = 0; | |
| 476 | 476 | /* make.footnote_item() invocations should pass args accordingly */ |
| 477 | 477 | const struct Blob * upc = text+1; |
| 478 | 478 | #define _joined_footnote_indicator "<ul class='fn-joined'>" |
| 479 | 479 | #define _jfi_sz (sizeof(_joined_footnote_indicator)-1) |
| 480 | 480 | assert( text ); |
| 481 | 481 | assert( blob_size(text) ); |
| 482 | - if( blob_size(text)>=_jfi_sz && | |
| 483 | - !memcmp(blob_buffer(text),_joined_footnote_indicator,_jfi_sz)){ | |
| 484 | - join = "fn-joined "; | |
| 485 | - } | |
| 486 | 482 | memset(pos,0,24); |
| 487 | 483 | sprintf(pos, "%s-%i", unique, iMark); |
| 488 | - blob_appendf(ob, "<li id='footnote%s' class='%s", pos, join); | |
| 489 | - append_footnote_upc(ob, upc, 0); | |
| 484 | + | |
| 485 | + blob_appendf(ob, "<li id='footnote%s' class='", pos); | |
| 486 | + if( nUsed ){ | |
| 487 | + if( blob_size(text)>=_jfi_sz && | |
| 488 | + !memcmp(blob_buffer(text),_joined_footnote_indicator,_jfi_sz)){ | |
| 489 | + bJoin = 1; | |
| 490 | + BLOB_APPEND_LITERAL(ob, "fn-joined "); | |
| 491 | + } | |
| 492 | + append_footnote_upc(ob, upc, 0); | |
| 493 | + }else{ | |
| 494 | + BLOB_APPEND_LITERAL(ob, "fn-toodeep "); | |
| 495 | + } | |
| 490 | 496 | |
| 491 | - if( nUsed == 1 ){ | |
| 497 | + if( nUsed <= 1 ){ | |
| 492 | 498 | BLOB_APPEND_LITERAL(ob, "fn-monoref'><sup class='fn-backrefs'>"); |
| 493 | 499 | blob_appendf(ob,"<a id='footnote%s-a' href='", pos); |
| 494 | 500 | BLOB_APPEND_URI(ob, ctx); |
| 495 | 501 | blob_appendf(ob,"#noteref%s-a'>^</a>", pos); |
| 496 | 502 | }else{ |
| @@ -512,22 +518,28 @@ | ||
| 512 | 518 | } |
| 513 | 519 | if( i < nUsed ) BLOB_APPEND_LITERAL(ob," …"); |
| 514 | 520 | } |
| 515 | 521 | BLOB_APPEND_LITERAL(ob,"</sup>\n"); |
| 516 | 522 | append_footnote_upc(ob, upc, 1); |
| 517 | - if( join[0] ){ | |
| 523 | + if( bJoin ){ | |
| 518 | 524 | BLOB_APPEND_LITERAL(ob,"<sup class='fn-joined'></sup><ul>"); |
| 519 | 525 | blob_append(ob,blob_buffer(text)+_jfi_sz,blob_size(text)-_jfi_sz); |
| 520 | - }else{ | |
| 526 | + }else if( nUsed ){ | |
| 521 | 527 | BLOB_APPEND_BLOB(ob, text); |
| 528 | + }else{ | |
| 529 | + BLOB_APPEND_LITERAL(ob,"<i></i>\n" | |
| 530 | + "<pre><code class='language-markdown'>"); | |
| 531 | + html_escape(ob, blob_buffer(text), blob_size(text)); | |
| 532 | + BLOB_APPEND_LITERAL(ob,"</code></pre>"); | |
| 522 | 533 | } |
| 523 | 534 | #undef _joined_footnote_indicator |
| 524 | 535 | #undef _jfi_sz |
| 525 | 536 | }else{ |
| 526 | 537 | /* a footnote was defined but wasn't used */ |
| 527 | 538 | /* make.footnote_item() invocations should pass args accordingly */ |
| 528 | 539 | const struct Blob * id = text-1; |
| 540 | + assert( !nUsed ); | |
| 529 | 541 | assert( text ); |
| 530 | 542 | assert( blob_size(text) ); |
| 531 | 543 | assert( blob_size(id) ); |
| 532 | 544 | BLOB_APPEND_LITERAL(ob,"<li class='fn-unreferenced'>\n[^ <code>"); |
| 533 | 545 | html_escape(ob, blob_buffer(id), blob_size(id)); |
| 534 | 546 |
| --- src/markdown_html.c | |
| +++ src/markdown_html.c | |
| @@ -468,29 +468,35 @@ | |
| 468 | } |
| 469 | if( i < nUsed ) BLOB_APPEND_LITERAL(ob," …"); |
| 470 | } |
| 471 | BLOB_APPEND_LITERAL(ob,"</sup>\n<span>Misreference</span>"); |
| 472 | |
| 473 | }else if( nUsed ){ /* a regular footnote */ |
| 474 | char pos[24]; |
| 475 | const char *join = ""; |
| 476 | /* make.footnote_item() invocations should pass args accordingly */ |
| 477 | const struct Blob * upc = text+1; |
| 478 | #define _joined_footnote_indicator "<ul class='fn-joined'>" |
| 479 | #define _jfi_sz (sizeof(_joined_footnote_indicator)-1) |
| 480 | assert( text ); |
| 481 | assert( blob_size(text) ); |
| 482 | if( blob_size(text)>=_jfi_sz && |
| 483 | !memcmp(blob_buffer(text),_joined_footnote_indicator,_jfi_sz)){ |
| 484 | join = "fn-joined "; |
| 485 | } |
| 486 | memset(pos,0,24); |
| 487 | sprintf(pos, "%s-%i", unique, iMark); |
| 488 | blob_appendf(ob, "<li id='footnote%s' class='%s", pos, join); |
| 489 | append_footnote_upc(ob, upc, 0); |
| 490 | |
| 491 | if( nUsed == 1 ){ |
| 492 | BLOB_APPEND_LITERAL(ob, "fn-monoref'><sup class='fn-backrefs'>"); |
| 493 | blob_appendf(ob,"<a id='footnote%s-a' href='", pos); |
| 494 | BLOB_APPEND_URI(ob, ctx); |
| 495 | blob_appendf(ob,"#noteref%s-a'>^</a>", pos); |
| 496 | }else{ |
| @@ -512,22 +518,28 @@ | |
| 512 | } |
| 513 | if( i < nUsed ) BLOB_APPEND_LITERAL(ob," …"); |
| 514 | } |
| 515 | BLOB_APPEND_LITERAL(ob,"</sup>\n"); |
| 516 | append_footnote_upc(ob, upc, 1); |
| 517 | if( join[0] ){ |
| 518 | BLOB_APPEND_LITERAL(ob,"<sup class='fn-joined'></sup><ul>"); |
| 519 | blob_append(ob,blob_buffer(text)+_jfi_sz,blob_size(text)-_jfi_sz); |
| 520 | }else{ |
| 521 | BLOB_APPEND_BLOB(ob, text); |
| 522 | } |
| 523 | #undef _joined_footnote_indicator |
| 524 | #undef _jfi_sz |
| 525 | }else{ |
| 526 | /* a footnote was defined but wasn't used */ |
| 527 | /* make.footnote_item() invocations should pass args accordingly */ |
| 528 | const struct Blob * id = text-1; |
| 529 | assert( text ); |
| 530 | assert( blob_size(text) ); |
| 531 | assert( blob_size(id) ); |
| 532 | BLOB_APPEND_LITERAL(ob,"<li class='fn-unreferenced'>\n[^ <code>"); |
| 533 | html_escape(ob, blob_buffer(id), blob_size(id)); |
| 534 |
| --- src/markdown_html.c | |
| +++ src/markdown_html.c | |
| @@ -468,29 +468,35 @@ | |
| 468 | } |
| 469 | if( i < nUsed ) BLOB_APPEND_LITERAL(ob," …"); |
| 470 | } |
| 471 | BLOB_APPEND_LITERAL(ob,"</sup>\n<span>Misreference</span>"); |
| 472 | |
| 473 | }else if( iMark > 0 ){ /* a regular footnote */ |
| 474 | char pos[24]; |
| 475 | int bJoin = 0; |
| 476 | /* make.footnote_item() invocations should pass args accordingly */ |
| 477 | const struct Blob * upc = text+1; |
| 478 | #define _joined_footnote_indicator "<ul class='fn-joined'>" |
| 479 | #define _jfi_sz (sizeof(_joined_footnote_indicator)-1) |
| 480 | assert( text ); |
| 481 | assert( blob_size(text) ); |
| 482 | memset(pos,0,24); |
| 483 | sprintf(pos, "%s-%i", unique, iMark); |
| 484 | |
| 485 | blob_appendf(ob, "<li id='footnote%s' class='", pos); |
| 486 | if( nUsed ){ |
| 487 | if( blob_size(text)>=_jfi_sz && |
| 488 | !memcmp(blob_buffer(text),_joined_footnote_indicator,_jfi_sz)){ |
| 489 | bJoin = 1; |
| 490 | BLOB_APPEND_LITERAL(ob, "fn-joined "); |
| 491 | } |
| 492 | append_footnote_upc(ob, upc, 0); |
| 493 | }else{ |
| 494 | BLOB_APPEND_LITERAL(ob, "fn-toodeep "); |
| 495 | } |
| 496 | |
| 497 | if( nUsed <= 1 ){ |
| 498 | BLOB_APPEND_LITERAL(ob, "fn-monoref'><sup class='fn-backrefs'>"); |
| 499 | blob_appendf(ob,"<a id='footnote%s-a' href='", pos); |
| 500 | BLOB_APPEND_URI(ob, ctx); |
| 501 | blob_appendf(ob,"#noteref%s-a'>^</a>", pos); |
| 502 | }else{ |
| @@ -512,22 +518,28 @@ | |
| 518 | } |
| 519 | if( i < nUsed ) BLOB_APPEND_LITERAL(ob," …"); |
| 520 | } |
| 521 | BLOB_APPEND_LITERAL(ob,"</sup>\n"); |
| 522 | append_footnote_upc(ob, upc, 1); |
| 523 | if( bJoin ){ |
| 524 | BLOB_APPEND_LITERAL(ob,"<sup class='fn-joined'></sup><ul>"); |
| 525 | blob_append(ob,blob_buffer(text)+_jfi_sz,blob_size(text)-_jfi_sz); |
| 526 | }else if( nUsed ){ |
| 527 | BLOB_APPEND_BLOB(ob, text); |
| 528 | }else{ |
| 529 | BLOB_APPEND_LITERAL(ob,"<i></i>\n" |
| 530 | "<pre><code class='language-markdown'>"); |
| 531 | html_escape(ob, blob_buffer(text), blob_size(text)); |
| 532 | BLOB_APPEND_LITERAL(ob,"</code></pre>"); |
| 533 | } |
| 534 | #undef _joined_footnote_indicator |
| 535 | #undef _jfi_sz |
| 536 | }else{ |
| 537 | /* a footnote was defined but wasn't used */ |
| 538 | /* make.footnote_item() invocations should pass args accordingly */ |
| 539 | const struct Blob * id = text-1; |
| 540 | assert( !nUsed ); |
| 541 | assert( text ); |
| 542 | assert( blob_size(text) ); |
| 543 | assert( blob_size(id) ); |
| 544 | BLOB_APPEND_LITERAL(ob,"<li class='fn-unreferenced'>\n[^ <code>"); |
| 545 | html_escape(ob, blob_buffer(id), blob_size(id)); |
| 546 |
+51
| --- test/markdown-test3.md | ||
| +++ test/markdown-test3.md | ||
| @@ -123,10 +123,54 @@ | ||
| 123 | 123 | is not interpreted as such, instead it is emitted as plain text. |
| 124 | 124 | (^ |
| 125 | 125 | .bare.classlist.inside.inline.footnote: |
| 126 | 126 | )[^bare1] |
| 127 | 127 | [^bare2] |
| 128 | + | |
| 129 | +<html> | |
| 130 | + Click | |
| 131 | + <a href="?a=B"e='&nonASCII=😂&script=<script>alert('Broken!');</script>"> | |
| 132 | + here</a> and | |
| 133 | + <a href='?a=B"e="&nonASCII=😂&script=<script>alert("Broken!");</script>'> | |
| 134 | + here</a> | |
| 135 | + to test escaping of REQUEST_URI in the generated footnote markers. | |
| 136 | +</html> | |
| 137 | + | |
| 138 | +A depth of nesting must be limited. | |
| 139 | +(^ | |
| 140 | + A long chain of nested inline footnotes... | |
| 141 | + (^ | |
| 142 | + is a rather unusual thing... | |
| 143 | + (^ | |
| 144 | + and requires extra CPU cycles for processing. | |
| 145 | + (^ | |
| 146 | + Theoretically speaking O(n<sup>2</sup>). | |
| 147 | + (^ | |
| 148 | + Thus it is worth dismissing those footnotes... | |
| 149 | + (^ | |
| 150 | + that are nested deeper than on a certain level. | |
| 151 | + (^ | |
| 152 | + A particular value for that limit... | |
| 153 | + (^ | |
| 154 | + is hard-coded in src/markdown.c ... | |
| 155 | + (^ | |
| 156 | + in function `markdown()` ... | |
| 157 | + (^ | |
| 158 | + in variable named `maxDepth`. | |
| 159 | + (^ | |
| 160 | + For the time being, its value is **5** | |
| 161 | + ) | |
| 162 | + ) | |
| 163 | + ) | |
| 164 | + ) | |
| 165 | + ) | |
| 166 | + ) | |
| 167 | + ) | |
| 168 | + ) | |
| 169 | + ) | |
| 170 | + ) | |
| 171 | +) | |
| 128 | 172 | |
| 129 | 173 | ## Footnotes |
| 130 | 174 | |
| 131 | 175 | [branch]: /timeline?r=markdown-footnotes&nowiki |
| 132 | 176 | |
| @@ -177,5 +221,12 @@ | ||
| 177 | 221 | |
| 178 | 222 | [^nostyle]: |
| 179 | 223 | .unused.classes: |
| 180 | 224 | In that case text of the footnote just looks like as if |
| 181 | 225 | no special processing occured. |
| 226 | + | |
| 227 | + | |
| 228 | +[^ <script>alert("You have been pwned!");</script> ]: Labels are escaped | |
| 229 | + | |
| 230 | +[^ <textarea>"Last words here...' ]: | |
| 231 | + <textarea>Content is also escaped</textarea> | |
| 232 | + | |
| 182 | 233 |
| --- test/markdown-test3.md | |
| +++ test/markdown-test3.md | |
| @@ -123,10 +123,54 @@ | |
| 123 | is not interpreted as such, instead it is emitted as plain text. |
| 124 | (^ |
| 125 | .bare.classlist.inside.inline.footnote: |
| 126 | )[^bare1] |
| 127 | [^bare2] |
| 128 | |
| 129 | ## Footnotes |
| 130 | |
| 131 | [branch]: /timeline?r=markdown-footnotes&nowiki |
| 132 | |
| @@ -177,5 +221,12 @@ | |
| 177 | |
| 178 | [^nostyle]: |
| 179 | .unused.classes: |
| 180 | In that case text of the footnote just looks like as if |
| 181 | no special processing occured. |
| 182 |
| --- test/markdown-test3.md | |
| +++ test/markdown-test3.md | |
| @@ -123,10 +123,54 @@ | |
| 123 | is not interpreted as such, instead it is emitted as plain text. |
| 124 | (^ |
| 125 | .bare.classlist.inside.inline.footnote: |
| 126 | )[^bare1] |
| 127 | [^bare2] |
| 128 | |
| 129 | <html> |
| 130 | Click |
| 131 | <a href="?a=B"e='&nonASCII=😂&script=<script>alert('Broken!');</script>"> |
| 132 | here</a> and |
| 133 | <a href='?a=B"e="&nonASCII=😂&script=<script>alert("Broken!");</script>'> |
| 134 | here</a> |
| 135 | to test escaping of REQUEST_URI in the generated footnote markers. |
| 136 | </html> |
| 137 | |
| 138 | A depth of nesting must be limited. |
| 139 | (^ |
| 140 | A long chain of nested inline footnotes... |
| 141 | (^ |
| 142 | is a rather unusual thing... |
| 143 | (^ |
| 144 | and requires extra CPU cycles for processing. |
| 145 | (^ |
| 146 | Theoretically speaking O(n<sup>2</sup>). |
| 147 | (^ |
| 148 | Thus it is worth dismissing those footnotes... |
| 149 | (^ |
| 150 | that are nested deeper than on a certain level. |
| 151 | (^ |
| 152 | A particular value for that limit... |
| 153 | (^ |
| 154 | is hard-coded in src/markdown.c ... |
| 155 | (^ |
| 156 | in function `markdown()` ... |
| 157 | (^ |
| 158 | in variable named `maxDepth`. |
| 159 | (^ |
| 160 | For the time being, its value is **5** |
| 161 | ) |
| 162 | ) |
| 163 | ) |
| 164 | ) |
| 165 | ) |
| 166 | ) |
| 167 | ) |
| 168 | ) |
| 169 | ) |
| 170 | ) |
| 171 | ) |
| 172 | |
| 173 | ## Footnotes |
| 174 | |
| 175 | [branch]: /timeline?r=markdown-footnotes&nowiki |
| 176 | |
| @@ -177,5 +221,12 @@ | |
| 221 | |
| 222 | [^nostyle]: |
| 223 | .unused.classes: |
| 224 | In that case text of the footnote just looks like as if |
| 225 | no special processing occured. |
| 226 | |
| 227 | |
| 228 | [^ <script>alert("You have been pwned!");</script> ]: Labels are escaped |
| 229 | |
| 230 | [^ <textarea>"Last words here...' ]: |
| 231 | <textarea>Content is also escaped</textarea> |
| 232 | |
| 233 |