Fossil SCM

New setting "vuln-report" determines what to do when tainted text is misused in a TH1 script. Enhance the /test-warning page to deliberately misuse tainted text in TH1 to verify error handling. Enhance /errorlog to separate out TH1 vulnerability reports as a new category the the error log.

drh 2025-04-20 16:13 th1-taint
Commit 295b814a2722fff73e65c10b5247fbec4cbcf7178a706621eb743efa98bf30ff
+29 -9
--- src/main.c
+++ src/main.c
@@ -3718,10 +3718,13 @@
37183718
** case=3 Extra db_end_transaction()
37193719
** case=4 Error during SQL processing
37203720
** case=5 Call the segfault handler
37213721
** case=6 Call webpage_assert()
37223722
** case=7 Call webpage_error()
3723
+** case=8 Simulate a timeout
3724
+** case=9 Simulate a TH1 XSS vulnerability
3725
+** case=10 Simulate a TH1 SQL-injection vulnerability
37233726
*/
37243727
void test_warning_page(void){
37253728
int iCase = atoi(PD("case","0"));
37263729
int i;
37273730
login_check_credentials();
@@ -3730,17 +3733,15 @@
37303733
return;
37313734
}
37323735
style_set_current_feature("test");
37333736
style_header("Warning Test Page");
37343737
style_submenu_element("Error Log","%R/errorlog");
3735
- if( iCase<1 || iCase>4 ){
3736
- @ <p>Generate a message to the <a href="%R/errorlog">error log</a>
3737
- @ by clicking on one of the following cases:
3738
- }else{
3739
- @ <p>This is the test page for case=%d(iCase). All possible cases:
3740
- }
3741
- for(i=1; i<=8; i++){
3738
+ @ <p>This page will generate various kinds of errors to test Fossil's
3739
+ @ reaction. Depending on settings, a message might be written
3740
+ @ into the <a href="%R/errorlog">error log</a>. Click on
3741
+ @ one of the following hyperlinks to generate a simulated error:
3742
+ for(i=1; i<=10; i++){
37423743
@ <a href='./test-warning?case=%d(i)'>[%d(i)]</a>
37433744
}
37443745
@ </p>
37453746
@ <p><ol>
37463747
@ <li value='1'> Call fossil_warning()
@@ -3769,20 +3770,39 @@
37693770
}
37703771
@ <li value='6'> call webpage_assert(0)
37713772
if( iCase==6 ){
37723773
webpage_assert( 5==7 );
37733774
}
3774
- @ <li value='7'> call webpage_error()"
3775
+ @ <li value='7'> call webpage_error()
37753776
if( iCase==7 ){
37763777
cgi_reset_content();
37773778
webpage_error("Case 7 from /test-warning");
37783779
}
3779
- @ <li value='8'> simulated timeout"
3780
+ @ <li value='8'> simulated timeout
37803781
if( iCase==8 ){
37813782
fossil_set_timeout(1);
37823783
cgi_reset_content();
37833784
sqlite3_sleep(1100);
3785
+ }
3786
+ @ <li value='9'> simulated TH1 XSS vulnerability
3787
+ @ <li value='10'> simulated TH1 SQL-injection vulnerability
3788
+ if( iCase==9 || iCase==10 ){
3789
+ const char *zR;
3790
+ int n, rc;
3791
+ static const char *zTH1[] = {
3792
+ /* case 9 */ "html [taint {<b>XSS</b>}]",
3793
+ /* case 10 */ "query [taint {SELECT 'SQL-injection' AS msg}] {\n"
3794
+ " html \"<b>[htmlize $msg]</b>\"\n"
3795
+ "}"
3796
+ };
3797
+ rc = Th_Eval(g.interp, 0, zTH1[iCase==10], -1);
3798
+ zR = Th_GetResult(g.interp, &n);
3799
+ if( rc==TH_OK ){
3800
+ @ <pre class="th1result">%h(zR)</pre>
3801
+ }else{
3802
+ @ <pre class="th1error">%h(zR)</pre>
3803
+ }
37843804
}
37853805
@ </ol>
37863806
@ <p>End of test</p>
37873807
style_finish_page();
37883808
}
37893809
--- src/main.c
+++ src/main.c
@@ -3718,10 +3718,13 @@
3718 ** case=3 Extra db_end_transaction()
3719 ** case=4 Error during SQL processing
3720 ** case=5 Call the segfault handler
3721 ** case=6 Call webpage_assert()
3722 ** case=7 Call webpage_error()
 
 
 
