Fossil SCM

Revert the change at [a3ab0c6186b43236]. In its place, add the blob_append_safe_html() routine that stricts HTML generated by Markdown to be in the set of safe elements as defined by Fossil-wiki. Omit any unsafe elements and/or attributes. Omit unmatched close-tags. Insert missing close-tags.

drh 2020-06-01 14:59 trunk
Commit aadf91723adc8df4904364369d6b3b4bf005187e59fbf55443680a0e6bcd3bc3
+2 -1
--- src/config.h
+++ src/config.h
@@ -254,11 +254,12 @@
254254
#endif
255255
256256
/*
257257
** Number of elements in an array
258258
*/
259
-#define count(X) (sizeof(X)/sizeof(X[0]))
259
+#define count(X) (int)(sizeof(X)/sizeof(X[0]))
260
+#define ArraySize(X) (int)(sizeof(X)/sizeof(X[0]))
260261
261262
/*
262263
** The pledge() interface is currently only available on OpenBSD 5.9
263264
** and later. Make calls to fossil_pledge() no-ops on all platforms
264265
** that omit the HAVE_PLEDGE configuration parameter.
265266
--- src/config.h
+++ src/config.h
@@ -254,11 +254,12 @@
254 #endif
255
256 /*
257 ** Number of elements in an array
258 */
259 #define count(X) (sizeof(X)/sizeof(X[0]))
 
