Fossil

Check-in [8a65cd18]
Login

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

Overview
Comment: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.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 8a65cd1831a5c2229aa77bdbd37e0b734d863840f8d3e8626e6d9820ca23d9bc
User & Date: mistachkin 2019-02-18 19:09:48.596
Context
2019-02-18
19:17
Include 'nonce' TH1 variable in the custom skin documentation. ... (check-in: 02367341 user: mistachkin tags: trunk)
19:09
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. ... (check-in: 8a65cd18 user: mistachkin tags: trunk)
2019-02-17
07:47
Make sure the TH1 interp is available before trying to use it. ... (Closed-Leaf check-in: 600accbe user: mistachkin tags: dynamicCsp)
2019-02-15
21:20
Show the SCGI environment variable value on the /test_env page if that variable exists. ... (check-in: f59faedb user: drh tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/style.c.
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411












412
413
414
415
416
417
418
419
** header template lacks a <body> tag, then all of the following is
** prepended.
*/
static char zDfltHeader[] = 
@ <html>
@ <head>
@ <base href="$baseurl/$current_page" />
@ <meta http-equiv="Content-Security-Policy" \
@  content="default-src 'self' data: ; \
@  script-src 'self' 'nonce-$<nonce>' ;\
@  style-src 'self' 'unsafe-inline'" />
@ <meta name="viewport" content="width=device-width, initial-scale=1.0">
@ <title>$<project_name>: $<title></title>
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed" \
@  href="$home/timeline.rss" />
@ <link rel="stylesheet" href="$stylesheet_url" type="text/css" \
@  media="screen" />
@ </head>
@ <body>
;

/*
** Initialize all the default TH1 variables
*/
static void style_init_th1_vars(const char *zTitle){












  Th_Store("nonce", style_nonce());
  Th_Store("project_name", db_get("project-name","Unnamed Fossil Project"));
  Th_Store("project_description", db_get("project-description",""));
  if( zTitle ) Th_Store("title", zTitle);
  Th_Store("baseurl", g.zBaseURL);
  Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL);
  Th_Store("home", g.zTop);
  Th_Store("index_page", db_get("index-page","/home"));







|
<
<
<














>
>
>
>
>
>
>
>
>
>
>
>
|







387
388
389
390
391
392
393
394



395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
** header template lacks a <body> tag, then all of the following is
** prepended.
*/
static char zDfltHeader[] = 
@ <html>
@ <head>
@ <base href="$baseurl/$current_page" />
@ <meta http-equiv="Content-Security-Policy" content="$default_csp" />



@ <meta name="viewport" content="width=device-width, initial-scale=1.0">
@ <title>$<project_name>: $<title></title>
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed" \
@  href="$home/timeline.rss" />
@ <link rel="stylesheet" href="$stylesheet_url" type="text/css" \
@  media="screen" />
@ </head>
@ <body>
;

/*
** Initialize all the default TH1 variables
*/
static void style_init_th1_vars(const char *zTitle){
  const char *zNonce = style_nonce();
  /*
  ** Do not overwrite the TH1 variable "default_csp" if it exists, as this
  ** allows it to be properly overridden via the TH1 setup script (i.e. it
  ** is evaluated before the header is rendered).
  */
  char *zDfltCsp = sqlite3_mprintf("default-src 'self' data: ; "
                                   "script-src 'self' 'nonce-%s' ; "
                                   "style-src 'self' 'unsafe-inline'",
                                   zNonce);
  Th_MaybeStore("default_csp", zDfltCsp);
  sqlite3_free(zDfltCsp);
  Th_Store("nonce", zNonce);
  Th_Store("project_name", db_get("project-name","Unnamed Fossil Project"));
  Th_Store("project_description", db_get("project-description",""));
  if( zTitle ) Th_Store("title", zTitle);
  Th_Store("baseurl", g.zBaseURL);
  Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL);
  Th_Store("home", g.zTop);
  Th_Store("index_page", db_get("index-page","/home"));
Changes to src/th_main.c.
409
410
411
412
413
414
415




















416
417
418
419
420
421
422
      manifest_destroy(pManifest);
      return rid;
    }
  }
  Th_SetResult(interp, "file name not found in manifest", -1);
  return 0;
}





















/*
** TH1 command: puts STRING
** TH1 command: html STRING
**
** Output STRING escaped for HTML (puts) or unchanged (html).
*/







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







