Fossil

Changes On Branch sec2020-2.12-patch
Login

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

Changes In Branch sec2020-2.12-patch Excluding Merge-Ins

This is equivalent to a diff from d0988e67 to 89d950ef

2020-08-20
13:01
2.12.1 release candidate with security fixes. ... (check-in: 40feec32 user: drh tags: branch-2.12)
00:04
Report the use of FOSSIL_LEGACY_ALLOW_SYMLINKS in the output "fossil version -v". ... (Leaf check-in: 89d950ef user: drh tags: sec2020-2.12-patch)
2020-08-19
21:08
The allow-symlinks setting is disabled by default and is not versionable, unless Fossil is compiled with the FOSSIL_LEGACY_ALLOW_SYMLINKS flag, in which case it follows the historic behavior. ... (check-in: cdc90f0c user: drh tags: sec2020-2.12-patch)
09:57
Increase the version number to 2.12.1. ... (check-in: 32646b27 user: drh tags: branch-2.12)
01:07
Cherrypick key fixes from the sec2020 branch in order to devise a minimal patch to get us to version 2.12.1. ... (check-in: fe1264d3 user: drh tags: sec2020-2.12-patch)
2020-08-18
21:03
Cherrypick [d2d8894bb2]: fossil.storage.clear() is now also sandboxed - no longer nuking all state for all repos on the same origin. ... (check-in: d0988e67 user: stephan tags: branch-2.12)
21:01
fossil.storage.clear() is now also sandboxed - no longer nuking all state for all repos on the same origin. ... (check-in: d2d8894b user: stephan tags: trunk)
20:51
Merged in [923affb930a27b], which reinstates localStorage but sandboxes access to fossil.storage on a per-repo basis. ... (check-in: 21fbd473 user: stephan tags: branch-2.12)

Changes to src/add.c.

   154    154   **
   155    155   ** Omit any file whose name is pOmit.
   156    156   */
   157    157   static int add_one_file(
   158    158     const char *zPath,   /* Tree-name of file to add. */
   159    159     int vid              /* Add to this VFILE */
   160    160   ){
          161  +  int doSkip = 0;
   161    162     if( !file_is_simple_pathname(zPath, 1) ){
   162    163       fossil_warning("filename contains illegal characters: %s", zPath);
   163    164       return 0;
   164    165     }
   165    166     if( db_exists("SELECT 1 FROM vfile"
   166    167                   " WHERE pathname=%Q %s", zPath, filename_collation()) ){
   167    168       db_multi_exec("UPDATE vfile SET deleted=0"
   168    169                     " WHERE pathname=%Q %s AND deleted",
   169    170                     zPath, filename_collation());
   170    171     }else{
   171    172       char *zFullname = mprintf("%s%s", g.zLocalRoot, zPath);
   172    173       int isExe = file_isexe(zFullname, RepoFILE);
          174  +    if( file_nondir_objects_on_path(g.zLocalRoot, zFullname) ){
          175  +      /* Do not add unsafe files to the vfile */
          176  +      doSkip = 1;
          177  +    }else{
   173    178       db_multi_exec(
   174    179         "INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe,islink,mhash)"
   175    180         "VALUES(%d,0,0,0,%Q,%d,%d,NULL)",
   176    181         vid, zPath, isExe, file_islink(0));
          182  +    }
   177    183       fossil_free(zFullname);
   178    184     }
   179         -  if( db_changes() ){
          185  +  if( db_changes() && !doSkip ){
   180    186       fossil_print("ADDED  %s\n", zPath);
   181    187       return 1;
   182    188     }else{
   183    189       fossil_print("SKIP   %s\n", zPath);
   184    190       return 0;
   185    191     }
   186    192   }
   187    193   
   188    194   /*
   189    195   ** Add all files in the sfile temp table.
   190    196   **
   191         -** Automatically exclude the repository file.
          197  +** Automatically exclude the repository file and any other files
          198  +** with reserved names. Also exclude files that are beneath an 
          199  +** existing symlink.
   192    200   */
   193    201   static int add_files_in_sfile(int vid){
   194    202     const char *zRepo;        /* Name of the repository database file */
   195    203     int nAdd = 0;             /* Number of files added */
   196    204     int i;                    /* Loop counter */
   197    205     const char *zReserved;    /* Name of a reserved file */
   198    206     Blob repoName;            /* Treename of the repository */
................................................................................
   206    214       zRepo = blob_str(&repoName);
   207    215     }
   208    216     if( filenames_are_case_sensitive() ){
   209    217       xCmp = fossil_strcmp;
   210    218     }else{
   211    219       xCmp = fossil_stricmp;
   212    220     }
   213         -  db_prepare(&loop, "SELECT pathname FROM sfile ORDER BY pathname");
          221  +  db_prepare(&loop, 
          222  +     "SELECT pathname FROM sfile"
          223  +     " WHERE pathname NOT IN ("
          224  +       "SELECT sfile.pathname FROM vfile, sfile"
          225  +       " WHERE vfile.islink"
          226  +       "   AND NOT vfile.deleted"
          227  +       "   AND sfile.pathname>(vfile.pathname||'/')"
          228  +       "   AND sfile.pathname<(vfile.pathname||'0'))"
          229  +     " ORDER BY pathname");
   214    230     while( db_step(&loop)==SQLITE_ROW ){
   215    231       const char *zToAdd = db_column_text(&loop, 0);
   216    232       if( fossil_strcmp(zToAdd, zRepo)==0 ) continue;
          233  +    if( strchr(zToAdd,'/') ){
          234  +      if( file_is_reserved_name(zToAdd, -1) ) continue;
          235  +    }else{
   217    236       for(i=0; (zReserved = fossil_reserved_name(i, 0))!=0; i++){
   218    237         if( xCmp(zToAdd, zReserved)==0 ) break;
   219    238       }
   220    239       if( zReserved ) continue;
          240  +    }
   221    241       nAdd += add_one_file(zToAdd, vid);
   222    242     }
   223    243     db_finalize(&loop);
   224    244     blob_reset(&repoName);
   225    245     return nAdd;
   226    246   }
   227    247   

