Fossil

Check-in [9a76760178]
Login

Check-in [9a76760178]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment: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.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | get-command
Files: files | file ages | folders
SHA3-256: 9a767601780a07700db90595857d5f48bf444b6241d1b9bf629a76f2f12b8147
User & Date: drh 2025-10-15 21:33:02.390
Context
2025-10-15
21:58
Improved comments and extra abuse defense. ... (check-in: a1f420c6c6 user: drh tags: get-command)
21:33
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. ... (check-in: 9a76760178 user: drh tags: get-command)
20:12
The "ssh:"-style requests are eating the query parameters, somewhere. I don't know where yet. Don't use query parameter for the time being. ... (check-in: 8028c868d3 user: drh tags: get-command)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/cgi.c.
2361
2362
2363
2364
2365
2366
2367

2368
2369

2370
2371
2372
2373
2374
2375
2376
    cgi_setenv("SCRIPT_NAME", "");
  }

  for(i=0; zToken[i] && zToken[i]!='?'; i++){}
  if( zToken[i] ) zToken[i++] = 0;
  if( nCycles==0 ){
    cgi_setenv("PATH_INFO", zToken);

  }else{
    cgi_replace_parameter("PATH_INFO", fossil_strdup(zToken));

  }

  /* Get all the optional fields that follow the first line.
  */
  while( fgets(zLine,sizeof(zLine),g.httpIn) ){
    char *zFieldName;
    char *zVal;







>


>







2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
    cgi_setenv("SCRIPT_NAME", "");
  }

  for(i=0; zToken[i] && zToken[i]!='?'; i++){}
  if( zToken[i] ) zToken[i++] = 0;
  if( nCycles==0 ){
    cgi_setenv("PATH_INFO", zToken);
    cgi_setenv("QUERY_STRING",&zToken[i]);
  }else{
    cgi_replace_parameter("PATH_INFO", fossil_strdup(zToken));
    cgi_replace_parameter("QUERY_STRING",fossil_strdup(&zToken[i]));
  }

  /* Get all the optional fields that follow the first line.
  */
  while( fgets(zLine,sizeof(zLine),g.httpIn) ){
    char *zFieldName;
    char *zVal;
2426
2427
2428
2429
2430
2431
2432

2433
2434
2435
2436
2437
2438
2439
      blob_uncompress(&g.cgiIn, &g.cgiIn);
    }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){
      blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
    }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
      blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
    }
  }

  cgi_trace(0);
  nCycles++;
}

/*
** This routine handles the old fossil SSH probes
*/







>







2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
      blob_uncompress(&g.cgiIn, &g.cgiIn);
    }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){
      blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
    }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
      blob_read_from_channel(&g.cgiIn, g.httpIn, content_length);
    }
  }
  cgi_init();
  cgi_trace(0);
  nCycles++;
}

/*
** This routine handles the old fossil SSH probes
*/
Changes to src/checkout.c.
470
471
472
473
474
475
476

477
478
479
480
481
482
483
  int bVerbose = find_option("verbose","v",0)!=0;
  int bQuiet = find_option("quiet","q",0)!=0;
  int bDebug = find_option("debug",0,0)!=0;
  int bList = find_option("list",0,0)!=0;
  const char *zSqlArchive = find_option("sqlar",0,1);
  const char *z;
  char *zDest = 0;        /* Where to store results */

  const char *zUrl;       /* Url to get */
  const char *zVers;      /* Version name to get */
  unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
  Blob in, out;           /* I/O for the HTTP request */
  Blob file;              /* A file to extract */
  sqlite3 *db;            /* Database containing downloaded sqlar */
  sqlite3_stmt *pStmt;    /* Statement for querying the database */







>







470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
  int bVerbose = find_option("verbose","v",0)!=0;
  int bQuiet = find_option("quiet","q",0)!=0;
  int bDebug = find_option("debug",0,0)!=0;
  int bList = find_option("list",0,0)!=0;
  const char *zSqlArchive = find_option("sqlar",0,1);
  const char *z;
  char *zDest = 0;        /* Where to store results */
  char *zSql;             /* SQL used to query the results */
  const char *zUrl;       /* Url to get */
  const char *zVers;      /* Version name to get */
  unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
  Blob in, out;           /* I/O for the HTTP request */
  Blob file;              /* A file to extract */
  sqlite3 *db;            /* Database containing downloaded sqlar */
  sqlite3_stmt *pStmt;    /* Statement for querying the database */
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
        fossil_fatal("\"%s\" already exists", zDest);
      }
    }
  }

  /* Construct a subpath on the URL if necessary */
  if( g.url.isSsh || g.url.isFile ){
    g.url.subpath = mprintf("/sqlar/%t/%t.sqlar", zVers, zDest);
  }

  if( bDebug ){
    urlparse_print(0);
  }

  /* Fetch the ZIP archive for the requested check-in */







|







540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
        fossil_fatal("\"%s\" already exists", zDest);
      }
    }
  }

  /* Construct a subpath on the URL if necessary */
  if( g.url.isSsh || g.url.isFile ){
    g.url.subpath = mprintf("/sqlar?r=%t&name=%t.sqlar", zVers, zDest);
  }

  if( bDebug ){
    urlparse_print(0);
  }

  /* Fetch the ZIP archive for the requested check-in */
