Fossil SCM

Fix CGI processing so that requests sent over SSH process query parameters. Add the --ssh-sim option to the test-http command (used to debug the previous). Harden the "fossil get" command so that it can checks filenames and does not write a file that is outside of the designated --dest.

drh 2025-10-15 21:33 get-command
Commit 9a767601780a07700db90595857d5f48bf444b6241d1b9bf629a76f2f12b8147
+3
--- src/cgi.c
+++ src/cgi.c
@@ -2363,12 +2363,14 @@
23632363
23642364
for(i=0; zToken[i] && zToken[i]!='?'; i++){}
23652365
if( zToken[i] ) zToken[i++] = 0;
23662366
if( nCycles==0 ){
23672367
cgi_setenv("PATH_INFO", zToken);
2368
+ cgi_setenv("QUERY_STRING",&zToken[i]);
23682369
}else{
23692370
cgi_replace_parameter("PATH_INFO", fossil_strdup(zToken));
2371
+ cgi_replace_parameter("QUERY_STRING",fossil_strdup(&zToken[i]));
23702372
}
23712373
23722374
/* Get all the optional fields that follow the first line.
23732375
*/
23742376
while( fgets(zLine,sizeof(zLine),g.httpIn) ){
@@ -2428,10 +2430,11 @@
24282430
blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
24292431
}else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
24302432
blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
24312433
}
24322434
}
2435
+ cgi_init();
24332436
cgi_trace(0);
24342437
nCycles++;
24352438
}
24362439
24372440
/*
24382441
--- src/cgi.c
+++ src/cgi.c
@@ -2363,12 +2363,14 @@
2363
2364 for(i=0; zToken[i] && zToken[i]!='?'; i++){}
2365 if( zToken[i] ) zToken[i++] = 0;
2366 if( nCycles==0 ){
2367 cgi_setenv("PATH_INFO", zToken);
 
2368 }else{
2369 cgi_replace_parameter("PATH_INFO", fossil_strdup(zToken));
 
2370 }
2371
2372 /* Get all the optional fields that follow the first line.
2373 */
2374 while( fgets(zLine,sizeof(zLine),g.httpIn) ){
@@ -2428,10 +2430,11 @@
2428 blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
2429 }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
2430 blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
2431 }
2432 }
 
