Fossil SCM
If a parameter's value starts with an uppercase letter then truncate resulting label to just the first path segment. This check-in also adds a few code-comments related to %s formatting.
Commit
b75ee4f28efd8ad8ab95d79af6d7e8fd46a46eca5932314eaf2c3227f174c213
Parent
d1651e7e1711660…
2 files changed
+1
+24
-6
+1
| --- src/report.c | ||
| +++ src/report.c | ||
| @@ -1037,10 +1037,11 @@ | ||
| 1037 | 1037 | struct GenerateHTML sState = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; |
| 1038 | 1038 | const char *zQS = PD("QUERY_STRING",""); |
| 1039 | 1039 | |
| 1040 | 1040 | db_multi_exec("PRAGMA empty_result_callbacks=ON"); |
| 1041 | 1041 | style_set_current_feature("report"); |
| 1042 | + /* style_finish_page() should provide escaping via %h formatting */ | |
| 1042 | 1043 | if( zQS[0] ){ |
| 1043 | 1044 | style_submenu_element("Raw","%R/%s?tablist=1&%s",g.zPath,zQS); |
| 1044 | 1045 | style_submenu_element("Reports","%R/reportlist?%s",zQS); |
| 1045 | 1046 | } else { |
| 1046 | 1047 | style_submenu_element("Raw","%R/%s?tablist=1",g.zPath); |
| 1047 | 1048 |
| --- src/report.c | |
| +++ src/report.c | |
| @@ -1037,10 +1037,11 @@ | |
| 1037 | struct GenerateHTML sState = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; |
| 1038 | const char *zQS = PD("QUERY_STRING",""); |
| 1039 | |
| 1040 | db_multi_exec("PRAGMA empty_result_callbacks=ON"); |
| 1041 | style_set_current_feature("report"); |
| 1042 | if( zQS[0] ){ |
| 1043 | style_submenu_element("Raw","%R/%s?tablist=1&%s",g.zPath,zQS); |
| 1044 | style_submenu_element("Reports","%R/reportlist?%s",zQS); |
| 1045 | } else { |
| 1046 | style_submenu_element("Raw","%R/%s?tablist=1",g.zPath); |
| 1047 |
| --- src/report.c | |
| +++ src/report.c | |
| @@ -1037,10 +1037,11 @@ | |
| 1037 | struct GenerateHTML sState = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; |
| 1038 | const char *zQS = PD("QUERY_STRING",""); |
| 1039 | |
| 1040 | db_multi_exec("PRAGMA empty_result_callbacks=ON"); |
| 1041 | style_set_current_feature("report"); |
| 1042 | /* style_finish_page() should provide escaping via %h formatting */ |
| 1043 | if( zQS[0] ){ |
| 1044 | style_submenu_element("Raw","%R/%s?tablist=1&%s",g.zPath,zQS); |
| 1045 | style_submenu_element("Reports","%R/reportlist?%s",zQS); |
| 1046 | } else { |
| 1047 | style_submenu_element("Raw","%R/%s?tablist=1",g.zPath); |
| 1048 |
+24
-6
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -339,15 +339,25 @@ | ||
| 339 | 339 | ** nothing or a positive digit. zPrefix must start with a lowercase |
| 340 | 340 | ** letter, be short and have no strange characters. Parameter's value |
| 341 | 341 | ** is well-formed if its first filepath segment (separated by '/') |
| 342 | 342 | ** has no strange characters. Malformed values are silently ignored. |
| 343 | 343 | ** |
| 344 | -** The text for the resulting submenu item equals to the value of the | |
| 344 | +** The text for the resulting submenu label equals to the value of the | |
| 345 | 345 | ** parameter modulus some prettification for better UX: |
| 346 | -** "✧" symbol is prepended unless parameter's value starts with a | |
| 347 | -** lowercase letter or contains '/', also underscores in the first | |
| 348 | -** path segment are replaced with spaces. | |
| 346 | +** 1) If a parameter's value starts with a lowercase letter and | |
| 347 | +** contains '/' then it goes unchanged into the user-visible label. | |
| 348 | +** 2a) If the first letter is uppercase then the label is | |
| 349 | +** truncated at the first '/' (if any), | |
| 350 | +** 2b) otherwise the first letter is capitalized. | |
| 351 | +** 3) Underscores in the first path segment are replaced with spaces. | |
| 352 | +** 4) If the resulting label starts with an uppercase letter | |
| 353 | +** then it is prepended with "✧" symbol for explicit distinction | |
| 354 | +** from the built-in labels | |
| 355 | +** | |
| 356 | +** Important security-related note: | |
| 357 | +** zLabel and zLink are formatted using %s because it is expected that | |
| 358 | +** style_finish_page() provides propper escaping via %h format. | |
| 349 | 359 | */ |
| 350 | 360 | void style_submenu_parametric( |
| 351 | 361 | const char *zPrefix /* common prefix of the query parameters names */ |
| 352 | 362 | ){ |
| 353 | 363 | static const char *suffix = "smpl"; /* common suffix for param names */ |
| @@ -369,11 +379,11 @@ | ||
| 369 | 379 | zQS = PD("QUERY_STRING",""); |
| 370 | 380 | for( i = 0; i <= 9; i++ ){ |
| 371 | 381 | const char *zV, *z; |
| 372 | 382 | zN[l] = ( i == 0 ? 0 : '0' + i ); /* ...smpl instead of ...smpl0 */ |
| 373 | 383 | zV = PD(zN,""); |
| 374 | - if( zV[0] == 0 || zV[0] == '/' ){ | |
| 384 | + if( zV[0] == 0 || zV[0] == '/' || zV[0] == '_' || zV[0] == '-' ){ | |
| 375 | 385 | continue; |
| 376 | 386 | } |
| 377 | 387 | /* require the first path segment to be unfancy ASCII string */ |
| 378 | 388 | for( z = zV; z[0] && z[0] != '/' ;){ |
| 379 | 389 | if( fossil_isalnum(z[0]) || z[0]=='_' || z[0]=='-' ) z++; |
| @@ -391,12 +401,16 @@ | ||
| 391 | 401 | char *z = mprintf("%s%s",mark,zV); |
| 392 | 402 | aSubmenu[nSubmenu].zLabel = z; |
| 393 | 403 | /* also prettify the first segment */ |
| 394 | 404 | z += strlen(mark); |
| 395 | 405 | z[0] = fossil_toupper(z[0]); |
| 396 | - for(; z[0]!=0 && z[0]!='/'; z++ ){ | |
| 406 | + for(; z[0]!=0; z++ ){ | |
| 397 | 407 | if( z[0]=='_' ) z[0] = ' '; |
| 408 | + else if( z[0] == '/' ){ /* show just the first segment */ | |
| 409 | + z[0] = 0; | |
| 410 | + break; | |
| 411 | + } | |
| 398 | 412 | } |
| 399 | 413 | } |
| 400 | 414 | if( zQS[0] ){ |
| 401 | 415 | aSubmenu[nSubmenu].zLink = mprintf("%R/%s?%s",zV,zQS); |
| 402 | 416 | }else{ |
| @@ -979,10 +993,14 @@ | ||
| 979 | 993 | @ <div class="submenu"> |
| 980 | 994 | if( nSubmenu>0 ){ |
| 981 | 995 | qsort(aSubmenu, nSubmenu, sizeof(aSubmenu[0]), submenuCompare); |
| 982 | 996 | for(i=0; i<nSubmenu; i++){ |
| 983 | 997 | struct Submenu *p = &aSubmenu[i]; |
| 998 | + /* switching away from the %h formatting below might be dangerous | |
| 999 | + ** because some places use %s to compose zLabel and zLink; | |
| 1000 | + ** e.g. /rptview page and the style_submenu_parametic() function | |
| 1001 | + */ | |
| 984 | 1002 | if( p->zLink==0 ){ |
| 985 | 1003 | @ <span class="label">%h(p->zLabel)</span> |
| 986 | 1004 | }else{ |
| 987 | 1005 | @ <a class="label" href="%h(p->zLink)">%h(p->zLabel)</a> |
| 988 | 1006 | } |
| 989 | 1007 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -339,15 +339,25 @@ | |
| 339 | ** nothing or a positive digit. zPrefix must start with a lowercase |
| 340 | ** letter, be short and have no strange characters. Parameter's value |
| 341 | ** is well-formed if its first filepath segment (separated by '/') |
| 342 | ** has no strange characters. Malformed values are silently ignored. |
| 343 | ** |
| 344 | ** The text for the resulting submenu item equals to the value of the |
| 345 | ** parameter modulus some prettification for better UX: |
| 346 | ** "✧" symbol is prepended unless parameter's value starts with a |
| 347 | ** lowercase letter or contains '/', also underscores in the first |
| 348 | ** path segment are replaced with spaces. |
| 349 | */ |
| 350 | void style_submenu_parametric( |
| 351 | const char *zPrefix /* common prefix of the query parameters names */ |
| 352 | ){ |
| 353 | static const char *suffix = "smpl"; /* common suffix for param names */ |
| @@ -369,11 +379,11 @@ | |
| 369 | zQS = PD("QUERY_STRING",""); |
| 370 | for( i = 0; i <= 9; i++ ){ |
| 371 | const char *zV, *z; |
| 372 | zN[l] = ( i == 0 ? 0 : '0' + i ); /* ...smpl instead of ...smpl0 */ |
| 373 | zV = PD(zN,""); |
| 374 | if( zV[0] == 0 || zV[0] == '/' ){ |
| 375 | continue; |
| 376 | } |
| 377 | /* require the first path segment to be unfancy ASCII string */ |
| 378 | for( z = zV; z[0] && z[0] != '/' ;){ |
| 379 | if( fossil_isalnum(z[0]) || z[0]=='_' || z[0]=='-' ) z++; |
| @@ -391,12 +401,16 @@ | |
| 391 | char *z = mprintf("%s%s",mark,zV); |
| 392 | aSubmenu[nSubmenu].zLabel = z; |
| 393 | /* also prettify the first segment */ |
| 394 | z += strlen(mark); |
| 395 | z[0] = fossil_toupper(z[0]); |
| 396 | for(; z[0]!=0 && z[0]!='/'; z++ ){ |
| 397 | if( z[0]=='_' ) z[0] = ' '; |
| 398 | } |
| 399 | } |
| 400 | if( zQS[0] ){ |
| 401 | aSubmenu[nSubmenu].zLink = mprintf("%R/%s?%s",zV,zQS); |
| 402 | }else{ |
| @@ -979,10 +993,14 @@ | |
| 979 | @ <div class="submenu"> |
| 980 | if( nSubmenu>0 ){ |
| 981 | qsort(aSubmenu, nSubmenu, sizeof(aSubmenu[0]), submenuCompare); |
| 982 | for(i=0; i<nSubmenu; i++){ |
| 983 | struct Submenu *p = &aSubmenu[i]; |
| 984 | if( p->zLink==0 ){ |
| 985 | @ <span class="label">%h(p->zLabel)</span> |
| 986 | }else{ |
| 987 | @ <a class="label" href="%h(p->zLink)">%h(p->zLabel)</a> |
| 988 | } |
| 989 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -339,15 +339,25 @@ | |
| 339 | ** nothing or a positive digit. zPrefix must start with a lowercase |
| 340 | ** letter, be short and have no strange characters. Parameter's value |
| 341 | ** is well-formed if its first filepath segment (separated by '/') |
| 342 | ** has no strange characters. Malformed values are silently ignored. |
| 343 | ** |
| 344 | ** The text for the resulting submenu label equals to the value of the |
| 345 | ** parameter modulus some prettification for better UX: |
| 346 | ** 1) If a parameter's value starts with a lowercase letter and |
| 347 | ** contains '/' then it goes unchanged into the user-visible label. |
| 348 | ** 2a) If the first letter is uppercase then the label is |
| 349 | ** truncated at the first '/' (if any), |
| 350 | ** 2b) otherwise the first letter is capitalized. |
| 351 | ** 3) Underscores in the first path segment are replaced with spaces. |
| 352 | ** 4) If the resulting label starts with an uppercase letter |
| 353 | ** then it is prepended with "✧" symbol for explicit distinction |
| 354 | ** from the built-in labels |
| 355 | ** |
| 356 | ** Important security-related note: |
| 357 | ** zLabel and zLink are formatted using %s because it is expected that |
| 358 | ** style_finish_page() provides propper escaping via %h format. |
| 359 | */ |
| 360 | void style_submenu_parametric( |
| 361 | const char *zPrefix /* common prefix of the query parameters names */ |
| 362 | ){ |
| 363 | static const char *suffix = "smpl"; /* common suffix for param names */ |
| @@ -369,11 +379,11 @@ | |
| 379 | zQS = PD("QUERY_STRING",""); |
| 380 | for( i = 0; i <= 9; i++ ){ |
| 381 | const char *zV, *z; |
| 382 | zN[l] = ( i == 0 ? 0 : '0' + i ); /* ...smpl instead of ...smpl0 */ |
| 383 | zV = PD(zN,""); |
| 384 | if( zV[0] == 0 || zV[0] == '/' || zV[0] == '_' || zV[0] == '-' ){ |
| 385 | continue; |
| 386 | } |
| 387 | /* require the first path segment to be unfancy ASCII string */ |
| 388 | for( z = zV; z[0] && z[0] != '/' ;){ |
| 389 | if( fossil_isalnum(z[0]) || z[0]=='_' || z[0]=='-' ) z++; |
| @@ -391,12 +401,16 @@ | |
| 401 | char *z = mprintf("%s%s",mark,zV); |
| 402 | aSubmenu[nSubmenu].zLabel = z; |
| 403 | /* also prettify the first segment */ |
| 404 | z += strlen(mark); |
| 405 | z[0] = fossil_toupper(z[0]); |
| 406 | for(; z[0]!=0; z++ ){ |
| 407 | if( z[0]=='_' ) z[0] = ' '; |
| 408 | else if( z[0] == '/' ){ /* show just the first segment */ |
| 409 | z[0] = 0; |
| 410 | break; |
| 411 | } |
| 412 | } |
| 413 | } |
| 414 | if( zQS[0] ){ |
| 415 | aSubmenu[nSubmenu].zLink = mprintf("%R/%s?%s",zV,zQS); |
| 416 | }else{ |
| @@ -979,10 +993,14 @@ | |
| 993 | @ <div class="submenu"> |
| 994 | if( nSubmenu>0 ){ |
| 995 | qsort(aSubmenu, nSubmenu, sizeof(aSubmenu[0]), submenuCompare); |
| 996 | for(i=0; i<nSubmenu; i++){ |
| 997 | struct Submenu *p = &aSubmenu[i]; |
| 998 | /* switching away from the %h formatting below might be dangerous |
| 999 | ** because some places use %s to compose zLabel and zLink; |
| 1000 | ** e.g. /rptview page and the style_submenu_parametic() function |
| 1001 | */ |
| 1002 | if( p->zLink==0 ){ |
| 1003 | @ <span class="label">%h(p->zLabel)</span> |
| 1004 | }else{ |
| 1005 | @ <a class="label" href="%h(p->zLink)">%h(p->zLabel)</a> |
| 1006 | } |
| 1007 |