Fossil SCM

Put all javascript inline using a nonce. Disallow 'unsafe-inline' CSP for javascript.

drh 2018-08-23 02:57 trunk
Commit 89c40851f08a77e67bb6d1ca5e673783fc1d8935a6858db757d8f4dd2057842f
2 files changed +1 +48 -6
--- src/codecheck1.c
+++ src/codecheck1.c
@@ -398,10 +398,11 @@
398398
{ "json_warn", 2, 0 },
399399
{ "mprintf", 1, 0 },
400400
{ "socket_set_errmsg", 1, 0 },
401401
{ "ssl_set_errmsg", 1, 0 },
402402
{ "style_header", 1, FMT_HTML },
403
+ { "style_js_onload", 1, FMT_HTML },
403404
{ "style_set_current_page", 1, FMT_URL },
404405
{ "style_submenu_element", 2, FMT_URL },
405406
{ "style_submenu_sql", 3, FMT_SQL },
406407
{ "webpage_error", 1, FMT_SAFE },
407408
{ "xhref", 2, FMT_URL },
408409
--- src/codecheck1.c
+++ src/codecheck1.c
@@ -398,10 +398,11 @@
398 { "json_warn", 2, 0 },
399 { "mprintf", 1, 0 },
400 { "socket_set_errmsg", 1, 0 },
401 { "ssl_set_errmsg", 1, 0 },
402 { "style_header", 1, FMT_HTML },
 