2433 cgi_trace(0);
2434 nCycles++;
2435 }
2436
2437 /*
2438
--- src/cgi.c
+++ src/cgi.c
@@ -2363,12 +2363,14 @@
2363
2364 for(i=0; zToken[i] && zToken[i]!='?'; i++){}
2365 if( zToken[i] ) zToken[i++] = 0;
2366 if( nCycles==0 ){
2367 cgi_setenv("PATH_INFO", zToken);
2368 cgi_setenv("QUERY_STRING",&zToken[i]);
2369 }else{
2370 cgi_replace_parameter("PATH_INFO", fossil_strdup(zToken));
2371 cgi_replace_parameter("QUERY_STRING",fossil_strdup(&zToken[i]));
2372 }
2373
2374 /* Get all the optional fields that follow the first line.
2375 */
2376 while( fgets(zLine,sizeof(zLine),g.httpIn) ){
@@ -2428,10 +2430,11 @@
2430 blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
2431 }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
2432 blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
2433 }
2434 }
2435 cgi_init();
2436 cgi_trace(0);
2437 nCycles++;
2438 }
2439
2440 /*
2441
+7 -5
--- src/checkout.c
+++ src/checkout.c
@@ -472,10 +472,11 @@
472472
int bDebug = find_option("debug",0,0)!=0;
473473
int bList = find_option("list",0,0)!=0;
474474
const char *zSqlArchive = find_option("sqlar",0,1);
475475
const char *z;
476476
char *zDest = 0; /* Where to store results */
477
+ char *zSql; /* SQL used to query the results */
477478
const char *zUrl; /* Url to get */
478479
const char *zVers; /* Version name to get */
479480
unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
480481
Blob in, out; /* I/O for the HTTP request */
481482
Blob file; /* A file to extract */
@@ -541,11 +542,11 @@
541542
}
542543
}
543544
544545
/* Construct a subpath on the URL if necessary */
545546
if( g.url.isSsh || g.url.isFile ){
546
- g.url.subpath = mprintf("/sqlar/%t/%t.sqlar", zVers, zDest);
547
+ g.url.subpath = mprintf("/sqlar?r=%t&name=%t.sqlar", zVers, zDest);
547548
}
548549
549550
if( bDebug ){
550551
urlparse_print(0);
551552
}
@@ -571,13 +572,14 @@
571572
}
572573
if( rc!=SQLITE_OK ){
573574
fossil_fatal("Cannot create an in-memory database: %s",
574575
sqlite3_errmsg(db));
575576
}
576
- rc = sqlite3_prepare_v2(db,
577
- "SELECT name, mode, sz, data"
578
- " FROM sqlar", -1, &pStmt, 0);
577
+ zSql = mprintf("SELECT name, mode, sz, data FROM sqlar"
578
+ " WHERE name GLOB '%q*'", zDest);
579
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
580
+ fossil_free(zSql);
579581
if( rc!=0 ){
580582
fossil_fatal("SQL error: %s\n", sqlite3_errmsg(db));
581583
}
582584
blob_init(&file, 0, 0);
583585
while( sqlite3_step(pStmt)==SQLITE_ROW ){
@@ -584,11 +586,11 @@
584586
const char *zFilename = (const char*)sqlite3_column_text(pStmt, 0);
585587
int mode = sqlite3_column_int(pStmt, 1);
586588
int sz = sqlite3_column_int(pStmt, 2);
587589
if( bList ){
588590
fossil_print("%s\n", zFilename);
589
- }else if( mode & 0x4000 ){
591
+ }else if( mode & 0x4000 ){
590592
/* A directory name */
591593
nDir++;
592594
file_mkdir(zFilename, ExtFILE, 1);
593595
}else{
594596
/* A file */
595597
--- src/checkout.c
+++ src/checkout.c
@@ -472,10 +472,11 @@
472 int bDebug = find_option("debug",0,0)!=0;
473 int bList = find_option("list",0,0)!=0;
474 const char *zSqlArchive = find_option("sqlar",0,1);
475 const char *z;
476 char *zDest = 0; /* Where to store results */
 
477 const char *zUrl; /* Url to get */
478 const char *zVers; /* Version name to get */
479 unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
480 Blob in, out; /* I/O for the HTTP request */
481 Blob file; /* A file to extract */
@@ -541,11 +542,11 @@
541 }
542 }
543
544 /* Construct a subpath on the URL if necessary */
545 if( g.url.isSsh || g.url.isFile ){
546 g.url.subpath = mprintf("/sqlar/%t/%t.sqlar", zVers, zDest);
547 }
548
549 if( bDebug ){
550 urlparse_print(0);
551 }
@@ -571,13 +572,14 @@
571 }
572 if( rc!=SQLITE_OK ){
573 fossil_fatal("Cannot create an in-memory database: %s",
574 sqlite3_errmsg(db));
575 }
576 rc = sqlite3_prepare_v2(db,
577 "SELECT name, mode, sz, data"
578 " FROM sqlar", -1, &pStmt, 0);
 
