Fossil SCM

Do not attempt to extract symbolic links from ZIP archive. See [https://sqlite.org/forum/forumpost/2026-02-21T11:04:36z|SQLite Forum Post 2026-02-21T11:04:36z] for an explanation of why this is a potential vulnerability. We could, in theory, enhance ZIP archive unpacking to handle symbolic links safely, but as the use of symbolic links in ZIP archives is rare, and because ZIP archive extraction by Fossil is rare, doing so might create more problems than it solves.

drh 2026-02-23 14:17 trunk
Commit 31ce0d31c4d0cbe64cb55973e0c77596964b143522f71b5e49c6971d4c263672
2 files changed +5 -3 +8 -2
+5 -3
--- src/xsystem.c
+++ src/xsystem.c
@@ -484,17 +484,19 @@
484484
a[0] = argv[0];
485485
a[1] = (char*)zZipfile;
486486
if( doList ){
487487
a[2] = ".mode column";
488488
a[3] = "SELECT sz AS Size, date(mtime,'unixepoch') AS Date,"
489
- " time(mtime,'unixepoch') AS Time, name AS Name"
489
+ " time(mtime,'unixepoch') AS Time,"
490
+ " if(((mode>>12)&15)=10,name||' -> '||data,name) AS Name"
490491
" FROM zip;";
491492
n = 4;
492493
}else{
493494
a[2] = ".mode list";
494
- a[3] = "SELECT if(writefile(name,data,mode,mtime) IS NULL,"
495
- "'error: '||name,'extracting: '||name) FROM zip;";
495
+ a[3] = "SELECT if(((mode>>12)&15)==10,'symlink-ignored: '||name,"
496
+ "writefile(name,data,mode,mtime) IS NULL,"
497
+ "'error: '||name,'extracting: '||name) FROM zip";
496498
n = 4;
497499
}
498500
a[n] = 0;
499501
sqlite3_shell(n,a);
500502
}
501503
--- src/xsystem.c
+++ src/xsystem.c
@@ -484,17 +484,19 @@
484 a[0] = argv[0];
485 a[1] = (char*)zZipfile;
486 if( doList ){
487 a[2] = ".mode column";
488 a[3] = "SELECT sz AS Size, date(mtime,'unixepoch') AS Date,"
489 " time(mtime,'unixepoch') AS Time, name AS Name"
 
490 " FROM zip;";
491 n = 4;
492 }else{
493 a[2] = ".mode list";
494 a[3] = "SELECT if(writefile(name,data,mode,mtime) IS NULL,"
495 "'error: '||name,'extracting: '||name) FROM zip;";
 
496 n = 4;
497 }
498 a[n] = 0;
499 sqlite3_shell(n,a);
500 }
501
--- src/xsystem.c
+++ src/xsystem.c
@@ -484,17 +484,19 @@
484 a[0] = argv[0];
485 a[1] = (char*)zZipfile;
486 if( doList ){
487 a[2] = ".mode column";
488 a[3] = "SELECT sz AS Size, date(mtime,'unixepoch') AS Date,"
489 " time(mtime,'unixepoch') AS Time,"
490 " if(((mode>>12)&15)=10,name||' -> '||data,name) AS Name"
491 " FROM zip;";
492 n = 4;
493 }else{
494 a[2] = ".mode list";
495 a[3] = "SELECT if(((mode>>12)&15)==10,'symlink-ignored: '||name,"
496 "writefile(name,data,mode,mtime) IS NULL,"
497 "'error: '||name,'extracting: '||name) FROM zip";
498 n = 4;
499 }
500 a[n] = 0;
501 sqlite3_shell(n,a);
502 }
503
+8 -2
--- src/zip.c
+++ src/zip.c
@@ -624,11 +624,14 @@
624624
if( g.argc>3 ){
625625
fossil_fatal("extra arguments after \"fossil test-filezip -l ARCHIVE\"");
626626
}
627627
sqlite3_zipfile_init(g.db, 0, 0);
628628
db_multi_exec("CREATE VIRTUAL TABLE z1 USING zipfile(%Q)", zArchiveName);
629
- db_prepare(&q, "SELECT sz, datetime(mtime,'unixepoch'), name FROM z1");
629
+ db_prepare(&q,
630
+ "SELECT sz, datetime(mtime,'unixepoch'),"
631
+ " if(((mode>>12)&15)==10,name||' -> '||data,name) FROM z1"
632
+ );
630633
while( db_step(&q)==SQLITE_ROW ){
631634
int sz = db_column_int(&q, 0);
632635
szTotal += sz;
633636
if( nRow==0 ){
634637
fossil_print(" Length Date Time Name\n");
@@ -652,11 +655,14 @@
652655
fossil_fatal("extra arguments after \"fossil test-filezip -x ARCHIVE\"");
653656
}
654657
sqlite3_zipfile_init(g.db, 0, 0);
655658
sqlite3_fileio_init(g.db, 0, 0);
656659
db_multi_exec("CREATE VIRTUAL TABLE z1 USING zipfile(%Q)", zArchiveName);
657
- db_multi_exec("SELECT writefile(name,data) FROM z1");
660
+ db_multi_exec(
661
+ "SELECT writefile(name,data) FROM z1"
662
+ " WHERE ((mode>>12)&15)!=10"
663
+ );
658664
}else{
659665
/* Without the -x or -l options, construct a new ZIP archive */
660666
int i;
661667
Blob zip;
662668
Blob file;
663669
--- src/zip.c
+++ src/zip.c
@@ -624,11 +624,14 @@
624 if( g.argc>3 ){
625 fossil_fatal("extra arguments after \"fossil test-filezip -l ARCHIVE\"");
626 }
627 sqlite3_zipfile_init(g.db, 0, 0);
628 db_multi_exec("CREATE VIRTUAL TABLE z1 USING zipfile(%Q)", zArchiveName);
629 db_prepare(&q, "SELECT sz, datetime(mtime,'unixepoch'), name FROM z1");
 
 
 
630 while( db_step(&q)==SQLITE_ROW ){
631 int sz = db_column_int(&q, 0);
632 szTotal += sz;
633 if( nRow==0 ){
634 fossil_print(" Length Date Time Name\n");
@@ -652,11 +655,14 @@
652 fossil_fatal("extra arguments after \"fossil test-filezip -x ARCHIVE\"");
653 }
654 sqlite3_zipfile_init(g.db, 0, 0);
655 sqlite3_fileio_init(g.db, 0, 0);
656 db_multi_exec("CREATE VIRTUAL TABLE z1 USING zipfile(%Q)", zArchiveName);
657 db_multi_exec("SELECT writefile(name,data) FROM z1");
 
 
 
658 }else{
659 /* Without the -x or -l options, construct a new ZIP archive */
660 int i;
661 Blob zip;
662 Blob file;
663
--- src/zip.c
+++ src/zip.c
@@ -624,11 +624,14 @@
624 if( g.argc>3 ){
625 fossil_fatal("extra arguments after \"fossil test-filezip -l ARCHIVE\"");
626 }
627 sqlite3_zipfile_init(g.db, 0, 0);
628 db_multi_exec("CREATE VIRTUAL TABLE z1 USING zipfile(%Q)", zArchiveName);
629 db_prepare(&q,
630 "SELECT sz, datetime(mtime,'unixepoch'),"
631 " if(((mode>>12)&15)==10,name||' -> '||data,name) FROM z1"
632 );
633 while( db_step(&q)==SQLITE_ROW ){
634 int sz = db_column_int(&q, 0);
635 szTotal += sz;
636 if( nRow==0 ){
637 fossil_print(" Length Date Time Name\n");
@@ -652,11 +655,14 @@
655 fossil_fatal("extra arguments after \"fossil test-filezip -x ARCHIVE\"");
656 }
657 sqlite3_zipfile_init(g.db, 0, 0);
658 sqlite3_fileio_init(g.db, 0, 0);
659 db_multi_exec("CREATE VIRTUAL TABLE z1 USING zipfile(%Q)", zArchiveName);
660 db_multi_exec(
661 "SELECT writefile(name,data) FROM z1"
662 " WHERE ((mode>>12)&15)!=10"
663 );
664 }else{
665 /* Without the -x or -l options, construct a new ZIP archive */
666 int i;
667 Blob zip;
668 Blob file;
669

Keyboard Shortcuts

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