3723 */
3724 void test_warning_page(void){
3725 int iCase = atoi(PD("case","0"));
3726 int i;
3727 login_check_credentials();
@@ -3730,17 +3733,15 @@
3730 return;
3731 }
3732 style_set_current_feature("test");
3733 style_header("Warning Test Page");
3734 style_submenu_element("Error Log","%R/errorlog");
3735 if( iCase<1 || iCase>4 ){
3736 @ <p>Generate a message to the <a href="%R/errorlog">error log</a>
3737 @ by clicking on one of the following cases:
3738 }else{
3739 @ <p>This is the test page for case=%d(iCase). All possible cases:
3740 }
3741 for(i=1; i<=8; i++){
3742 @ <a href='./test-warning?case=%d(i)'>[%d(i)]</a>
3743 }
3744 @ </p>
3745 @ <p><ol>
3746 @ <li value='1'> Call fossil_warning()
@@ -3769,20 +3770,39 @@
3769 }
3770 @ <li value='6'> call webpage_assert(0)
3771 if( iCase==6 ){
3772 webpage_assert( 5==7 );
3773 }
3774 @ <li value='7'> call webpage_error()"
3775 if( iCase==7 ){
3776 cgi_reset_content();
3777 webpage_error("Case 7 from /test-warning");
3778 }
3779 @ <li value='8'> simulated timeout"
3780 if( iCase==8 ){
3781 fossil_set_timeout(1);
3782 cgi_reset_content();
3783 sqlite3_sleep(1100);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3784 }
3785 @ </ol>
3786 @ <p>End of test</p>
3787 style_finish_page();
3788 }
3789
--- src/main.c
+++ src/main.c
@@ -3718,10 +3718,13 @@
3718 ** case=3 Extra db_end_transaction()
3719 ** case=4 Error during SQL processing
3720 ** case=5 Call the segfault handler
3721 ** case=6 Call webpage_assert()
3722 ** case=7 Call webpage_error()
3723 ** case=8 Simulate a timeout
3724 ** case=9 Simulate a TH1 XSS vulnerability
3725 ** case=10 Simulate a TH1 SQL-injection vulnerability
3726 */
3727 void test_warning_page(void){
3728 int iCase = atoi(PD("case","0"));
3729 int i;
3730 login_check_credentials();
@@ -3730,17 +3733,15 @@
3733 return;
3734 }
3735 style_set_current_feature("test");
3736 style_header("Warning Test Page");
3737 style_submenu_element("Error Log","%R/errorlog");
3738 @ <p>This page will generate various kinds of errors to test Fossil's
3739 @ reaction. Depending on settings, a message might be written
3740 @ into the <a href="%R/errorlog">error log</a>. Click on
3741 @ one of the following hyperlinks to generate a simulated error:
3742 for(i=1; i<=10; i++){
 
 
3743 @ <a href='./test-warning?case=%d(i)'>[%d(i)]</a>
3744 }
3745 @ </p>
3746 @ <p><ol>
3747 @ <li value='1'> Call fossil_warning()
@@ -3769,20 +3770,39 @@
3770 }
3771 @ <li value='6'> call webpage_assert(0)
3772 if( iCase==6 ){
3773 webpage_assert( 5==7 );
3774 }
3775 @ <li value='7'> call webpage_error()
3776 if( iCase==7 ){
3777 cgi_reset_content();
3778 webpage_error("Case 7 from /test-warning");
3779 }
3780 @ <li value='8'> simulated timeout
3781 if( iCase==8 ){
3782 fossil_set_timeout(1);
3783 cgi_reset_content();
3784 sqlite3_sleep(1100);
3785 }
3786 @ <li value='9'> simulated TH1 XSS vulnerability
3787 @ <li value='10'> simulated TH1 SQL-injection vulnerability
3788 if( iCase==9 || iCase==10 ){
3789 const char *zR;
3790 int n, rc;
3791 static const char *zTH1[] = {
3792 /* case 9 */ "html [taint {<b>XSS</b>}]",
3793 /* case 10 */ "query [taint {SELECT 'SQL-injection' AS msg}] {\n"
3794 " html \"<b>[htmlize $msg]</b>\"\n"
3795 "}"
3796 };
3797 rc = Th_Eval(g.interp, 0, zTH1[iCase==10], -1);
3798 zR = Th_GetResult(g.interp, &n);
3799 if( rc==TH_OK ){
3800 @ <pre class="th1result">%h(zR)</pre>
3801 }else{
3802 @ <pre class="th1error">%h(zR)</pre>
3803 }
3804 }
3805 @ </ol>
3806 @ <p>End of test</p>
3807 style_finish_page();
3808 }
3809
--- src/security_audit.c
+++ src/security_audit.c
@@ -810,27 +810,28 @@
810810
** WEBPAGE: errorlog
811811
**
812812
** Show the content of the error log. Only the administrator can view
813813
** this page.
814814
**
815
-** y=0x01 Show only hack attempts
816
-** y=0x02 Show only panics and assertion faults
817
-** y=0x04 Show hung backoffice processes
818
-** y=0x08 Show POST requests from a different origin
819
-** y=0x10 Show SQLITE_AUTH and similar
820
-** y=0x20 Show SMTP error reports
821
-** y=0x40 Show other uncategorized messages
815
+** y=0x001 Show only hack attempts
816
+** y=0x002 Show only panics and assertion faults
817
+** y=0x004 Show hung backoffice processes
818
+** y=0x008 Show POST requests from a different origin
819
+** y=0x010 Show SQLITE_AUTH and similar
820
+** y=0x020 Show SMTP error reports
821
+** y=0x040 Show TH1 vulnerability reports
822
+** y=0x800 Show other uncategorized messages
822823
**
823824
** If y is omitted or is zero, a count of the various message types is
824825
** shown.
825826
*/
826827
void errorlog_page(void){
827828
i64 szFile;
828829
FILE *in;
829830
char *zLog;
830831
const char *zType = P("y");
831
- static const int eAllTypes = 0x7f;
832
+ static const int eAllTypes = 0x87f;
832833
long eType = 0;
833834
int bOutput = 0;
834835
int prevWasTime = 0;
835836
int nHack = 0;
836837
int nPanic = 0;
@@ -837,10 +838,11 @@
837838
int nOther = 0;
838839
int nHang = 0;
839840
int nXPost = 0;
840841
int nAuth = 0;
841842
int nSmtp = 0;
843
+ int nVuln = 0;
842844
char z[10000];
843845
char zTime[10000];
844846
845847
login_check_credentials();
846848
if( !g.perm.Admin ){
@@ -917,10 +919,13 @@
917919
}
918920
if( eType & 0x20 ){
919921
@ <li>SMTP malfunctions
920922
}
921923
if( eType & 0x40 ){
924
+ @ <li>TH1 vulnerabilities
925
+ }
926
+ if( eType & 0x800 ){
922927
@ <li>Other uncategorized messages
923928
}
924929
@ </ul>
925930
}
926931
@ <hr>
@@ -953,12 +958,16 @@
953958
|| sqlite3_strglob("warning: SQLITE_AUTH*",z)==0
954959
){
955960
bOutput = (eType & 0x10)!=0;
956961
nAuth++;
957962
}else
958
- {
963
+ if( strncmp(z,"possible", 8)==0 && strstr(z,"tainted")!=0 ){
959964
bOutput = (eType & 0x40)!=0;
965
+ nVuln++;
966
+ }else
967
+ {
968
+ bOutput = (eType & 0x800)!=0;
960969
nOther++;
961970
}
962971
if( bOutput ){
963972
@ %h(zTime)\
964973
}
@@ -978,17 +987,21 @@
978987
fclose(in);
979988
if( eType ){
980989
@ </pre>
981990
}
982991
if( eType==0 ){
983
- int nNonHack = nPanic + nHang + nAuth + nSmtp + nOther;
992
+ int nNonHack = nPanic + nHang + nAuth + nSmtp + nVuln + nOther;
984993
int nTotal = nNonHack + nHack + nXPost;
985994
@ <p><table border="a" cellspacing="0" cellpadding="5">
986995
if( nPanic>0 ){
987996
@ <tr><td align="right">%d(nPanic)</td>
988997
@ <td><a href="./errorlog?y=2">Panics</a></td>
989998
}
999
+ if( nVuln>0 ){
1000
+ @ <tr><td align="right">%d(nVuln)</td>
1001
+ @ <td><a href="./errorlog?y=64">TH1 Vulnerabilities</a></td>
1002
+ }
9901003
if( nHack>0 ){
9911004
@ <tr><td align="right">%d(nHack)</td>
9921005
@ <td><a href="./errorlog?y=1">Hack Attempts</a></td>
9931006
}
9941007
if( nHang>0 ){
@@ -1007,17 +1020,17 @@
10071020
@ <tr><td align="right">%d(nSmtp)</td>
10081021
@ <td><a href="./errorlog?y=32">SMTP faults</a></td>
10091022
}
10101023
if( nOther>0 ){
10111024
@ <tr><td align="right">%d(nOther)</td>
1012
- @ <td><a href="./errorlog?y=64">Other</a></td>
1025
+ @ <td><a href="./errorlog?y=2048">Other</a></td>
10131026
}
10141027
@ <tr><td align="right">%d(nTotal)</td>
10151028
if( nTotal>0 ){
1016
- @ <td><a href="./errorlog?y=255">All Messages</a></td>
1029
+ @ <td><a href="./errorlog?y=4095">All Messages</a></td>
10171030
}else{
10181031
@ <td>All Messages</td>
10191032
}
10201033
@ </table>
10211034
}
10221035
style_finish_page();
10231036
}
10241037
--- src/security_audit.c
+++ src/security_audit.c
@@ -810,27 +810,28 @@
810 ** WEBPAGE: errorlog
811 **
812 ** Show the content of the error log. Only the administrator can view
813 ** this page.
814 **
815 ** y=0x01 Show only hack attempts
816 ** y=0x02 Show only panics and assertion faults
817 ** y=0x04 Show hung backoffice processes
818 ** y=0x08 Show POST requests from a different origin
819 ** y=0x10 Show SQLITE_AUTH and similar
820 ** y=0x20 Show SMTP error reports
821 ** y=0x40 Show other uncategorized messages
 