579 if( rc!=0 ){
580 fossil_fatal("SQL error: %s\n", sqlite3_errmsg(db));
581 }
582 blob_init(&file, 0, 0);
583 while( sqlite3_step(pStmt)==SQLITE_ROW ){
@@ -584,11 +586,11 @@
584 const char *zFilename = (const char*)sqlite3_column_text(pStmt, 0);
585 int mode = sqlite3_column_int(pStmt, 1);
586 int sz = sqlite3_column_int(pStmt, 2);
587 if( bList ){
588 fossil_print("%s\n", zFilename);
589 }else if( mode & 0x4000 ){
590 /* A directory name */
591 nDir++;
592 file_mkdir(zFilename, ExtFILE, 1);
593 }else{
594 /* A file */
595
--- src/checkout.c
+++ src/checkout.c
@@ -472,10 +472,11 @@
472 int bDebug = find_option("debug",0,0)!=0;
473 int bList = find_option("list",0,0)!=0;
474 const char *zSqlArchive = find_option("sqlar",0,1);
475 const char *z;
476 char *zDest = 0; /* Where to store results */
477 char *zSql; /* SQL used to query the results */
478 const char *zUrl; /* Url to get */
479 const char *zVers; /* Version name to get */
480 unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
481 Blob in, out; /* I/O for the HTTP request */
482 Blob file; /* A file to extract */
@@ -541,11 +542,11 @@
542 }
543 }
544
545 /* Construct a subpath on the URL if necessary */
546 if( g.url.isSsh || g.url.isFile ){
547 g.url.subpath = mprintf("/sqlar?r=%t&name=%t.sqlar", zVers, zDest);
548 }
549
550 if( bDebug ){
551 urlparse_print(0);
552 }
@@ -571,13 +572,14 @@
572 }
573 if( rc!=SQLITE_OK ){
574 fossil_fatal("Cannot create an in-memory database: %s",
575 sqlite3_errmsg(db));
576 }
577 zSql = mprintf("SELECT name, mode, sz, data FROM sqlar"
578 " WHERE name GLOB '%q*'", zDest);
579 rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
580 fossil_free(zSql);
581 if( rc!=0 ){
582 fossil_fatal("SQL error: %s\n", sqlite3_errmsg(db));
583 }
584 blob_init(&file, 0, 0);
585 while( sqlite3_step(pStmt)==SQLITE_ROW ){
@@ -584,11 +586,11 @@
586 const char *zFilename = (const char*)sqlite3_column_text(pStmt, 0);
587 int mode = sqlite3_column_int(pStmt, 1);
588 int sz = sqlite3_column_int(pStmt, 2);
589 if( bList ){
590 fossil_print("%s\n", zFilename);
591 }else if( mode & 0x4000 ){
592 /* A directory name */
593 nDir++;
594 file_mkdir(zFilename, ExtFILE, 1);
595 }else{
596 /* A file */
597
+4
--- src/main.c
+++ src/main.c
@@ -3101,10 +3101,11 @@
31013101
** breaking legacy.
31023102
**
31033103
** Options:
31043104
** --csrf-safe N Set cgi_csrf_safe() to to return N
31053105
** --nobody Pretend to be user "nobody"
3106
+** --ssh-sim Pretend to be over an SSH connection
31063107
** --test Do not do special "sync" processing when operating
31073108
** over an SSH link
31083109
** --th-trace Trace TH1 execution (for debugging purposes)
31093110
** --usercap CAP User capability string (Default: "sxy")
31103111
*/
@@ -3112,10 +3113,13 @@
31123113
const char *zIpAddr; /* IP address of remote client */
31133114
const char *zUserCap;
31143115
int bTest = 0;
31153116
const char *zCsrfSafe = find_option("csrf-safe",0,1);
31163117
3118
+ if( find_option("ssh-sim",0,0)!=0 ){
3119
+ putenv("SSH_CONNECTION=127.0.0.1 12345 127.0.0.2 23456");
3120
+ }
31173121
Th_InitTraceLog();
31183122
if( zCsrfSafe ) g.okCsrf = atoi(zCsrfSafe);
31193123
zUserCap = find_option("usercap",0,1);
31203124
if( !find_option("nobody",0,0) ){
31213125
if( zUserCap==0 ){
31223126
--- src/main.c
+++ src/main.c
@@ -3101,10 +3101,11 @@
3101 ** breaking legacy.
3102 **
3103 ** Options:
3104 ** --csrf-safe N Set cgi_csrf_safe() to to return N
3105 ** --nobody Pretend to be user "nobody"
 
3106 ** --test Do not do special "sync" processing when operating
3107 ** over an SSH link
3108 ** --th-trace Trace TH1 execution (for debugging purposes)
3109 ** --usercap CAP User capability string (Default: "sxy")
3110 */
@@ -3112,10 +3113,13 @@
3112 const char *zIpAddr; /* IP address of remote client */
3113 const char *zUserCap;
3114 int bTest = 0;
3115 const char *zCsrfSafe = find_option("csrf-safe",0,1);
3116
 
 
 
3117 Th_InitTraceLog();
3118 if( zCsrfSafe ) g.okCsrf = atoi(zCsrfSafe);
3119 zUserCap = find_option("usercap",0,1);
3120 if( !find_option("nobody",0,0) ){
3121 if( zUserCap==0 ){
3122
--- src/main.c
+++ src/main.c
@@ -3101,10 +3101,11 @@
3101 ** breaking legacy.
3102 **
3103 ** Options:
3104 ** --csrf-safe N Set cgi_csrf_safe() to to return N
3105 ** --nobody Pretend to be user "nobody"
3106 ** --ssh-sim Pretend to be over an SSH connection
3107 ** --test Do not do special "sync" processing when operating
3108 ** over an SSH link
3109 ** --th-trace Trace TH1 execution (for debugging purposes)
3110 ** --usercap CAP User capability string (Default: "sxy")
3111 */
@@ -3112,10 +3113,13 @@
3113 const char *zIpAddr; /* IP address of remote client */
3114 const char *zUserCap;
3115 int bTest = 0;
3116 const char *zCsrfSafe = find_option("csrf-safe",0,1);
3117
3118 if( find_option("ssh-sim",0,0)!=0 ){
3119 putenv("SSH_CONNECTION=127.0.0.1 12345 127.0.0.2 23456");
3120 }
3121 Th_InitTraceLog();
3122 if( zCsrfSafe ) g.okCsrf = atoi(zCsrfSafe);
3123 zUserCap = find_option("usercap",0,1);
3124 if( !find_option("nobody",0,0) ){
3125 if( zUserCap==0 ){
3126

Keyboard Shortcuts

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