]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Meld -safe option into CLI
authorlarrybr <larrybr@noemail.net>
Thu, 2 Sep 2021 01:21:14 +0000 (01:21 +0000)
committerlarrybr <larrybr@noemail.net>
Thu, 2 Sep 2021 01:21:14 +0000 (01:21 +0000)
FossilOrigin-Name: c7c84998dbec5dfd71d62e12cfa741edba4b6439cc384ab46b21199f290feedd

manifest
manifest.uuid
src/shell.c.in

index e0bb5175008e7c5e50f4718bf885e937c329f871..c527c93e5eea3078cbf9d36907dcc43dd54d11ee 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Sync\sto\strunk
-D 2021-08-09T19:15:03.129
+C Meld\s-safe\soption\sinto\sCLI
+D 2021-09-02T01:21:14.704
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -545,7 +545,7 @@ F src/random.c 097dc8b31b8fba5a9aca1697aeb9fd82078ec91be734c16bffda620ced7ab83c
 F src/resolve.c 42b94d37a54200707a95566eff4f7e8a380e32d080016b699f23bd79a73a5028
 F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
 F src/select.c 63077c0243ded1432d97c90c1a4c3419b3a574b36634c674599a68bfe4c3bdc2
-F src/shell.c.in 92fbe48f2b96c62a57ca17530624f61a4076d17ec082dd1da09e19d6792051e9
+F src/shell.c.in 16e84ce4dc4c538fe9b2759b1c1ea7e5923e5f297691a4b97bf1ef9528541b31
 F src/sqlite.h.in 43fcf0fe2af04081f420a906fc020bde1243851ba44b0aa567a27f94bf8c3145
 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
 F src/sqlite3ext.h e97f4e9b509408fea4c4e9bef5a41608dfac343b4d3c7a990dedde1e19af9510
@@ -1920,7 +1920,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P d449941b81a796fa30382bd00e88dc744a6745dc3d5a0eb8377aa90e4966a391 d44f74f14a387960ce105913526b572f4a3d1935351c6aab10cee85946488a9e
-R 7070e57e23b183c775afc6963903e7ac
+P dd356ace4ffa57518c16fa7fd9b6bc7bef0bb0ddcbc9bdf60ab0a17c25f8e5c0
+R bde6c7d36d347a265cfb3284dd778d5f
 U larrybr
