Fossil SCM
The stricter attribute checking applied in [6436ecf911] cause the img srcset attribute to not get the repo-root (%R) rewriting that src gets. This breaks existing docs that used this for resolution-dependent image rendering in MD and such. For testing the fix, this adds the --top flag to the test-safe-html command, since a regular "fossil ui" doesn't show the problem; it either has to be hosted underneath a URL by an RP server or via this test option to see the effect of %R rewriting.
Commit
fbffbe115252be5e20782643eeb542186f7903332c28589fc5f02577b6760760
Parent
407e6c63ae06eb2…
1 file changed
+55
-7
+55
-7
| --- src/wikiformat.c | ||
| +++ src/wikiformat.c | ||
| @@ -984,10 +984,57 @@ | ||
| 984 | 984 | while( fossil_isspace(z[i]) ){ i++; } |
| 985 | 985 | if( z[i]==0 || z[i]=='>' || (z[i]=='/' && z[i+1]=='>') ) break; |
| 986 | 986 | } |
| 987 | 987 | return seen; |
| 988 | 988 | } |
| 989 | + | |
| 990 | +/* | |
| 991 | +** Append an HTML attribute value, rewriting root-relative URLs in src | |
| 992 | +** and srcset the same way (%R prefix). | |
| 993 | +*/ | |
| 994 | +static void appendRewrittenAttrValue(Blob *pOut, int iACode, const char *zVal){ | |
| 995 | + if( iACode==ATTR_SRC && zVal[0]=='/' ){ | |
| 996 | + blob_appendf(pOut, "=\"%R%s\"", zVal); | |
| 997 | + return; | |
| 998 | + } | |
| 999 | + if( iACode==ATTR_SRCSET ){ | |
| 1000 | + const char *z = zVal; | |
| 1001 | + int nCand = 0; | |
| 1002 | + blob_append_char(pOut, '='); | |
| 1003 | + blob_append_char(pOut, '"'); | |
| 1004 | + while( z[0] ){ | |
| 1005 | + const char *zComma = strchr(z, ','); | |
| 1006 | + int len = zComma ? (int)(zComma - z) : (int)strlen(z); | |
| 1007 | + const char *zCand = z; | |
| 1008 | + int urlStart = 0; | |
| 1009 | + int urlLen; | |
| 1010 | + if( len>0 ){ | |
| 1011 | + while( urlStart<len && fossil_isspace(zCand[urlStart]) ) urlStart++; | |
| 1012 | + urlLen = urlStart; | |
| 1013 | + while( urlLen<len && !fossil_isspace(zCand[urlLen]) ) urlLen++; | |
| 1014 | + urlLen -= urlStart; | |
| 1015 | + if( nCand++ ) blob_append_char(pOut, ','); | |
| 1016 | + if( urlStart>0 ){ | |
| 1017 | + blob_append(pOut, zCand, urlStart); | |
| 1018 | + } | |
| 1019 | + if( urlLen>0 && zCand[urlStart]=='/' ){ | |
| 1020 | + blob_appendf(pOut, "%R%.*s", urlLen, zCand+urlStart); | |
| 1021 | + }else if( urlLen>0 ){ | |
| 1022 | + blob_appendf(pOut, "%.*s", urlLen, zCand+urlStart); | |
| 1023 | + } | |
| 1024 | + if( urlStart+urlLen < len ){ | |
| 1025 | + blob_appendf(pOut, "%.*s", len-urlStart-urlLen, zCand+urlStart+urlLen); | |
| 1026 | + } | |
| 1027 | + } | |
| 1028 | + if( zComma==0 ) break; | |
| 1029 | + z = zComma + 1; | |
| 1030 | + } | |
| 1031 | + blob_append_char(pOut, '"'); | |
| 1032 | + return; | |
| 1033 | + } | |
| 1034 | + blob_appendf(pOut, "=\"%s\"", zVal); | |
| 1035 | +} | |
| 989 | 1036 | |
| 990 | 1037 | /* |
| 991 | 1038 | ** Render markup on the given blob. |
| 992 | 1039 | */ |
| 993 | 1040 | static void renderMarkup(Blob *pOut, ParsedMarkup *p){ |
| @@ -997,16 +1044,11 @@ | ||
| 997 | 1044 | }else{ |
| 998 | 1045 | blob_appendf(pOut, "<%s", aMarkup[p->iCode].zName); |
| 999 | 1046 | for(i=0; i<p->nAttr; i++){ |
| 1000 | 1047 | blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iACode].zName); |
| 1001 | 1048 | if( p->aAttr[i].zValue ){ |
| 1002 | - const char *zVal = p->aAttr[i].zValue; | |
| 1003 | - if( p->aAttr[i].iACode==ATTR_SRC && zVal[0]=='/' ){ | |
| 1004 | - blob_appendf(pOut, "=\"%R%s\"", zVal); | |
| 1005 | - }else{ | |
| 1006 | - blob_appendf(pOut, "=\"%s\"", zVal); | |
| 1007 | - } | |
| 1049 | + appendRewrittenAttrValue(pOut, p->aAttr[i].iACode, p->aAttr[i].zValue); | |
| 1008 | 1050 | } |
| 1009 | 1051 | } |
| 1010 | 1052 | if (p->iType & MUTYPE_SINGLE){ |
| 1011 | 1053 | blob_append_string(pOut, " /"); |
| 1012 | 1054 | } |
| @@ -3105,19 +3147,25 @@ | ||
| 3105 | 3147 | } |
| 3106 | 3148 | |
| 3107 | 3149 | /* |
| 3108 | 3150 | ** COMMAND: test-safe-html |
| 3109 | 3151 | ** |
| 3110 | -** Usage: %fossil test-safe-html FILE ... | |
| 3152 | +** Usage: %fossil test-safe-html ?OPTIONS? FILE ... | |
| 3111 | 3153 | ** |
| 3112 | 3154 | ** Read files named on the command-line. Send the text of each file |
| 3113 | 3155 | ** through safe_html_append() and then write the result on |
| 3114 | 3156 | ** standard output. |
| 3157 | +** | |
| 3158 | +** Options: | |
| 3159 | +** --top PATH Use PATH as g.zTop when rewriting root-relative URLs | |
| 3115 | 3160 | */ |
| 3116 | 3161 | void test_safe_html_cmd(void){ |
| 3117 | 3162 | int i; |
| 3118 | 3163 | Blob x; |
| 3164 | + const char *zTop = find_option("top",0,1); | |
| 3165 | + verify_all_options(); | |
| 3166 | + if( zTop ) g.zTop = (char*)zTop; | |
| 3119 | 3167 | for(i=2; i<g.argc; i++){ |
| 3120 | 3168 | char *z; |
| 3121 | 3169 | int n; |
| 3122 | 3170 | blob_read_from_file(&x, g.argv[i], ExtFILE); |
| 3123 | 3171 | blob_terminate(&x); |
| 3124 | 3172 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -984,10 +984,57 @@ | |
| 984 | while( fossil_isspace(z[i]) ){ i++; } |
| 985 | if( z[i]==0 || z[i]=='>' || (z[i]=='/' && z[i+1]=='>') ) break; |
| 986 | } |
| 987 | return seen; |
| 988 | } |
| 989 | |
| 990 | /* |
| 991 | ** Render markup on the given blob. |
| 992 | */ |
| 993 | static void renderMarkup(Blob *pOut, ParsedMarkup *p){ |
| @@ -997,16 +1044,11 @@ | |
| 997 | }else{ |
| 998 | blob_appendf(pOut, "<%s", aMarkup[p->iCode].zName); |
| 999 | for(i=0; i<p->nAttr; i++){ |
| 1000 | blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iACode].zName); |
| 1001 | if( p->aAttr[i].zValue ){ |
| 1002 | const char *zVal = p->aAttr[i].zValue; |
| 1003 | if( p->aAttr[i].iACode==ATTR_SRC && zVal[0]=='/' ){ |
| 1004 | blob_appendf(pOut, "=\"%R%s\"", zVal); |
| 1005 | }else{ |
| 1006 | blob_appendf(pOut, "=\"%s\"", zVal); |
| 1007 | } |
| 1008 | } |
| 1009 | } |
| 1010 | if (p->iType & MUTYPE_SINGLE){ |
| 1011 | blob_append_string(pOut, " /"); |
| 1012 | } |
| @@ -3105,19 +3147,25 @@ | |
| 3105 | } |
| 3106 | |
| 3107 | /* |
| 3108 | ** COMMAND: test-safe-html |
| 3109 | ** |
| 3110 | ** Usage: %fossil test-safe-html FILE ... |
| 3111 | ** |
| 3112 | ** Read files named on the command-line. Send the text of each file |
| 3113 | ** through safe_html_append() and then write the result on |
| 3114 | ** standard output. |
| 3115 | */ |
| 3116 | void test_safe_html_cmd(void){ |
| 3117 | int i; |
| 3118 | Blob x; |
| 3119 | for(i=2; i<g.argc; i++){ |
| 3120 | char *z; |
| 3121 | int n; |
| 3122 | blob_read_from_file(&x, g.argv[i], ExtFILE); |
| 3123 | blob_terminate(&x); |
| 3124 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -984,10 +984,57 @@ | |
| 984 | while( fossil_isspace(z[i]) ){ i++; } |
| 985 | if( z[i]==0 || z[i]=='>' || (z[i]=='/' && z[i+1]=='>') ) break; |
| 986 | } |
| 987 | return seen; |
| 988 | } |
| 989 | |
| 990 | /* |
| 991 | ** Append an HTML attribute value, rewriting root-relative URLs in src |
| 992 | ** and srcset the same way (%R prefix). |
| 993 | */ |
| 994 | static void appendRewrittenAttrValue(Blob *pOut, int iACode, const char *zVal){ |
| 995 | if( iACode==ATTR_SRC && zVal[0]=='/' ){ |
| 996 | blob_appendf(pOut, "=\"%R%s\"", zVal); |
| 997 | return; |
| 998 | } |
| 999 | if( iACode==ATTR_SRCSET ){ |
| 1000 | const char *z = zVal; |
| 1001 | int nCand = 0; |
| 1002 | blob_append_char(pOut, '='); |
| 1003 | blob_append_char(pOut, '"'); |
| 1004 | while( z[0] ){ |
| 1005 | const char *zComma = strchr(z, ','); |
| 1006 | int len = zComma ? (int)(zComma - z) : (int)strlen(z); |
| 1007 | const char *zCand = z; |
| 1008 | int urlStart = 0; |
| 1009 | int urlLen; |
| 1010 | if( len>0 ){ |
| 1011 | while( urlStart<len && fossil_isspace(zCand[urlStart]) ) urlStart++; |
| 1012 | urlLen = urlStart; |
| 1013 | while( urlLen<len && !fossil_isspace(zCand[urlLen]) ) urlLen++; |
| 1014 | urlLen -= urlStart; |
| 1015 | if( nCand++ ) blob_append_char(pOut, ','); |
| 1016 | if( urlStart>0 ){ |
| 1017 | blob_append(pOut, zCand, urlStart); |
| 1018 | } |
| 1019 | if( urlLen>0 && zCand[urlStart]=='/' ){ |
| 1020 | blob_appendf(pOut, "%R%.*s", urlLen, zCand+urlStart); |
| 1021 | }else if( urlLen>0 ){ |
| 1022 | blob_appendf(pOut, "%.*s", urlLen, zCand+urlStart); |
| 1023 | } |
| 1024 | if( urlStart+urlLen < len ){ |
| 1025 | blob_appendf(pOut, "%.*s", len-urlStart-urlLen, zCand+urlStart+urlLen); |
| 1026 | } |
| 1027 | } |
| 1028 | if( zComma==0 ) break; |
| 1029 | z = zComma + 1; |
| 1030 | } |
| 1031 | blob_append_char(pOut, '"'); |
| 1032 | return; |
| 1033 | } |
| 1034 | blob_appendf(pOut, "=\"%s\"", zVal); |
| 1035 | } |
| 1036 | |
| 1037 | /* |
| 1038 | ** Render markup on the given blob. |
| 1039 | */ |
| 1040 | static void renderMarkup(Blob *pOut, ParsedMarkup *p){ |
| @@ -997,16 +1044,11 @@ | |
| 1044 | }else{ |
| 1045 | blob_appendf(pOut, "<%s", aMarkup[p->iCode].zName); |
| 1046 | for(i=0; i<p->nAttr; i++){ |
| 1047 | blob_appendf(pOut, " %s", aAttribute[p->aAttr[i].iACode].zName); |
| 1048 | if( p->aAttr[i].zValue ){ |
| 1049 | appendRewrittenAttrValue(pOut, p->aAttr[i].iACode, p->aAttr[i].zValue); |
| 1050 | } |
| 1051 | } |
| 1052 | if (p->iType & MUTYPE_SINGLE){ |
| 1053 | blob_append_string(pOut, " /"); |
| 1054 | } |
| @@ -3105,19 +3147,25 @@ | |
| 3147 | } |
| 3148 | |
| 3149 | /* |
| 3150 | ** COMMAND: test-safe-html |
| 3151 | ** |
| 3152 | ** Usage: %fossil test-safe-html ?OPTIONS? FILE ... |
| 3153 | ** |
| 3154 | ** Read files named on the command-line. Send the text of each file |
| 3155 | ** through safe_html_append() and then write the result on |
| 3156 | ** standard output. |
| 3157 | ** |
| 3158 | ** Options: |
| 3159 | ** --top PATH Use PATH as g.zTop when rewriting root-relative URLs |
| 3160 | */ |
| 3161 | void test_safe_html_cmd(void){ |
| 3162 | int i; |
| 3163 | Blob x; |
| 3164 | const char *zTop = find_option("top",0,1); |
| 3165 | verify_all_options(); |
| 3166 | if( zTop ) g.zTop = (char*)zTop; |
| 3167 | for(i=2; i<g.argc; i++){ |
| 3168 | char *z; |
| 3169 | int n; |
| 3170 | blob_read_from_file(&x, g.argv[i], ExtFILE); |
| 3171 | blob_terminate(&x); |
| 3172 |