Changes to src/checkin.c.

   854    854     /* We should be done with options.. */
   855    855     verify_all_options();
   856    856   
   857    857     if( zIgnoreFlag==0 ){
   858    858       zIgnoreFlag = db_get("ignore-glob", 0);
   859    859     }
   860    860     pIgnore = glob_create(zIgnoreFlag);
          861  +#ifdef FOSSIL_LEGACY_ALLOW_SYMLINKS
   861    862     /* Always consider symlinks. */
   862    863     g.allowSymlinks = db_allow_symlinks_by_default();
          864  +#endif
   863    865     locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore);
   864    866     glob_free(pIgnore);
   865    867   
   866    868     blob_zero(&report);
   867    869     status_report(&report, flags);
   868    870     if( blob_size(&report) ){
   869    871       if( showHdr ){
................................................................................
  1013   1015     }
  1014   1016     if( db_get_boolean("dotfiles", 0) ) scanFlags |= SCAN_ALL;
  1015   1017     verify_all_options();
  1016   1018     pIgnore = glob_create(zIgnoreFlag);
  1017   1019     pKeep = glob_create(zKeepFlag);
  1018   1020     pClean = glob_create(zCleanFlag);
  1019   1021     nRoot = (int)strlen(g.zLocalRoot);
         1022  +#ifdef FOSSIL_LEGACY_ALLOW_SYMLINKS
  1020   1023     /* Always consider symlinks. */
  1021   1024     g.allowSymlinks = db_allow_symlinks_by_default();
         1025  +#endif
  1022   1026     if( !dirsOnlyFlag ){
  1023   1027       Stmt q;
  1024   1028       Blob repo;
  1025   1029       if( !dryRunFlag && !disableUndo ) undo_begin();
  1026   1030       locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore);
  1027   1031       db_prepare(&q,
  1028   1032           "SELECT %Q || pathname FROM sfile"

Changes to src/configure.c.

   141    141     { "clean-glob",             CONFIGSET_PROJ },
   142    142     { "ignore-glob",            CONFIGSET_PROJ },
   143    143     { "keep-glob",              CONFIGSET_PROJ },
   144    144     { "crlf-glob",              CONFIGSET_PROJ },
   145    145     { "crnl-glob",              CONFIGSET_PROJ },
   146    146     { "encoding-glob",          CONFIGSET_PROJ },
   147    147     { "empty-dirs",             CONFIGSET_PROJ },
          148  +#ifdef FOSSIL_LEGACY_ALLOW_SYMLINKS
   148    149     { "allow-symlinks",         CONFIGSET_PROJ },
          150  +#endif
   149    151     { "dotfiles",               CONFIGSET_PROJ },
   150    152     { "parent-project-code",    CONFIGSET_PROJ },
   151    153     { "parent-project-name",    CONFIGSET_PROJ },
   152    154     { "hash-policy",            CONFIGSET_PROJ },
   153    155     { "comment-format",         CONFIGSET_PROJ },
   154    156     { "mimetypes",              CONFIGSET_PROJ },
   155    157     { "forbid-delta-manifests", CONFIGSET_PROJ },

Changes to src/db.c.

   128    128     } aHook[5];
   129    129     char *azDeleteOnFail[3];  /* Files to delete on a failure */
   130    130     char *azBeforeCommit[5];  /* Commands to run prior to COMMIT */
   131    131     int nBeforeCommit;        /* Number of entries in azBeforeCommit */
   132    132     int nPriorChanges;        /* sqlite3_total_changes() at transaction start */
   133    133     const char *zStartFile;   /* File in which transaction was started */
   134    134     int iStartLine;           /* Line of zStartFile where transaction started */
          135  +  int (*xAuth)(void*,int,const char*,const char*,const char*,const char*);
          136  +  void *pAuthArg;           /* Argument to the authorizer */
          137  +  const char *zAuthName;    /* Name of the authorizer */
   135    138   } db = {0, 0, 0, 0, 0, 0, };
   136    139   
   137    140   /*
   138    141   ** Arrange for the given file to be deleted on a failure.
   139    142   */
   140    143   void db_delete_on_failure(const char *zFilename){
   141    144     assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );
................................................................................
   314    317         db.aHook[i].xHook = xS;
   315    318       }
   316    319     }
   317    320     db.aHook[db.nCommitHook].sequence = sequence;
   318    321     db.aHook[db.nCommitHook].xHook = x;
   319    322     db.nCommitHook++;
   320    323   }
          324  +
          325  +/*
          326  +** Set or unset the query authorizer callback function
          327  +*/
          328  +void db_set_authorizer(
          329  +  int(*xAuth)(void*,int,const char*,const char*,const char*,const char*),
          330  +  void *pArg,
          331  +  const char *zName /* for tracing */
          332  +){
          333  +  if( db.xAuth ){
          334  +    fossil_panic("multiple active db_set_authorizer() calls");
          335  +  }
          336  +  if( g.db ) sqlite3_set_authorizer(g.db, xAuth, pArg);
          337  +  db.xAuth = xAuth;
          338  +  db.pAuthArg = pArg;
          339  +  db.zAuthName = zName;
          340  +  if( g.fSqlTrace ) fossil_trace("-- set authorizer %s\n", zName);
          341  +}
          342  +void db_clear_authorizer(void){
          343  +  if( db.zAuthName && g.fSqlTrace ){
          344  +    fossil_trace("-- discontinue authorizer %s\n", db.zAuthName);
          345  +  }
          346  +  if( g.db ) sqlite3_set_authorizer(g.db, 0, 0);
          347  +  db.xAuth = 0;
          348  +  db.pAuthArg = 0;
          349  +}
   321    350   
   322    351   #if INTERFACE
   323    352   /*
   324    353   ** Possible flags to db_vprepare
   325    354   */
   326    355   #define DB_PREPARE_IGNORE_ERROR  0x001  /* Suppress errors */
   327    356   #define DB_PREPARE_PERSISTENT    0x002  /* Stmt will stick around for a while */
................................................................................
   842    871   ** database.
   843    872   */
   844    873   void db_init_database(
   845    874     const char *zFileName,   /* Name of database file to create */
   846    875     const char *zSchema,     /* First part of schema */
   847    876     ...                      /* Additional SQL to run.  Terminate with NULL. */
   848    877   ){
   849         -  sqlite3 *db;
          878  +  sqlite3 *xdb;
   850    879     int rc;
   851    880     const char *zSql;
   852    881     va_list ap;
   853    882   
   854         -  db = db_open(zFileName ? zFileName : ":memory:");
   855         -  sqlite3_exec(db, "BEGIN EXCLUSIVE", 0, 0, 0);
   856         -  rc = sqlite3_exec(db, zSchema, 0, 0, 0);
          883  +  xdb = db_open(zFileName ? zFileName : ":memory:");
          884  +  sqlite3_exec(xdb, "BEGIN EXCLUSIVE", 0, 0, 0);
          885  +  if( db.xAuth ){
          886  +    sqlite3_set_authorizer(xdb, db.xAuth, db.pAuthArg);
          887  +  }
          888  +  rc = sqlite3_exec(xdb, zSchema, 0, 0, 0);
   857    889     if( rc!=SQLITE_OK ){
   858         -    db_err("%s", sqlite3_errmsg(db));
          890  +    db_err("%s", sqlite3_errmsg(xdb));
   859    891     }
   860    892     va_start(ap, zSchema);
   861    893     while( (zSql = va_arg(ap, const char*))!=0 ){
   862         -    rc = sqlite3_exec(db, zSql, 0, 0, 0);
          894  +    rc = sqlite3_exec(xdb, zSql, 0, 0, 0);
   863    895       if( rc!=SQLITE_OK ){
   864         -      db_err("%s", sqlite3_errmsg(db));
          896  +      db_err("%s", sqlite3_errmsg(xdb));
   865    897       }
   866    898     }
   867    899     va_end(ap);
   868         -  sqlite3_exec(db, "COMMIT", 0, 0, 0);
          900  +  sqlite3_exec(xdb, "COMMIT", 0, 0, 0);
   869    901     if( zFileName || g.db!=0 ){
   870         -    sqlite3_close(db);
          902  +    sqlite3_close(xdb);
   871    903     }else{
   872         -    g.db = db;
          904  +    g.db = xdb;
   873    905     }
   874    906   }
   875    907   
   876    908   /*
   877    909   ** Function to return the number of seconds since 1970.  This is
   878    910   ** the same as strftime('%s','now') but is more compact.
   879    911   */
