| | @@ -86,10 +86,16 @@ |
| 86 | 86 | */ |
| 87 | 87 | static int needHrefJs = 0; /* href.js */ |
| 88 | 88 | static int needSortJs = 0; /* sorttable.js */ |
| 89 | 89 | static int needGraphJs = 0; /* graph.js */ |
| 90 | 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 | + |
| 91 | 97 | /* |
| 92 | 98 | ** Generate and return a anchor tag like this: |
| 93 | 99 | ** |
| 94 | 100 | ** <a href="URL"> |
| 95 | 101 | ** or <a id="ID"> |
| | @@ -360,10 +366,24 @@ |
| 360 | 366 | char *zConfigName = mprintf("%s-image", zImageName); |
| 361 | 367 | url_var(zVarPrefix, zConfigName, zImageName); |
| 362 | 368 | free(zVarPrefix); |
| 363 | 369 | free(zConfigName); |
| 364 | 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 | +} |
| 365 | 385 | |
| 366 | 386 | /* |
| 367 | 387 | ** Default HTML page header text through <body>. If the repository-specific |
| 368 | 388 | ** header template lacks a <body> tag, then all of the following is |
| 369 | 389 | ** prepended. |
| | @@ -371,11 +391,13 @@ |
| 371 | 391 | static char zDfltHeader[] = |
| 372 | 392 | @ <html> |
| 373 | 393 | @ <head> |
| 374 | 394 | @ <base href="$baseurl/$current_page" /> |
| 375 | 395 | @ <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'" /> |
| 377 | 399 | @ <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 378 | 400 | @ <title>$<project_name>: $<title></title> |
| 379 | 401 | @ <link rel="alternate" type="application/rss+xml" title="RSS Feed" \ |
| 380 | 402 | @ href="$home/timeline.rss" /> |
| 381 | 403 | @ <link rel="stylesheet" href="$stylesheet_url" type="text/css" \ |
| | @@ -402,10 +424,11 @@ |
| 402 | 424 | @ <!DOCTYPE html> |
| 403 | 425 | |
| 404 | 426 | if( g.thTrace ) Th_Trace("BEGIN_HEADER<br />\n", -1); |
| 405 | 427 | |
| 406 | 428 | /* Generate the header up through the main menu */ |
| 429 | + Th_Store("nonce", style_nonce()); |
| 407 | 430 | Th_Store("project_name", db_get("project-name","Unnamed Fossil Project")); |
| 408 | 431 | Th_Store("project_description", db_get("project-description","")); |
| 409 | 432 | Th_Store("title", zTitle); |
| 410 | 433 | Th_Store("baseurl", g.zBaseURL); |
| 411 | 434 | Th_Store("secureurl", login_wants_https_redirect()? g.zHttpsURL: g.zBaseURL); |
| | @@ -523,11 +546,11 @@ |
| 523 | 546 | int i; |
| 524 | 547 | for(i=0; i<nJsToLoad; i++){ |
| 525 | 548 | if( fossil_strcmp(zName, azJsToLoad[i])==0 ) return; |
| 526 | 549 | } |
| 527 | 550 | if( nJsToLoad>=sizeof(azJsToLoad)/sizeof(azJsToLoad[0]) ){ |
| 528 | | - fossil_panic("too man JS files"); |
| 551 | + fossil_panic("too many JS files"); |
| 529 | 552 | } |
| 530 | 553 | azJsToLoad[nJsToLoad++] = zName; |
| 531 | 554 | } |
| 532 | 555 | |
| 533 | 556 | /* |
| | @@ -541,21 +564,40 @@ |
| 541 | 564 | /* Load up the page data */ |
| 542 | 565 | bMouseover = (!g.isHuman || db_get_boolean("auto-hyperlink-ishuman",0)) |
| 543 | 566 | && db_get_boolean("auto-hyperlink-mouseover",0); |
| 544 | 567 | @ <script id='href-data' type='application/json'>\ |
| 545 | 568 | @ {"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); |
| 547 | 573 | } |
| 548 | 574 | if( needSortJs ){ |
| 549 | | - style_load_one_js_file("sorttable.js"); |
| 575 | + cgi_append_content(builtin_text("sorttable.js"),-1); |
| 550 | 576 | } |
| 551 | 577 | if( needGraphJs ){ |
| 552 | | - style_load_one_js_file("graph.js"); |
| 578 | + cgi_append_content(builtin_text("graph.js"),-1); |
| 553 | 579 | } |
| 554 | 580 | 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); |
| 556 | 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); |
| 557 | 599 | } |
| 558 | 600 | |
| 559 | 601 | /* |
| 560 | 602 | ** Draw the footer at the bottom of the page. |
| 561 | 603 | */ |
| 562 | 604 | |