403 { "style_set_current_page", 1, FMT_URL },
404 { "style_submenu_element", 2, FMT_URL },
405 { "style_submenu_sql", 3, FMT_SQL },
406 { "webpage_error", 1, FMT_SAFE },
407 { "xhref", 2, FMT_URL },
408
--- src/codecheck1.c
+++ src/codecheck1.c
@@ -398,10 +398,11 @@
398 { "json_warn", 2, 0 },
399 { "mprintf", 1, 0 },
400 { "socket_set_errmsg", 1, 0 },
401 { "ssl_set_errmsg", 1, 0 },
402 { "style_header", 1, FMT_HTML },
403 { "style_js_onload", 1, FMT_HTML },
404 { "style_set_current_page", 1, FMT_URL },
405 { "style_submenu_element", 2, FMT_URL },
406 { "style_submenu_sql", 3, FMT_SQL },
407 { "webpage_error", 1, FMT_SAFE },
408 { "xhref", 2, FMT_URL },
409
+48 -6
--- src/style.c
+++ src/style.c
@@ -86,10 +86,16 @@
8686
*/
8787
static int needHrefJs = 0; /* href.js */
8888
static int needSortJs = 0; /* sorttable.js */
8989
static int needGraphJs = 0; /* graph.js */
9090
91
+/*
92
+** Extra JS added to the end of the file.
93
+*/
94
+static Blob blobJs = BLOB_INITIALIZER;
95
+static Blob blobOnLoad = BLOB_INITIALIZER;
96
+
9197
/*
9298
** Generate and return a anchor tag like this:
9399
**
94100
** <a href="URL">
95101
** or <a id="ID">
@@ -360,10 +366,24 @@
360366
char *zConfigName = mprintf("%s-image", zImageName);
361367
url_var(zVarPrefix, zConfigName, zImageName);
362368
free(zVarPrefix);
363369
free(zConfigName);
364370
}
371
+
372
+/*
373
+** Return a random nonce that is stored in static space. For a particular
374
+** run, the same nonce is always returned.
375
+*/
376
+char *style_nonce(void){
377
+ static char zNonce[52];
378
+ if( zNonce[0]==0 ){
379
+ unsigned char zSeed[24];
380
+ sqlite3_randomness(24, zSeed);
381
+ encode16(zSeed,(unsigned char*)zNonce,24);
382
+ }
383
+ return zNonce;
384
+}
365385
366386
/*
367387
** Default HTML page header text through <body>. If the repository-specific
368388
** header template lacks a <body> tag, then all of the following is
369389
** prepended.
@@ -371,11 +391,13 @@
371391
static char zDfltHeader[] =
372392
@ <html>
373393
@ <head>
374394
@ <base href="$baseurl/$current_page" />
375395
@ <meta http-equiv="Content-Security-Policy" \
376
-@ content="default-src 'self' data: 'unsafe-inline'" />
396
+@ content="default-src 'self' data: ; \
397
+@ script-src 'self' 'nonce-$<nonce>' ;\
398
+@ style-src 'self' 'unsafe-inline'" />
377399
@ <meta name="viewport" content="width=device-width, initial-scale=1.0">
378400
@ <title>$<project_name>: $<title></title>
379401
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed" \
380402
@ href="$home/timeline.rss" />
381403
@ <link rel="stylesheet" href="$stylesheet_url" type="text/css" \
@@ -402,10 +424,11 @@
402424
@ <!DOCTYPE html>
403425
404426
if( g.thTrace ) Th_Trace("BEGIN_HEADER<br />\n", -1);
405427
406428
/* Generate the header up through the main menu */
429
+ Th_Store("nonce", style_nonce());
407430
Th_Store("project_name", db_get("project-name","Unnamed Fossil Project"));
408431
Th_Store("project_description", db_get("project-description",""));
409432
Th_Store("title", zTitle);
410433
Th_Store("baseurl", g.zBaseURL);
411434
Th_Store("secureurl", login_wants_https_redirect()? g.zHttpsURL: g.zBaseURL);
@@ -523,11 +546,11 @@
523546
int i;
524547
for(i=0; i<nJsToLoad; i++){
525548
if( fossil_strcmp(zName, azJsToLoad[i])==0 ) return;
526549
}
527550
if( nJsToLoad>=sizeof(azJsToLoad)/sizeof(azJsToLoad[0]) ){
528
- fossil_panic("too man JS files");
551
+ fossil_panic("too many JS files");
529552
}
530553
azJsToLoad[nJsToLoad++] = zName;
531554
}
532555
533556
/*
@@ -541,21 +564,40 @@
541564
/* Load up the page data */
542565
bMouseover = (!g.isHuman || db_get_boolean("auto-hyperlink-ishuman",0))
543566
&& db_get_boolean("auto-hyperlink-mouseover",0);
544567
@ <script id='href-data' type='application/json'>\
545568
@ {"delay":%d(nDelay),"mouseover":%d(bMouseover)}</script>
546
- style_load_one_js_file("href.js");
569
+ }
570
+ @ <script nonce="%h(style_nonce())">
571
+ if( needHrefJs ){
572
+ cgi_append_content(builtin_text("href.js"),-1);
547573
}
548574
if( needSortJs ){
549
- style_load_one_js_file("sorttable.js");
575
+ cgi_append_content(builtin_text("sorttable.js"),-1);
550576
}
551577
if( needGraphJs ){
552
- style_load_one_js_file("graph.js");
578
+ cgi_append_content(builtin_text("graph.js"),-1);
553579
}
554580
for(i=0; i<nJsToLoad; i++){
555
- style_load_one_js_file(azJsToLoad[i]);
581
+ cgi_append_content(builtin_text(azJsToLoad[i]),-1);
582
+ }
583
+ if( blob_size(&blobOnLoad)>0 ){
584
+ @ window.onload = function(){
585
+ cgi_append_content(blob_buffer(&blobOnLoad), blob_size(&blobOnLoad));
586
+ cgi_append_content("\n}\n", -1);
556587
}
588
+ @ </script>
589
+}
590
+
591
+/*
592
+** Extra JS to run after all content is loaded.
593
+*/
594
+void style_js_onload(const char *zFormat, ...){
595
+ va_list ap;
596
+ va_start(ap, zFormat);
597
+ blob_vappendf(&blobOnLoad, zFormat, ap);
598
+ va_end(ap);
557599
}
558600
559601
/*
560602
** Draw the footer at the bottom of the page.
561603
*/
562604
--- src/style.c
+++ src/style.c
@@ -86,10 +86,16 @@
86 */
87 static int needHrefJs = 0; /* href.js */
88 static int needSortJs = 0; /* sorttable.js */
89 static int needGraphJs = 0; /* graph.js */
90
 
 
 
 
 
 
91 /*
92 ** Generate and return a anchor tag like this:
93 **
94 ** <a href="URL">
95 ** or <a id="ID">
@@ -360,10 +366,24 @@
360 char *zConfigName = mprintf("%s-image", zImageName);
361 url_var(zVarPrefix, zConfigName, zImageName);
362 free(zVarPrefix);
363 free(zConfigName);
364 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
365
366 /*
367 ** Default HTML page header text through <body>. If the repository-specific
368 ** header template lacks a <body> tag, then all of the following is
369 ** prepended.
@@ -371,11 +391,13 @@
371 static char zDfltHeader[] =
372 @ <html>
373 @ <head>
374 @ <base href="$baseurl/$current_page" />
375 @ <meta http-equiv="Content-Security-Policy" \
376 @ content="default-src 'self' data: 'unsafe-inline'" />
 
 
377 @ <meta name="viewport" content="width=device-width, initial-scale=1.0">
378 @ <title>$<project_name>: $<title></title>
379 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" \
380 @ href="$home/timeline.rss" />
381 @ <link rel="stylesheet" href="$stylesheet_url" type="text/css" \
@@ -402,10 +424,11 @@
402 @ <!DOCTYPE html>
403
404 if( g.thTrace ) Th_Trace("BEGIN_HEADER<br />\n", -1);
405
406 /* Generate the header up through the main menu */
 