569
570
571
572
573
574
575
576
577

578

579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
    rc = sqlite3_deserialize(db, 0, (unsigned char*)blob_buffer(&out), sz, sz,
                             SQLITE_DESERIALIZE_READONLY);
  }
  if( rc!=SQLITE_OK ){
    fossil_fatal("Cannot create an in-memory database: %s",
                 sqlite3_errmsg(db));
  }
  rc = sqlite3_prepare_v2(db,
     "SELECT name, mode, sz, data"

     "  FROM sqlar", -1, &pStmt, 0);

  if( rc!=0 ){
    fossil_fatal("SQL error: %s\n", sqlite3_errmsg(db));
  }
  blob_init(&file, 0, 0);
  while( sqlite3_step(pStmt)==SQLITE_ROW ){
    const char *zFilename = (const char*)sqlite3_column_text(pStmt, 0);
    int mode = sqlite3_column_int(pStmt, 1);
    int sz = sqlite3_column_int(pStmt, 2);
    if( bList ){
      fossil_print("%s\n", zFilename);
    }else if( mode & 0x4000 ){
      /* A directory name */
      nDir++;
      file_mkdir(zFilename, ExtFILE, 1);
    }else{
      /* A file */
      unsigned char *inBuf = (unsigned char*)sqlite3_column_blob(pStmt,3);
      unsigned int nIn = (unsigned int)sqlite3_column_bytes(pStmt,3);







<
|
>
|
>










|







570
571
572
573
574
575
576

577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
    rc = sqlite3_deserialize(db, 0, (unsigned char*)blob_buffer(&out), sz, sz,
                             SQLITE_DESERIALIZE_READONLY);
  }
  if( rc!=SQLITE_OK ){
    fossil_fatal("Cannot create an in-memory database: %s",
                 sqlite3_errmsg(db));
  }

  zSql = mprintf("SELECT name, mode, sz, data FROM sqlar"
                 " WHERE name GLOB '%q*'", zDest);
  rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
  fossil_free(zSql);
  if( rc!=0 ){
    fossil_fatal("SQL error: %s\n", sqlite3_errmsg(db));
  }
  blob_init(&file, 0, 0);
  while( sqlite3_step(pStmt)==SQLITE_ROW ){
    const char *zFilename = (const char*)sqlite3_column_text(pStmt, 0);
    int mode = sqlite3_column_int(pStmt, 1);
    int sz = sqlite3_column_int(pStmt, 2);
    if( bList ){
      fossil_print("%s\n", zFilename);
    }else  if( mode & 0x4000 ){
      /* A directory name */
      nDir++;
      file_mkdir(zFilename, ExtFILE, 1);
    }else{
      /* A file */
      unsigned char *inBuf = (unsigned char*)sqlite3_column_blob(pStmt,3);
      unsigned int nIn = (unsigned int)sqlite3_column_bytes(pStmt,3);
Changes to src/main.c.
3099
3100
3101
3102
3103
3104
3105

3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116



3117
3118
3119
3120
3121
3122
3123
** using this command interactively over SSH.  A better solution would be
** to use a different command for "ssh" sync, but we cannot do that without
** breaking legacy.
**
** Options:
**   --csrf-safe N       Set cgi_csrf_safe() to to return N
**   --nobody            Pretend to be user "nobody"

**   --test              Do not do special "sync" processing when operating
**                       over an SSH link
**   --th-trace          Trace TH1 execution (for debugging purposes)
**   --usercap   CAP     User capability string (Default: "sxy")
*/
void cmd_test_http(void){
  const char *zIpAddr;    /* IP address of remote client */
  const char *zUserCap;
  int bTest = 0;
  const char *zCsrfSafe = find_option("csrf-safe",0,1);




  Th_InitTraceLog();
  if( zCsrfSafe ) g.okCsrf = atoi(zCsrfSafe);
  zUserCap = find_option("usercap",0,1);
  if( !find_option("nobody",0,0) ){
    if( zUserCap==0 ){
      g.useLocalauth = 1;
      zUserCap = "sxy";







>











>
>
>







3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
** using this command interactively over SSH.  A better solution would be
** to use a different command for "ssh" sync, but we cannot do that without
** breaking legacy.
**
** Options:
**   --csrf-safe N       Set cgi_csrf_safe() to to return N
**   --nobody            Pretend to be user "nobody"
**   --ssh-sim           Pretend to be over an SSH connection
**   --test              Do not do special "sync" processing when operating
**                       over an SSH link
**   --th-trace          Trace TH1 execution (for debugging purposes)
**   --usercap   CAP     User capability string (Default: "sxy")
*/
void cmd_test_http(void){
  const char *zIpAddr;    /* IP address of remote client */
  const char *zUserCap;
  int bTest = 0;
  const char *zCsrfSafe = find_option("csrf-safe",0,1);

  if( find_option("ssh-sim",0,0)!=0 ){
    putenv("SSH_CONNECTION=127.0.0.1 12345 127.0.0.2 23456");
  }
  Th_InitTraceLog();
  if( zCsrfSafe ) g.okCsrf = atoi(zCsrfSafe);
  zUserCap = find_option("usercap",0,1);
  if( !find_option("nobody",0,0) ){
    if( zUserCap==0 ){
      g.useLocalauth = 1;
      zUserCap = "sxy";