-Z 9e19bb94e770b4ba1f8a18c5933850e6
+Z ac16bca9a518d5a67bb303386dbab385
index 13b73e4aee8139fa9eb7df53a8b4ec532faebf4b..440abb6081a5e44fe9593ffca5a2e9e6af362498 100644 (file)
@@ -1 +1 @@
-dd356ace4ffa57518c16fa7fd9b6bc7bef0bb0ddcbc9bdf60ab0a17c25f8e5c0
\ No newline at end of file
+c7c84998dbec5dfd71d62e12cfa741edba4b6439cc384ab46b21199f290feedd
\ No newline at end of file
index f7af553c52c4f195cf0d9a94bd069c1c1f54e5f4..7ba35838f8e263e8a4bd3bbd4bc8de98d0be6c69 100644 (file)
@@ -1106,6 +1106,8 @@ struct ShellState {
   u8 doXdgOpen;          /* Invoke start/open/xdg-open in output_reset() */
   u8 nEqpLevel;          /* Depth of the EQP output graph */
   u8 eTraceType;         /* SHELL_TRACE_* value for type of trace */
+  u8 bSafeMode;          /* True when unsafe operations are prohibited */
+  u8 bSafeModeFuture;    /* Next bSafeMode - 0, 1 or suspending downto 1 */
   unsigned statsOn;      /* True to display memory stats before each finalize */
   unsigned mEqpLines;    /* Mask of veritical lines in the EQP output graph */
   int outCount;          /* Revert to stdout when reaching zero */
@@ -1157,8 +1159,9 @@ struct ShellState {
   int *aiIndent;         /* Array of indents used in MODE_Explain */
   int nIndent;           /* Size of array aiIndent[] */
   int iIndent;           /* Index of current op in aiIndent[] */
+  char *zNonce;          /* Nonce for temporary safe-mode excapes */
   EQPGraph sGraph;       /* Information for the graphical EXPLAIN QUERY PLAN */
-  ExpertInfo expert;        /* Valid if previous command was ".expert OPT..." */
+  ExpertInfo expert;     /* Valid if previous command was ".expert OPT..." */
 };
 
 
@@ -1294,6 +1297,27 @@ static void shellPutsFunc(
   sqlite3_result_value(pCtx, apVal[0]);
 }
 
+/*
+** If in safe mode, print an error message described by the arguments
+** and exit immediately.
+*/
+static void failIfSafeMode(
+  ShellState *p,
+  const char *zErrMsg,
+  ...
+){
+  if( p->bSafeMode ){
+    va_list ap;
+    char *zMsg;
+    va_start(ap, zErrMsg);
+    zMsg = sqlite3_vmprintf(zErrMsg, ap);
+    va_end(ap);
+    raw_printf(stderr, "line %d: ", p->lineno);
+    utf8_printf(stderr, "%s\n", zMsg);
+    exit(1);
+  }
+}
+
 /*
 ** SQL function:   edit(VALUE)
 **                 edit(VALUE,EDITOR)
@@ -1771,6 +1795,49 @@ static BOOL WINAPI ConsoleCtrlHandler(
 #endif
 
 #ifndef SQLITE_OMIT_AUTHORIZATION
+/*
+** This authorizer runs in safe mode.
+*/
+static int safeModeAuth(
+  void *pClientData,
+  int op,
+  const char *zA1,
+  const char *zA2,
+  const char *zA3,
+  const char *zA4
+){
+  ShellState *p = (ShellState*)pClientData;
+  static const char *azProhibitedFunctions[] = {
+    "edit",
+    "fts3_tokenizer",
+    "load_extension",
+    "readfile",
+    "writefile",
+    "zipfile",
+    "zipfile_cds",
+  };
+  UNUSED_PARAMETER(zA2);
+  UNUSED_PARAMETER(zA3);
+  UNUSED_PARAMETER(zA4);
+  switch( op ){
+    case SQLITE_ATTACH: {
+      failIfSafeMode(p, "cannot run ATTACH in safe mode");
+      break;
+    }
+    case SQLITE_FUNCTION: {
+      int i;
+      for(i=0; i<ArraySize(azProhibitedFunctions); i++){
+        if( sqlite3_stricmp(zA1, azProhibitedFunctions[i])==0 ){
+          failIfSafeMode(p, "cannot use the %s() function in safe mode",
+                         azProhibitedFunctions[i]);
+        }
+      }
+      break;
+    }
+  }
+  return SQLITE_OK;
+}
+
 /*
 ** When the ".auth ON" is set, the following authorizer callback is
 ** invoked.  It always returns SQLITE_OK.
@@ -1813,6 +1880,7 @@ static int shellAuth(
     }
   }
   raw_printf(p->out, "\n");
+  if( p->bSafeMode ) (void)safeModeAuth(pClientData, op, zA1, zA2, zA3, zA4);
   return SQLITE_OK;
 }
 #endif
@@ -4450,6 +4518,9 @@ static void open_db(ShellState *p, int openFlags){
     }
 #endif
   }
+  if( p->bSafeModeFuture && p->db!=0 ){
+    sqlite3_set_authorizer(p->db, safeModeAuth, p);
+  }
 }
 
 /*
@@ -6823,6 +6894,7 @@ static int writeDb( char *azArg[], int nArg, ShellState *p ){
   int j;
   int bAsync = 0;
   const char *zVfs = 0;
+  failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
   for(j=1; j<nArg; j++){
     const char *z = azArg[j];
     if( z[0]=='-' ){
@@ -6952,6 +7024,7 @@ COLLECT_HELP_TEXT[
 ];
 DISPATCHABLE_COMMAND( archive ? 3 0 azArg nArg p ){
   open_db(p, 0);
+  failIfSafeMode(p, "cannot run .archive in safe mode");
   return arDotCommand(p, 0, azArg, nArg);
 }
 
@@ -6967,6 +7040,8 @@ DISPATCHABLE_COMMAND( auth 3 2 2 azArg nArg p ){
   open_db(p, 0);
   if( booleanValue(azArg[1]) ){
     sqlite3_set_authorizer(p->db, shellAuth, p);
+  }else if( p->bSafeModeFuture ){
+    sqlite3_set_authorizer(p->db, safeModeAuth, p);
   }else{
     sqlite3_set_authorizer(p->db, 0, 0);
   }
@@ -7026,6 +7101,7 @@ DISPATCHABLE_COMMAND( binary 3 2 2 ){
 
 DISPATCHABLE_COMMAND( cd ? 2 2 ){
   int rc=0;
+  failIfSafeMode(p, "cannot run .cd in safe mode");
 #if defined(_WIN32) || defined(WIN32)
   wchar_t *z = sqlite3_win32_utf8_to_unicode(azArg[1]);
   rc = !SetCurrentDirectoryW(z);
@@ -7088,6 +7164,7 @@ DISPATCHABLE_COMMAND( check 3 0 0 ){
   return rc;
 }
 DISPATCHABLE_COMMAND( clone ? 2 2 ){
+  failIfSafeMode(p, "cannot run .clone in safe mode");
   tryToClone(p, azArg[1]);
   return 0;
 }
@@ -7471,6 +7548,7 @@ static int outputRedirs(char *azArg[], int nArg, ShellState *p,
   int bTxtMode = 0;
   int i;
   int bBOM = 0;
+  failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
   for(i=1; i<nArg; i++){
     char *z = azArg[i];
     if( z[0]=='-' ){
@@ -7828,6 +7906,7 @@ DISPATCHABLE_COMMAND( import ? 3 7 ){
   int useOutputMode = 1;      /* Use output mode to determine separators */
   int rc = 0;
 
+  failIfSafeMode(p, "cannot run .import in safe mode");
   memset(&sCtx, 0, sizeof(sCtx));
   if( p->mode==MODE_Ascii ){
     xRead = ascii_read_one_field;
@@ -8283,6 +8362,7 @@ DISPATCHABLE_COMMAND( lint 3 1 0 ){
 DISPATCHABLE_COMMAND( load ? 2 3 ){
   const char *zFile, *zProc;
   char *zErrMsg = 0;
+  failIfSafeMode(p, "cannot run .load in safe mode");
   zFile = azArg[1];
   zProc = nArg>=3 ? azArg[2] : 0;
   open_db(p, 0);
@@ -8296,6 +8376,7 @@ DISPATCHABLE_COMMAND( load ? 2 3 ){
 
 DISPATCHABLE_COMMAND( log ? 2 2 ){
   const char *zFile = azArg[1];
+  failIfSafeMode(p, "cannot run .log in safe mode");
   output_file_close(p->pLog);
   p->pLog = output_file_open(zFile, 0);
   return 0;
@@ -8416,7 +8497,7 @@ DISPATCHABLE_COMMAND( oom ? 1 4 ){
 }
 
 /*****************
- * The .open and .nullvalue commands
+ * The .open, .nonce and .nullvalue commands
  */
 COLLECT_HELP_TEXT[
   ".open ?OPTIONS? ?FILE?   Close existing database and reopen FILE",
@@ -8431,6 +8512,7 @@ COLLECT_HELP_TEXT[
   "        --nofollow      Do not follow symbolic links",
   "        --readonly      Open FILE readonly",
   "        --zip           FILE is a ZIP archive",
+  ".nonce STRING            Disable safe mode for one command if nonce matches",
   ".nullvalue STRING        Use STRING in place of NULL values",
 ];
 DISPATCHABLE_COMMAND( open 3 1 0 ){
@@ -8484,7 +8566,14 @@ DISPATCHABLE_COMMAND( open 3 1 0 ){
   }
   /* If a filename is specified, try to open it first */
   if( zNewFilename || p->openMode==SHELL_OPEN_HEXDB ){
-    if( newFlag ) shellDeleteFile(zNewFilename);
+    if( newFlag && !p->bSafeMode ) shellDeleteFile(zNewFilename);
+    if( p->bSafeMode
+        && p->openMode!=SHELL_OPEN_HEXDB
+        && zNewFilename
+        && strcmp(zNewFilename,":memory:")!=0
+        ){
+      failIfSafeMode(p, "cannot open disk-based database files in safe mode");
+    }
     p->pAuxDb->zDbFilename = zNewFilename;
     open_db(p, OPEN_DB_KEEPALIVE);
     if( p->db==0 ){
@@ -8502,6 +8591,17 @@ DISPATCHABLE_COMMAND( open 3 1 0 ){
   return 0;
 }
 
+DISPATCHABLE_COMMAND( nonce ? 2 2 ){
+  if( p->zNonce==0 || strcmp(azArg[1],p->zNonce)!=0 ){
+    raw_printf(stderr, "line %d: incorrect nonce: \"%s\"\n",
+               p->lineno, azArg[1]);
+    exit(1);
+  }
+  /* Suspend safe mode for 1 meta-command after this. */
+  p->bSafeModeFuture = 2;
+  return 0;
+}
+
 DISPATCHABLE_COMMAND( nullvalue ? 2 2 ){
   sqlite3_snprintf(sizeof(p->nullValue), p->nullValue,
                    "%.*s", (int)ArraySize(p->nullValue)-1, azArg[1]);
@@ -8720,6 +8820,7 @@ DISPATCHABLE_COMMAND( read 3 2 2 ){
   int rc = 0;
   FILE *inSaved = p->in;
   int savedLineno = p->lineno;
+  failIfSafeMode(p, "cannot run .read in safe mode");
   if( azArg[1][0]=='|' ){
 #ifdef SQLITE_OMIT_POPEN
     raw_printf(stderr, "Error: pipes are not supported in this OS\n");
@@ -9075,6 +9176,7 @@ DISPATCHABLE_COMMAND( restore ? 2 3 ){
   sqlite3_backup *pBackup;
   int nTimeout = 0;
 
+  failIfSafeMode(p, "cannot run .restore in safe mode");
   if( nArg==2 ){
     zSrcFile = azArg[1];
     zDb = "main";
@@ -9366,6 +9468,7 @@ DISPATCHABLE_COMMAND( session 3 2 0 ){
     */
     if( strcmp(azCmd[0],"changeset")==0 || strcmp(azCmd[0],"patchset")==0 ){
       FILE *out = 0;
+      failIfSafeMode(p, "cannot run \".session %s\" in safe mode", azCmd[0]);
       if( nCmd!=2 ) goto session_syntax_error;
       if( pSession->p==0 ) goto session_not_open;
       out = fopen(azCmd[1], "wb");
@@ -9758,6 +9861,7 @@ DISPATCHABLE_COMMAND( selftest 4 0 0 ){
 static int shellOut(char *azArg[], int nArg, ShellState *p){
   char *zCmd;
   int i, x;
+  failIfSafeMode(p, "cannot run .%s in safe mode", azArg[0]);
   zCmd = sqlite3_mprintf(strchr(azArg[1],' ')==0?"%s":"\"%s\"", azArg[1]);
   for(i=2; i<nArg; i++){
     zCmd = sqlite3_mprintf(strchr(azArg[i],' ')==0?"%z %s":"%z \"%s\"",
@@ -10699,6 +10803,16 @@ meta_command_exit:
     p->outCount--;
     if( p->outCount==0 ) output_reset(p);
   }
+  switch( p->bSafeModeFuture ){
+  default:
+    --p->bSafeModeFuture;
+    /* fall thru */
+  case 0:
+    p->bSafeMode = 0;
+    break;
+  case 1:
+    p->bSafeMode = 1;
+  }
   return rc;
 }
 
@@ -10896,6 +11010,7 @@ static int process_input(ShellState *p){
       }else{
         clearTempFile(p);
       }
+      p->bSafeMode = p->bSafeModeFuture!= 1;
     }else if( nSql && _all_whitespace(zSql) ){
       if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zSql);
       nSql = 0;
@@ -11062,10 +11177,12 @@ static const char zOptions[] =
 #endif
   "   -newline SEP         set output row separator. Default: '\\n'\n"
   "   -nofollow            refuse to open symbolic links to database files\n"
+  "   -nonce STRING        set the safe-mode escape nonce\n"
   "   -nullvalue TEXT      set text string for NULL values. Default ''\n"
   "   -pagecache SIZE N    use N slots of SZ bytes each for page cache memory\n"
   "   -quote               set output mode to 'quote'\n"
   "   -readonly            open the database read-only\n"
+  "   -safe                enable safe-mode\n"
   "   -separator SEP       set output column separator. Default: '|'\n"
 #ifdef SQLITE_ENABLE_SORTER_REFERENCES
   "   -sorterref SIZE      sorter references threshold size\n"
@@ -11411,6 +11528,11 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
       sqlite3MemTraceActivate(stderr);
     }else if( strcmp(z,"-bail")==0 ){
       bail_on_error = 1;
+    }else if( strcmp(z,"-nonce")==0 ){
+      free(data.zNonce);
+      data.zNonce = strdup(argv[++i]);
+    }else if( strcmp(z,"-safe")==0 ){
+      /* no-op - catch this on the second pass */
     }
   }
   verify_uninitialized();
@@ -11573,6 +11695,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
       i+=2;
     }else if( strcmp(z,"-threadsafe")==0 ){
       i+=2;
+    }else if( strcmp(z,"-nonce")==0 ){
+      i += 2;
     }else if( strcmp(z,"-mmap")==0 ){
       i++;
     }else if( strcmp(z,"-memtrace")==0 ){
@@ -11631,6 +11755,8 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
       readStdin = 0;
       break;
 #endif
+    }else if( strcmp(z,"-safe")==0 ){
+      data.bSafeMode = data.bSafeModeFuture = 1;
     }else{
       utf8_printf(stderr,"%s: Error: unknown option: %s\n", Argv0, z);
       raw_printf(stderr,"Use -help for a list of options.\n");
@@ -11733,6 +11859,7 @@ int SQLITE_CDECL wmain(int argc, wchar_t **wargv){
   free(argvToFree);
 #endif
   free(data.colWidth);
+  free(data.zNonce);
   /* Clear the global data structure so that valgrind will detect memory
   ** leaks */
   memset(&data, 0, sizeof(data));