409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
      manifest_destroy(pManifest);
      return rid;
    }
  }
  Th_SetResult(interp, "file name not found in manifest", -1);
  return 0;
}

/*
** TH1 command: nonce
**
** Returns the value of the cryptographic nonce for the request being
** processed.
*/
static int nonceCmd(
  Th_Interp *interp,
  void *pConvert,
  int argc,
  const char **argv,
  int *argl
){
  if( argc!=1 ){
    return Th_WrongNumArgs(interp, "nonce");
  }
  Th_SetResult(interp, style_nonce(), -1);
  return TH_OK;
}

/*
** TH1 command: puts STRING
** TH1 command: html STRING
**
** Output STRING escaped for HTML (puts) or unchanged (html).
*/
2019
2020
2021
2022
2023
2024
2025

2026
2027
2028
2029
2030
2031
2032
    {"hasfeature",    hasfeatureCmd,        0},
    {"html",          putsCmd,              (void*)&aFlags[0]},
    {"htmlize",       htmlizeCmd,           0},
    {"http",          httpCmd,              0},
    {"insertCsrf",    insertCsrfCmd,        0},
    {"linecount",     linecntCmd,           0},
    {"markdown",      markdownCmd,          0},

    {"puts",          putsCmd,              (void*)&aFlags[1]},
    {"query",         queryCmd,             0},
    {"randhex",       randhexCmd,           0},
    {"redirect",      redirectCmd,          0},
    {"regexp",        regexpCmd,            0},
    {"reinitialize",  reinitializeCmd,      0},
    {"render",        renderCmd,            0},







>







2039
2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
    {"hasfeature",    hasfeatureCmd,        0},
    {"html",          putsCmd,              (void*)&aFlags[0]},
    {"htmlize",       htmlizeCmd,           0},
    {"http",          httpCmd,              0},
    {"insertCsrf",    insertCsrfCmd,        0},
    {"linecount",     linecntCmd,           0},
    {"markdown",      markdownCmd,          0},
    {"nonce",         nonceCmd,             0},
    {"puts",          putsCmd,              (void*)&aFlags[1]},
    {"query",         queryCmd,             0},
    {"randhex",       randhexCmd,           0},
    {"redirect",      redirectCmd,          0},
    {"regexp",        regexpCmd,            0},
    {"reinitialize",  reinitializeCmd,      0},
    {"render",        renderCmd,            0},
2102
2103
2104
2105
2106
2107
2108














2109
2110
2111
2112
2113
2114
2115
      Th_Trace("th1-setup {%h} => %h<br />\n", g.th1Setup,
               Th_ReturnCodeName(rc, 0));
    }
  }
  g.th1Flags &= ~TH_INIT_MASK;
  g.th1Flags |= (flags & TH_INIT_MASK);
}















/*
** Store a string value in a variable in the interpreter.
*/
void Th_Store(const char *zName, const char *zValue){
  Th_FossilInit(TH_INIT_DEFAULT);
  if( zValue ){







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







2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
      Th_Trace("th1-setup {%h} => %h<br />\n", g.th1Setup,
               Th_ReturnCodeName(rc, 0));
    }
  }
  g.th1Flags &= ~TH_INIT_MASK;
  g.th1Flags |= (flags & TH_INIT_MASK);
}

/*
** Store a string value in a variable in the interpreter if the variable
** does not already exist.
*/
void Th_MaybeStore(const char *zName, const char *zValue){
  Th_FossilInit(TH_INIT_DEFAULT);
  if( zValue && !Th_ExistsVar(g.interp, zName, -1) ){
    if( g.thTrace ){
      Th_Trace("maybe_set %h {%h}<br />\n", zName, zValue);
    }
    Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
  }
}

/*
** Store a string value in a variable in the interpreter.
*/
void Th_Store(const char *zName, const char *zValue){
  Th_FossilInit(TH_INIT_DEFAULT);
  if( zValue ){
Changes to test/th1.test.
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
fossil test-th-eval "info commands"
set sorted_result [lsort $RESULT]
protOut "Sorted: $sorted_result"
set base_commands {anoncap anycap array artifact break breakpoint catch\
      cgiHeaderLine checkout combobox continue date decorate dir enable_output \
      encode64 error expr for getParameter glob_match globalState hascap \
      hasfeature html htmlize http httpize if info insertCsrf lindex linecount \
      list llength lsearch markdown proc puts query randhex redirect regexp\
      reinitialize rename render repository return searchable set\
      setParameter setting stime string styleFooter styleHeader styleScript\
      tclReady trace unset unversioned uplevel upvar utime verifyCsrf wiki}
set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe}
if {$th1Tcl} {
  test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]}
} else {
  test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]}
}


