Fossil

Check-in [90b63bc5d1]
Login

Check-in [90b63bc5d1]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add documentation regarding tainted strings in TH1. Mention the introduction of tainted strings in the 2.25 change log.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 90b63bc5d142d1eaa326e205bb8e15268681338dd7f5e0c7f570e4b72fda1de9
User & Date: drh 2025-04-24 15:04:20.894
Context
2025-04-24
15:19
Fix the version numbers in the new documentation on tainted strings. ... (check-in: 807b73e6b5 user: drh tags: trunk)
15:04
Add documentation regarding tainted strings in TH1. Mention the introduction of tainted strings in the 2.25 change log. ... (check-in: 90b63bc5d1 user: drh tags: trunk)
11:18
Preserve taint across TH1 commands: foreach, lappend, lindex, string index, string range, and string trim. Add test cases for taint. ... (check-in: 5291edac07 user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to www/changes.wiki.
136
137
138
139
140
141
142







143
144
145
146
147
148
149
  *  Add a "user permissions changes" [/doc/trunk/www/alerts.md|subscription]
     which alerts subscribers when an admin creates a new user or
     when a user's permissions change.
  *  Show project description on repository list.
  *  Make [/help?cmd=/chat|/chat] better-behaved during server outages, reducing
     the frequency of reconnection attempts over time and providing feedback
     to the user when the connection is down.







  *  Diverse minor fixes and additions.


<h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>

  *  The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
     that have non-ASCII filenames







>
>
>
>
>
>
>







136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
  *  Add a "user permissions changes" [/doc/trunk/www/alerts.md|subscription]
     which alerts subscribers when an admin creates a new user or
     when a user's permissions change.
  *  Show project description on repository list.
  *  Make [/help?cmd=/chat|/chat] better-behaved during server outages, reducing
     the frequency of reconnection attempts over time and providing feedback
     to the user when the connection is down.
  *  The [/doc/trunk/www/th1.md|TH1 script language] now makes a distinction
     between [/doc/trunk/www/th1.md#taint|tainted and untainted string values].
     This is a security enhancement that makes it more difficult to write
     custom TH1 scripts that contain XSS or SQL-injection bugs.  As part of
     this enhancement, the [/help?cmd=vuln-report|vuln-report] setting was
     added to control what Fossil does when it encounters a potential TH1
     security problem.
  *  Diverse minor fixes and additions.


<h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>

  *  The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
     that have non-ASCII filenames
Changes to www/th1.md.
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
of the command, is `if`.
The second token is `$current eq "dev"` - an expression.  (The outer {...}
are removed from each token by the command parser.)  The third token
is the `puts "hello"`, with its whitespace and newlines.  The fourth token
is `else` and the fifth and last token is `puts "world"`.

The `if` command evaluates its first argument (the second token)
as an expression, and if that expression is true, evaluates its
second argument (the third token) as a TH1 script.
If the expression is false and the third argument is `else`, then
the fourth argument is evaluated as a TH1 expression.

So, you see, even though the example above spans five lines, it is really
just a single command.








|







66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
of the command, is `if`.
The second token is `$current eq "dev"` - an expression.  (The outer {...}
are removed from each token by the command parser.)  The third token
is the `puts "hello"`, with its whitespace and newlines.  The fourth token
is `else` and the fifth and last token is `puts "world"`.

The `if` command evaluates its first argument (the second token)
as an expression, and if that expression is true, it evaluates its
second argument (the third token) as a TH1 script.
If the expression is false and the third argument is `else`, then
the fourth argument is evaluated as a TH1 expression.

So, you see, even though the example above spans five lines, it is really
just a single command.

104
105
106
107
108
109
110







































111
112
113
114
115
116
117
    return [lindex [regexp -line -inline -nocase -- \
        {^uuid:\s+([0-9A-F]{40}) } [eval [getFossilCommand \
        $repository "" info trunk]]] end]

Those backslashes allow the command to wrap nicely within a standard
terminal width while telling the interpreter to consider those three
lines as a single command.









































Summary of Core TH1 Commands
----------------------------

The original Tcl language (after which TH1 is modeled) has a very rich
repertoire of commands.  TH1, as it is designed to be minimalist and







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
    return [lindex [regexp -line -inline -nocase -- \
        {^uuid:\s+([0-9A-F]{40}) } [eval [getFossilCommand \
        $repository "" info trunk]]] end]

Those backslashes allow the command to wrap nicely within a standard
terminal width while telling the interpreter to consider those three
lines as a single command.

<a id="taint"></a>Tainted And Untainted Strings
-----------------------------------------------

Beginning with Fossil version 2.25 (circa 2025), TH1 distinguishes between
"tainted" and "untainted" strings.  Tainted strings are strings that are
derived from user inputs that might contain text that is designed to subvert
the script.  Untainted strings are known to come from secure sources and
are assumed to contain no malicious content.

Beginning with Fossil version 2.25, and depending on the value of the
[vuln-report setting](/help?cmd=vuln-report), TH1 will prevent tainted
strings from being used in ways that might lead to XSS or SQL-injection
attacks.  This feature helps to ensure that XSS and SQL-injection
vulnerabilities are not *accidentally* added to Fossil when
custom TH1 scripts for headers or footers or tickets are added to a
repository.  Note that the tainted/untainted distinction in strings does
make it impossible to introduce XSS and SQL-injections vulnerabilities
using poorly-written TH1 scripts; it just makes it more difficult and
less likely to happen by accident.  Developers must still consider the
security implications TH1 customizations they add to Fossil, and take
appropriate precautions when writing custom TH1.  Peer review of TH1
script changes is encouraged.

In Fossil version 2.25, if the vuln-report setting is set to "block"
or "fatal", the [html](#html) and [query](#query) TH1 commands will
fail with an error if their argument is a tainted string.  This helps
to prevent XSS and SQL-injection attacks, respectively.  Note that
the default value of the vuln-report setting is "log", which allows those
commands to continue working and only writes a warning message into the
error log.  <b>Future versions of Fossil may change the default value
of the vuln-report setting to "block" or "fatal".</b>  Fossil users
with customized TH1 scripts are encouraged to audit their customizations
and fix any potential vulnerabilities soon, so as to avoid breakage
caused by future upgrades.  <b>Future versions of Fossil might also
place additional restrictions on the use of tainted strings.</b>
For example, it is likely that future versions of Fossil will disallow
using tainted strings as script, for example as the body of a "for"
loop or of a "proc".


Summary of Core TH1 Commands
----------------------------

The original Tcl language (after which TH1 is modeled) has a very rich
repertoire of commands.  TH1, as it is designed to be minimalist and
145
146
147
148
149
150
151



152
153
154
155
156
157
158
  *  string index STRING INDEX
  *  string is CLASS STRING
  *  string last NEEDLE HAYSTACK ?START-INDEX?
  *  string match PATTERN STRING
  *  string length STRING
  *  string range STRING FIRST LAST
  *  string repeat STRING COUNT



  *  unset VARNAME
  *  uplevel ?LEVEL? SCRIPT
  *  upvar ?FRAME? OTHERVAR MYVAR ?OTHERVAR MYVAR?

All of the above commands work as in the original Tcl.  Refer to the
<a href="https://www.tcl-lang.org/man/tcl/contents.htm">Tcl documentation</a>
for details.







>
>
>







184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
  *  string index STRING INDEX
  *  string is CLASS STRING
  *  string last NEEDLE HAYSTACK ?START-INDEX?
  *  string match PATTERN STRING
  *  string length STRING
  *  string range STRING FIRST LAST
  *  string repeat STRING COUNT
  *  string trim STRING
  *  string trimleft STRING
  *  string trimright STRING
  *  unset VARNAME
  *  uplevel ?LEVEL? SCRIPT
  *  upvar ?FRAME? OTHERVAR MYVAR ?OTHERVAR MYVAR?

All of the above commands work as in the original Tcl.  Refer to the
<a href="https://www.tcl-lang.org/man/tcl/contents.htm">Tcl documentation</a>
for details.
212
213
214
215
216
217
218

219
220
221
222
223
224
225

226
227
228
229
230
231
232
  *  [setParameter](#setParameter)
  *  [setting](#setting)
  *  [stime](#stime)
  *  [styleHeader](#styleHeader)
  *  [styleFooter](#styleFooter)
  *  [styleScript](#styleScript)
  *  [submenu](#submenu)

  *  [tclEval](#tclEval)
  *  [tclExpr](#tclExpr)
  *  [tclInvoke](#tclInvoke)
  *  [tclIsSafe](#tclIsSafe)
  *  [tclMakeSafe](#tclMakeSafe)
  *  [tclReady](#tclReady)
  *  [trace](#trace)

  *  [unversioned content](#unversioned_content)
  *  [unversioned list](#unversioned_list)
  *  [utime](#utime)
  *  [verifyCsrf](#verifyCsrf)
  *  [verifyLogin](#verifyLogin)
  *  [wiki](#wiki)
  *  [wiki_assoc](#wiki_assoc)







>







>







254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
  *  [setParameter](#setParameter)
  *  [setting](#setting)
  *  [stime](#stime)
  *  [styleHeader](#styleHeader)
  *  [styleFooter](#styleFooter)
  *  [styleScript](#styleScript)
  *  [submenu](#submenu)
  *  [taint](#taintCmd)
  *  [tclEval](#tclEval)
  *  [tclExpr](#tclExpr)
  *  [tclInvoke](#tclInvoke)
  *  [tclIsSafe](#tclIsSafe)
  *  [tclMakeSafe](#tclMakeSafe)
  *  [tclReady](#tclReady)
  *  [trace](#trace)
  *  [untaint](#untaintCmd)
  *  [unversioned content](#unversioned_content)
  *  [unversioned list](#unversioned_list)
  *  [utime](#utime)
  *  [verifyCsrf](#verifyCsrf)
  *  [verifyLogin](#verifyLogin)
  *  [wiki](#wiki)
  *  [wiki_assoc](#wiki_assoc)
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544


545
546
547
548
549
550
551

<a id="html"></a>TH1 html Command
-----------------------------------

  *  html STRING

Outputs the STRING literally.  It is assumed that STRING contains
valid HTML, or that if it text then any characters that are
significant to HTML (such as `<`, `>`, `'`, or `&`) have already
been escaped, perhaps by the htmlize command.  Use the
[puts](#puts) command to output text that might contain unescaped
HTML markup.

**Beware of XSS attacks!**  If the STRING value to the html command
can be controlled by a hostile user, then he might be able to sneak
in malicious HTML or Javascript which could result in a
cross-site scripting (XSS) attack.  Be careful that all text that
in STRING that might come from user input has been sanitized by the
[htmlize](#htmlize) command or similar.



<a id="htmlize"></a>TH1 htmlize Command
-----------------------------------------

  *  htmlize STRING

Escape all characters of STRING which have special meaning in HTML.







|

|








|
>
>







570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597

<a id="html"></a>TH1 html Command
-----------------------------------

  *  html STRING

Outputs the STRING literally.  It is assumed that STRING contains
valid HTML, or that if STRING contains any characters that are
significant to HTML (such as `<`, `>`, `'`, or `&`) have already
been escaped, perhaps by the [htmlize](#htmlize) command.  Use the
[puts](#puts) command to output text that might contain unescaped
HTML markup.

**Beware of XSS attacks!**  If the STRING value to the html command
can be controlled by a hostile user, then he might be able to sneak
in malicious HTML or Javascript which could result in a
cross-site scripting (XSS) attack.  Be careful that all text that
in STRING that might come from user input has been sanitized by the
[htmlize](#htmlize) command or similar.  In recent versions of Fossil,
the STRING value must be [untainted](#taint) or else the "html" command 
will fail.

<a id="htmlize"></a>TH1 htmlize Command
-----------------------------------------

  *  htmlize STRING

Escape all characters of STRING which have special meaning in HTML.
608
609
610
611
612
613
614
615


616
617
618
619
620
621
622
623
624
625
626
627



628
629
630
631
632
633
634
-----------------------------------

  *  puts STRING

Outputs STRING.  Characters within STRING that have special meaning
in HTML are escaped prior to being output.  Thus is it safe for STRING
to be derived from user inputs.  See also the [html](#html) command
which behaves similarly except does not escape HTML markup.



<a id="query"></a>TH1 query Command
-------------------------------------

  *  query ?-nocomplain? SQL CODE

Runs the SQL query given by the SQL argument.  For each row in the result
set, run CODE.

In SQL, parameters such as $var are filled in using the value of variable
"var".  Result values are stored in variables with the column name prior
to each invocation of CODE.




**Beware of SQL injections in the `query` command!**
The SQL argument to the query command should always be literal SQL
text enclosed in {...}.  The SQL argument should never be a double-quoted
string or the value of a \$variable, as those constructs can lead to
an SQL Injection attack.  If you need to include the values of one or
more TH1 variables as part of the SQL, then put \$variable inside the







|
>
>











|
>
>
>







654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
-----------------------------------

  *  puts STRING

Outputs STRING.  Characters within STRING that have special meaning
in HTML are escaped prior to being output.  Thus is it safe for STRING
to be derived from user inputs.  See also the [html](#html) command
which behaves similarly except does not escape HTML markup.  This
command ("puts") is safe to use on [tainted strings](#taint), but the "html"
command is not.

<a id="query"></a>TH1 query Command
-------------------------------------

  *  query ?-nocomplain? SQL CODE

Runs the SQL query given by the SQL argument.  For each row in the result
set, run CODE.

In SQL, parameters such as $var are filled in using the value of variable
"var".  Result values are stored in variables with the column name prior
to each invocation of CODE.  The names of the variables in which results
are stored can be controlled using "AS name" clauses in the SQL.  As
the database will often contain content that originates from untrusted
users, all result values are marked as [tainted](#taint).

**Beware of SQL injections in the `query` command!**
The SQL argument to the query command should always be literal SQL
text enclosed in {...}.  The SQL argument should never be a double-quoted
string or the value of a \$variable, as those constructs can lead to
an SQL Injection attack.  If you need to include the values of one or
more TH1 variables as part of the SQL, then put \$variable inside the
647
648
649
650
651
652
653




654
655
656
657
658
659
660
~~~
   query "SELECT res FROM tab1 WHERE key='$mykey'" {...}  ;# <-- UNSAFE!
~~~

In this second example, TH1 does the expansion of `$mykey` prior to passing
the text down into SQLite.  So if `$mykey` contains a single-quote character,
followed by additional hostile text, that will result in an SQL injection.





<a id="randhex"></a>TH1 randhex Command
-----------------------------------------

  *  randhex N

Returns a string of N*2 random hexadecimal digits with N<50.  If N is







>
>
>
>







698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
~~~
   query "SELECT res FROM tab1 WHERE key='$mykey'" {...}  ;# <-- UNSAFE!
~~~

In this second example, TH1 does the expansion of `$mykey` prior to passing
the text down into SQLite.  So if `$mykey` contains a single-quote character,
followed by additional hostile text, that will result in an SQL injection.

To help guard against SQL-injections, recent versions of Fossil require
that the SQL argument be [untainted](#taint) or else the "query" command
will fail.

<a id="randhex"></a>TH1 randhex Command
-----------------------------------------

  *  randhex N

Returns a string of N*2 random hexadecimal digits with N<50.  If N is
781
782
783
784
785
786
787














788
789
790
791
792
793
794
<a id="submenu"></a>TH1 submenu Command
-----------------------------------------

  *  submenu link LABEL URL

Add hyperlink to the submenu of the current page.















<a id="tclEval"></a>TH1 tclEval Command
-----------------------------------------

**This command requires the Tcl integration feature.**

  *  tclEval arg ?arg ...?








>
>
>
>
>
>
>
>
>
>
>
>
>
>







836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
<a id="submenu"></a>TH1 submenu Command
-----------------------------------------

  *  submenu link LABEL URL

Add hyperlink to the submenu of the current page.

<a id="taintCmd"></a>TH1 taint Command
-----------------------------------------

  *  taint STRING

This command returns a copy of STRING that has been marked as
[tainted](#taint).  Tainted strings are strings which might be
controlled by an attacker and might contain hostile inputs and
are thus unsafe to use in certain contexts.  For example, tainted
strings should not be output as part of a webpage as they might
contain rogue HTML or Javascript that could lead to an XSS
vulnerability.  Similarly, tainted strings should not be run as
SQL since they might contain an SQL-injection vulerability.

<a id="tclEval"></a>TH1 tclEval Command
-----------------------------------------

**This command requires the Tcl integration feature.**

  *  tclEval arg ?arg ...?

852
853
854
855
856
857
858












859
860
861
862
863
864
865
<a id="trace"></a>TH1 trace Command
-------------------------------------

  *  trace STRING

Generates a TH1 trace message if TH1 tracing is enabled.













<a id="unversioned_content"></a>TH1 unversioned content Command
-----------------------------------------------------------------

  *  unversioned content FILENAME

Attempts to locate the specified unversioned file and return its contents.
An error is generated if the repository is not open or the unversioned file







>
>
>
>
>
>
>
>
>
>
>
>







921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
<a id="trace"></a>TH1 trace Command
-------------------------------------

  *  trace STRING

Generates a TH1 trace message if TH1 tracing is enabled.

<a id="untaintCmd"></a>TH1 taint Command
-----------------------------------------

  *  untaint STRING

This command returns a copy of STRING that has been marked as
[untainted](#taint).  Untainted strings are strings which are
believed to be free of potentially hostile content.  Use this
command with caution, as it overwrites the tainted-string protection
mechanisms that are built into TH1.  If you do not understand all
the implications of executing this command, then do not use it.

<a id="unversioned_content"></a>TH1 unversioned content Command
-----------------------------------------------------------------

  *  unversioned content FILENAME

Attempts to locate the specified unversioned file and return its contents.
An error is generated if the repository is not open or the unversioned file