822 **
823 ** If y is omitted or is zero, a count of the various message types is
824 ** shown.
825 */
826 void errorlog_page(void){
827 i64 szFile;
828 FILE *in;
829 char *zLog;
830 const char *zType = P("y");
831 static const int eAllTypes = 0x7f;
832 long eType = 0;
833 int bOutput = 0;
834 int prevWasTime = 0;
835 int nHack = 0;
836 int nPanic = 0;
@@ -837,10 +838,11 @@
837 int nOther = 0;
838 int nHang = 0;
839 int nXPost = 0;
840 int nAuth = 0;
841 int nSmtp = 0;
 
842 char z[10000];
843 char zTime[10000];
844
845 login_check_credentials();
846 if( !g.perm.Admin ){
@@ -917,10 +919,13 @@
917 }
918 if( eType & 0x20 ){
919 @ <li>SMTP malfunctions
920 }
921 if( eType & 0x40 ){
 
 
 
922 @ <li>Other uncategorized messages
923 }
924 @ </ul>
925 }
926 @ <hr>
@@ -953,12 +958,16 @@
953 || sqlite3_strglob("warning: SQLITE_AUTH*",z)==0
954 ){
955 bOutput = (eType & 0x10)!=0;
956 nAuth++;
957 }else
958 {
959 bOutput = (eType & 0x40)!=0;
 
 
 
 
960 nOther++;
961 }
962 if( bOutput ){
963 @ %h(zTime)\
964 }
@@ -978,17 +987,21 @@
978 fclose(in);
979 if( eType ){
980 @ </pre>
981 }
982 if( eType==0 ){
983 int nNonHack = nPanic + nHang + nAuth + nSmtp + nOther;
984 int nTotal = nNonHack + nHack + nXPost;
985 @ <p><table border="a" cellspacing="0" cellpadding="5">
986 if( nPanic>0 ){
987 @ <tr><td align="right">%d(nPanic)</td>
988 @ <td><a href="./errorlog?y=2">Panics</a></td>
989 }
 
 
 
 
990 if( nHack>0 ){
991 @ <tr><td align="right">%d(nHack)</td>
992 @ <td><a href="./errorlog?y=1">Hack Attempts</a></td>
993 }
994 if( nHang>0 ){
@@ -1007,17 +1020,17 @@
1007 @ <tr><td align="right">%d(nSmtp)</td>
1008 @ <td><a href="./errorlog?y=32">SMTP faults</a></td>
1009 }
1010 if( nOther>0 ){
1011 @ <tr><td align="right">%d(nOther)</td>
1012 @ <td><a href="./errorlog?y=64">Other</a></td>
1013 }
1014 @ <tr><td align="right">%d(nTotal)</td>
1015 if( nTotal>0 ){
1016 @ <td><a href="./errorlog?y=255">All Messages</a></td>
1017 }else{
1018 @ <td>All Messages</td>
1019 }
1020 @ </table>
1021 }
1022 style_finish_page();
1023 }
1024
--- src/security_audit.c
+++ src/security_audit.c
@@ -810,27 +810,28 @@
810 ** WEBPAGE: errorlog
811 **
812 ** Show the content of the error log. Only the administrator can view
813 ** this page.
814 **
815 ** y=0x001 Show only hack attempts
816 ** y=0x002 Show only panics and assertion faults
817 ** y=0x004 Show hung backoffice processes
818 ** y=0x008 Show POST requests from a different origin
819 ** y=0x010 Show SQLITE_AUTH and similar
820 ** y=0x020 Show SMTP error reports
821 ** y=0x040 Show TH1 vulnerability reports
822 ** y=0x800 Show other uncategorized messages
823 **
824 ** If y is omitted or is zero, a count of the various message types is
825 ** shown.
826 */
827 void errorlog_page(void){
828 i64 szFile;
829 FILE *in;
830 char *zLog;
831 const char *zType = P("y");
832 static const int eAllTypes = 0x87f;
833 long eType = 0;
834 int bOutput = 0;
835 int prevWasTime = 0;
836 int nHack = 0;
837 int nPanic = 0;
@@ -837,10 +838,11 @@
838 int nOther = 0;
839 int nHang = 0;
840 int nXPost = 0;
841 int nAuth = 0;
842 int nSmtp = 0;
843 int nVuln = 0;
844 char z[10000];
845 char zTime[10000];
846
847 login_check_credentials();
848 if( !g.perm.Admin ){
@@ -917,10 +919,13 @@
919 }
920 if( eType & 0x20 ){
921 @ <li>SMTP malfunctions
922 }
923 if( eType & 0x40 ){
924 @ <li>TH1 vulnerabilities
925 }
926 if( eType & 0x800 ){
927 @ <li>Other uncategorized messages
928 }
929 @ </ul>
930 }
931 @ <hr>
@@ -953,12 +958,16 @@
958 || sqlite3_strglob("warning: SQLITE_AUTH*",z)==0
959 ){
960 bOutput = (eType & 0x10)!=0;
961 nAuth++;
962 }else
963 if( strncmp(z,"possible", 8)==0 && strstr(z,"tainted")!=0 ){
964 bOutput = (eType & 0x40)!=0;
965 nVuln++;
966 }else
967 {
968 bOutput = (eType & 0x800)!=0;
969 nOther++;
970 }
971 if( bOutput ){
972 @ %h(zTime)\
973 }
@@ -978,17 +987,21 @@
987 fclose(in);
988 if( eType ){
989 @ </pre>
990 }
991 if( eType==0 ){
992 int nNonHack = nPanic + nHang + nAuth + nSmtp + nVuln + nOther;
993 int nTotal = nNonHack + nHack + nXPost;
994 @ <p><table border="a" cellspacing="0" cellpadding="5">
995 if( nPanic>0 ){
996 @ <tr><td align="right">%d(nPanic)</td>
997 @ <td><a href="./errorlog?y=2">Panics</a></td>
998 }
999 if( nVuln>0 ){
1000 @ <tr><td align="right">%d(nVuln)</td>
1001 @ <td><a href="./errorlog?y=64">TH1 Vulnerabilities</a></td>
1002 }
1003 if( nHack>0 ){
1004 @ <tr><td align="right">%d(nHack)</td>
1005 @ <td><a href="./errorlog?y=1">Hack Attempts</a></td>
1006 }
1007 if( nHang>0 ){
@@ -1007,17 +1020,17 @@
1020 @ <tr><td align="right">%d(nSmtp)</td>
1021 @ <td><a href="./errorlog?y=32">SMTP faults</a></td>
1022 }
1023 if( nOther>0 ){
1024 @ <tr><td align="right">%d(nOther)</td>
1025 @ <td><a href="./errorlog?y=2048">Other</a></td>
1026 }
1027 @ <tr><td align="right">%d(nTotal)</td>
1028 if( nTotal>0 ){
1029 @ <td><a href="./errorlog?y=4095">All Messages</a></td>
1030 }else{
1031 @ <td>All Messages</td>
1032 }
1033 @ </table>
1034 }
1035 style_finish_page();
1036 }
1037
-24
--- src/th.c
+++ src/th.c
@@ -909,34 +909,10 @@
909909
thBufferFree(interp, &strbuf);
910910
thBufferFree(interp, &lenbuf);
911911
return rc;
912912
}
913913
914
-/*
915
-** Report misuse of a tainted string.
916
-**
917
-** In the current implementation, this routine issues a warning to the
918
-** error log and returns 0, causing processing to continue. This is so
919
-** that the new taint detection will not disrupt legacy configurations.
920
-** However, if modified so that this routine returns non-zero, then it
921
-** will cause an error in the script.
922
-*/
923
-int Th_ReportTaint(
924
- Th_Interp *interp, /* Report error here, if an error is reported */
925
- const char *zWhere, /* Where the tainted string appears */
926
- const char *zStr, /* The tainted string */
927
- int nStr /* Length of the tainted string */
928
-){
929
- nStr = TH1_LEN(nStr);
930
- if( nStr>0 ){
931
- fossil_errorlog("warning: tainted %s: \"%.*s\"", zWhere, nStr, zStr);
932
- }else{
933
- fossil_errorlog("warning: tainted %s", zWhere);
934
- }
935
- return 0;
936
-}
937
-
938914
/*
939915
** Evaluate the th1 script contained in the string (zProgram, nProgram)
940916
** in the current stack frame.
941917
*/
942918
static int thEvalLocal(Th_Interp *interp, const char *zProgram, int nProgram){
943919
--- src/th.c
+++ src/th.c
@@ -909,34 +909,10 @@
909 thBufferFree(interp, &strbuf);
910 thBufferFree(interp, &lenbuf);
911 return rc;
912 }
913
914 /*
915 ** Report misuse of a tainted string.
916 **
917 ** In the current implementation, this routine issues a warning to the
918 ** error log and returns 0, causing processing to continue. This is so
919 ** that the new taint detection will not disrupt legacy configurations.
920 ** However, if modified so that this routine returns non-zero, then it
921 ** will cause an error in the script.
922 */
923 int Th_ReportTaint(
924 Th_Interp *interp, /* Report error here, if an error is reported */
925 const char *zWhere, /* Where the tainted string appears */
926 const char *zStr, /* The tainted string */
927 int nStr /* Length of the tainted string */
928 ){
929 nStr = TH1_LEN(nStr);
930 if( nStr>0 ){
931 fossil_errorlog("warning: tainted %s: \"%.*s\"", zWhere, nStr, zStr);
932 }else{
933 fossil_errorlog("warning: tainted %s", zWhere);
934 }
935 return 0;
936 }
937
938 /*
939 ** Evaluate the th1 script contained in the string (zProgram, nProgram)
940 ** in the current stack frame.
941 */
942 static int thEvalLocal(Th_Interp *interp, const char *zProgram, int nProgram){
943
--- src/th.c
+++ src/th.c
@@ -909,34 +909,10 @@
909 thBufferFree(interp, &strbuf);
910 thBufferFree(interp, &lenbuf);
911 return rc;
912 }
913
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
914 /*
915 ** Evaluate the th1 script contained in the string (zProgram, nProgram)
916 ** in the current stack frame.
917 */
918 static int thEvalLocal(Th_Interp *interp, const char *zProgram, int nProgram){
919
+79 -7
--- src/th_main.c
+++ src/th_main.c
@@ -389,19 +389,15 @@
389389
pOut = pThOut;
390390
}
391391
if(TH_INIT_NO_ENCODE & g.th1Flags){
392392
encode = 0;
393393
}
394
- if( encode==0 && n>0 && TH1_TAINTED(n) ){
395
- if( Th_ReportTaint(0, "output string", z, n) ){
396
- return;
397
- }
398
- n = TH1_LEN(n);
399
- }
400394
if( enableOutput && n ){
401395
if( n<0 ){
402396
n = strlen(z);
397
+ }else{
398
+ n = TH1_LEN(n);
403399
}
404400
if( encode ){
405401
z = htmlize(z, n);
406402
n = strlen(z);
407403
}
@@ -534,14 +530,23 @@
534530
void *pConvert,
535531
int argc,
536532
const char **argv,
537533
int *argl
538534
){
535
+ int encode = *(unsigned int*)pConvert;
536
+ int n;
539537
if( argc!=2 ){
540538
return Th_WrongNumArgs(interp, "puts STRING");
541539
}
542
- sendText(0,(char*)argv[1], argl[1], *(unsigned int*)pConvert);
540
+ n = argl[1];
541
+ if( encode==0 && n>0 && TH1_TAINTED(n) ){
542
+ if( Th_ReportTaint(interp, "output string", argv[1], n) ){
543
+ return TH_ERROR;
544
+ }
545
+ n = TH1_LEN(n);
546
+ }
547
+ sendText(0,(char*)argv[1], n, encode);
543548
return TH_OK;
544549
}
545550
546551
/*
547552
** TH1 command: redirect URL ?withMethod?
@@ -3038,10 +3043,77 @@
30383043
** as appropriate. We need to pass on g.th1Flags for the case of
30393044
** recursive calls, so that, e.g., TH_INIT_NO_ENCODE does not get
30403045
** inadvertently toggled off by a recursive call.
30413046
*/;
30423047
}
3048
+
3049
+/*
3050
+** SETTING: vuln-report width=8 default=log
3051
+**
3052
+** This setting controls Fossil's behavior when it encounters a potential
3053
+** XSS or SQL-injection vulnerability due to misuse of TH1 configuration
3054
+** scripts. Choices are:
3055
+**
3056
+** off Do nothing. Ignore the vulnerability.
3057
+**
3058
+** log Write a report of the problem into the error log.
3059
+**
3060
+** block Like "log" but also prevent the offending TH1 command
3061
+** from running.
3062
+**
3063
+** fatal Render an error message page instead of the requested
3064
+** page.
3065
+*/
3066
+
3067
+/*
3068
+** Report misuse of a tainted string in TH1.
3069
+**
3070
+** The behavior depends on the vuln-report setting. If "off", this routine
3071
+** is a no-op. Otherwise, right a message into the error log. If
3072
+** vuln-report is "log", that is all that happens. But for any other
3073
+** value of vuln-report, a fatal error is raised.
3074
+*/
3075
+int Th_ReportTaint(
3076
+ Th_Interp *interp, /* Report error here, if an error is reported */
3077
+ const char *zWhere, /* Where the tainted string appears */
3078
+ const char *zStr, /* The tainted string */
3079
+ int nStr /* Length of the tainted string */
3080
+){
3081
+ char *zDisp; /* Dispensation */
3082
+ const char *zVulnType; /* Type of vulnerability */
3083
+
3084
+ zDisp = db_get("vuln-report","log");
3085
+ if( is_false(zDisp) ) return 0;
3086
+ if( strstr(zWhere,"SQL")!=0 ){
3087
+ zVulnType = "SQL-injection";
3088
+ }else{
3089
+ zVulnType = "XSS";
3090
+ }
3091
+ nStr = TH1_LEN(nStr);
3092
+ fossil_errorlog("possible %s vulnerability due to tainted TH1 %s: \"%.*s\"",
3093
+ zVulnType, zWhere, nStr, zStr);
3094
+ if( strcmp(zDisp,"log")==0 ){
3095
+ return 0;
3096
+ }
3097
+ if( strcmp(zDisp,"block")==0 ){
3098
+ char *z = mprintf("tainted %s: \"", zWhere);
3099
+ Th_ErrorMessage(interp, z, zStr, nStr);
3100
+ fossil_free(z);
3101
+ }else{
3102
+ char *z = mprintf("%#h", nStr, zStr);
3103
+ cgi_reset_content();
3104
+ style_submenu_enable(0);
3105
+ style_set_current_feature("error");
3106
+ style_header("Configuration Error");
3107
+ @ <p>Error in a TH1 configuration script:
3108
+ @ tainted %h(zWhere): "%z(z)"
3109
+ style_finish_page();
3110
+ cgi_reply();
3111
+ fossil_exit(1);
3112
+ }
3113
+ return 1;
3114
+}
30433115
30443116
/*
30453117
** COMMAND: test-th-render
30463118
**
30473119
** Usage: %fossil test-th-render FILE
30483120
--- src/th_main.c
+++ src/th_main.c
@@ -389,19 +389,15 @@
389 pOut = pThOut;
390 }
391 if(TH_INIT_NO_ENCODE & g.th1Flags){
392 encode = 0;
393 }
394 if( encode==0 && n>0 && TH1_TAINTED(n) ){
395 if( Th_ReportTaint(0, "output string", z, n) ){
396 return;
397 }
398 n = TH1_LEN(n);
399 }
400 if( enableOutput && n ){
401 if( n<0 ){
402 n = strlen(z);
 
 
403 }
404 if( encode ){
405 z = htmlize(z, n);
406 n = strlen(z);
407 }
@@ -534,14 +530,23 @@
534 void *pConvert,
535 int argc,
536 const char **argv,
537 int *argl
538 ){
 
 
539 if( argc!=2 ){
540 return Th_WrongNumArgs(interp, "puts STRING");
541 }
542 sendText(0,(char*)argv[1], argl[1], *(unsigned int*)pConvert);
 
 
 
 
 
 
 
543 return TH_OK;
544 }
545
546 /*
547 ** TH1 command: redirect URL ?withMethod?
@@ -3038,10 +3043,77 @@
3038 ** as appropriate. We need to pass on g.th1Flags for the case of
3039 ** recursive calls, so that, e.g., TH_INIT_NO_ENCODE does not get
3040 ** inadvertently toggled off by a recursive call.
3041 */;
3042 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3043
3044 /*
3045 ** COMMAND: test-th-render
3046 **
3047 ** Usage: %fossil test-th-render FILE
3048
--- src/th_main.c
+++ src/th_main.c
@@ -389,19 +389,15 @@
389 pOut = pThOut;
390 }
391 if(TH_INIT_NO_ENCODE & g.th1Flags){
392 encode = 0;
393 }
 
 
 
 
 
 
394 if( enableOutput && n ){
395 if( n<0 ){
396 n = strlen(z);
397 }else{
398 n = TH1_LEN(n);
399 }
400 if( encode ){
401 z = htmlize(z, n);
402 n = strlen(z);
403 }
@@ -534,14 +530,23 @@
530 void *pConvert,
531 int argc,
532 const char **argv,
533 int *argl
534 ){
535 int encode = *(unsigned int*)pConvert;
536 int n;
537 if( argc!=2 ){
538 return Th_WrongNumArgs(interp, "puts STRING");
539 }
540 n = argl[1];
541 if( encode==0 && n>0 && TH1_TAINTED(n) ){
542 if( Th_ReportTaint(interp, "output string", argv[1], n) ){
543 return TH_ERROR;
544 }
545 n = TH1_LEN(n);
546 }
547 sendText(0,(char*)argv[1], n, encode);
548 return TH_OK;
549 }
550
551 /*
552 ** TH1 command: redirect URL ?withMethod?
@@ -3038,10 +3043,77 @@
3043 ** as appropriate. We need to pass on g.th1Flags for the case of
3044 ** recursive calls, so that, e.g., TH_INIT_NO_ENCODE does not get
3045 ** inadvertently toggled off by a recursive call.
3046 */;
3047 }
3048
3049 /*
3050 ** SETTING: vuln-report width=8 default=log
3051 **
3052 ** This setting controls Fossil's behavior when it encounters a potential
3053 ** XSS or SQL-injection vulnerability due to misuse of TH1 configuration
3054 ** scripts. Choices are:
3055 **
3056 ** off Do nothing. Ignore the vulnerability.
3057 **
3058 ** log Write a report of the problem into the error log.
3059 **
3060 ** block Like "log" but also prevent the offending TH1 command
3061 ** from running.
3062 **
3063 ** fatal Render an error message page instead of the requested
3064 ** page.
3065 */
3066
3067 /*
3068 ** Report misuse of a tainted string in TH1.
3069 **
3070 ** The behavior depends on the vuln-report setting. If "off", this routine
3071 ** is a no-op. Otherwise, right a message into the error log. If
3072 ** vuln-report is "log", that is all that happens. But for any other
3073 ** value of vuln-report, a fatal error is raised.
3074 */
3075 int Th_ReportTaint(
3076 Th_Interp *interp, /* Report error here, if an error is reported */
3077 const char *zWhere, /* Where the tainted string appears */
3078 const char *zStr, /* The tainted string */
3079 int nStr /* Length of the tainted string */
3080 ){
3081 char *zDisp; /* Dispensation */
3082 const char *zVulnType; /* Type of vulnerability */
3083
3084 zDisp = db_get("vuln-report","log");
3085 if( is_false(zDisp) ) return 0;
3086 if( strstr(zWhere,"SQL")!=0 ){
3087 zVulnType = "SQL-injection";
3088 }else{
3089 zVulnType = "XSS";
3090 }
3091 nStr = TH1_LEN(nStr);
3092 fossil_errorlog("possible %s vulnerability due to tainted TH1 %s: \"%.*s\"",
3093 zVulnType, zWhere, nStr, zStr);
3094 if( strcmp(zDisp,"log")==0 ){
3095 return 0;
3096 }
3097 if( strcmp(zDisp,"block")==0 ){
3098 char *z = mprintf("tainted %s: \"", zWhere);
3099 Th_ErrorMessage(interp, z, zStr, nStr);
3100 fossil_free(z);
3101 }else{
3102 char *z = mprintf("%#h", nStr, zStr);
3103 cgi_reset_content();
3104 style_submenu_enable(0);
3105 style_set_current_feature("error");
3106 style_header("Configuration Error");
3107 @ <p>Error in a TH1 configuration script:
3108 @ tainted %h(zWhere): "%z(z)"
3109 style_finish_page();
3110 cgi_reply();
3111 fossil_exit(1);
3112 }
3113 return 1;
3114 }
3115
3116 /*
3117 ** COMMAND: test-th-render
3118 **
3119 ** Usage: %fossil test-th-render FILE
3120

Keyboard Shortcuts

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