###############################################################################

fossil test-th-eval "info vars"

if {$th1Hooks} {
  test th1-info-vars-1 {[lsort $RESULT] eq \







|
|








<







1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046

1047
1048
1049
1050
1051
1052
1053
fossil test-th-eval "info commands"
set sorted_result [lsort $RESULT]
protOut "Sorted: $sorted_result"
set base_commands {anoncap anycap array artifact break breakpoint catch\
      cgiHeaderLine checkout combobox continue date decorate dir enable_output \
      encode64 error expr for getParameter glob_match globalState hascap \
      hasfeature html htmlize http httpize if info insertCsrf lindex linecount \
      list llength lsearch markdown nonce proc puts query randhex redirect\
      regexp reinitialize rename render repository return searchable set\
      setParameter setting stime string styleFooter styleHeader styleScript\
      tclReady trace unset unversioned uplevel upvar utime verifyCsrf wiki}
set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe}
if {$th1Tcl} {
  test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]}
} else {
  test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]}
}


###############################################################################

fossil test-th-eval "info vars"

if {$th1Hooks} {
  test th1-info-vars-1 {[lsort $RESULT] eq \
Changes to www/customskin.md.
231
232
233
234
235
236
237



238
239
240
241
242
243
244
       specified by the Admin/Configuration setup page.

   *   **current_page** - The name of the page currently being processed,
       without the leading "/" and without query parameters.
       Examples:  "timeline", "doc/trunk/README.txt", "wiki".

   *   **csrf_token** - A token used to prevent cross-site request forgery.




   *   **release_version** - The release version of Fossil.  Ex: "1.31"

   *   **manifest_version** - A prefix on the check-in hash of the
       specific version of fossil that is running.  Ex: "\[47bb6432a1\]"

   *   **manifest_date** - The date of the source-code check-in for the







>
>
>







231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
       specified by the Admin/Configuration setup page.

   *   **current_page** - The name of the page currently being processed,
       without the leading "/" and without query parameters.
       Examples:  "timeline", "doc/trunk/README.txt", "wiki".

   *   **csrf_token** - A token used to prevent cross-site request forgery.

   *   **default_csp** - The content to be used within the default header
       for the "Content-Security-Policy" meta tag.

   *   **release_version** - The release version of Fossil.  Ex: "1.31"

   *   **manifest_version** - A prefix on the check-in hash of the
       specific version of fossil that is running.  Ex: "\[47bb6432a1\]"

   *   **manifest_date** - The date of the source-code check-in for the
Changes to www/th1.md.
185
186
187
188
189
190
191

192
193
194
195
196
197
198
  *  html
  *  htmlize
  *  http
  *  httpize
  *  insertCsrf
  *  linecount
  *  markdown

  *  puts
  *  query
  *  randhex
  *  redirect
  *  regexp
  *  reinitialize
  *  render







>







185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
  *  html
  *  htmlize
  *  http
  *  httpize
  *  insertCsrf
  *  linecount
  *  markdown
  *  nonce
  *  puts
  *  query
  *  randhex
  *  redirect
  *  regexp
  *  reinitialize
  *  render
451
452
453
454
455
456
457







458
459
460
461
462
463
464

  *  markdown STRING

Renders the input string as markdown.  The result is a two-element list.
The first element contains the body, rendered as HTML.  The second element
is the text-only title string.








<a name="puts"></a>TH1 puts Command
-----------------------------------

  *  puts STRING

Outputs the STRING unchanged.








>
>
>
>
>
>
>







452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472

  *  markdown STRING

Renders the input string as markdown.  The result is a two-element list.
The first element contains the body, rendered as HTML.  The second element
is the text-only title string.

<a name="nonce"></a>TH1 nonce Command
-------------------------------------

  *  nonce

Returns the value of the cryptographic nonce for the request being processed.

<a name="puts"></a>TH1 puts Command
-----------------------------------

  *  puts STRING

Outputs the STRING unchanged.