Fossil SCM

Preserve taint across TH1 commands: foreach, lappend, lindex, string index, string range, and string trim. Add test cases for taint.

drh 2025-04-24 11:18 trunk
Commit 5291edac072416b1a9fda05d6fc73e20e99cb0076676d6627d1cb4530495b351
+19 -4
--- src/th_lang.c
+++ src/th_lang.c
@@ -180,20 +180,24 @@
180180
int nVar;
181181
char **azValue = 0;
182182
int *anValue;
183183
int nValue;
184184
int ii, jj;
185
+ int bTaint = 0;
185186
186187
if( argc!=4 ){
187188
return Th_WrongNumArgs(interp, "foreach varlist list script");
188189
}
189190
rc = Th_SplitList(interp, argv[1], argl[1], &azVar, &anVar, &nVar);
190191
if( rc ) return rc;
192
+ TH1_XFER_TAINT(bTaint, argl[2]);
191193
rc = Th_SplitList(interp, argv[2], argl[2], &azValue, &anValue, &nValue);
192194
for(ii=0; rc==TH_OK && ii<=nValue-nVar; ii+=nVar){
193195
for(jj=0; jj<nVar; jj++){
194
- Th_SetVar(interp, azVar[jj], anVar[jj], azValue[ii+jj], anValue[ii+jj]);
196
+ int x = anValue[ii+jj];
197
+ TH1_XFER_TAINT(x, bTaint);
198
+ Th_SetVar(interp, azVar[jj], anVar[jj], azValue[ii+jj], x);
195199
}
196200
rc = eval_loopbody(interp, argv[3], argl[3]);
197201
}
198202
if( rc==TH_BREAK ) rc = TH_OK;
199203
Th_Free(interp, azVar);
@@ -257,10 +261,11 @@
257261
rc = Th_GetVar(interp, argv[1], argl[1]);
258262
if( rc==TH_OK ){
259263
zList = Th_TakeResult(interp, &nList);
260264
}
261265
266
+ TH1_XFER_TAINT(bTaint, nList);
262267
for(i=2; i<argc; i++){
263268
TH1_XFER_TAINT(bTaint, argl[i]);
264269
Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]);
265270
}
266271
@@ -289,23 +294,27 @@
289294
int rc;
290295
291296
char **azElem;
292297
int *anElem;
293298
int nCount;
299
+ int bTaint = 0;
294300
295301
if( argc!=3 ){
296302
return Th_WrongNumArgs(interp, "lindex list index");
297303
}
298304
299305
if( TH_OK!=Th_ToInt(interp, argv[2], argl[2], &iElem) ){
300306
return TH_ERROR;
301307
}
302308
309
+ TH1_XFER_TAINT(bTaint, argl[1]);
303310
rc = Th_SplitList(interp, argv[1], argl[1], &azElem, &anElem, &nCount);
304311
if( rc==TH_OK ){
305312
if( iElem<nCount && iElem>=0 ){
306
- Th_SetResult(interp, azElem[iElem], anElem[iElem]);
313
+ int sz = anElem[iElem];
314
+ TH1_XFER_TAINT(sz, bTaint);
315
+ Th_SetResult(interp, azElem[iElem], sz);
307316
}else{
308317
Th_SetResult(interp, 0, 0);
309318
}
310319
Th_Free(interp, azElem);
311320
}
@@ -832,11 +841,13 @@
832841
interp, "Expected \"end\" or integer, got:", argv[3], argl[3]);
833842
return TH_ERROR;
834843
}
835844
836845
if( iIndex>=0 && iIndex<TH1_LEN(argl[2]) ){
837
- return Th_SetResult(interp, &argv[2][iIndex], 1);
846
+ int sz = 1;
847
+ TH1_XFER_TAINT(sz, argl[2]);
848
+ return Th_SetResult(interp, &argv[2][iIndex], sz);
838849
}else{
839850
return Th_SetResult(interp, 0, 0);
840851
}
841852
}
842853
@@ -970,10 +981,11 @@
970981
static int string_range_command(
971982
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
972983
){
973984
int iStart;
974985
int iEnd;
986
+ int sz;
975987
976988
if( argc!=5 ){
977989
return Th_WrongNumArgs(interp, "string range string first last");
978990
}
979991
@@ -989,12 +1001,14 @@
9891001
}
9901002
9911003
if( iStart<0 ) iStart = 0;
9921004
if( iEnd>=TH1_LEN(argl[2]) ) iEnd = TH1_LEN(argl[2])-1;
9931005
if( iStart>iEnd ) iEnd = iStart-1;
1006
+ sz = iEnd - iStart + 1;
1007
+ TH1_XFER_TAINT(sz, argl[2]);
9941008
995
- return Th_SetResult(interp, &argv[2][iStart], iEnd-iStart+1);
1009
+ return Th_SetResult(interp, &argv[2][iStart], sz);
9961010
}
9971011
9981012
/*
9991013
** TH Syntax:
10001014
**
@@ -1054,10 +1068,11 @@
10541068
while( n && th_isspace(z[0]) ){ z++; n--; }
10551069
}
10561070
if( TH1_LEN(argl[1])<5 || argv[1][4]=='r' ){
10571071
while( n && th_isspace(z[n-1]) ){ n--; }
10581072
}
1073
+ TH1_XFER_TAINT(n, argl[2]);
10591074
Th_SetResult(interp, z, n);
10601075
return TH_OK;
10611076
}
10621077
10631078
/*
10641079
10651080
ADDED test/th1-taint.test
--- src/th_lang.c
+++ src/th_lang.c
@@ -180,20 +180,24 @@
180 int nVar;
181 char **azValue = 0;
182 int *anValue;
183 int nValue;
184 int ii, jj;
 
185
186 if( argc!=4 ){
187 return Th_WrongNumArgs(interp, "foreach varlist list script");
188 }
189 rc = Th_SplitList(interp, argv[1], argl[1], &azVar, &anVar, &nVar);
190 if( rc ) return rc;
 
191 rc = Th_SplitList(interp, argv[2], argl[2], &azValue, &anValue, &nValue);
192 for(ii=0; rc==TH_OK && ii<=nValue-nVar; ii+=nVar){
193 for(jj=0; jj<nVar; jj++){
194 Th_SetVar(interp, azVar[jj], anVar[jj], azValue[ii+jj], anValue[ii+jj]);
 
 
195 }
196 rc = eval_loopbody(interp, argv[3], argl[3]);
197 }
198 if( rc==TH_BREAK ) rc = TH_OK;
199 Th_Free(interp, azVar);
@@ -257,10 +261,11 @@
257 rc = Th_GetVar(interp, argv[1], argl[1]);
258 if( rc==TH_OK ){
259 zList = Th_TakeResult(interp, &nList);
260 }
261
 
262 for(i=2; i<argc; i++){
263 TH1_XFER_TAINT(bTaint, argl[i]);
264 Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]);
265 }
266
@@ -289,23 +294,27 @@
289 int rc;
290
291 char **azElem;
292 int *anElem;
293 int nCount;
 
294
295 if( argc!=3 ){
296 return Th_WrongNumArgs(interp, "lindex list index");
297 }
298
299 if( TH_OK!=Th_ToInt(interp, argv[2], argl[2], &iElem) ){
300 return TH_ERROR;
301 }
302
 
303 rc = Th_SplitList(interp, argv[1], argl[1], &azElem, &anElem, &nCount);
304 if( rc==TH_OK ){
305 if( iElem<nCount && iElem>=0 ){
306 Th_SetResult(interp, azElem[iElem], anElem[iElem]);
 
 
307 }else{
308 Th_SetResult(interp, 0, 0);
309 }
310 Th_Free(interp, azElem);
311 }
@@ -832,11 +841,13 @@
832 interp, "Expected \"end\" or integer, got:", argv[3], argl[3]);
833 return TH_ERROR;
834 }
835
836 if( iIndex>=0 && iIndex<TH1_LEN(argl[2]) ){
837 return Th_SetResult(interp, &argv[2][iIndex], 1);
 
 
838 }else{
839 return Th_SetResult(interp, 0, 0);
840 }
841 }
842
@@ -970,10 +981,11 @@
970 static int string_range_command(
971 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
972 ){
973 int iStart;
974 int iEnd;
 
975
976 if( argc!=5 ){
977 return Th_WrongNumArgs(interp, "string range string first last");
978 }
979
@@ -989,12 +1001,14 @@
989 }
990
991 if( iStart<0 ) iStart = 0;
992 if( iEnd>=TH1_LEN(argl[2]) ) iEnd = TH1_LEN(argl[2])-1;
993 if( iStart>iEnd ) iEnd = iStart-1;
 
 
994
995 return Th_SetResult(interp, &argv[2][iStart], iEnd-iStart+1);
996 }
997
998 /*
999 ** TH Syntax:
1000 **
@@ -1054,10 +1068,11 @@
1054 while( n && th_isspace(z[0]) ){ z++; n--; }
1055 }
1056 if( TH1_LEN(argl[1])<5 || argv[1][4]=='r' ){
1057 while( n && th_isspace(z[n-1]) ){ n--; }
1058 }
 
1059 Th_SetResult(interp, z, n);
1060 return TH_OK;
1061 }
1062
1063 /*
1064
1065 DDED test/th1-taint.test
--- src/th_lang.c
+++ src/th_lang.c
@@ -180,20 +180,24 @@
180 int nVar;
181 char **azValue = 0;
182 int *anValue;
183 int nValue;
184 int ii, jj;
185 int bTaint = 0;
186
187 if( argc!=4 ){
188 return Th_WrongNumArgs(interp, "foreach varlist list script");
189 }
190 rc = Th_SplitList(interp, argv[1], argl[1], &azVar, &anVar, &nVar);
191 if( rc ) return rc;
192 TH1_XFER_TAINT(bTaint, argl[2]);
193 rc = Th_SplitList(interp, argv[2], argl[2], &azValue, &anValue, &nValue);
194 for(ii=0; rc==TH_OK && ii<=nValue-nVar; ii+=nVar){
195 for(jj=0; jj<nVar; jj++){
196 int x = anValue[ii+jj];
197 TH1_XFER_TAINT(x, bTaint);
198 Th_SetVar(interp, azVar[jj], anVar[jj], azValue[ii+jj], x);
199 }
200 rc = eval_loopbody(interp, argv[3], argl[3]);
201 }
202 if( rc==TH_BREAK ) rc = TH_OK;
203 Th_Free(interp, azVar);
@@ -257,10 +261,11 @@
261 rc = Th_GetVar(interp, argv[1], argl[1]);
262 if( rc==TH_OK ){
263 zList = Th_TakeResult(interp, &nList);
264 }
265
266 TH1_XFER_TAINT(bTaint, nList);
267 for(i=2; i<argc; i++){
268 TH1_XFER_TAINT(bTaint, argl[i]);
269 Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]);
270 }
271
@@ -289,23 +294,27 @@
294 int rc;
295
296 char **azElem;
297 int *anElem;
298 int nCount;
299 int bTaint = 0;
300
301 if( argc!=3 ){
302 return Th_WrongNumArgs(interp, "lindex list index");
303 }
304
305 if( TH_OK!=Th_ToInt(interp, argv[2], argl[2], &iElem) ){
306 return TH_ERROR;
307 }
308
309 TH1_XFER_TAINT(bTaint, argl[1]);
310 rc = Th_SplitList(interp, argv[1], argl[1], &azElem, &anElem, &nCount);
311 if( rc==TH_OK ){
312 if( iElem<nCount && iElem>=0 ){
313 int sz = anElem[iElem];
314 TH1_XFER_TAINT(sz, bTaint);
315 Th_SetResult(interp, azElem[iElem], sz);
316 }else{
317 Th_SetResult(interp, 0, 0);
318 }
319 Th_Free(interp, azElem);
320 }
@@ -832,11 +841,13 @@
841 interp, "Expected \"end\" or integer, got:", argv[3], argl[3]);
842 return TH_ERROR;
843 }
844
845 if( iIndex>=0 && iIndex<TH1_LEN(argl[2]) ){
846 int sz = 1;
847 TH1_XFER_TAINT(sz, argl[2]);
848 return Th_SetResult(interp, &argv[2][iIndex], sz);
849 }else{
850 return Th_SetResult(interp, 0, 0);
851 }
852 }
853
@@ -970,10 +981,11 @@
981 static int string_range_command(
982 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
983 ){
984 int iStart;
985 int iEnd;
986 int sz;
987
988 if( argc!=5 ){
989 return Th_WrongNumArgs(interp, "string range string first last");
990 }
991
@@ -989,12 +1001,14 @@
1001 }
1002
1003 if( iStart<0 ) iStart = 0;
1004 if( iEnd>=TH1_LEN(argl[2]) ) iEnd = TH1_LEN(argl[2])-1;
1005 if( iStart>iEnd ) iEnd = iStart-1;
1006 sz = iEnd - iStart + 1;
1007 TH1_XFER_TAINT(sz, argl[2]);
1008
1009 return Th_SetResult(interp, &argv[2][iStart], sz);
1010 }
1011
1012 /*
1013 ** TH Syntax:
1014 **
@@ -1054,10 +1068,11 @@
1068 while( n && th_isspace(z[0]) ){ z++; n--; }
1069 }
1070 if( TH1_LEN(argl[1])<5 || argv[1][4]=='r' ){
1071 while( n && th_isspace(z[n-1]) ){ n--; }
1072 }
1073 TH1_XFER_TAINT(n, argl[2]);
1074 Th_SetResult(interp, z, n);
1075 return TH_OK;
1076 }
1077
1078 /*
1079
1080 DDED test/th1-taint.test
--- a/test/th1-taint.test
+++ b/test/th1-taint.test
@@ -0,0 +1,84 @@
1
+#
2
+# Copyright (c) 2025 D. Richard Hipp
3
+#
4
+# This program is free software; you can redistribute it and/or
5
+# modify it under the terms of the Simplified BSD License (also
6
+# known as the "2-Clause License" or "FreeBSD License".)
7
+#
8
+# This program is distributed in the hope that it will be useful,
9
+# but without any warranty; without even the implied warranty of
10
+# merchantability or fitness for a particular purpose.
11
+#
12
+# Author contact information:
13
+# [email protected]
14
+# http://www.hwaci.com/drh/
15
+#
16
+############################################################################
17
+#
18
+# TH1 Commands
19
+#
20
+
21
+set path [file dirname [info script]]; test_setup
22
+
23
+proc taint-test {testnum th1script expected} {
24
+ global fossilexe
25
+ set rc [catch {exec $fossilexe test-th-eval $th1script} got]
26
+ if {$rc} {
27
+ test th1-taint-$testnum 0
28
+ puts $got
29
+ return
30
+ }
31
+ if {$got ne $expected} {
32
+ test th1-taint-$testnum 0
33
+ puts " Expected: $expected"
34
+ puts " Got: $got"
35
+ } else {
36
+ test th1-taint-$testnum 1
37
+ }
38
+}
39
+
40
+taint-test 10 {string is tainted abcd} 0
41
+taint-test 20 {string is tainted [taint abcd]} 1
42
+taint-test 30 {string is tainted [untaint [taint abcd]]} 0
43
+taint-test 40 {string is tainted [untaint abcde]} 0
44
+taint-test 50 {string is tainted "abc[taint def]ghi"} 1
45
+taint-test 60 {set t1 [taint abc]; string is tainted "123 $t1 456"} 1
46
+
47
+taint-test 100 {set t1 [taint abc]; lappend t1 def; string is tainted $t1} 1
48
+taint-test 110 {set t1 abc; lappend t1 [taint def]; string is tainted $t1} 1
49
+
50
+taint-test 200 {string is tainted [list abc def ghi]} 0
51
+taint-test 210 {string is tainted [list [taint abc] def ghi]} 1
52
+taint-test 220 {string is tainted [list abc [taint def] ghi]} 1
53
+taint-test 230 {string is tainted [list abc def [taint ghi]]} 1
54
+
55
+taint-test 300 {
56
+ set res {}
57
+ foreach x [list abc [taint def] ghi] {
58
+ lappend res [string is tainted $x]
59
+ }
60
+ set res
61
+} {1 1 1}
62
+taint-test 310 {
63
+ set res {}
64
+ foreach {x y} [list abc [taint def] ghi jkl] {
65
+ lappend res [string is tainted $x] [string is tainted $y]
66
+ }
67
+ set res
68
+} {1 1 1 1}
69
+
70
+taint-test 400 {string is tainted [lindex "abc [taint def] ghi" 0]} 1
71
+taint-test 410 {string is tainted [lindex "abc [taint def] ghi" 1]} 1
72
+taint-test 420 {string is tainted [lindex "abc [taint def] ghi" 2]} 1
73
+taint-test 430 {string is tainted [lindex "abc [taint def] ghi" 3]} 0
74
+
75
+taint-test 500 {string is tainted [string index [taint abcdefg] 3]} 1
76
+
77
+taint-test 600 {string is tainted [string range [taint abcdefg] 3 5]} 1
78
+
79
+taint-test 700 {string is tainted [string trim [taint " abcdefg "]]} 1
80
+taint-test 710 {string is tainted [string trimright [taint " abcdefg "]]} 1
81
+taint-test 720 {string is tainted [string trimleft [taint " abcdefg "]]} 1
82
+
83
+
84
+test_cleanup
--- a/test/th1-taint.test
+++ b/test/th1-taint.test
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/test/th1-taint.test
+++ b/test/th1-taint.test
@@ -0,0 +1,84 @@
1 #
2 # Copyright (c) 2025 D. Richard Hipp
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the Simplified BSD License (also
6 # known as the "2-Clause License" or "FreeBSD License".)
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but without any warranty; without even the implied warranty of
10 # merchantability or fitness for a particular purpose.
11 #
12 # Author contact information:
13 # [email protected]
14 # http://www.hwaci.com/drh/
15 #
16 ############################################################################
17 #
18 # TH1 Commands
19 #
20
21 set path [file dirname [info script]]; test_setup
22
23 proc taint-test {testnum th1script expected} {
24 global fossilexe
25 set rc [catch {exec $fossilexe test-th-eval $th1script} got]
26 if {$rc} {
27 test th1-taint-$testnum 0
28 puts $got
29 return
30 }
31 if {$got ne $expected} {
32 test th1-taint-$testnum 0
33 puts " Expected: $expected"
34 puts " Got: $got"
35 } else {
36 test th1-taint-$testnum 1
37 }
38 }
39
40 taint-test 10 {string is tainted abcd} 0
41 taint-test 20 {string is tainted [taint abcd]} 1
42 taint-test 30 {string is tainted [untaint [taint abcd]]} 0
43 taint-test 40 {string is tainted [untaint abcde]} 0
44 taint-test 50 {string is tainted "abc[taint def]ghi"} 1
45 taint-test 60 {set t1 [taint abc]; string is tainted "123 $t1 456"} 1
46
47 taint-test 100 {set t1 [taint abc]; lappend t1 def; string is tainted $t1} 1
48 taint-test 110 {set t1 abc; lappend t1 [taint def]; string is tainted $t1} 1
49
50 taint-test 200 {string is tainted [list abc def ghi]} 0
51 taint-test 210 {string is tainted [list [taint abc] def ghi]} 1
52 taint-test 220 {string is tainted [list abc [taint def] ghi]} 1
53 taint-test 230 {string is tainted [list abc def [taint ghi]]} 1
54
55 taint-test 300 {
56 set res {}
57 foreach x [list abc [taint def] ghi] {
58 lappend res [string is tainted $x]
59 }
60 set res
61 } {1 1 1}
62 taint-test 310 {
63 set res {}
64 foreach {x y} [list abc [taint def] ghi jkl] {
65 lappend res [string is tainted $x] [string is tainted $y]
66 }
67 set res
68 } {1 1 1 1}
69
70 taint-test 400 {string is tainted [lindex "abc [taint def] ghi" 0]} 1
71 taint-test 410 {string is tainted [lindex "abc [taint def] ghi" 1]} 1
72 taint-test 420 {string is tainted [lindex "abc [taint def] ghi" 2]} 1
73 taint-test 430 {string is tainted [lindex "abc [taint def] ghi" 3]} 0
74
75 taint-test 500 {string is tainted [string index [taint abcdefg] 3]} 1
76
77 taint-test 600 {string is tainted [string range [taint abcdefg] 3 5]} 1
78
79 taint-test 700 {string is tainted [string trim [taint " abcdefg "]]} 1
80 taint-test 710 {string is tainted [string trimright [taint " abcdefg "]]} 1
81 taint-test 720 {string is tainted [string trimleft [taint " abcdefg "]]} 1
82
83
84 test_cleanup

Keyboard Shortcuts

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