Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Add the new "%$" conversion specifier in mprintf(), for escaping filenames for use in shell commands. Use this feature for added system() security whereever appropriate. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
2209f553233fca8c9519c20d4bffb56b |
| User & Date: | drh 2020-06-09 00:27:20.402 |
Context
|
2020-06-09
| ||
| 00:38 | Allow '&' as the last character in a fossil_system() string. check-in: 84346a317b user: drh tags: trunk | |
| 00:27 | Add the new "%$" conversion specifier in mprintf(), for escaping filenames for use in shell commands. Use this feature for added system() security whereever appropriate. check-in: 2209f55323 user: drh tags: trunk | |
|
2020-06-08
| ||
| 23:37 | Add a safety scan of all strings passed into fossil_system(). Fatal error if the input string seems unsafe. check-in: a046f916d3 user: drh tags: trunk | |
Changes
Changes to src/allrepo.c.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 | ** ** This file contains code to implement the "all" command-line method. */ #include "config.h" #include "allrepo.h" #include <assert.h> | < < < < < < < < < < < < < < < < < < < < < < < < < < | | | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
**
** This file contains code to implement the "all" command-line method.
*/
#include "config.h"
#include "allrepo.h"
#include <assert.h>
/*
** Build a string that contains all of the command-line options
** specified as arguments. If the option name begins with "+" then
** it takes an argument. Without the "+" it does not.
*/
static void collect_argument(Blob *pExtra,const char *zArg,const char *zShort){
const char *z = find_option(zArg, zShort, 0);
if( z!=0 ){
blob_appendf(pExtra, " %s", z);
}
}
static void collect_argument_value(Blob *pExtra, const char *zArg){
const char *zValue = find_option(zArg, 0, 1);
if( zValue ){
if( zValue[0] ){
blob_appendf(pExtra, " --%s %$", zArg, zValue);
}else{
blob_appendf(pExtra, " --%s \"\"", zArg);
}
}
}
static void collect_argv(Blob *pExtra, int iStart){
int i;
|
| ︙ | ︙ | |||
167 168 169 170 171 172 173 |
** --dry-run If given, display instead of run actions.
*/
void all_cmd(void){
int n;
Stmt q;
const char *zCmd;
char *zSyscmd;
| < < | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
** --dry-run If given, display instead of run actions.
*/
void all_cmd(void){
int n;
Stmt q;
const char *zCmd;
char *zSyscmd;
Blob extra;
int useCheckouts = 0;
int quiet = 0;
int dryRunFlag = 0;
int showFile = find_option("showfile",0,0)!=0;
int stopOnError = find_option("dontstop",0,0)==0;
int nToDel = 0;
|
| ︙ | ︙ | |||
368 369 370 371 372 373 374 |
collect_argv(&extra, 3);
}else{
fossil_fatal("\"all\" subcommand should be one of: "
"add cache changes clean dbstat extras fts-config ignore "
"info list ls pull push rebuild server setting sync ui unset");
}
verify_all_options();
| < | 340 341 342 343 344 345 346 347 348 349 350 351 352 353 |
collect_argv(&extra, 3);
}else{
fossil_fatal("\"all\" subcommand should be one of: "
"add cache changes clean dbstat extras fts-config ignore "
"info list ls pull push rebuild server setting sync ui unset");
}
verify_all_options();
db_multi_exec("CREATE TEMP TABLE repolist(name,tag);");
if( useCheckouts ){
db_multi_exec(
"INSERT INTO repolist "
"SELECT DISTINCT substr(name, 7), name COLLATE nocase"
" FROM global_config"
" WHERE substr(name, 1, 6)=='ckout:'"
|
| ︙ | ︙ | |||
410 411 412 413 414 415 416 |
if( zCmd[0]=='l' ){
fossil_print("%s\n", zFilename);
continue;
}else if( showFile ){
fossil_print("%s: %s\n", useCheckouts ? "checkout" : "repository",
zFilename);
}
| < | | < | 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 |
if( zCmd[0]=='l' ){
fossil_print("%s\n", zFilename);
continue;
}else if( showFile ){
fossil_print("%s: %s\n", useCheckouts ? "checkout" : "repository",
zFilename);
}
zSyscmd = mprintf("%$ %s %$%s",
g.nameOfExe, zCmd, zFilename, blob_str(&extra));
if( showLabel ){
int len = (int)strlen(zFilename);
int nStar = 80 - (len + 15);
if( nStar<2 ) nStar = 1;
fossil_print("%.13c %s %.*c\n", '*', zFilename, nStar, '*');
fflush(stdout);
}
if( !quiet || dryRunFlag ){
fossil_print("%s\n", zSyscmd);
fflush(stdout);
}
rc = dryRunFlag ? 0 : fossil_system(zSyscmd);
free(zSyscmd);
if( stopOnError && rc ){
break;
}
}
db_finalize(&q);
blob_reset(&extra);
|
| ︙ | ︙ |
Changes to src/checkin.c.
| ︙ | ︙ | |||
1172 1173 1174 1175 1176 1177 1178 | const char *zEditor; char *zCmd; char *zFile; Blob reply, line; char *zComment; int i; | | < < < < < < < < < < < < < < < | 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 |
const char *zEditor;
char *zCmd;
char *zFile;
Blob reply, line;
char *zComment;
int i;
zEditor = fossil_text_editor();db_get("editor", 0);
if( zEditor==0 ){
if( blob_size(pPrompt)>0 ){
blob_append(pPrompt,
"#\n"
"# Since no default text editor is set using EDITOR or VISUAL\n"
"# environment variables or the \"fossil set editor\" command,\n"
"# and because no comment was specified using the \"-m\" or \"-M\"\n"
|
| ︙ | ︙ | |||
1217 1218 1219 1220 1221 1222 1223 |
blob_reset(&fname);
}
#if defined(_WIN32)
blob_add_cr(pPrompt);
#endif
if( blob_size(pPrompt)>0 ) blob_write_to_file(pPrompt, zFile);
if( zEditor ){
| | | 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 |
blob_reset(&fname);
}
#if defined(_WIN32)
blob_add_cr(pPrompt);
#endif
if( blob_size(pPrompt)>0 ) blob_write_to_file(pPrompt, zFile);
if( zEditor ){
zCmd = mprintf("%s %$", zEditor, zFile);
fossil_print("%s\n", zCmd);
if( fossil_system(zCmd) ){
fossil_fatal("editor aborted: \"%s\"", zCmd);
}
blob_read_from_file(&reply, zFile, ExtFILE);
}else{
|
| ︙ | ︙ |
Changes to src/diffcmd.c.
| ︙ | ︙ | |||
741 742 743 744 745 746 747 |
* If evaluation of the Tcl script fails, the reason may be that Tk
* could not be found by the loaded Tcl, or that Tcl cannot be loaded
* dynamically (e.g. x64 Tcl with x86 Fossil). Therefore, fallback
* to using the external "tclsh", if available.
*/
#endif
zTempFile = write_blob_to_temp_file(&script);
| | | 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 |
* If evaluation of the Tcl script fails, the reason may be that Tk
* could not be found by the loaded Tcl, or that Tcl cannot be loaded
* dynamically (e.g. x64 Tcl with x86 Fossil). Therefore, fallback
* to using the external "tclsh", if available.
*/
#endif
zTempFile = write_blob_to_temp_file(&script);
zCmd = mprintf("%$ %$", zTclsh, zTempFile);
fossil_system(zCmd);
file_delete(zTempFile);
fossil_free(zCmd);
}
blob_reset(&script);
}
|
| ︙ | ︙ |
Changes to src/export.c.
| ︙ | ︙ | |||
1310 1311 1312 1313 1314 1315 1316 |
/* Make sure the GIT repository directory exists */
rc = file_mkdir(zMirror, ExtFILE, 0);
if( rc ) fossil_fatal("cannot create directory \"%s\"", zMirror);
/* Make sure GIT has been initialized */
z = mprintf("%s/.git", zMirror);
if( !file_isdir(z, ExtFILE) ){
| | | 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 |
/* Make sure the GIT repository directory exists */
rc = file_mkdir(zMirror, ExtFILE, 0);
if( rc ) fossil_fatal("cannot create directory \"%s\"", zMirror);
/* Make sure GIT has been initialized */
z = mprintf("%s/.git", zMirror);
if( !file_isdir(z, ExtFILE) ){
zCmd = mprintf("git init %$",zMirror);
gitmirror_message(VERB_NORMAL, "%s\n", zCmd);
rc = fossil_system(zCmd);
if( rc ){
fossil_fatal("cannot initialize the git repository using: \"%s\"", zCmd);
}
fossil_free(zCmd);
bNeedRepack = 1;
|
| ︙ | ︙ | |||
1508 1509 1510 1511 1512 1513 1514 |
" JOIN mmark ON mmark.uuid=blob.uuid;"
);
while( db_step(&q)==SQLITE_ROW ){
char *zTagname = fossil_strdup(db_column_text(&q,0));
const char *zObj = db_column_text(&q,1);
char *zTagCmd;
gitmirror_sanitize_name(zTagname);
| | | 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 |
" JOIN mmark ON mmark.uuid=blob.uuid;"
);
while( db_step(&q)==SQLITE_ROW ){
char *zTagname = fossil_strdup(db_column_text(&q,0));
const char *zObj = db_column_text(&q,1);
char *zTagCmd;
gitmirror_sanitize_name(zTagname);
zTagCmd = mprintf("git tag -f %$ %$", zTagname, zObj);
fossil_free(zTagname);
gitmirror_message(VERB_NORMAL, "%s\n", zTagCmd);
fossil_system(zTagCmd);
fossil_free(zTagCmd);
}
db_finalize(&q);
|
| ︙ | ︙ | |||
1543 1544 1545 1546 1547 1548 1549 |
char *zRefCmd;
if( fossil_strcmp(zBrname,"trunk")==0 ){
fossil_free(zBrname);
zBrname = fossil_strdup("master");
}else{
gitmirror_sanitize_name(zBrname);
}
| | | 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 |
char *zRefCmd;
if( fossil_strcmp(zBrname,"trunk")==0 ){
fossil_free(zBrname);
zBrname = fossil_strdup("master");
}else{
gitmirror_sanitize_name(zBrname);
}
zRefCmd = mprintf("git update-ref \"refs/heads/%s\" %$", zBrname, zObj);
fossil_free(zBrname);
gitmirror_message(VERB_NORMAL, "%s\n", zRefCmd);
fossil_system(zRefCmd);
fossil_free(zRefCmd);
}
db_finalize(&q);
|
| ︙ | ︙ | |||
1580 1581 1582 1583 1584 1585 1586 |
url_parse_local(zPushUrl, 0, &url);
zPushCmd = mprintf("git push --mirror %s", url.canonical);
}else{
zPushCmd = mprintf("git push --mirror %s", zPushUrl);
}
gitmirror_message(VERB_NORMAL, "%s\n", zPushCmd);
fossil_free(zPushCmd);
| | | 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 |
url_parse_local(zPushUrl, 0, &url);
zPushCmd = mprintf("git push --mirror %s", url.canonical);
}else{
zPushCmd = mprintf("git push --mirror %s", zPushUrl);
}
gitmirror_message(VERB_NORMAL, "%s\n", zPushCmd);
fossil_free(zPushCmd);
zPushCmd = mprintf("git push --mirror %$", zPushUrl);
fossil_system(zPushCmd);
fossil_free(zPushCmd);
}
}
/*
** Implementation of the "fossil git status" command.
|
| ︙ | ︙ |
Changes to src/http_transport.c.
| ︙ | ︙ | |||
261 262 263 264 265 266 267 |
n -= sent;
}
}
}
/*
** This routine is called when the outbound message is complete and
| | | | | 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
n -= sent;
}
}
}
/*
** This routine is called when the outbound message is complete and
** it is time to begin receiving a reply.
*/
void transport_flip(UrlData *pUrlData){
if( pUrlData->isFile ){
char *zCmd;
fclose(transport.pFile);
zCmd = mprintf("%$ http --in %$ --out %$ --ipaddr 127.0.0.1"
" %$ --localauth",
g.nameOfExe, transport.zOutFile, transport.zInFile, pUrlData->name
);
fossil_system(zCmd);
free(zCmd);
transport.pFile = fossil_fopen(transport.zInFile, "rb");
}
}
|
| ︙ | ︙ |
Changes to src/printf.c.
| ︙ | ︙ | |||
99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
#define etFOSSILIZE 20 /* The fossil header encoding format. */
#define etPATH 21 /* Path type */
#define etWIKISTR 22 /* Timeline comment text rendered from a char*: %W */
#define etSTRINGID 23 /* String with length limit for a hash prefix: %S */
#define etROOT 24 /* String value of g.zTop: %R */
#define etJSONSTR 25 /* String encoded as a JSON string literal: %j
Use %!j to include double-quotes around it. */
/*
** An "etByte" is an 8-bit unsigned value.
*/
typedef unsigned char etByte;
| > > | 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
#define etFOSSILIZE 20 /* The fossil header encoding format. */
#define etPATH 21 /* Path type */
#define etWIKISTR 22 /* Timeline comment text rendered from a char*: %W */
#define etSTRINGID 23 /* String with length limit for a hash prefix: %S */
#define etROOT 24 /* String value of g.zTop: %R */
#define etJSONSTR 25 /* String encoded as a JSON string literal: %j
Use %!j to include double-quotes around it. */
#define etSHELLESC 26 /* Escape a filename for use in a shell command: %$
See blob_append_escaped_arg() for details */
/*
** An "etByte" is an 8-bit unsigned value.
*/
typedef unsigned char etByte;
|
| ︙ | ︙ | |||
165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
{ 'E', 0, 1, etEXP, 14, 0 },
{ 'G', 0, 1, etGENERIC, 14, 0 },
{ 'i', 10, 1, etRADIX, 0, 0 },
{ 'n', 0, 0, etSIZE, 0, 0 },
{ '%', 0, 0, etPERCENT, 0, 0 },
{ 'p', 16, 0, etPOINTER, 0, 1 },
{ '/', 0, 0, etPATH, 0, 0 },
};
#define etNINFO count(fmtinfo)
/*
** "*val" is a double such that 0.1 <= *val < 10.0
** Return the ascii code for the leading digit of *val, then
** multiply "*val" by 10.0 to renormalize.
| > | 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
{ 'E', 0, 1, etEXP, 14, 0 },
{ 'G', 0, 1, etGENERIC, 14, 0 },
{ 'i', 10, 1, etRADIX, 0, 0 },
{ 'n', 0, 0, etSIZE, 0, 0 },
{ '%', 0, 0, etPERCENT, 0, 0 },
{ 'p', 16, 0, etPOINTER, 0, 1 },
{ '/', 0, 0, etPATH, 0, 0 },
{ '$', 0, 0, etSHELLESC, 0, 0 },
};
#define etNINFO count(fmtinfo)
/*
** "*val" is a double such that 0.1 <= *val < 10.0
** Return the ascii code for the leading digit of *val, then
** multiply "*val" by 10.0 to renormalize.
|
| ︙ | ︙ | |||
811 812 813 814 815 816 817 818 819 820 821 822 823 824 |
char *zWiki = va_arg(ap, char*);
Blob wiki;
blob_init(&wiki, zWiki, limit);
wiki_convert(&wiki, pBlob, wiki_convert_flags(flag_altform2));
blob_reset(&wiki);
length = width = 0;
break;
}
case etERROR:
buf[0] = '%';
buf[1] = c;
errorflag = 0;
idx = 1+(c!=0);
blob_append(pBlob,"%",idx);
| > > > > > > | 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 |
char *zWiki = va_arg(ap, char*);
Blob wiki;
blob_init(&wiki, zWiki, limit);
wiki_convert(&wiki, pBlob, wiki_convert_flags(flag_altform2));
blob_reset(&wiki);
length = width = 0;
break;
}
case etSHELLESC: {
char *zArg = va_arg(ap, char*);
blob_append_escaped_arg(pBlob, zArg);
length = width = 0;
break;
}
case etERROR:
buf[0] = '%';
buf[1] = c;
errorflag = 0;
idx = 1+(c!=0);
blob_append(pBlob,"%",idx);
|
| ︙ | ︙ |
Changes to src/unversioned.c.
| ︙ | ︙ | |||
375 376 377 378 379 380 381 |
if( looks_like_binary(&content) ){
fossil_fatal("cannot edit binary content");
}
#if defined(_WIN32) || defined(__CYGWIN__)
blob_add_cr(&content);
#endif
blob_write_to_file(&content, zTFile);
| | | 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 |
if( looks_like_binary(&content) ){
fossil_fatal("cannot edit binary content");
}
#if defined(_WIN32) || defined(__CYGWIN__)
blob_add_cr(&content);
#endif
blob_write_to_file(&content, zTFile);
zCmd = mprintf("%s %$", zEditor, zTFile);
if( fossil_system(zCmd) ){
fossil_fatal("editor aborted: %Q", zCmd);
}
fossil_free(zCmd);
blob_reset(&content);
blob_read_from_file(&content, zTFile, ExtFILE);
#if defined(_WIN32) || defined(__CYGWIN__)
|
| ︙ | ︙ |