Fossil SCM

Integrate the improved filename escaping logic for windows. Also add the "fossil test-escaped-arg" command for testing the logic.

drh 2020-06-09 23:46 trunk merge
Commit 7a2d06a46a57b2b2bd867a2fdef710482693557e50401df2af628eca49821e67
1 file changed +53 -4
+53 -4
--- src/blob.c
+++ src/blob.c
@@ -1301,35 +1301,84 @@
13011301
char c;
13021302
int needEscape = 0;
13031303
int n = blob_size(pBlob);
13041304
char *z = blob_buffer(pBlob);
13051305
#if defined(_WIN32)
1306
+ const char cDirSep = '\\'; /* Use \ as directory separator */
13061307
const char cQuote = '"'; /* Use "..." quoting on windows */
1308
+ const char cEscape = '^'; /* Use ^X escaping on windows */
13071309
#else
1310
+ const char cDirSep = '/'; /* Use / as directory separator */
13081311
const char cQuote = '\''; /* Use '...' quoting on unix */
1312
+ const char cEscape = '\\'; /* Use \X escaping on unix */
13091313
#endif
13101314
13111315
for(i=0; (c = zIn[i])!=0; i++){
13121316
if( c==cQuote || (unsigned char)c<' ' ||
1313
- c=='\\' || c==';' || c=='*' || c=='?' || c=='[' ){
1317
+ c==cEscape || c==';' || c=='*' || c=='?' || c=='[' ){
13141318
Blob bad;
13151319
blob_token(pBlob, &bad);
13161320
fossil_fatal("the [%s] argument to the \"%s\" command contains "
13171321
"a character (ascii 0x%02x) that is a security risk",
13181322
zIn, blob_str(&bad), c);
13191323
}
1320
- if( !needEscape && !fossil_isalnum(c) && c!='/' && c!='.' && c!='_' ){
1324
+ if( !needEscape && !fossil_isalnum(c) && c!=cDirSep && c!='.' && c!='_' ){
13211325
needEscape = 1;
13221326
}
13231327
}
13241328
if( n>0 && !fossil_isspace(z[n-1]) ){
13251329
blob_append_char(pBlob, ' ');
13261330
}
13271331
if( needEscape ) blob_append_char(pBlob, cQuote);
1328
- if( zIn[0]=='-' ) blob_append(pBlob, "./", 2);
1332
+ if( zIn[0]=='-' ){
1333
+ blob_append_char(pBlob, '.');
1334
+ blob_append_char(pBlob, cDirSep);
1335
+ }
1336
+#if defined(_WIN32)
1337
+ if( needEscape ){
1338
+ for(i=0; (c = zIn[i])!=0; i++){
1339
+ if( c==cQuote || c==cEscape ) blob_append_char(pBlob, cEscape);
1340
+ blob_append_char(pBlob, c);
1341
+ }
1342
+ }else{
1343
+ blob_append(pBlob, zIn, -1);
1344
+ }
1345
+#else
13291346
blob_append(pBlob, zIn, -1);
1330
- if( needEscape ) blob_append_char(pBlob, cQuote);
1347
+#endif
1348
+ if( needEscape ){
1349
+#if defined(_WIN32)
1350
+ /* NOTE: Trailing backslash must be doubled before final double quote. */
1351
+ if( pBlob->aData[pBlob->nUsed-1]==cEscape ){
1352
+ blob_append_char(pBlob, cEscape);
1353
+ }
1354
+#endif
1355
+ blob_append_char(pBlob, cQuote);
1356
+ }
1357
+}
1358
+
1359
+/*
1360
+** COMMAND: test-escaped-arg
1361
+**
1362
+** Usage %fossil ARG ...
1363
+**
1364
+** Run each argment through blob_append_escaped_arg() and show the
1365
+** result. Append each argument to "fossil test-echo" and run that
1366
+** using fossil_system() to verify that it really does get escaped
1367
+** correctly.
1368
+*/
1369
+void test_escaped_arg__cmd(void){
1370
+ int i;
1371
+ Blob x;
1372
+ blob_init(&x, 0, 0);
1373
+ for(i=2; i<g.argc; i++){
1374
+ fossil_print("%3d [%s]: ", i, g.argv[i]);
1375
+ blob_appendf(&x, "fossil test-echo %$", g.argv[i]);
1376
+ fossil_print("%s\n", blob_str(&x));
1377
+ fossil_system(blob_str(&x));
1378
+ blob_reset(&x);
1379
+ }
13311380
}
13321381
13331382
/*
13341383
** A read(2)-like impl for the Blob class. Reads (copies) up to nLen
13351384
** bytes from pIn, starting at position pIn->iCursor, and copies them
13361385
--- src/blob.c
+++ src/blob.c
@@ -1301,35 +1301,84 @@
1301 char c;
1302 int needEscape = 0;
1303 int n = blob_size(pBlob);
1304 char *z = blob_buffer(pBlob);
1305 #if defined(_WIN32)
 
1306 const char cQuote = '"'; /* Use "..." quoting on windows */
 
1307 #else
 
1308 const char cQuote = '\''; /* Use '...' quoting on unix */
 