................................................................................
  1791   1823   }
  1792   1824   
  1793   1825   /*
  1794   1826   ** Returns non-zero if the default value for the "allow-symlinks" setting
  1795   1827   ** is "on".  When on Windows, this always returns false.
  1796   1828   */
  1797   1829   int db_allow_symlinks_by_default(void){
  1798         -#if defined(_WIN32)
         1830  +#if defined(_WIN32) || !defined(FOSSIL_LEGACY_ALLOW_SYMLINKS)
  1799   1831     return 0;
  1800   1832   #else
  1801   1833     return 1;
  1802   1834   #endif
  1803   1835   }
  1804   1836   
  1805   1837   /*
................................................................................
  2084   2116   **
  2085   2117   ** Check for unfinalized statements and report errors if the reportErrors
  2086   2118   ** argument is true.  Ignore unfinalized statements when false.
  2087   2119   */
  2088   2120   void db_close(int reportErrors){
  2089   2121     sqlite3_stmt *pStmt;
  2090   2122     if( g.db==0 ) return;
         2123  +  sqlite3_set_authorizer(g.db, 0, 0);
  2091   2124     if( g.fSqlStats ){
  2092   2125       int cur, hiwtr;
  2093   2126       sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_USED, &cur, &hiwtr, 0);
  2094   2127       fprintf(stderr, "-- LOOKASIDE_USED         %10d %10d\n", cur, hiwtr);
  2095   2128       sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_HIT, &cur, &hiwtr, 0);
  2096   2129       fprintf(stderr, "-- LOOKASIDE_HIT                     %10d\n", hiwtr);
  2097   2130       sqlite3_db_status(g.db, SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE, &cur,&hiwtr,0);
