Fossil SCM
When rendering the default header via TH1, allow the default Content-Security-Policy content to be overridden via the 'default_csp' variable. Also, add the 'nonce' command to TH1.
Commit
8a65cd1831a5c2229aa77bdbd37e0b734d863840f8d3e8626e6d9820ca23d9bc
Parent
f59faedb71d84b8…
5 files changed
+14
-5
+35
+2
-3
+3
+8
+14
-5
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -389,14 +389,11 @@ | ||
| 389 | 389 | */ |
| 390 | 390 | static char zDfltHeader[] = |
| 391 | 391 | @ <html> |
| 392 | 392 | @ <head> |
| 393 | 393 | @ <base href="$baseurl/$current_page" /> |
| 394 | -@ <meta http-equiv="Content-Security-Policy" \ | |
| 395 | -@ content="default-src 'self' data: ; \ | |
| 396 | -@ script-src 'self' 'nonce-$<nonce>' ;\ | |
| 397 | -@ style-src 'self' 'unsafe-inline'" /> | |
| 394 | +@ <meta http-equiv="Content-Security-Policy" content="$default_csp" /> | |
| 398 | 395 | @ <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 399 | 396 | @ <title>$<project_name>: $<title></title> |
| 400 | 397 | @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" \ |
| 401 | 398 | @ href="$home/timeline.rss" /> |
| 402 | 399 | @ <link rel="stylesheet" href="$stylesheet_url" type="text/css" \ |
| @@ -407,11 +404,23 @@ | ||
| 407 | 404 | |
| 408 | 405 | /* |
| 409 | 406 | ** Initialize all the default TH1 variables |
| 410 | 407 | */ |
| 411 | 408 | static void style_init_th1_vars(const char *zTitle){ |
| 412 | - Th_Store("nonce", style_nonce()); | |
| 409 | + const char *zNonce = style_nonce(); | |
| 410 | + /* | |
| 411 | + ** Do not overwrite the TH1 variable "default_csp" if it exists, as this | |
| 412 | + ** allows it to be properly overridden via the TH1 setup script (i.e. it | |
| 413 | + ** is evaluated before the header is rendered). | |
| 414 | + */ | |
| 415 | + char *zDfltCsp = sqlite3_mprintf("default-src 'self' data: ; " | |
| 416 | + "script-src 'self' 'nonce-%s' ; " | |
| 417 | + "style-src 'self' 'unsafe-inline'", | |
| 418 | + zNonce); | |
| 419 | + Th_MaybeStore("default_csp", zDfltCsp); | |
| 420 | + sqlite3_free(zDfltCsp); | |
| 421 | + Th_Store("nonce", zNonce); | |
| 413 | 422 | Th_Store("project_name", db_get("project-name","Unnamed Fossil Project")); |
| 414 | 423 | Th_Store("project_description", db_get("project-description","")); |
| 415 | 424 | if( zTitle ) Th_Store("title", zTitle); |
| 416 | 425 | Th_Store("baseurl", g.zBaseURL); |
| 417 | 426 | Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL); |
| 418 | 427 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -389,14 +389,11 @@ | |
| 389 | */ |
| 390 | static char zDfltHeader[] = |
| 391 | @ <html> |
| 392 | @ <head> |
| 393 | @ <base href="$baseurl/$current_page" /> |
| 394 | @ <meta http-equiv="Content-Security-Policy" \ |
| 395 | @ content="default-src 'self' data: ; \ |
| 396 | @ script-src 'self' 'nonce-$<nonce>' ;\ |
| 397 | @ style-src 'self' 'unsafe-inline'" /> |
| 398 | @ <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 399 | @ <title>$<project_name>: $<title></title> |
| 400 | @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" \ |
| 401 | @ href="$home/timeline.rss" /> |
| 402 | @ <link rel="stylesheet" href="$stylesheet_url" type="text/css" \ |
| @@ -407,11 +404,23 @@ | |
| 407 | |
| 408 | /* |
| 409 | ** Initialize all the default TH1 variables |
| 410 | */ |
| 411 | static void style_init_th1_vars(const char *zTitle){ |
| 412 | Th_Store("nonce", style_nonce()); |
| 413 | Th_Store("project_name", db_get("project-name","Unnamed Fossil Project")); |
| 414 | Th_Store("project_description", db_get("project-description","")); |
| 415 | if( zTitle ) Th_Store("title", zTitle); |
| 416 | Th_Store("baseurl", g.zBaseURL); |
| 417 | Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL); |
| 418 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -389,14 +389,11 @@ | |
| 389 | */ |
| 390 | static char zDfltHeader[] = |
| 391 | @ <html> |
| 392 | @ <head> |
| 393 | @ <base href="$baseurl/$current_page" /> |
| 394 | @ <meta http-equiv="Content-Security-Policy" content="$default_csp" /> |
| 395 | @ <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 396 | @ <title>$<project_name>: $<title></title> |
| 397 | @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" \ |
| 398 | @ href="$home/timeline.rss" /> |
| 399 | @ <link rel="stylesheet" href="$stylesheet_url" type="text/css" \ |
| @@ -407,11 +404,23 @@ | |
| 404 | |
| 405 | /* |
| 406 | ** Initialize all the default TH1 variables |
| 407 | */ |
| 408 | static void style_init_th1_vars(const char *zTitle){ |
| 409 | const char *zNonce = style_nonce(); |
| 410 | /* |
| 411 | ** Do not overwrite the TH1 variable "default_csp" if it exists, as this |
| 412 | ** allows it to be properly overridden via the TH1 setup script (i.e. it |
| 413 | ** is evaluated before the header is rendered). |
| 414 | */ |
| 415 | char *zDfltCsp = sqlite3_mprintf("default-src 'self' data: ; " |
| 416 | "script-src 'self' 'nonce-%s' ; " |
| 417 | "style-src 'self' 'unsafe-inline'", |
| 418 | zNonce); |
| 419 | Th_MaybeStore("default_csp", zDfltCsp); |
| 420 | sqlite3_free(zDfltCsp); |
| 421 | Th_Store("nonce", zNonce); |
| 422 | Th_Store("project_name", db_get("project-name","Unnamed Fossil Project")); |
| 423 | Th_Store("project_description", db_get("project-description","")); |
| 424 | if( zTitle ) Th_Store("title", zTitle); |
| 425 | Th_Store("baseurl", g.zBaseURL); |
| 426 | Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL); |
| 427 |
+35
| --- src/th_main.c | ||
| +++ src/th_main.c | ||
| @@ -411,10 +411,30 @@ | ||
| 411 | 411 | } |
| 412 | 412 | } |
| 413 | 413 | Th_SetResult(interp, "file name not found in manifest", -1); |
| 414 | 414 | return 0; |
| 415 | 415 | } |
| 416 | + | |
| 417 | +/* | |
| 418 | +** TH1 command: nonce | |
| 419 | +** | |
| 420 | +** Returns the value of the cryptographic nonce for the request being | |
| 421 | +** processed. | |
| 422 | +*/ | |
| 423 | +static int nonceCmd( | |
| 424 | + Th_Interp *interp, | |
| 425 | + void *pConvert, | |
| 426 | + int argc, | |
| 427 | + const char **argv, | |
| 428 | + int *argl | |
| 429 | +){ | |
| 430 | + if( argc!=1 ){ | |
| 431 | + return Th_WrongNumArgs(interp, "nonce"); | |
| 432 | + } | |
| 433 | + Th_SetResult(interp, style_nonce(), -1); | |
| 434 | + return TH_OK; | |
| 435 | +} | |
| 416 | 436 | |
| 417 | 437 | /* |
| 418 | 438 | ** TH1 command: puts STRING |
| 419 | 439 | ** TH1 command: html STRING |
| 420 | 440 | ** |
| @@ -2021,10 +2041,11 @@ | ||
| 2021 | 2041 | {"htmlize", htmlizeCmd, 0}, |
| 2022 | 2042 | {"http", httpCmd, 0}, |
| 2023 | 2043 | {"insertCsrf", insertCsrfCmd, 0}, |
| 2024 | 2044 | {"linecount", linecntCmd, 0}, |
| 2025 | 2045 | {"markdown", markdownCmd, 0}, |
| 2046 | + {"nonce", nonceCmd, 0}, | |
| 2026 | 2047 | {"puts", putsCmd, (void*)&aFlags[1]}, |
| 2027 | 2048 | {"query", queryCmd, 0}, |
| 2028 | 2049 | {"randhex", randhexCmd, 0}, |
| 2029 | 2050 | {"redirect", redirectCmd, 0}, |
| 2030 | 2051 | {"regexp", regexpCmd, 0}, |
| @@ -2104,10 +2125,24 @@ | ||
| 2104 | 2125 | } |
| 2105 | 2126 | } |
| 2106 | 2127 | g.th1Flags &= ~TH_INIT_MASK; |
| 2107 | 2128 | g.th1Flags |= (flags & TH_INIT_MASK); |
| 2108 | 2129 | } |
| 2130 | + | |
| 2131 | +/* | |
| 2132 | +** Store a string value in a variable in the interpreter if the variable | |
| 2133 | +** does not already exist. | |
| 2134 | +*/ | |
| 2135 | +void Th_MaybeStore(const char *zName, const char *zValue){ | |
| 2136 | + Th_FossilInit(TH_INIT_DEFAULT); | |
| 2137 | + if( zValue && !Th_ExistsVar(g.interp, zName, -1) ){ | |
| 2138 | + if( g.thTrace ){ | |
| 2139 | + Th_Trace("maybe_set %h {%h}<br />\n", zName, zValue); | |
| 2140 | + } | |
| 2141 | + Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue)); | |
| 2142 | + } | |
| 2143 | +} | |
| 2109 | 2144 | |
| 2110 | 2145 | /* |
| 2111 | 2146 | ** Store a string value in a variable in the interpreter. |
| 2112 | 2147 | */ |
| 2113 | 2148 | void Th_Store(const char *zName, const char *zValue){ |
| 2114 | 2149 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -411,10 +411,30 @@ | |
| 411 | } |
| 412 | } |
| 413 | Th_SetResult(interp, "file name not found in manifest", -1); |
| 414 | return 0; |
| 415 | } |
| 416 | |
| 417 | /* |
| 418 | ** TH1 command: puts STRING |
| 419 | ** TH1 command: html STRING |
| 420 | ** |
| @@ -2021,10 +2041,11 @@ | |
| 2021 | {"htmlize", htmlizeCmd, 0}, |
| 2022 | {"http", httpCmd, 0}, |
| 2023 | {"insertCsrf", insertCsrfCmd, 0}, |
| 2024 | {"linecount", linecntCmd, 0}, |
| 2025 | {"markdown", markdownCmd, 0}, |
| 2026 | {"puts", putsCmd, (void*)&aFlags[1]}, |
| 2027 | {"query", queryCmd, 0}, |
| 2028 | {"randhex", randhexCmd, 0}, |
| 2029 | {"redirect", redirectCmd, 0}, |
| 2030 | {"regexp", regexpCmd, 0}, |
| @@ -2104,10 +2125,24 @@ | |
| 2104 | } |
| 2105 | } |
| 2106 | g.th1Flags &= ~TH_INIT_MASK; |
| 2107 | g.th1Flags |= (flags & TH_INIT_MASK); |
| 2108 | } |
| 2109 | |
| 2110 | /* |
| 2111 | ** Store a string value in a variable in the interpreter. |
| 2112 | */ |
| 2113 | void Th_Store(const char *zName, const char *zValue){ |
| 2114 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -411,10 +411,30 @@ | |
| 411 | } |
| 412 | } |
| 413 | Th_SetResult(interp, "file name not found in manifest", -1); |
| 414 | return 0; |
| 415 | } |
| 416 | |
| 417 | /* |
| 418 | ** TH1 command: nonce |
| 419 | ** |
| 420 | ** Returns the value of the cryptographic nonce for the request being |
| 421 | ** processed. |
| 422 | */ |
| 423 | static int nonceCmd( |
| 424 | Th_Interp *interp, |
| 425 | void *pConvert, |
| 426 | int argc, |
| 427 | const char **argv, |
| 428 | int *argl |
| 429 | ){ |
| 430 | if( argc!=1 ){ |
| 431 | return Th_WrongNumArgs(interp, "nonce"); |
| 432 | } |
| 433 | Th_SetResult(interp, style_nonce(), -1); |
| 434 | return TH_OK; |
| 435 | } |
| 436 | |
| 437 | /* |
| 438 | ** TH1 command: puts STRING |
| 439 | ** TH1 command: html STRING |
| 440 | ** |
| @@ -2021,10 +2041,11 @@ | |
| 2041 | {"htmlize", htmlizeCmd, 0}, |
| 2042 | {"http", httpCmd, 0}, |
| 2043 | {"insertCsrf", insertCsrfCmd, 0}, |
| 2044 | {"linecount", linecntCmd, 0}, |
| 2045 | {"markdown", markdownCmd, 0}, |
| 2046 | {"nonce", nonceCmd, 0}, |
| 2047 | {"puts", putsCmd, (void*)&aFlags[1]}, |
| 2048 | {"query", queryCmd, 0}, |
| 2049 | {"randhex", randhexCmd, 0}, |
| 2050 | {"redirect", redirectCmd, 0}, |
| 2051 | {"regexp", regexpCmd, 0}, |
| @@ -2104,10 +2125,24 @@ | |
| 2125 | } |
| 2126 | } |
| 2127 | g.th1Flags &= ~TH_INIT_MASK; |
| 2128 | g.th1Flags |= (flags & TH_INIT_MASK); |
| 2129 | } |
| 2130 | |
| 2131 | /* |
| 2132 | ** Store a string value in a variable in the interpreter if the variable |
| 2133 | ** does not already exist. |
| 2134 | */ |
| 2135 | void Th_MaybeStore(const char *zName, const char *zValue){ |
| 2136 | Th_FossilInit(TH_INIT_DEFAULT); |
| 2137 | if( zValue && !Th_ExistsVar(g.interp, zName, -1) ){ |
| 2138 | if( g.thTrace ){ |
| 2139 | Th_Trace("maybe_set %h {%h}<br />\n", zName, zValue); |
| 2140 | } |
| 2141 | Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue)); |
| 2142 | } |
| 2143 | } |
| 2144 | |
| 2145 | /* |
| 2146 | ** Store a string value in a variable in the interpreter. |
| 2147 | */ |
| 2148 | void Th_Store(const char *zName, const char *zValue){ |
| 2149 |
+2
-3
| --- test/th1.test | ||
| +++ test/th1.test | ||
| @@ -1032,21 +1032,20 @@ | ||
| 1032 | 1032 | protOut "Sorted: $sorted_result" |
| 1033 | 1033 | set base_commands {anoncap anycap array artifact break breakpoint catch\ |
| 1034 | 1034 | cgiHeaderLine checkout combobox continue date decorate dir enable_output \ |
| 1035 | 1035 | encode64 error expr for getParameter glob_match globalState hascap \ |
| 1036 | 1036 | hasfeature html htmlize http httpize if info insertCsrf lindex linecount \ |
| 1037 | - list llength lsearch markdown proc puts query randhex redirect regexp\ | |
| 1038 | - reinitialize rename render repository return searchable set\ | |
| 1037 | + list llength lsearch markdown nonce proc puts query randhex redirect\ | |
| 1038 | + regexp reinitialize rename render repository return searchable set\ | |
| 1039 | 1039 | setParameter setting stime string styleFooter styleHeader styleScript\ |
| 1040 | 1040 | tclReady trace unset unversioned uplevel upvar utime verifyCsrf wiki} |
| 1041 | 1041 | set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe} |
| 1042 | 1042 | if {$th1Tcl} { |
| 1043 | 1043 | test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]} |
| 1044 | 1044 | } else { |
| 1045 | 1045 | test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]} |
| 1046 | 1046 | } |
| 1047 | - | |
| 1048 | 1047 | |
| 1049 | 1048 | ############################################################################### |
| 1050 | 1049 | |
| 1051 | 1050 | fossil test-th-eval "info vars" |
| 1052 | 1051 | |
| 1053 | 1052 |
| --- test/th1.test | |
| +++ test/th1.test | |
| @@ -1032,21 +1032,20 @@ | |
| 1032 | protOut "Sorted: $sorted_result" |
| 1033 | set base_commands {anoncap anycap array artifact break breakpoint catch\ |
| 1034 | cgiHeaderLine checkout combobox continue date decorate dir enable_output \ |
| 1035 | encode64 error expr for getParameter glob_match globalState hascap \ |
| 1036 | hasfeature html htmlize http httpize if info insertCsrf lindex linecount \ |
| 1037 | list llength lsearch markdown proc puts query randhex redirect regexp\ |
| 1038 | reinitialize rename render repository return searchable set\ |
| 1039 | setParameter setting stime string styleFooter styleHeader styleScript\ |
| 1040 | tclReady trace unset unversioned uplevel upvar utime verifyCsrf wiki} |
| 1041 | set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe} |
| 1042 | if {$th1Tcl} { |
| 1043 | test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]} |
| 1044 | } else { |
| 1045 | test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]} |
| 1046 | } |
| 1047 | |
| 1048 | |
| 1049 | ############################################################################### |
| 1050 | |
| 1051 | fossil test-th-eval "info vars" |
| 1052 | |
| 1053 |
| --- test/th1.test | |
| +++ test/th1.test | |
| @@ -1032,21 +1032,20 @@ | |
| 1032 | protOut "Sorted: $sorted_result" |
| 1033 | set base_commands {anoncap anycap array artifact break breakpoint catch\ |
| 1034 | cgiHeaderLine checkout combobox continue date decorate dir enable_output \ |
| 1035 | encode64 error expr for getParameter glob_match globalState hascap \ |
| 1036 | hasfeature html htmlize http httpize if info insertCsrf lindex linecount \ |
| 1037 | list llength lsearch markdown nonce proc puts query randhex redirect\ |
| 1038 | regexp reinitialize rename render repository return searchable set\ |
| 1039 | setParameter setting stime string styleFooter styleHeader styleScript\ |
| 1040 | tclReady trace unset unversioned uplevel upvar utime verifyCsrf wiki} |
| 1041 | set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe} |
| 1042 | if {$th1Tcl} { |
| 1043 | test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]} |
| 1044 | } else { |
| 1045 | test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]} |
| 1046 | } |
| 1047 | |
| 1048 | ############################################################################### |
| 1049 | |
| 1050 | fossil test-th-eval "info vars" |
| 1051 | |
| 1052 |
+3
| --- www/customskin.md | ||
| +++ www/customskin.md | ||
| @@ -233,10 +233,13 @@ | ||
| 233 | 233 | * **current_page** - The name of the page currently being processed, |
| 234 | 234 | without the leading "/" and without query parameters. |
| 235 | 235 | Examples: "timeline", "doc/trunk/README.txt", "wiki". |
| 236 | 236 | |
| 237 | 237 | * **csrf_token** - A token used to prevent cross-site request forgery. |
| 238 | + | |
| 239 | + * **default_csp** - The content to be used within the default header | |
| 240 | + for the "Content-Security-Policy" meta tag. | |
| 238 | 241 | |
| 239 | 242 | * **release_version** - The release version of Fossil. Ex: "1.31" |
| 240 | 243 | |
| 241 | 244 | * **manifest_version** - A prefix on the check-in hash of the |
| 242 | 245 | specific version of fossil that is running. Ex: "\[47bb6432a1\]" |
| 243 | 246 |
| --- www/customskin.md | |
| +++ www/customskin.md | |
| @@ -233,10 +233,13 @@ | |
| 233 | * **current_page** - The name of the page currently being processed, |
| 234 | without the leading "/" and without query parameters. |
| 235 | Examples: "timeline", "doc/trunk/README.txt", "wiki". |
| 236 | |
| 237 | * **csrf_token** - A token used to prevent cross-site request forgery. |
| 238 | |
| 239 | * **release_version** - The release version of Fossil. Ex: "1.31" |
| 240 | |
| 241 | * **manifest_version** - A prefix on the check-in hash of the |
| 242 | specific version of fossil that is running. Ex: "\[47bb6432a1\]" |
| 243 |
| --- www/customskin.md | |
| +++ www/customskin.md | |
| @@ -233,10 +233,13 @@ | |
| 233 | * **current_page** - The name of the page currently being processed, |
| 234 | without the leading "/" and without query parameters. |
| 235 | Examples: "timeline", "doc/trunk/README.txt", "wiki". |
| 236 | |
| 237 | * **csrf_token** - A token used to prevent cross-site request forgery. |
| 238 | |
| 239 | * **default_csp** - The content to be used within the default header |
| 240 | for the "Content-Security-Policy" meta tag. |
| 241 | |
| 242 | * **release_version** - The release version of Fossil. Ex: "1.31" |
| 243 | |
| 244 | * **manifest_version** - A prefix on the check-in hash of the |
| 245 | specific version of fossil that is running. Ex: "\[47bb6432a1\]" |
| 246 |
+8
| --- www/th1.md | ||
| +++ www/th1.md | ||
| @@ -187,10 +187,11 @@ | ||
| 187 | 187 | * http |
| 188 | 188 | * httpize |
| 189 | 189 | * insertCsrf |
| 190 | 190 | * linecount |
| 191 | 191 | * markdown |
| 192 | + * nonce | |
| 192 | 193 | * puts |
| 193 | 194 | * query |
| 194 | 195 | * randhex |
| 195 | 196 | * redirect |
| 196 | 197 | * regexp |
| @@ -453,10 +454,17 @@ | ||
| 453 | 454 | |
| 454 | 455 | Renders the input string as markdown. The result is a two-element list. |
| 455 | 456 | The first element contains the body, rendered as HTML. The second element |
| 456 | 457 | is the text-only title string. |
| 457 | 458 | |
| 459 | +<a name="nonce"></a>TH1 nonce Command | |
| 460 | +------------------------------------- | |
| 461 | + | |
| 462 | + * nonce | |
| 463 | + | |
| 464 | +Returns the value of the cryptographic nonce for the request being processed. | |
| 465 | + | |
| 458 | 466 | <a name="puts"></a>TH1 puts Command |
| 459 | 467 | ----------------------------------- |
| 460 | 468 | |
| 461 | 469 | * puts STRING |
| 462 | 470 | |
| 463 | 471 |
| --- www/th1.md | |
| +++ www/th1.md | |
| @@ -187,10 +187,11 @@ | |
| 187 | * http |
| 188 | * httpize |
| 189 | * insertCsrf |
| 190 | * linecount |
| 191 | * markdown |
| 192 | * puts |
| 193 | * query |
| 194 | * randhex |
| 195 | * redirect |
| 196 | * regexp |
| @@ -453,10 +454,17 @@ | |
| 453 | |
| 454 | Renders the input string as markdown. The result is a two-element list. |
| 455 | The first element contains the body, rendered as HTML. The second element |
| 456 | is the text-only title string. |
| 457 | |
| 458 | <a name="puts"></a>TH1 puts Command |
| 459 | ----------------------------------- |
| 460 | |
| 461 | * puts STRING |
| 462 | |
| 463 |
| --- www/th1.md | |
| +++ www/th1.md | |
| @@ -187,10 +187,11 @@ | |
| 187 | * http |
| 188 | * httpize |
| 189 | * insertCsrf |
| 190 | * linecount |
| 191 | * markdown |
| 192 | * nonce |
| 193 | * puts |
| 194 | * query |
| 195 | * randhex |
| 196 | * redirect |
| 197 | * regexp |
| @@ -453,10 +454,17 @@ | |
| 454 | |
| 455 | Renders the input string as markdown. The result is a two-element list. |
| 456 | The first element contains the body, rendered as HTML. The second element |
| 457 | is the text-only title string. |
| 458 | |
| 459 | <a name="nonce"></a>TH1 nonce Command |
| 460 | ------------------------------------- |
| 461 | |
| 462 | * nonce |
| 463 | |
| 464 | Returns the value of the cryptographic nonce for the request being processed. |
| 465 | |
| 466 | <a name="puts"></a>TH1 puts Command |
| 467 | ----------------------------------- |
| 468 | |
| 469 | * puts STRING |
| 470 | |
| 471 |