1309 #endif
1310
1311 for(i=0; (c = zIn[i])!=0; i++){
1312 if( c==cQuote || (unsigned char)c<' ' ||
1313 c=='\\' || c==';' || c=='*' || c=='?' || c=='[' ){
1314 Blob bad;
1315 blob_token(pBlob, &bad);
1316 fossil_fatal("the [%s] argument to the \"%s\" command contains "
1317 "a character (ascii 0x%02x) that is a security risk",
1318 zIn, blob_str(&bad), c);
1319 }
1320 if( !needEscape && !fossil_isalnum(c) && c!='/' && c!='.' && c!='_' ){
1321 needEscape = 1;
1322 }
1323 }
1324 if( n>0 && !fossil_isspace(z[n-1]) ){
1325 blob_append_char(pBlob, ' ');
1326 }
1327 if( needEscape ) blob_append_char(pBlob, cQuote);
1328 if( zIn[0]=='-' ) blob_append(pBlob, "./", 2);
 
 
 
 
 
 
 
 
 
 
 
 
 
1329 blob_append(pBlob, zIn, -1);
1330 if( needEscape ) blob_append_char(pBlob, cQuote);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1331 }
1332
1333 /*
1334 ** A read(2)-like impl for the Blob class. Reads (copies) up to nLen
1335 ** bytes from pIn, starting at position pIn->iCursor, and copies them
1336
--- src/blob.c
+++ src/blob.c
@@ -1301,35 +1301,84 @@
1301 char c;
1302 int needEscape = 0;
1303 int n = blob_size(pBlob);
1304 char *z = blob_buffer(pBlob);
1305 #if defined(_WIN32)
1306 const char cDirSep = '\\'; /* Use \ as directory separator */
1307 const char cQuote = '"'; /* Use "..." quoting on windows */
1308 const char cEscape = '^'; /* Use ^X escaping on windows */
1309 #else
1310 const char cDirSep = '/'; /* Use / as directory separator */
1311 const char cQuote = '\''; /* Use '...' quoting on unix */
1312 const char cEscape = '\\'; /* Use \X escaping on unix */
1313 #endif
1314
1315 for(i=0; (c = zIn[i])!=0; i++){
1316 if( c==cQuote || (unsigned char)c<' ' ||
1317 c==cEscape || c==';' || c=='*' || c=='?' || c=='[' ){
1318 Blob bad;
1319 blob_token(pBlob, &bad);
1320 fossil_fatal("the [%s] argument to the \"%s\" command contains "
1321 "a character (ascii 0x%02x) that is a security risk",
1322 zIn, blob_str(&bad), c);
1323 }
1324 if( !needEscape && !fossil_isalnum(c) && c!=cDirSep && c!='.' && c!='_' ){
1325 needEscape = 1;
1326 }
1327 }
1328 if( n>0 && !fossil_isspace(z[n-1]) ){
1329 blob_append_char(pBlob, ' ');
1330 }
1331 if( needEscape ) blob_append_char(pBlob, cQuote);
1332 if( zIn[0]=='-' ){
1333 blob_append_char(pBlob, '.');
1334 blob_append_char(pBlob, cDirSep);
1335 }
1336 #if defined(_WIN32)
1337 if( needEscape ){
1338 for(i=0; (c = zIn[i])!=0; i++){
1339 if( c==cQuote || c==cEscape ) blob_append_char(pBlob, cEscape);
1340 blob_append_char(pBlob, c);
1341 }
1342 }else{
1343 blob_append(pBlob, zIn, -1);
1344 }
1345 #else
1346 blob_append(pBlob, zIn, -1);
1347 #endif
1348 if( needEscape ){
1349 #if defined(_WIN32)
1350 /* NOTE: Trailing backslash must be doubled before final double quote. */
1351 if( pBlob->aData[pBlob->nUsed-1]==cEscape ){
1352 blob_append_char(pBlob, cEscape);
1353 }
1354 #endif
1355 blob_append_char(pBlob, cQuote);
1356 }
1357 }
1358
1359 /*
1360 ** COMMAND: test-escaped-arg
1361 **
1362 ** Usage %fossil ARG ...
1363 **
1364 ** Run each argment through blob_append_escaped_arg() and show the
1365 ** result. Append each argument to "fossil test-echo" and run that
1366 ** using fossil_system() to verify that it really does get escaped
1367 ** correctly.
1368 */
1369 void test_escaped_arg__cmd(void){
1370 int i;
1371 Blob x;
1372 blob_init(&x, 0, 0);
1373 for(i=2; i<g.argc; i++){
1374 fossil_print("%3d [%s]: ", i, g.argv[i]);
1375 blob_appendf(&x, "fossil test-echo %$", g.argv[i]);
1376 fossil_print("%s\n", blob_str(&x));
1377 fossil_system(blob_str(&x));
1378 blob_reset(&x);
1379 }
1380 }
1381
1382 /*
1383 ** A read(2)-like impl for the Blob class. Reads (copies) up to nLen
1384 ** bytes from pIn, starting at position pIn->iCursor, and copies them
1385

Keyboard Shortcuts

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