................................................................................
  2113   2146       sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &cur, &hiwtr, 0);
  2114   2147       fprintf(stderr, "-- PCACHE_OVFLOW          %10d %10d\n", cur, hiwtr);
  2115   2148       fprintf(stderr, "-- prepared statements    %10d\n", db.nPrepare);
  2116   2149     }
  2117   2150     while( db.pAllStmt ){
  2118   2151       db_finalize(db.pAllStmt);
  2119   2152     }
  2120         -  if( db.nBegin && reportErrors ){
         2153  +  if( db.nBegin ){
         2154  +    if( reportErrors ){
  2121   2155       fossil_warning("Transaction started at %s:%d never commits",
  2122   2156                      db.zStartFile, db.iStartLine);
         2157  +    }
  2123   2158       db_end_transaction(1);
  2124   2159     }
  2125   2160     pStmt = 0;
  2126         -  g.dbIgnoreErrors++; /* Stop "database locked" warnings from PRAGMA optimize */
         2161  +  sqlite3_busy_timeout(g.db, 0);
         2162  +  g.dbIgnoreErrors++; /* Stop "database locked" warnings */
  2127   2163     sqlite3_exec(g.db, "PRAGMA optimize", 0, 0, 0);
  2128   2164     g.dbIgnoreErrors--;
  2129   2165     db_close_config();
  2130   2166   
  2131   2167     /* If the localdb has a lot of unused free space,
  2132   2168     ** then VACUUM it as we shut down.
  2133   2169     */
................................................................................
  2163   2199   */
  2164   2200   void db_panic_close(void){
  2165   2201     if( g.db ){
  2166   2202       int rc;
  2167   2203       sqlite3_wal_checkpoint(g.db, 0);
  2168   2204       rc = sqlite3_close(g.db);
  2169   2205       if( g.fSqlTrace ) fossil_trace("-- sqlite3_close(%d)\n", rc);
         2206  +    db_clear_authorizer();
  2170   2207     }
  2171   2208     g.db = 0;
  2172   2209     g.repositoryOpen = 0;
  2173   2210     g.localOpen = 0;
  2174   2211   }
  2175   2212   
  2176   2213   /*
................................................................................
  2917   2954       dflt = 1;
  2918   2955     }else if( is_false(zVal) ){
  2919   2956       dflt = 0;
  2920   2957     }
  2921   2958     fossil_free(zVal);
  2922   2959     return dflt;
  2923   2960   }
         2961  +#ifdef FOSSIL_LEGACY_ALLOW_SYMLINKS
  2924   2962   int db_get_versioned_boolean(const char *zName, int dflt){
  2925   2963     char *zVal = db_get_versioned(zName, 0);
  2926   2964     if( zVal==0 ) return dflt;
  2927   2965     if( is_truth(zVal) ) return 1;
  2928   2966     if( is_false(zVal) ) return 0;
  2929   2967     return dflt;
  2930   2968   }
         2969  +#endif /* FOSSIL_LEGACY_ALLOW_SYMLINKS */
  2931   2970   char *db_lget(const char *zName, const char *zDefault){
  2932   2971     return db_text(zDefault,
  2933   2972                    "SELECT value FROM vvar WHERE name=%Q", zName);
  2934   2973   }
  2935   2974   void db_lset(const char *zName, const char *zValue){
  2936   2975     db_multi_exec("REPLACE INTO vvar(name,value) VALUES(%Q,%Q)", zName, zValue);
  2937   2976   }
................................................................................
  3126   3165   ** See also: [[close]], [[clone]]
  3127   3166   */
  3128   3167   void cmd_open(void){
  3129   3168     int emptyFlag;
  3130   3169     int keepFlag;
  3131   3170     int forceMissingFlag;
  3132   3171     int allowNested;
         3172  +#ifdef FOSSIL_LEGACY_ALLOW_SYMLINKS
  3133   3173     int allowSymlinks;
         3174  +#endif
  3134   3175     int setmtimeFlag;              /* --setmtime.  Set mtimes on files */
  3135   3176     int bForce = 0;                /* --force.  Open even if non-empty dir */
  3136   3177     static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 };
  3137   3178     const char *zWorkDir;          /* --workdir value */
  3138   3179     const char *zRepo = 0;         /* Name of the repository file */
  3139   3180     const char *zRepoDir = 0;      /* --repodir value */
  3140   3181     char *zPwd;                    /* Initial working directory */
................................................................................
  3237   3278       if( g.argc==4 ){
  3238   3279         g.zOpenRevision = g.argv[3];
  3239   3280       }else if( db_exists("SELECT 1 FROM event WHERE type='ci'") ){
  3240   3281         g.zOpenRevision = db_get("main-branch", 0);
  3241   3282       }
  3242   3283     }
  3243   3284   
         3285  +#ifdef FOSSIL_LEGACY_ALLOW_SYMLINKS
  3244   3286     if( g.zOpenRevision ){
  3245   3287       /* Since the repository is open and we know the revision now,
  3246   3288       ** refresh the allow-symlinks flag.  Since neither the local
  3247   3289       ** checkout nor the configuration database are open at this
  3248   3290       ** point, this should always return the versioned setting,
  3249   3291       ** if any, or the default value, which is negative one.  The
  3250   3292       ** value negative one, in this context, means that the code
  3251   3293       ** below should fallback to using the setting value from the
  3252   3294       ** repository or global configuration databases only. */
  3253   3295       allowSymlinks = db_get_versioned_boolean("allow-symlinks", -1);
  3254   3296     }else{
  3255   3297       allowSymlinks = -1; /* Use non-versioned settings only. */
  3256   3298     }
         3299  +#endif
  3257   3300   
  3258   3301   #if defined(_WIN32) || defined(__CYGWIN__)
  3259   3302   # define LOCALDB_NAME "./_FOSSIL_"
  3260   3303   #else
  3261   3304   # define LOCALDB_NAME "./.fslckout"
  3262   3305   #endif
  3263   3306     db_init_database(LOCALDB_NAME, zLocalSchema, zLocalSchemaVmerge,
  3264   3307   #ifdef FOSSIL_LOCAL_WAL
  3265   3308                      "COMMIT; PRAGMA journal_mode=WAL; BEGIN;",
  3266   3309   #endif
  3267   3310                      (char*)0);
  3268   3311     db_delete_on_failure(LOCALDB_NAME);
  3269   3312     db_open_local(0);
         3313  +#ifdef FOSSIL_LEGACY_ALLOW_SYMLINKS
  3270   3314     if( allowSymlinks>=0 ){
  3271   3315       /* Use the value from the versioned setting, which was read
  3272   3316       ** prior to opening the local checkout (i.e. which is most
  3273   3317       ** likely empty and does not actually contain any versioned
  3274   3318       ** setting files yet).  Normally, this value would be given
  3275   3319       ** first priority within db_get_boolean(); however, this is
  3276   3320       ** a special case because we know the on-disk files may not
................................................................................
  3279   3323     }else{
  3280   3324       /* Since the local checkout may not have any files at this
  3281   3325       ** point, this will probably be the setting value from the
  3282   3326       ** repository or global configuration databases. */
  3283   3327       g.allowSymlinks = db_get_boolean("allow-symlinks",
  3284   3328                                        db_allow_symlinks_by_default());
  3285   3329     }
         3330  +#endif /* FOSSIL_LEGACY_ALLOW_SYMLINKS */
  3286   3331     db_lset("repository", zRepo);
  3287   3332     db_record_repository_filename(zRepo);
  3288   3333     db_set_checkout(0);
  3289   3334     azNewArgv[0] = g.argv[0];
  3290   3335     g.argv = azNewArgv;
  3291   3336     if( !emptyFlag ){
  3292   3337       g.argc = 3;
................................................................................
  3390   3435   */
  3391   3436   /*
  3392   3437   ** SETTING: admin-log       boolean default=off
  3393   3438   **
  3394   3439   ** When the admin-log setting is enabled, configuration changes are recorded
  3395   3440   ** in the "admin_log" table of the repository.
  3396   3441   */
  3397         -#if defined(_WIN32)
         3442  +#if !defined(FOSSIL_LEGACY_ALLOW_SYMLINKS)
         3443  +/*
         3444  +** SETTING: allow-symlinks  boolean default=off
         3445  +**
         3446  +** When allow-symlinks is OFF (which is the default and recommended setting)
         3447  +** symbolic links are treated like text files that contain a single line of
         3448  +** content which is the name of their target.  If allow-symlinks is ON,
         3449  +** the symbolic links are actually followed.
         3450  +**
         3451  +** The use of symbolic links is dangerous.  If you checkout a maliciously
         3452  +** crafted checkin that contains symbolic links, it is possible that files
         3453  +** outside of the working directory might be overwritten.
         3454  +**
         3455  +** Keep this setting OFF unless you have a very good reason to turn it
         3456  +** on and you implicitly trust the integrity of the repositories you
         3457  +** open.
         3458  +*/
         3459  +#endif
         3460  +#if defined(_WIN32) && defined(FOSSIL_LEGACY_ALLOW_SYMLINKS)
  3398   3461   /*
  3399   3462   ** SETTING: allow-symlinks  boolean default=off versionable
  3400   3463   **
  3401   3464   ** When allow-symlinks is OFF, symbolic links in the repository are followed
  3402   3465   ** and treated no differently from real files.  When allow-symlinks is ON,
  3403   3466   ** the object to which the symbolic link points is ignored, and the content
  3404   3467   ** of the symbolic link that is stored in the repository is the name of the
  3405   3468   ** object to which the symbolic link points.
  3406   3469   */
  3407   3470   #endif
  3408         -#if !defined(_WIN32)
         3471  +#if !defined(_WIN32) && defined(FOSSIL_LEGACY_ALLOW_SYMLINKS)
  3409   3472   /*
  3410   3473   ** SETTING: allow-symlinks  boolean default=on versionable
  3411   3474   **
  3412   3475   ** When allow-symlinks is OFF, symbolic links in the repository are followed
  3413   3476   ** and treated no differently from real files.  When allow-symlinks is ON,
  3414   3477   ** the object to which the symbolic link points is ignored, and the content
  3415   3478   ** of the symbolic link that is stored in the repository is the name of the

Changes to src/file.c.

   321    321   ** This routines RepoFILE - that zFilename is always a file under management.
   322    322   **
   323    323   ** On Windows, always return False.
   324    324   */
   325    325   int file_islink(const char *zFilename){
   326    326     return file_perm(zFilename, RepoFILE)==PERM_LNK;
   327    327   }
          328  +
          329  +/*
          330  +** Check every sub-directory of zRoot along the path to zFile.
          331  +** If any sub-directory is really an ordinary file or a symbolic link,
          332  +** return an integer which is the length of the prefix of zFile which
          333  +** is the name of that object.  Return 0 if all no non-directory
          334  +** objects are found along the path.
          335  +**
          336  +** Example:  Given inputs
          337  +**
          338  +**     zRoot = /home/alice/project1
          339  +**     zFile = /home/alice/project1/main/src/js/fileA.js
          340  +**
          341  +** Look for objects in the following order:
          342  +**
          343  +**      /home/alice/project/main
          344  +**      /home/alice/project/main/src
          345  +**      /home/alice/project/main/src/js
          346  +**
          347  +** If any of those objects exist and are something other than a directory
          348  +** then return the length of the name of the first non-directory object
          349  +** seen.
          350  +*/
          351  +int file_nondir_objects_on_path(const char *zRoot, const char *zFile){
          352  +  int i = (int)strlen(zRoot);
          353  +  char *z = fossil_strdup(zFile);
          354  +  assert( fossil_strnicmp(zRoot, z, i)==0 );
          355  +  if( i && zRoot[i-1]=='/' ) i--;
          356  +  while( z[i]=='/' ){
          357  +    int j, rc;
          358  +    for(j=i+1; z[j] && z[j]!='/'; j++){}
          359  +    if( z[j]!='/' ) break;
          360  +    z[j] = 0;
          361  +    rc = file_isdir(z, SymFILE);
          362  +    if( rc!=1 ){
          363  +      if( rc==2 ){
          364  +        fossil_free(z);
          365  +        return j;
          366  +      }
          367  +      break;
          368  +    }
          369  +    z[j] = '/';
          370  +    i = j;
          371  +  }
          372  +  fossil_free(z);
          373  +  return 0;
          374  +}
          375  +
          376  +/*
          377  +** The file named zFile is suppose to be an in-tree file.  Check to
          378  +** ensure that it will be safe to write to this file by verifying that
          379  +** there are no symlinks or other non-directory objects in between the
          380  +** root of the checkout and zFile.
          381  +**
          382  +** If a problem is found, print a warning message (using fossil_warning())
          383  +** and return non-zero.  If everything is ok, return zero.
          384  +*/
          385  +int file_unsafe_in_tree_path(const char *zFile){
          386  +  int n;
          387  +  if( !file_is_absolute_path(zFile) ){
          388  +    fossil_panic("%s is not an absolute pathname",zFile);
          389  +  }
          390  +  if( fossil_strnicmp(g.zLocalRoot, zFile, (int)strlen(g.zLocalRoot)) ){
          391  +    fossil_panic("%s is not a prefix of %s", g.zLocalRoot, zFile);
          392  +  }
          393  +  n = file_nondir_objects_on_path(g.zLocalRoot, zFile);
          394  +  if( n ){
          395  +    fossil_warning("cannot write to %s because non-directory object %.*s"
          396  +                   " is in the way", zFile, n, zFile);
          397  +  }
          398  +  return n;
          399  +}
   328    400   
   329    401   /*
   330    402   ** Return 1 if zFilename is a directory.  Return 0 if zFilename
   331    403   ** does not exist.  Return 2 if zFilename exists but is something
   332    404   ** other than a directory.
   333    405   */
   334    406   int file_isdir(const char *zFilename, int eFType){
................................................................................
   568    640   ** zFilename is a symbolic link, it is the object that zFilename points
   569    641   ** to that is modified.
   570    642   */
   571    643   int file_setexe(const char *zFilename, int onoff){
   572    644     int rc = 0;
   573    645   #if !defined(_WIN32)
   574    646     struct stat buf;
   575         -  if( fossil_stat(zFilename, &buf, RepoFILE)!=0 || S_ISLNK(buf.st_mode) ){
          647  +  if( fossil_stat(zFilename, &buf, RepoFILE)!=0 
          648  +   || S_ISLNK(buf.st_mode)
          649  +   || S_ISDIR(buf.st_mode)
          650  +  ){
   576    651       return 0;
   577    652     }
   578    653     if( onoff ){
   579    654       int targetMode = (buf.st_mode & 0444)>>2;
   580    655       if( (buf.st_mode & 0100)==0 ){
   581    656         chmod(zFilename, buf.st_mode | targetMode);
   582    657         rc = 1;
................................................................................
  2395   2470     if( dryRunFlag!=0 ){
  2396   2471       fossil_print("dry-run: would have touched %d file(s)\n",
  2397   2472                    changeCount);
  2398   2473     }else{
  2399   2474       fossil_print("Touched %d file(s)\n", changeCount);
  2400   2475     }
  2401   2476   }
         2477  +
         2478  +/*
         2479  +** Returns non-zero if the specified file name ends with any reserved name,
         2480  +** e.g.: _FOSSIL_ or .fslckout.  Specifically, it returns 1 for exact match
         2481  +** or 2 for a tail match on a longer file name.
         2482  +**
         2483  +** For the sake of efficiency, zFilename must be a canonical name, e.g. an
         2484  +** absolute path using only forward slash ('/') as a directory separator.
         2485  +**
         2486  +** nFilename must be the length of zFilename.  When negative, strlen() will
         2487  +** be used to calculate it.
         2488  +*/
         2489  +int file_is_reserved_name(const char *zFilename, int nFilename){
         2490  +  const char *zEnd;  /* one-after-the-end of zFilename */
         2491  +  int gotSuffix = 0; /* length of suffix (-wal, -shm, -journal) */
         2492  +
         2493  +  assert( zFilename && "API misuse" );
         2494  +  if( nFilename<0 ) nFilename = (int)strlen(zFilename);
         2495  +  if( nFilename<8 ) return 0; /* strlen("_FOSSIL_") */
         2496  +  zEnd = zFilename + nFilename;
         2497  +  if( nFilename>=12 ){ /* strlen("_FOSSIL_-(shm|wal)") */
         2498  +    /* Check for (-wal, -shm, -journal) suffixes, with an eye towards
         2499  +    ** runtime speed. */
         2500  +    if( zEnd[-4]=='-' ){
         2501  +      if( fossil_strnicmp("wal", &zEnd[-3], 3)
         2502  +       && fossil_strnicmp("shm", &zEnd[-3], 3) ){
         2503  +        return 0;
         2504  +      }
         2505  +      gotSuffix = 4;
         2506  +    }else if( nFilename>=16 && zEnd[-8]=='-' ){ /*strlen(_FOSSIL_-journal) */
         2507  +      if( fossil_strnicmp("journal", &zEnd[-7], 7) ) return 0;
         2508  +      gotSuffix = 8;
         2509  +    }
         2510  +    if( gotSuffix ){
         2511  +      assert( 4==gotSuffix || 8==gotSuffix );
         2512  +      zEnd -= gotSuffix;
         2513  +      nFilename -= gotSuffix;
         2514  +      gotSuffix = 1;
         2515  +    }
         2516  +    assert( nFilename>=8 && "strlen(_FOSSIL_)" );
         2517  +    assert( gotSuffix==0 || gotSuffix==1 );
         2518  +  }
         2519  +  switch( zEnd[-1] ){
         2520  +    case '_':{
         2521  +      if( fossil_strnicmp("_FOSSIL_", &zEnd[-8], 8) ) return 0;
         2522  +      if( 8==nFilename ) return 1;
         2523  +      return zEnd[-9]=='/' ? 2 : gotSuffix;
         2524  +    }
         2525  +    case 'T':
         2526  +    case 't':{
         2527  +      if( nFilename<9 || zEnd[-9]!='.'
         2528  +       || fossil_strnicmp(".fslckout", &zEnd[-9], 9) ){
         2529  +        return 0; 
         2530  +      }
         2531  +      if( 9==nFilename ) return 1;
         2532  +      return zEnd[-10]=='/' ? 2 : gotSuffix;
         2533  +    }
         2534  +    default:{
         2535  +      return 0;
         2536  +    }
         2537  +  }
         2538  +}
         2539  +
         2540  +/*
         2541  +** COMMAND: test-is-reserved-name
         2542  +**
         2543  +** Usage: %fossil test-is-ckout-db FILENAMES...
         2544  +**
         2545  +** Passes each given name to file_is_reserved_name() and outputs one
         2546  +** line per file: the result value of that function followed by the
         2547  +** name.
         2548  +*/
         2549  +void test_is_reserved_name_cmd(void){
         2550  +  int i;
         2551  +
         2552  +  if(g.argc<3){
         2553  +    usage("FILENAME_1 [...FILENAME_N]");
         2554  +  }
         2555  +  for( i = 2; i < g.argc; ++i ){
         2556  +    const int check = file_is_reserved_name(g.argv[i], -1);
         2557  +    fossil_print("%d %s\n", check, g.argv[i]);
         2558  +  }
         2559  +}

Changes to src/http.c.

   373    373         j = strlen(zLine) - 1;
   374    374         while( j>4 && fossil_strcmp(&zLine[j-4],"/xfer")==0 ){
   375    375            j -= 4;
   376    376            zLine[j] = 0;
   377    377         }
   378    378         if( (mHttpFlags & HTTP_QUIET)==0 ){
   379    379           fossil_print("redirect with status %d to %s\n", rc, &zLine[i]);
          380  +      }
          381  +      if( g.url.isFile || g.url.isSsh ){
          382  +        fossil_warning("cannot redirect from %s to %s", g.url.canonical,
          383  +                       &zLine[i]);
          384  +        goto write_err;
   380    385         }
   381    386         wasHttps = g.url.isHttps;
   382    387         url_parse(&zLine[i], 0);
   383    388         if( wasHttps && !g.url.isHttps ){
   384    389           fossil_warning("cannot redirect from HTTPS to HTTP");
   385    390           goto write_err;
          391  +      }
          392  +      if( g.url.isSsh || g.url.isFile ){
          393  +        fossil_warning("cannot redirect to %s", &zLine[i]);
          394  +        goto write_err;
   386    395          }
   387    396         transport_close(&g.url);
   388    397         transport_global_shutdown(&g.url);
   389    398         fSeenHttpAuth = 0;
   390    399         if( g.zHttpAuth ) free(g.zHttpAuth);
   391    400         g.zHttpAuth = get_httpauth();
   392    401         if( rc==301 || rc==308 ) url_remember();

Changes to src/json_config.c.

    81     81   { "clean-glob",             CONFIGSET_PROJ },
    82     82   { "ignore-glob",            CONFIGSET_PROJ },
    83     83   { "keep-glob",              CONFIGSET_PROJ },
    84     84   { "crlf-glob",              CONFIGSET_PROJ },
    85     85   { "crnl-glob",              CONFIGSET_PROJ },
    86     86   { "encoding-glob",          CONFIGSET_PROJ },
    87     87   { "empty-dirs",             CONFIGSET_PROJ },
           88  +#ifdef FOSSIL_LEGACY_ALLOW_SYMLINKS
    88     89   { "allow-symlinks",         CONFIGSET_PROJ },
           90  +#endif
    89     91   { "dotfiles",               CONFIGSET_PROJ },
    90     92   
    91     93   { "ticket-table",           CONFIGSET_TKT  },
    92     94   { "ticket-common",          CONFIGSET_TKT  },
    93     95   { "ticket-change",          CONFIGSET_TKT  },
    94     96   { "ticket-newpage",         CONFIGSET_TKT  },
    95     97   { "ticket-viewpage",        CONFIGSET_TKT  },

Changes to src/main.c.

  1225   1225     blob_append(pOut, "UNICODE_COMMAND_LINE\n", -1);
  1226   1226   #endif
  1227   1227   #if defined(FOSSIL_DYNAMIC_BUILD)
  1228   1228     blob_append(pOut, "FOSSIL_DYNAMIC_BUILD\n", -1);
  1229   1229   #else
  1230   1230     blob_append(pOut, "FOSSIL_STATIC_BUILD\n", -1);
  1231   1231   #endif
         1232  +#if defined(FOSSIL_LEGACY_ALLOW_SYMLINKS)
         1233  +  blob_append(pOut, "FOSSIL_LEGACY_ALLOW_SYMLINKS\n", -1);
         1234  +#endif
  1232   1235   #if defined(HAVE_PLEDGE)
  1233   1236     blob_append(pOut, "HAVE_PLEDGE\n", -1);
  1234   1237   #endif
  1235   1238   #if defined(USE_MMAN_H)
  1236   1239     blob_append(pOut, "USE_MMAN_H\n", -1);
  1237   1240   #endif
  1238   1241   #if defined(USE_SEE)

Changes to src/manifest.c.

   479    479     if( n<10 || z[0]<'A' || z[0]>'Z' || z[1]!=' ' ){
   480    480       blob_reset(pContent);
   481    481       blob_appendf(pErr, "line 1 not recognized");
   482    482       return 0;
   483    483     }
   484    484     /* Then verify the Z-card.
   485    485     */
          486  +#if 1
          487  +  /* Disable this ***ONLY*** (ONLY!) when testing hand-written inputs
          488  +     for card-related syntax errors. */
   486    489     if( verify_z_card(z, n, pErr)==2 ){
   487    490       blob_reset(pContent);
   488    491       return 0;
   489    492     }
          493  +#else
          494  +#warning ACHTUNG - z-card check is disabled for testing purposes.
          495  +  if(0 && verify_z_card(NULL, 0, NULL)){
          496  +    /*avoid unused static func error*/
          497  +  }
          498  +#endif
   490    499   
   491    500     /* Allocate a Manifest object to hold the parsed control artifact.
   492    501     */
   493    502     p = fossil_malloc( sizeof(*p) );
   494    503     memset(p, 0, sizeof(*p));
   495    504     memcpy(&p->content, pContent, sizeof(p->content));
   496    505     p->rid = rid;
................................................................................
   599    608         ** is when the specific event is said to occur.
   600    609         */
   601    610         case 'E': {
   602    611           if( p->rEventDate>0.0 ) SYNTAX("more than one E-card");
   603    612           p->rEventDate = db_double(0.0,"SELECT julianday(%Q)", next_token(&x,0));
   604    613           if( p->rEventDate<=0.0 ) SYNTAX("malformed date on E-card");
   605    614           p->zEventId = next_token(&x, &sz);
          615  +        if( p->zEventId==0 ) SYNTAX("missing hash on E-card");
   606    616           if( !hname_validate(p->zEventId, sz) ){
   607    617             SYNTAX("malformed hash on E-card");
   608    618           }
   609    619           p->type = CFTYPE_EVENT;
   610    620           break;
   611    621         }
   612    622   
................................................................................
   623    633           if( zName==0 ) SYNTAX("missing filename on F-card");
   624    634           defossilize(zName);
   625    635           if( !file_is_simple_pathname_nonstrict(zName) ){
   626    636             SYNTAX("F-card filename is not a simple path");
   627    637           }
   628    638           zUuid = next_token(&x, &sz);
   629    639           if( p->zBaseline==0 || zUuid!=0 ){
          640  +          if( zUuid==0 ) SYNTAX("missing hash on F-card");
   630    641             if( !hname_validate(zUuid,sz) ){
   631    642               SYNTAX("F-card hash invalid");
   632    643             }
   633    644           }
   634    645           zPerm = next_token(&x,0);
   635    646           zPriorName = next_token(&x,0);
   636    647           if( zPriorName ){
................................................................................
   641    652           }
   642    653           if( p->nFile>=p->nFileAlloc ){
   643    654             p->nFileAlloc = p->nFileAlloc*2 + 10;
   644    655             p->aFile = fossil_realloc(p->aFile,
   645    656                                       p->nFileAlloc*sizeof(p->aFile[0]) );
   646    657           }
   647    658           i = p->nFile++;
          659  +        if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){
          660  +          SYNTAX("incorrect F-card sort order");
          661  +        }
          662  +        if( file_is_reserved_name(zName,-1) ){
          663  +          /* If reserved names leaked into historical manifests due to
          664  +          ** slack oversight by older versions of Fossil, simply ignore
          665  +          ** those files */
          666  +          p->nFile--;
          667  +          break;
          668  +        }
   648    669           p->aFile[i].zName = zName;
   649    670           p->aFile[i].zUuid = zUuid;
   650    671           p->aFile[i].zPerm = zPerm;
   651    672           p->aFile[i].zPrior = zPriorName;
   652         -        if( i>0 && fossil_strcmp(p->aFile[i-1].zName, zName)>=0 ){
   653         -          SYNTAX("incorrect F-card sort order");
   654         -        }
   655    673           p->type = CFTYPE_MANIFEST;
   656    674           break;
   657    675         }
   658    676   
   659    677         /*
   660    678         **    G <hash>
   661    679         **

Changes to src/report.c.

   228    228     return rc;
   229    229   }
   230    230   
   231    231   /*
   232    232   ** Activate the query authorizer
   233    233   */
   234    234   void report_restrict_sql(char **pzErr){
   235         -  sqlite3_set_authorizer(g.db, report_query_authorizer, (void*)pzErr);
          235  +  db_set_authorizer(report_query_authorizer,(void*)pzErr,"Ticket-Report");
   236    236     sqlite3_limit(g.db, SQLITE_LIMIT_VDBE_OP, 10000);
   237    237   }
   238    238   void report_unrestrict_sql(void){
   239         -  sqlite3_set_authorizer(g.db, 0, 0);
          239  +  db_clear_authorizer();
   240    240   }
   241    241   
   242    242   
   243    243   /*
   244    244   ** Check the given SQL to see if is a valid query that does not
   245    245   ** attempt to do anything dangerous.  Return 0 on success and a
   246    246   ** pointer to an error message string (obtained from malloc) if
................................................................................
   678    678   
   679    679     /* Do initialization
   680    680     */
   681    681     if( pState->nCount==0 ){
   682    682       /* Turn off the authorizer.  It is no longer doing anything since the
   683    683       ** query has already been prepared.
   684    684       */
   685         -    sqlite3_set_authorizer(g.db, 0, 0);
          685  +    db_clear_authorizer();
   686    686   
   687    687       /* Figure out the number of columns, the column that determines background
   688    688       ** color, and whether or not this row of data is represented by multiple
   689    689       ** rows in the table.
   690    690       */
   691    691       pState->nCol = 0;
   692    692       pState->isMultirow = 0;

Changes to src/stash.c.

   332    332         db_multi_exec("INSERT OR IGNORE INTO sfile(pathname) VALUES(%Q)", zNew);
   333    333         db_ephemeral_blob(&q, 6, &delta);
   334    334         blob_write_to_file(&delta, zNPath);
   335    335         file_setexe(zNPath, isExec);
   336    336       }else if( isRemoved ){
   337    337         fossil_print("DELETE %s\n", zOrig);
   338    338         file_delete(zOPath);
          339  +    }else if( file_unsafe_in_tree_path(zNPath) ){
          340  +      /* Ignore the unsafe path */
   339    341       }else{
   340    342         Blob a, b, out, disk;
   341    343         int isNewLink = file_islink(zOPath);
   342    344         db_ephemeral_blob(&q, 6, &delta);
   343    345         blob_read_from_file(&disk, zOPath, RepoFILE);
   344    346         content_get(rid, &a);
   345    347         blob_delta_apply(&a, &delta, &b);

Changes to src/tkt.c.

   368    368   int ticket_change(const char *zUuid){
   369    369     const char *zConfig;
   370    370     Th_FossilInit(TH_INIT_DEFAULT);
   371    371     Th_Store("uuid", zUuid);
   372    372     zConfig = ticket_change_code();
   373    373     return Th_Eval(g.interp, 0, zConfig, -1);
   374    374   }
          375  +
          376  +/*
          377  +** An authorizer function for the SQL used to initialize the
          378  +** schema for the ticketing system.  Only allow CREATE TABLE and
          379  +** CREATE INDEX for tables whose names begin with "ticket" and
          380  +** changes to tables whose names begin with "ticket".
          381  +*/
          382  +static int ticket_schema_auth(
          383  +  void *pNErr,
          384  +  int eCode,
          385  +  const char *z0,
          386  +  const char *z1,
          387  +  const char *z2,
          388  +  const char *z3
          389  +){
          390  +  switch( eCode ){
          391  +    case SQLITE_CREATE_TABLE: {
          392  +      if( sqlite3_stricmp(z2,"main")!=0
          393  +       && sqlite3_stricmp(z2,"repository")!=0
          394  +      ){
          395  +        goto ticket_schema_error;
          396  +      }
          397  +      if( sqlite3_strnicmp(z0,"ticket",6)!=0 ){
          398  +        goto ticket_schema_error;
          399  +      }
          400  +      break;
          401  +    }
          402  +    case SQLITE_CREATE_INDEX: {
          403  +      if( sqlite3_stricmp(z2,"main")!=0
          404  +       && sqlite3_stricmp(z2,"repository")!=0
          405  +      ){
          406  +        goto ticket_schema_error;
          407  +      }
          408  +      if( sqlite3_strnicmp(z1,"ticket",6)!=0 ){
          409  +        goto ticket_schema_error;
          410  +      }
          411  +      break;
          412  +    }
          413  +    case SQLITE_INSERT:
          414  +    case SQLITE_UPDATE:
          415  +    case SQLITE_DELETE: {
          416  +      if( sqlite3_stricmp(z2,"main")!=0
          417  +       && sqlite3_stricmp(z2,"repository")!=0
          418  +      ){
          419  +        goto ticket_schema_error;
          420  +      }
          421  +      if( sqlite3_strnicmp(z0,"ticket",6)!=0
          422  +       && sqlite3_strnicmp(z0,"sqlite_",7)!=0
          423  +      ){
          424  +        goto ticket_schema_error;
          425  +      }
          426  +      break;
          427  +    }
          428  +    case SQLITE_REINDEX:
          429  +    case SQLITE_TRANSACTION:
          430  +    case SQLITE_READ: {
          431  +      break;
          432  +    }
          433  +    default: {
          434  +      goto ticket_schema_error;
          435  +    }
          436  +  }
          437  +  return SQLITE_OK;
          438  +
          439  +ticket_schema_error:
          440  +  if( pNErr ) *(int*)pNErr  = 1;
          441  +  return SQLITE_DENY;
          442  +}
          443  +
   375    444   
   376    445   /*
   377    446   ** Recreate the TICKET and TICKETCHNG tables.
   378    447   */
   379    448   void ticket_create_table(int separateConnection){
   380    449     char *zSql;
   381    450   
................................................................................
   382    451     db_multi_exec(
   383    452       "DROP TABLE IF EXISTS ticket;"
   384    453       "DROP TABLE IF EXISTS ticketchng;"
   385    454     );
   386    455     zSql = ticket_table_schema();
   387    456     if( separateConnection ){
   388    457       if( db_transaction_nesting_depth() ) db_end_transaction(0);
          458  +    db_set_authorizer(ticket_schema_auth,0,"Ticket-Schema");
   389    459       db_init_database(g.zRepositoryName, zSql, 0);
   390    460     }else{
          461  +    db_set_authorizer(ticket_schema_auth,0,"Ticket-Schema");
   391    462       db_multi_exec("%s", zSql/*safe-for-%s*/);
   392    463     }
          464  +  db_clear_authorizer();
   393    465     fossil_free(zSql);
   394    466   }
   395    467   
   396    468   /*
   397    469   ** Repopulate the TICKET and TICKETCHNG tables from scratch using all
   398    470   ** available ticket artifacts.
   399    471   */

Changes to src/undo.c.

    50     50       int new_exists;
    51     51       int old_exe;
    52     52       int new_exe;
    53     53       int new_link;
    54     54       int old_link;
    55     55       Blob current;
    56     56       Blob new;
    57         -    zFullname = mprintf("%s/%s", g.zLocalRoot, zPathname);
           57  +    zFullname = mprintf("%s%s", g.zLocalRoot, zPathname);
    58     58       old_link = db_column_int(&q, 3);
    59     59       new_exists = file_size(zFullname, RepoFILE)>=0;
    60     60       new_link = file_islink(0);
    61     61       if( new_exists ){
    62     62         blob_read_from_file(&current, zFullname, RepoFILE);
    63     63         new_exe = file_isexe(0,0);
    64     64       }else{
................................................................................
    67     67       }
    68     68       blob_zero(&new);
    69     69       old_exists = db_column_int(&q, 1);
    70     70       old_exe = db_column_int(&q, 2);
    71     71       if( old_exists ){
    72     72         db_ephemeral_blob(&q, 0, &new);
    73     73       }
    74         -    if( old_exists ){
           74  +    if( file_unsafe_in_tree_path(zFullname) ){
           75  +      /* do nothign with this unsafe file */
           76  +    }else if( old_exists ){
    75     77         if( new_exists ){
    76     78           fossil_print("%s   %s\n", redoFlag ? "REDO" : "UNDO", zPathname);
    77     79         }else{
    78     80           fossil_print("NEW    %s\n", zPathname);
    79     81         }
    80     82         if( new_exists && (new_link || old_link) ){
    81     83           file_delete(zFullname);

Changes to src/update.c.

   925    925         db_multi_exec(
   926    926           "UPDATE OR REPLACE vfile"
   927    927           "   SET pathname=origname, origname=NULL"
   928    928           " WHERE pathname=%Q AND origname!=pathname;"
   929    929           "DELETE FROM vfile WHERE pathname=%Q",
   930    930           zFile, zFile
   931    931         );
          932  +    }else if( file_unsafe_in_tree_path(zFull) ){
          933  +      /* Ignore this file */
   932    934       }else{
   933    935         sqlite3_int64 mtime;
   934    936         int rvChnged = 0;
   935    937         int rvPerm = manifest_file_mperm(pRvFile);
   936    938   
   937    939         /* Determine if reverted-to file is different than checked out file. */
   938    940         if( pCoManifest && (pCoFile = manifest_file_find(pCoManifest, zFile)) ){

Changes to src/vfile.c.

   311    311       const char *zName;
   312    312   
   313    313       id = db_column_int(&q, 0);
   314    314       zName = db_column_text(&q, 1);
   315    315       rid = db_column_int(&q, 2);
   316    316       isExe = db_column_int(&q, 3);
   317    317       isLink = db_column_int(&q, 4);
          318  +    if( file_unsafe_in_tree_path(zName) ){
          319  +      continue;
          320  +    }
   318    321       content_get(rid, &content);
   319    322       if( file_is_the_same(&content, zName) ){
   320    323         blob_reset(&content);
   321    324         if( file_setexe(zName, isExe) ){
   322    325           db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
   323    326                         file_mtime(zName, RepoFILE), id);
   324    327         }