407 Th_Store("project_name", db_get("project-name","Unnamed Fossil Project"));
408 Th_Store("project_description", db_get("project-description",""));
409 Th_Store("title", zTitle);
410 Th_Store("baseurl", g.zBaseURL);
411 Th_Store("secureurl", login_wants_https_redirect()? g.zHttpsURL: g.zBaseURL);
@@ -523,11 +546,11 @@
523 int i;
524 for(i=0; i<nJsToLoad; i++){
525 if( fossil_strcmp(zName, azJsToLoad[i])==0 ) return;
526 }
527 if( nJsToLoad>=sizeof(azJsToLoad)/sizeof(azJsToLoad[0]) ){
528 fossil_panic("too man JS files");
529 }
530 azJsToLoad[nJsToLoad++] = zName;
531 }
532
533 /*
@@ -541,21 +564,40 @@
541 /* Load up the page data */
542 bMouseover = (!g.isHuman || db_get_boolean("auto-hyperlink-ishuman",0))
543 && db_get_boolean("auto-hyperlink-mouseover",0);
544 @ <script id='href-data' type='application/json'>\
545 @ {"delay":%d(nDelay),"mouseover":%d(bMouseover)}</script>
546 style_load_one_js_file("href.js");
 
 
 
547 }
548 if( needSortJs ){
549 style_load_one_js_file("sorttable.js");
550 }
551 if( needGraphJs ){
552 style_load_one_js_file("graph.js");
553 }
554 for(i=0; i<nJsToLoad; i++){
555 style_load_one_js_file(azJsToLoad[i]);
 
 
 
 
 
556 }
 
 
 
 
 
 
 
 
 
 
 