260
261 /*
262 ** The pledge() interface is currently only available on OpenBSD 5.9
263 ** and later. Make calls to fossil_pledge() no-ops on all platforms
264 ** that omit the HAVE_PLEDGE configuration parameter.
265
--- src/config.h
+++ src/config.h
@@ -254,11 +254,12 @@
254 #endif
255
256 /*
257 ** Number of elements in an array
258 */
259 #define count(X) (int)(sizeof(X)/sizeof(X[0]))
260 #define ArraySize(X) (int)(sizeof(X)/sizeof(X[0]))
261
262 /*
263 ** The pledge() interface is currently only available on OpenBSD 5.9
264 ** and later. Make calls to fossil_pledge() no-ops on all platforms
265 ** that omit the HAVE_PLEDGE configuration parameter.
266
+29 -33
--- src/markdown.c
+++ src/markdown.c
@@ -163,45 +163,48 @@
163163
164164
/* html_tag -- structure for quick HTML tag search (inspired from discount) */
165165
struct html_tag {
166166
const char *text;
167167
int size;
168
- int flags;
169168
};
170169
171
-/* Allowed bits in html_tag.flags */
172
-#define HTMLTAG_FORBIDDEN 0x001 /* escape */
173170
174171
175172
/********************
176173
* GLOBAL VARIABLES *
177174
********************/
178175
179176
/* block_tags -- recognised block tags, sorted by cmp_html_tag */
180177
static const struct html_tag block_tags[] = {
181
- { "p", 1, 0 },
182
- { "dl", 2, 0 },
183
- { "h1", 2, 0 },
184
- { "h2", 2, 0 },
185
- { "h3", 2, 0 },
186
- { "h4", 2, 0 },
187
- { "h5", 2, 0 },
188
- { "h6", 2, 0 },
189
- { "ol", 2, 0 },
190
- { "ul", 2, 0 },
191
- { "div", 3, 0 },
192
- { "pre", 3, 0 },
193
- { "form", 4, HTMLTAG_FORBIDDEN },
194
- { "math", 4, 0 },
195
- { "style", 5, HTMLTAG_FORBIDDEN },
196
- { "table", 5, 0 },
197
- { "iframe", 6, HTMLTAG_FORBIDDEN },
198
- { "script", 6, HTMLTAG_FORBIDDEN },
199
- { "fieldset", 8, 0 },
200
- { "noscript", 8, HTMLTAG_FORBIDDEN },
201
- { "blockquote", 10, 0 }
178
+ { "p", 1 },
179
+ { "dl", 2 },
180
+ { "h1", 2 },
181
+ { "h2", 2 },
182
+ { "h3", 2 },
183
+ { "h4", 2 },
184
+ { "h5", 2 },
185
+ { "h6", 2 },
186
+ { "ol", 2 },
187
+ { "ul", 2 },
188
+ { "del", 3 },
189
+ { "div", 3 },
190
+ { "ins", 3 },
191
+ { "pre", 3 },
192
+ { "form", 4 },
193
+ { "math", 4 },
194
+ { "table", 5 },
195
+ { "iframe", 6 },
196
+ { "script", 6 },
197
+ { "fieldset", 8 },
198
+ { "noscript", 8 },
199
+ { "blockquote", 10 }
202200
};
201
+
202
+#define INS_TAG (block_tags + 12)
203
+#define DEL_TAG (block_tags + 10)
204
+
205
+
203206
204207
/***************************
205208
* STATIC HELPER FUNCTIONS *
206209
***************************/
207210
@@ -1780,11 +1783,12 @@
17801783
}
17811784
}
17821785
#endif
17831786
17841787
/* if not found, trying a second pass looking for indented match */
1785
- if( !found ){
1788
+ /* but not if tag is "ins" or "del" (following original Markdown.pl) */
1789
+ if( !found && curtag!=INS_TAG && curtag!=DEL_TAG ){
17861790
i = 1;
17871791
while( i<size ){
17881792
i++;
17891793
while( i<size && !(data[i-1]=='<' && data[i]=='/') ){ i++; }
17901794
if( (i+2+curtag->size)>=size ) break;
@@ -1795,18 +1799,10 @@
17951799
break;
17961800
}
17971801
}
17981802
}
17991803
1800
- /* Do not display certain HTML tags */
1801
- if( curtag->flags & HTMLTAG_FORBIDDEN ){
1802
- if( !found ){
1803
- for(i=1; i<size && data[i]!='>'; i++){}
1804
- }
1805
- return i;
1806
- }
1807
-
18081804
if( !found ) return 0;
18091805
18101806
/* the end of the block has been found */
18111807
blob_init(&work, data, i);
18121808
if( rndr->make.blockhtml ){
18131809
--- src/markdown.c
+++ src/markdown.c
@@ -163,45 +163,48 @@
163
164 /* html_tag -- structure for quick HTML tag search (inspired from discount) */
165 struct html_tag {
166 const char *text;
167 int size;
168 int flags;
169 };
170
171 /* Allowed bits in html_tag.flags */
172 #define HTMLTAG_FORBIDDEN 0x001 /* escape */
173
174
175 /********************
176 * GLOBAL VARIABLES *
177 ********************/
178
179 /* block_tags -- recognised block tags, sorted by cmp_html_tag */
180 static const struct html_tag block_tags[] = {
181 { "p", 1, 0 },
182 { "dl", 2, 0 },
183 { "h1", 2, 0 },
184 { "h2", 2, 0 },
185 { "h3", 2, 0 },
186 { "h4", 2, 0 },
187 { "h5", 2, 0 },
188 { "h6", 2, 0 },
189 { "ol", 2, 0 },
190 { "ul", 2, 0 },
191 { "div", 3, 0 },
192 { "pre", 3, 0 },
193 { "form", 4, HTMLTAG_FORBIDDEN },
194 { "math", 4, 0 },
195 { "style", 5, HTMLTAG_FORBIDDEN },
196 { "table", 5, 0 },
197 { "iframe", 6, HTMLTAG_FORBIDDEN },
198 { "script", 6, HTMLTAG_FORBIDDEN },
199 { "fieldset", 8, 0 },
200 { "noscript", 8, HTMLTAG_FORBIDDEN },
201 { "blockquote", 10, 0 }
 
202 };
 
 
 
 
 
203
204 /***************************
205 * STATIC HELPER FUNCTIONS *
206 ***************************/
207
@@ -1780,11 +1783,12 @@
1780 }
1781 }
1782 #endif
1783
1784 /* if not found, trying a second pass looking for indented match */
1785 if( !found ){
 
1786 i = 1;
1787 while( i<size ){
1788 i++;
1789 while( i<size && !(data[i-1]=='<' && data[i]=='/') ){ i++; }
1790 if( (i+2+curtag->size)>=size ) break;
@@ -1795,18 +1799,10 @@
1795 break;
1796 }
1797 }
1798 }
1799
1800 /* Do not display certain HTML tags */
1801 if( curtag->flags & HTMLTAG_FORBIDDEN ){
1802 if( !found ){
1803 for(i=1; i<size && data[i]!='>'; i++){}
1804 }
1805 return i;
1806 }
1807
1808 if( !found ) return 0;
1809
1810 /* the end of the block has been found */
1811 blob_init(&work, data, i);
1812 if( rndr->make.blockhtml ){
1813
--- src/markdown.c
+++ src/markdown.c
@@ -163,45 +163,48 @@
163
164 /* html_tag -- structure for quick HTML tag search (inspired from discount) */
165 struct html_tag {
166 const char *text;
167 int size;
 
168 };
169
 
 
170
171
172 /********************
173 * GLOBAL VARIABLES *
174 ********************/
175
176 /* block_tags -- recognised block tags, sorted by cmp_html_tag */
177 static const struct html_tag block_tags[] = {
178 { "p", 1 },
179 { "dl", 2 },
180 { "h1", 2 },
181 { "h2", 2 },
182 { "h3", 2 },
183 { "h4", 2 },
184 { "h5", 2 },
185 { "h6", 2 },
186 { "ol", 2 },
187 { "ul", 2 },
188 { "del", 3 },
189 { "div", 3 },
190 { "ins", 3 },
191 { "pre", 3 },
192 { "form", 4 },
193 { "math", 4 },
194 { "table", 5 },
195 { "iframe", 6 },
196 { "script", 6 },
197 { "fieldset", 8 },
198 { "noscript", 8 },
199 { "blockquote", 10 }
200 };
201
202 #define INS_TAG (block_tags + 12)
203 #define DEL_TAG (block_tags + 10)
204
205
206
207 /***************************
208 * STATIC HELPER FUNCTIONS *
209 ***************************/
210
@@ -1780,11 +1783,12 @@
1783 }
1784 }
1785 #endif
1786
1787 /* if not found, trying a second pass looking for indented match */
1788 /* but not if tag is "ins" or "del" (following original Markdown.pl) */
1789 if( !found && curtag!=INS_TAG && curtag!=DEL_TAG ){
1790 i = 1;
1791 while( i<size ){
1792 i++;
1793 while( i<size && !(data[i-1]=='<' && data[i]=='/') ){ i++; }
1794 if( (i+2+curtag->size)>=size ) break;
@@ -1795,18 +1799,10 @@
1799 break;
1800 }
1801 }
1802 }
1803
 
 
 
 
 
 
 
 
1804 if( !found ) return 0;
1805
1806 /* the end of the block has been found */
1807 blob_init(&work, data, i);
1808 if( rndr->make.blockhtml ){
1809
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -45,11 +45,11 @@
4545
* the error is not overly explicit.
4646
*/
4747
4848
/* BLOB_APPEND_BLOB -- append blob contents to another */
4949
#define BLOB_APPEND_BLOB(dest, src) \
50
- blob_append((dest), blob_buffer(src), blob_size(src))
50
+ blob_append_safe_html((dest), blob_buffer(src), blob_size(src))
5151
5252
5353
/* HTML escapes
5454
**
5555
** html_escape() converts < to &lt;, > to &gt;, and & to &amp;.
@@ -147,11 +147,11 @@
147147
int nTag = html_tag_length(data);
148148
blob_append(title, data+nTag, size - nTag - 5);
149149
return;
150150
}
151151
INTER_BLOCK(ob);
152
- blob_append(ob, data, size);
152
+ blob_append_safe_html(ob, data, size);
153153
BLOB_APPEND_LITERAL(ob, "\n");
154154
}
155155
156156
static void html_blockcode(struct Blob *ob, struct Blob *text, void *opaque){
157157
INTER_BLOCK(ob);
158158
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -45,11 +45,11 @@
45 * the error is not overly explicit.
46 */
47
48 /* BLOB_APPEND_BLOB -- append blob contents to another */
49 #define BLOB_APPEND_BLOB(dest, src) \
50 blob_append((dest), blob_buffer(src), blob_size(src))
51
52
53 /* HTML escapes
54 **
55 ** html_escape() converts < to &lt;, > to &gt;, and & to &amp;.
@@ -147,11 +147,11 @@
147 int nTag = html_tag_length(data);
148 blob_append(title, data+nTag, size - nTag - 5);
149 return;
150 }
151 INTER_BLOCK(ob);
152 blob_append(ob, data, size);
153 BLOB_APPEND_LITERAL(ob, "\n");
154 }
155
156 static void html_blockcode(struct Blob *ob, struct Blob *text, void *opaque){
157 INTER_BLOCK(ob);
158
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -45,11 +45,11 @@
45 * the error is not overly explicit.
46 */
47
48 /* BLOB_APPEND_BLOB -- append blob contents to another */
49 #define BLOB_APPEND_BLOB(dest, src) \
50 blob_append_safe_html((dest), blob_buffer(src), blob_size(src))
51
52
53 /* HTML escapes
54 **
55 ** html_escape() converts < to &lt;, > to &gt;, and & to &amp;.
@@ -147,11 +147,11 @@
147 int nTag = html_tag_length(data);
148 blob_append(title, data+nTag, size - nTag - 5);
149 return;
150 }
151 INTER_BLOCK(ob);
152 blob_append_safe_html(ob, data, size);
153 BLOB_APPEND_LITERAL(ob, "\n");
154 }
155
156 static void html_blockcode(struct Blob *ob, struct Blob *text, void *opaque){
157 INTER_BLOCK(ob);
158
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -2396,5 +2396,179 @@
23962396
blob_reset(&in);
23972397
fossil_puts(blob_str(&out), 0);
23982398
blob_reset(&out);
23992399
}
24002400
}
2401
+
2402
+/*
2403
+** An instance of this object keeps track of the nesting of HTML
2404
+** elements for blob_append_safe_html().
2405
+*/
2406
+#if LOCAL_INTERFACE
2407
+struct HtmlTagStack {
2408
+ int n; /* Current tag stack depth */
2409
+ int nAlloc; /* Space allocated for aStack[] */
2410
+ int *aStack; /* The stack of tags */
2411
+ int aSpace[10]; /* Initial static space, to avoid malloc() */
2412
+};
2413
+#endif /* LOCAL_INTERFACE */
2414
+
2415
+/*
2416
+** Initialize bulk memory to a valid empty tagstack.
2417
+*/
2418
+void html_tagstack_init(HtmlTagStack *p){
2419
+ p->n = 0;
2420
+ p->nAlloc = 0;
2421
+ p->aStack = p->aSpace;
2422
+}
2423
+
2424
+/*
2425
+** Push a new element onto the tag statk
2426
+*/
2427
+void html_tagstack_push(HtmlTagStack *p, int e){
2428
+ if( p->n>=ArraySize(p->aSpace) && p->n>=p->nAlloc ){
2429
+ if( p->nAlloc==0 ){
2430
+ int *aNew;
2431
+ p->nAlloc = 50;
2432
+ aNew = fossil_malloc( sizeof(p->aStack[0])*p->nAlloc );
2433
+ memcpy(aNew, p->aStack, sizeof(p->aStack[0])*p->n );
2434
+ p->aStack = aNew;
2435
+ }else{
2436
+ p->nAlloc *= 2;
2437
+ p->aStack = fossil_realloc(p->aStack, sizeof(p->aStack[0])*p->nAlloc );
2438
+ }
2439
+ }
2440
+ p->aStack[p->n++] = e;
2441
+}
2442
+
2443
+/*
2444
+** Clear a tag stack, reclaiming any memory allocations.
2445
+*/
2446
+void html_tagstack_clear(HtmlTagStack *p){
2447
+ if( p->nAlloc ){
2448
+ fossil_free(p->aStack);
2449
+ p->nAlloc = 0;
2450
+ p->aStack = p->aSpace;
2451
+ }
2452
+ p->n = 0;
2453
+}
2454
+
2455
+/*
2456
+** The HTML end-tag eEnd wants to be added to pBlob.
2457
+**
2458
+** If an open-tag for eEnd exists anywhere on the stack, then
2459
+** pop it and all prior elements from the task, issuing appropriate
2460
+** end-tags as you go.
2461
+**
2462
+** If there is no open-tag for eEnd on the stack, then this
2463
+** routine is a no-op.
2464
+*/
2465
+void html_tagstack_pop(HtmlTagStack *p, Blob *pBlob, int eEnd){
2466
+ int i;
2467
+ for(i=p->n-1; i>=0 && p->aStack[i]!=eEnd; i--){}
2468
+ if( i<0 ) return;
2469
+ do{
2470
+ p->n--;
2471
+ blob_appendf(pBlob, "</%s>", aMarkup[eEnd].zName);
2472
+ }while( p->aStack[p->n]!=eEnd );
2473
+}
2474
+
2475
+/*
2476
+** Append HTML text to a Blob object. The appended text is modified
2477
+** changed in the following ways:
2478
+**
2479
+** 1. Omit any elements that are not on the AllowedMarkup list.
2480
+**
2481
+** 2. Omit any attributes that are not on the AllowedMarkup list.
2482
+**
2483
+** 3. Omit any surplus close-tags.
2484
+**
2485
+** 4. Insert additional close-tags as necessary so that all
2486
+** non-empty tags in the input have a corresponding close tag.
2487
+** Non-empty tags are elements other than <br>, <hr>, <img>, etc.
2488
+**
2489
+** The input must be writable. Temporary changes may be made to the
2490
+** input, but the input is restored to its original state prior to
2491
+** returning. If zHtml[nHtml] is not a zero character, then a zero
2492
+** might be written in that position temporarily, but that slot will
2493
+** also be restored before this routine returns.
2494
+*/
2495
+void blob_append_safe_html(Blob *pBlob, char *zHtml, int nHtml){
2496
+ char cLast;
2497
+ int i, j, n;
2498
+ HtmlTagStack s;
2499
+ ParsedMarkup markup;
2500
+ cLast = zHtml[nHtml];
2501
+ zHtml[nHtml] = 0;
2502
+ html_tagstack_init(&s);
2503
+
2504
+ i = 0;
2505
+ while( i<nHtml ){
2506
+ if( zHtml[i]=='<' ){
2507
+ j = i;
2508
+ }else{
2509
+ char *z = strchr(zHtml+i, '<');
2510
+ if( z==0 ){
2511
+ blob_append(pBlob, zHtml+i, nHtml-i);
2512
+ break;
2513
+ }
2514
+ j = (int)(z - zHtml);
2515
+ blob_append(pBlob, zHtml+i, j-i);
2516
+ }
2517
+ n = html_tag_length(zHtml+j);
2518
+ if( n==0 ){
2519
+ blob_append(pBlob, "&lt;", 4);
2520
+ i = j+1;
2521
+ continue;
2522
+ }else{
2523
+ i = j + n;
2524
+ }
2525
+ parseMarkup(&markup, zHtml+j);
2526
+ if( markup.iCode!=MARKUP_INVALID ){
2527
+ if( markup.endTag ){
2528
+ html_tagstack_pop(&s, pBlob, markup.iCode);
2529
+ }else{
2530
+ renderMarkup(pBlob, &markup);
2531
+ if( markup.iType!=MUTYPE_SINGLE ){
2532
+ html_tagstack_push(&s, markup.iCode);
2533
+ }
2534
+ }
2535
+ }
2536
+ unparseMarkup(&markup);
2537
+ }
2538
+ while( s.n>0 ){
2539
+ s.n--;
2540
+ blob_appendf(pBlob, "</%s>", aMarkup[s.aStack[s.n]]);
2541
+ }
2542
+ html_tagstack_clear(&s);
2543
+ zHtml[nHtml] = cLast;
2544
+}
2545
+
2546
+
2547
+/*
2548
+** COMMAND: test-safe-html
2549
+**
2550
+** Usage: %fossil test-safe-html FILE ...
2551
+**
2552
+** Read files named on the command-line. Send the text of each file
2553
+** through blob_append_safe_html() and then write the result on
2554
+** standard output.
2555
+*/
2556
+void test_safe_html_cmd(void){
2557
+ int i;
2558
+ Blob x;
2559
+ Blob y;
2560
+ for(i=2; i<g.argc; i++){
2561
+ char *z;
2562
+ int n;
2563
+ blob_read_from_file(&x, g.argv[i], ExtFILE);
2564
+ blob_init(&y, 0, 0);
2565
+ blob_terminate(&x);
2566
+ blob_append_safe_html(&y, blob_buffer(&x), blob_size(&x));
2567
+ blob_reset(&x);
2568
+ z = blob_str(&y);
2569
+ n = blob_size(&y);
2570
+ while( n>0 && (z[n-1]=='\n' || z[n-1]=='\r') ) n--;
2571
+ fossil_print("%.*s\n", n, z);
2572
+ blob_reset(&y);
2573
+ }
2574
+}
24012575
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -2396,5 +2396,179 @@
2396 blob_reset(&in);
2397 fossil_puts(blob_str(&out), 0);
2398 blob_reset(&out);
2399 }
2400 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2401
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -2396,5 +2396,179 @@
2396 blob_reset(&in);
2397 fossil_puts(blob_str(&out), 0);
2398 blob_reset(&out);
2399 }
2400 }
2401
2402 /*
2403 ** An instance of this object keeps track of the nesting of HTML
2404 ** elements for blob_append_safe_html().
2405 */
2406 #if LOCAL_INTERFACE
2407 struct HtmlTagStack {
2408 int n; /* Current tag stack depth */
2409 int nAlloc; /* Space allocated for aStack[] */
2410 int *aStack; /* The stack of tags */
2411 int aSpace[10]; /* Initial static space, to avoid malloc() */
2412 };
2413 #endif /* LOCAL_INTERFACE */
2414
2415 /*
2416 ** Initialize bulk memory to a valid empty tagstack.
2417 */
2418 void html_tagstack_init(HtmlTagStack *p){
2419 p->n = 0;
2420 p->nAlloc = 0;
2421 p->aStack = p->aSpace;
2422 }
2423
2424 /*
2425 ** Push a new element onto the tag statk
2426 */
2427 void html_tagstack_push(HtmlTagStack *p, int e){
2428 if( p->n>=ArraySize(p->aSpace) && p->n>=p->nAlloc ){
2429 if( p->nAlloc==0 ){
2430 int *aNew;
2431 p->nAlloc = 50;
2432 aNew = fossil_malloc( sizeof(p->aStack[0])*p->nAlloc );
2433 memcpy(aNew, p->aStack, sizeof(p->aStack[0])*p->n );
2434 p->aStack = aNew;
2435 }else{
2436 p->nAlloc *= 2;
2437 p->aStack = fossil_realloc(p->aStack, sizeof(p->aStack[0])*p->nAlloc );
2438 }
2439 }
2440 p->aStack[p->n++] = e;
2441 }
2442
2443 /*
2444 ** Clear a tag stack, reclaiming any memory allocations.
2445 */
2446 void html_tagstack_clear(HtmlTagStack *p){
2447 if( p->nAlloc ){
2448 fossil_free(p->aStack);
2449 p->nAlloc = 0;
2450 p->aStack = p->aSpace;
2451 }
2452 p->n = 0;
2453 }
2454
2455 /*
2456 ** The HTML end-tag eEnd wants to be added to pBlob.
2457 **
2458 ** If an open-tag for eEnd exists anywhere on the stack, then
2459 ** pop it and all prior elements from the task, issuing appropriate
2460 ** end-tags as you go.
2461 **
2462 ** If there is no open-tag for eEnd on the stack, then this
2463 ** routine is a no-op.
2464 */
2465 void html_tagstack_pop(HtmlTagStack *p, Blob *pBlob, int eEnd){
2466 int i;
2467 for(i=p->n-1; i>=0 && p->aStack[i]!=eEnd; i--){}
2468 if( i<0 ) return;
2469 do{
2470 p->n--;
2471 blob_appendf(pBlob, "</%s>", aMarkup[eEnd].zName);
2472 }while( p->aStack[p->n]!=eEnd );
2473 }
2474
2475 /*
2476 ** Append HTML text to a Blob object. The appended text is modified
2477 ** changed in the following ways:
2478 **
2479 ** 1. Omit any elements that are not on the AllowedMarkup list.
2480 **
2481 ** 2. Omit any attributes that are not on the AllowedMarkup list.
2482 **
2483 ** 3. Omit any surplus close-tags.
2484 **
2485 ** 4. Insert additional close-tags as necessary so that all
2486 ** non-empty tags in the input have a corresponding close tag.
2487 ** Non-empty tags are elements other than <br>, <hr>, <img>, etc.
2488 **
2489 ** The input must be writable. Temporary changes may be made to the
2490 ** input, but the input is restored to its original state prior to
2491 ** returning. If zHtml[nHtml] is not a zero character, then a zero
2492 ** might be written in that position temporarily, but that slot will
2493 ** also be restored before this routine returns.
2494 */
2495 void blob_append_safe_html(Blob *pBlob, char *zHtml, int nHtml){
2496 char cLast;
2497 int i, j, n;
2498 HtmlTagStack s;
2499 ParsedMarkup markup;
2500 cLast = zHtml[nHtml];
2501 zHtml[nHtml] = 0;
2502 html_tagstack_init(&s);
2503
2504 i = 0;
2505 while( i<nHtml ){
2506 if( zHtml[i]=='<' ){
2507 j = i;
2508 }else{
2509 char *z = strchr(zHtml+i, '<');
2510 if( z==0 ){
2511 blob_append(pBlob, zHtml+i, nHtml-i);
2512 break;
2513 }
2514 j = (int)(z - zHtml);
2515 blob_append(pBlob, zHtml+i, j-i);
2516 }
2517 n = html_tag_length(zHtml+j);
2518 if( n==0 ){
2519 blob_append(pBlob, "&lt;", 4);
2520 i = j+1;
2521 continue;
2522 }else{
2523 i = j + n;
2524 }
2525 parseMarkup(&markup, zHtml+j);
2526 if( markup.iCode!=MARKUP_INVALID ){
2527 if( markup.endTag ){
2528 html_tagstack_pop(&s, pBlob, markup.iCode);
2529 }else{
2530 renderMarkup(pBlob, &markup);
2531 if( markup.iType!=MUTYPE_SINGLE ){
2532 html_tagstack_push(&s, markup.iCode);
2533 }
2534 }
2535 }
2536 unparseMarkup(&markup);
2537 }
2538 while( s.n>0 ){
2539 s.n--;
2540 blob_appendf(pBlob, "</%s>", aMarkup[s.aStack[s.n]]);
2541 }
2542 html_tagstack_clear(&s);
2543 zHtml[nHtml] = cLast;
2544 }
2545
2546
2547 /*
2548 ** COMMAND: test-safe-html
2549 **
2550 ** Usage: %fossil test-safe-html FILE ...
2551 **
2552 ** Read files named on the command-line. Send the text of each file
2553 ** through blob_append_safe_html() and then write the result on
2554 ** standard output.
2555 */
2556 void test_safe_html_cmd(void){
2557 int i;
2558 Blob x;
2559 Blob y;
2560 for(i=2; i<g.argc; i++){
2561 char *z;
2562 int n;
2563 blob_read_from_file(&x, g.argv[i], ExtFILE);
2564 blob_init(&y, 0, 0);
2565 blob_terminate(&x);
2566 blob_append_safe_html(&y, blob_buffer(&x), blob_size(&x));
2567 blob_reset(&x);
2568 z = blob_str(&y);
2569 n = blob_size(&y);
2570 while( n>0 && (z[n-1]=='\n' || z[n-1]=='\r') ) n--;
2571 fossil_print("%.*s\n", n, z);
2572 blob_reset(&y);
2573 }
2574 }
2575

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button