557 }
558
559 /*
560 ** Draw the footer at the bottom of the page.
561 */
562
--- src/style.c
+++ src/style.c
@@ -86,10 +86,16 @@
86 */
87 static int needHrefJs = 0; /* href.js */
88 static int needSortJs = 0; /* sorttable.js */
89 static int needGraphJs = 0; /* graph.js */
90
91 /*
92 ** Extra JS added to the end of the file.
93 */
94 static Blob blobJs = BLOB_INITIALIZER;
95 static Blob blobOnLoad = BLOB_INITIALIZER;
96
97 /*
98 ** Generate and return a anchor tag like this:
99 **
100 ** <a href="URL">
101 ** or <a id="ID">
@@ -360,10 +366,24 @@
366 char *zConfigName = mprintf("%s-image", zImageName);
367 url_var(zVarPrefix, zConfigName, zImageName);
368 free(zVarPrefix);
369 free(zConfigName);
370 }
371
372 /*
373 ** Return a random nonce that is stored in static space. For a particular
374 ** run, the same nonce is always returned.
375 */
376 char *style_nonce(void){
377 static char zNonce[52];
378 if( zNonce[0]==0 ){
379 unsigned char zSeed[24];
380 sqlite3_randomness(24, zSeed);
381 encode16(zSeed,(unsigned char*)zNonce,24);
382 }
383 return zNonce;
384 }
385
386 /*
387 ** Default HTML page header text through <body>. If the repository-specific
388 ** header template lacks a <body> tag, then all of the following is
389 ** prepended.
@@ -371,11 +391,13 @@
391 static char zDfltHeader[] =
392 @ <html>
393 @ <head>
394 @ <base href="$baseurl/$current_page" />
395 @ <meta http-equiv="Content-Security-Policy" \
396 @ content="default-src 'self' data: ; \
397 @ script-src 'self' 'nonce-$<nonce>' ;\
398 @ style-src 'self' 'unsafe-inline'" />
399 @ <meta name="viewport" content="width=device-width, initial-scale=1.0">
400 @ <title>$<project_name>: $<title></title>
401 @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" \
402 @ href="$home/timeline.rss" />
403 @ <link rel="stylesheet" href="$stylesheet_url" type="text/css" \
@@ -402,10 +424,11 @@
424 @ <!DOCTYPE html>
425
426 if( g.thTrace ) Th_Trace("BEGIN_HEADER<br />\n", -1);
427
428 /* Generate the header up through the main menu */
429 Th_Store("nonce", style_nonce());
430 Th_Store("project_name", db_get("project-name","Unnamed Fossil Project"));
431 Th_Store("project_description", db_get("project-description",""));
432 Th_Store("title", zTitle);
433 Th_Store("baseurl", g.zBaseURL);
434 Th_Store("secureurl", login_wants_https_redirect()? g.zHttpsURL: g.zBaseURL);
@@ -523,11 +546,11 @@
546 int i;
547 for(i=0; i<nJsToLoad; i++){
548 if( fossil_strcmp(zName, azJsToLoad[i])==0 ) return;
549 }
550 if( nJsToLoad>=sizeof(azJsToLoad)/sizeof(azJsToLoad[0]) ){
551 fossil_panic("too many JS files");
552 }
553 azJsToLoad[nJsToLoad++] = zName;
554 }
555
556 /*
@@ -541,21 +564,40 @@
564 /* Load up the page data */
565 bMouseover = (!g.isHuman || db_get_boolean("auto-hyperlink-ishuman",0))
566 && db_get_boolean("auto-hyperlink-mouseover",0);
567 @ <script id='href-data' type='application/json'>\
568 @ {"delay":%d(nDelay),"mouseover":%d(bMouseover)}</script>
569 }
570 @ <script nonce="%h(style_nonce())">
571 if( needHrefJs ){
572 cgi_append_content(builtin_text("href.js"),-1);
573 }
574 if( needSortJs ){
575 cgi_append_content(builtin_text("sorttable.js"),-1);
576 }
577 if( needGraphJs ){
578 cgi_append_content(builtin_text("graph.js"),-1);
579 }
580 for(i=0; i<nJsToLoad; i++){
581 cgi_append_content(builtin_text(azJsToLoad[i]),-1);
582 }
583 if( blob_size(&blobOnLoad)>0 ){
584 @ window.onload = function(){
585 cgi_append_content(blob_buffer(&blobOnLoad), blob_size(&blobOnLoad));
586 cgi_append_content("\n}\n", -1);
587 }
588 @ </script>
589 }
590
591 /*
592 ** Extra JS to run after all content is loaded.
593 */
594 void style_js_onload(const char *zFormat, ...){
595 va_list ap;
596 va_start(ap, zFormat);
597 blob_vappendf(&blobOnLoad, zFormat, ap);
598 va_end(ap);
599 }
600
601 /*
602 ** Draw the footer at the bottom of the page.
603 */
604

Keyboard Shortcuts

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