Fossil SCM
Major improvements to the new defcsp.md article. Expanded the introductory material to better describe what the CSP does; added named anchors to headers; moved the discussion of $default_csp overrides into this document from customskin.md, which now just says how you use that variable read-only; and added an entirely new section, "Replacing the Default CSP".
Commit
366b23a180f7734c244b57be240bde233cf9f7f694ff46b9827a054c5d23a1ba
Parent
14ac2cacdd93ced…
2 files changed
+4
-9
+111
-11
+4
-9
| --- www/customskin.md | ||
| +++ www/customskin.md | ||
| @@ -237,19 +237,14 @@ | ||
| 237 | 237 | without the leading "/" and without query parameters. |
| 238 | 238 | Examples: "timeline", "doc/trunk/README.txt", "wiki". |
| 239 | 239 | |
| 240 | 240 | * **csrf_token** - A token used to prevent cross-site request forgery. |
| 241 | 241 | |
| 242 | - * **default_csp** - The content to be used within the default header | |
| 243 | - for the "Content-Security-Policy" `<meta>` tag. You can use | |
| 244 | - [Fossil’s default CSP](./defcsp.md) from this variable in your | |
| 245 | - [own custom `<head>`](#headfoot). Alternately, you can override | |
| 246 | - the default CSP from the [`th1-setup` script](./th1-hooks.md), | |
| 247 | - which runs before TH1 processing happens during skin processing, | |
| 248 | - allowing you to set a custom CSP without recompiling Fossil or | |
| 249 | - providing a hand-written `<head>` section in the Header section | |
| 250 | - of a custom skin. | |
| 242 | + * **default_csp** - [Fossil’s default CSP](./defcsp.md) unless | |
| 243 | + [overridden by custom TH1 code](./defcsp.md#th1). Useful within | |
| 244 | + the skin for inserting the CSP into a `<meta>` tag within [a | |
| 245 | + custom `<head>` element](#headfoot). | |
| 251 | 246 | |
| 252 | 247 | * **nonce** - The value of the cryptographic nonce for the request |
| 253 | 248 | being processed. |
| 254 | 249 | |
| 255 | 250 | * **release_version** - The release version of Fossil. Ex: "1.31" |
| 256 | 251 |
| --- www/customskin.md | |
| +++ www/customskin.md | |
| @@ -237,19 +237,14 @@ | |
| 237 | without the leading "/" and without query parameters. |
| 238 | Examples: "timeline", "doc/trunk/README.txt", "wiki". |
| 239 | |
| 240 | * **csrf_token** - A token used to prevent cross-site request forgery. |
| 241 | |
| 242 | * **default_csp** - The content to be used within the default header |
| 243 | for the "Content-Security-Policy" `<meta>` tag. You can use |
| 244 | [Fossil’s default CSP](./defcsp.md) from this variable in your |
| 245 | [own custom `<head>`](#headfoot). Alternately, you can override |
| 246 | the default CSP from the [`th1-setup` script](./th1-hooks.md), |
| 247 | which runs before TH1 processing happens during skin processing, |
| 248 | allowing you to set a custom CSP without recompiling Fossil or |
| 249 | providing a hand-written `<head>` section in the Header section |
| 250 | of a custom skin. |
| 251 | |
| 252 | * **nonce** - The value of the cryptographic nonce for the request |
| 253 | being processed. |
| 254 | |
| 255 | * **release_version** - The release version of Fossil. Ex: "1.31" |
| 256 |
| --- www/customskin.md | |
| +++ www/customskin.md | |
| @@ -237,19 +237,14 @@ | |
| 237 | without the leading "/" and without query parameters. |
| 238 | Examples: "timeline", "doc/trunk/README.txt", "wiki". |
| 239 | |
| 240 | * **csrf_token** - A token used to prevent cross-site request forgery. |
| 241 | |
| 242 | * **default_csp** - [Fossil’s default CSP](./defcsp.md) unless |
| 243 | [overridden by custom TH1 code](./defcsp.md#th1). Useful within |
| 244 | the skin for inserting the CSP into a `<meta>` tag within [a |
| 245 | custom `<head>` element](#headfoot). |
| 246 | |
| 247 | * **nonce** - The value of the cryptographic nonce for the request |
| 248 | being processed. |
| 249 | |
| 250 | * **release_version** - The release version of Fossil. Ex: "1.31" |
| 251 |
+111
-11
| --- www/defcsp.md | ||
| +++ www/defcsp.md | ||
| @@ -1,14 +1,38 @@ | ||
| 1 | 1 | # The Default Content Security Policy (CSP) |
| 2 | 2 | |
| 3 | -One of the most important things you have to know about the default | |
| 4 | -[Fossil-provided `<head>` text](./customskin.md#headfoot) is the | |
| 5 | -[Content Security Policy][csp] (CSP) it applies to your repository’s web | |
| 6 | -interface. The current version applies the following restrictions: | |
| 3 | +When Fossil’s web interface generates an HTML page, it | |
| 4 | +normally includes a [Content Security Policy][csp] (CSP) | |
| 5 | +which applies to all content on that page. The CSP tells the browser | |
| 6 | +which types of content (HTML, image, CSS, JavaScript...) the repository | |
| 7 | +is expected to serve, and thus which types of content a compliant | |
| 8 | +browser is allowed to pay attention to. All of the major browsers | |
| 9 | +enforce CSP restrictions. | |
| 10 | + | |
| 11 | +A CSP is an important security measure on any site where some of the | |
| 12 | +content it serves is user-provided. It defines a “white list” of content | |
| 13 | +types believed to be safe and desirable, so that if any content outside | |
| 14 | +of that definition gets inserted into the repository later, the browser | |
| 15 | +will treat that unwanted content as if it was not part of the document | |
| 16 | +at all. | |
| 17 | + | |
| 18 | +A CSP is a fairly blunt tool. It cannot tell good and visually appealing | |
| 19 | +enhancements from vandalism or outright attacks. All it does is tell the | |
| 20 | +browser what types and sources for page content were anticipated by the | |
| 21 | +site’s creators. The browser treats everything else as “unwanted” even | |
| 22 | +if that’s not actually true. When the CSP prevents the browser from | |
| 23 | +displaying wanted content, you then have to understand the current rules | |
| 24 | +and what they’re trying to accomplish to decide on [an appropriate | |
| 25 | +workaround](#override). | |
| 26 | + | |
| 27 | + | |
| 28 | +## The Default Restrictions | |
| 29 | + | |
| 30 | +The Fossil default CSP declares the following content restrictions: | |
| 7 | 31 | |
| 8 | 32 | |
| 9 | -## default-src 'self' data | |
| 33 | +### <a name="base"></a> default-src 'self' data | |
| 10 | 34 | |
| 11 | 35 | This policy means mixed-origin content isn’t allowed, so you can’t refer to |
| 12 | 36 | resources on other web domains, so the following Markdown for an inline |
| 13 | 37 | image hosted on another site will cause a CSP error: |
| 14 | 38 | |
| @@ -37,20 +61,20 @@ | ||
| 37 | 61 | server and the dynamic content by Fossil. That allows a URI scheme that |
| 38 | 62 | prevents the browser’s CSP enforcement from distinguishing content from |
| 39 | 63 | Fossil proper and that from the front-end proxy. |
| 40 | 64 | |
| 41 | 65 | |
| 42 | -## style-src 'self' 'unsafe-inline' | |
| 66 | +### <a name="style"></a> style-src 'self' 'unsafe-inline' | |
| 43 | 67 | |
| 44 | 68 | This policy means CSS files can only come from the Fossil server or via |
| 45 | 69 | a front-end proxy as in the inline image workarounds above. It also says |
| 46 | 70 | that inline CSS is disallowed; this will give a CSP error: |
| 47 | 71 | |
| 48 | 72 | <p style="margin-left: 4em">Some bit of indented text</p> |
| 49 | 73 | |
| 50 | 74 | In practice, this means you must put your CSS into [the “CSS” section of |
| 51 | -a custom skin](./customskin.md), not inline within Markdown, Wiki, or | |
| 75 | +a custom skin][cs], not inline within Markdown, Wiki, or | |
| 52 | 76 | HTML tags. You can refer to specific tags in the document through “`id`” |
| 53 | 77 | and “`class`” attributes. |
| 54 | 78 | |
| 55 | 79 | The reason for this restriction might not be obvious, but the risks boil |
| 56 | 80 | down to this: CSS is sufficiently powerful that if someone can apply |
| @@ -61,11 +85,11 @@ | ||
| 61 | 85 | We do currently trust CSS checked into the repository as a file, but |
| 62 | 86 | that stance might be overly-trusting, so we might revoke it later, as we |
| 63 | 87 | do for JavaScript: |
| 64 | 88 | |
| 65 | 89 | |
| 66 | -## script-src 'self' 'nonce-%s' | |
| 90 | +### <a name="script"></a> script-src 'self' 'nonce-%s' | |
| 67 | 91 | |
| 68 | 92 | This policy means HTML `<script>` tags are only allowed to be emitted |
| 69 | 93 | into the output HTML by Fossil C or TH1 code, because only code running |
| 70 | 94 | in those contexts can correctly apply the random “nonce” attribute to |
| 71 | 95 | the tag that matches the one declared in the CSP, which changes on each |
| @@ -77,11 +101,87 @@ | ||
| 77 | 101 | behind the default [hamburger menu](./customskin.md#menu). |
| 78 | 102 | |
| 79 | 103 | We’re so restrictive about how we treat JavaScript because it can lead |
| 80 | 104 | to [difficult-to-avoid cross-site scripting attacks][xssci]. |
| 81 | 105 | |
| 106 | + | |
| 107 | + | |
| 108 | +## <a name="override"></a>Replacing the Default CSP | |
| 109 | + | |
| 110 | +If you wish to relax the default CSP’s restrictions or to tighten them | |
| 111 | +further, there are two ways to accomplish that: | |
| 112 | + | |
| 113 | + | |
| 114 | +### <a name="th1"></a>TH1 Setup Hook | |
| 115 | + | |
| 116 | +The stock CSP text is hard-coded in the Fossil C source code, but it’s | |
| 117 | +only used to set the default value of one of [the TH1 skinning | |
| 118 | +variables](./customskin.md#vars), `$default_csp`. That means you can | |
| 119 | +override the default CSP by giving this variable a value before Fossil | |
| 120 | +sees that it’s undefined and uses this default. | |
| 121 | + | |
| 122 | +The best place to do that is from the [`th1-setup` | |
| 123 | +script](./th1-hooks.md), which runs before TH1 processing happens during | |
| 124 | +skin processing: | |
| 125 | + | |
| 126 | + $ fossil set th1-setup "set default_csp {default-src: 'self'}" | |
| 127 | + | |
| 128 | +This is the cleanest method, allowing you to set a custom CSP without | |
| 129 | +recompiling Fossil or providing a hand-written `<head>` section in the | |
| 130 | +Header section of a custom skin. | |
| 131 | + | |
| 132 | +You can’t remove the CSP entirely with this method, but you can get the | |
| 133 | +same effect by telling the browser there are no content restrictions: | |
| 134 | + | |
| 135 | + $ fossil set th1-setup 'set default_csp {default-src: *}' | |
| 136 | + | |
| 137 | + | |
| 138 | +### <a name="header"></a>Custom Skin Header | |
| 139 | + | |
| 140 | +Fossil only inserts a CSP into the HTML pages it generates when the | |
| 141 | +[skin’s Header section](./customskin.md#headfoot) doesn’t contain a | |
| 142 | +`<head>` tag. None of the stock skins include a `<head>` tag,¹ so if you | |
| 143 | +haven’t [created a custom skin][cs], you should be getting Fossil’s | |
| 144 | +default CSP. | |
| 145 | + | |
| 146 | +We say “should” because long-time Fossil users may be hanging onto a | |
| 147 | +legacy behavior from before Fossil 2.5, when Fossil added this automatic | |
| 148 | +`<head>` insertion feature. Repositories created before that release | |
| 149 | +where the admin either defined a custom skin *or chose one of the stock | |
| 150 | +skins* (!) will effectively override this automatic HTML `<head>` | |
| 151 | +insertion feature because the skins from before that time did include | |
| 152 | +these elements. Unless the admin for such a repository updated the skin | |
| 153 | +to track this switch to automatic `<head>` insertion, the default CSP | |
| 154 | +added to the generated header text in Fossil 2.7 is probably being | |
| 155 | +overridden by the skin. | |
| 156 | + | |
| 157 | +If you want the protection of the default CSP in your custom skin, the | |
| 158 | +simplest method is to leave the `<html><head>...` elements out of the | |
| 159 | +skin’s Header section, starting it with the `<div class="head">` element | |
| 160 | +instead as described in the custom skinning guide. Alternately, you can | |
| 161 | +[make use of `$default_csp`](#th1). | |
| 162 | + | |
| 163 | +This then tells you one way to override Fossil’s default CSP: provide | |
| 164 | +your own HTML header in a custom skin. | |
| 165 | + | |
| 166 | +A useful combination is to entirely override the default CSP in the skin | |
| 167 | +but then provide a new CSP [in the front-end proxy layer](./server/) | |
| 168 | +using any of the many reverse proxy servers that can define custom HTTP | |
| 169 | +headers. | |
| 170 | + | |
| 171 | + | |
| 172 | +------------ | |
| 173 | + | |
| 174 | + | |
| 175 | +**Asides and Digressions:** | |
| 176 | + | |
| 177 | +1. The stock Bootstrap skin does actually include a `<head>` tag, but | |
| 178 | + from Fossil 2.7 through Fossil 2.9, it just repeated the same CSP | |
| 179 | + text that Fossil’s C code inserts into the HTML header for all other | |
| 180 | + stock skins. With Fossil 2.10, the stock Bootstrap skin uses | |
| 181 | + `$default_csp` instead, so you can [override it as above](#th1). | |
| 182 | + | |
| 183 | + | |
| 184 | +[cs]: ./customskin.md | |
| 82 | 185 | [csp]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP |
| 83 | 186 | [de]: https://dopiaza.org/tools/datauri/index.php |
| 84 | 187 | [xssci]: https://fossil-scm.org/forum/forumpost/e7c386b21f |
| 85 | - | |
| 86 | - | |
| 87 | - | |
| 88 | 188 |
| --- www/defcsp.md | |
| +++ www/defcsp.md | |
| @@ -1,14 +1,38 @@ | |
| 1 | # The Default Content Security Policy (CSP) |
| 2 | |
| 3 | One of the most important things you have to know about the default |
| 4 | [Fossil-provided `<head>` text](./customskin.md#headfoot) is the |
| 5 | [Content Security Policy][csp] (CSP) it applies to your repository’s web |
| 6 | interface. The current version applies the following restrictions: |
| 7 | |
| 8 | |
| 9 | ## default-src 'self' data |
| 10 | |
| 11 | This policy means mixed-origin content isn’t allowed, so you can’t refer to |
| 12 | resources on other web domains, so the following Markdown for an inline |
| 13 | image hosted on another site will cause a CSP error: |
| 14 | |
| @@ -37,20 +61,20 @@ | |
| 37 | server and the dynamic content by Fossil. That allows a URI scheme that |
| 38 | prevents the browser’s CSP enforcement from distinguishing content from |
| 39 | Fossil proper and that from the front-end proxy. |
| 40 | |
| 41 | |
| 42 | ## style-src 'self' 'unsafe-inline' |
| 43 | |
| 44 | This policy means CSS files can only come from the Fossil server or via |
| 45 | a front-end proxy as in the inline image workarounds above. It also says |
| 46 | that inline CSS is disallowed; this will give a CSP error: |
| 47 | |
| 48 | <p style="margin-left: 4em">Some bit of indented text</p> |
| 49 | |
| 50 | In practice, this means you must put your CSS into [the “CSS” section of |
| 51 | a custom skin](./customskin.md), not inline within Markdown, Wiki, or |
| 52 | HTML tags. You can refer to specific tags in the document through “`id`” |
| 53 | and “`class`” attributes. |
| 54 | |
| 55 | The reason for this restriction might not be obvious, but the risks boil |
| 56 | down to this: CSS is sufficiently powerful that if someone can apply |
| @@ -61,11 +85,11 @@ | |
| 61 | We do currently trust CSS checked into the repository as a file, but |
| 62 | that stance might be overly-trusting, so we might revoke it later, as we |
| 63 | do for JavaScript: |
| 64 | |
| 65 | |
| 66 | ## script-src 'self' 'nonce-%s' |
| 67 | |
| 68 | This policy means HTML `<script>` tags are only allowed to be emitted |
| 69 | into the output HTML by Fossil C or TH1 code, because only code running |
| 70 | in those contexts can correctly apply the random “nonce” attribute to |
| 71 | the tag that matches the one declared in the CSP, which changes on each |
| @@ -77,11 +101,87 @@ | |
| 77 | behind the default [hamburger menu](./customskin.md#menu). |
| 78 | |
| 79 | We’re so restrictive about how we treat JavaScript because it can lead |
| 80 | to [difficult-to-avoid cross-site scripting attacks][xssci]. |
| 81 | |
| 82 | [csp]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP |
| 83 | [de]: https://dopiaza.org/tools/datauri/index.php |
| 84 | [xssci]: https://fossil-scm.org/forum/forumpost/e7c386b21f |
| 85 | |
| 86 | |
| 87 | |
| 88 |
| --- www/defcsp.md | |
| +++ www/defcsp.md | |
| @@ -1,14 +1,38 @@ | |
| 1 | # The Default Content Security Policy (CSP) |
| 2 | |
| 3 | When Fossil’s web interface generates an HTML page, it |
| 4 | normally includes a [Content Security Policy][csp] (CSP) |
| 5 | which applies to all content on that page. The CSP tells the browser |
| 6 | which types of content (HTML, image, CSS, JavaScript...) the repository |
| 7 | is expected to serve, and thus which types of content a compliant |
| 8 | browser is allowed to pay attention to. All of the major browsers |
| 9 | enforce CSP restrictions. |
| 10 | |
| 11 | A CSP is an important security measure on any site where some of the |
| 12 | content it serves is user-provided. It defines a “white list” of content |
| 13 | types believed to be safe and desirable, so that if any content outside |
| 14 | of that definition gets inserted into the repository later, the browser |
| 15 | will treat that unwanted content as if it was not part of the document |
| 16 | at all. |
| 17 | |
| 18 | A CSP is a fairly blunt tool. It cannot tell good and visually appealing |
| 19 | enhancements from vandalism or outright attacks. All it does is tell the |
| 20 | browser what types and sources for page content were anticipated by the |
| 21 | site’s creators. The browser treats everything else as “unwanted” even |
| 22 | if that’s not actually true. When the CSP prevents the browser from |
| 23 | displaying wanted content, you then have to understand the current rules |
| 24 | and what they’re trying to accomplish to decide on [an appropriate |
| 25 | workaround](#override). |
| 26 | |
| 27 | |
| 28 | ## The Default Restrictions |
| 29 | |
| 30 | The Fossil default CSP declares the following content restrictions: |
| 31 | |
| 32 | |
| 33 | ### <a name="base"></a> default-src 'self' data |
| 34 | |
| 35 | This policy means mixed-origin content isn’t allowed, so you can’t refer to |
| 36 | resources on other web domains, so the following Markdown for an inline |
| 37 | image hosted on another site will cause a CSP error: |
| 38 | |
| @@ -37,20 +61,20 @@ | |
| 61 | server and the dynamic content by Fossil. That allows a URI scheme that |
| 62 | prevents the browser’s CSP enforcement from distinguishing content from |
| 63 | Fossil proper and that from the front-end proxy. |
| 64 | |
| 65 | |
| 66 | ### <a name="style"></a> style-src 'self' 'unsafe-inline' |
| 67 | |
| 68 | This policy means CSS files can only come from the Fossil server or via |
| 69 | a front-end proxy as in the inline image workarounds above. It also says |
| 70 | that inline CSS is disallowed; this will give a CSP error: |
| 71 | |
| 72 | <p style="margin-left: 4em">Some bit of indented text</p> |
| 73 | |
| 74 | In practice, this means you must put your CSS into [the “CSS” section of |
| 75 | a custom skin][cs], not inline within Markdown, Wiki, or |
| 76 | HTML tags. You can refer to specific tags in the document through “`id`” |
| 77 | and “`class`” attributes. |
| 78 | |
| 79 | The reason for this restriction might not be obvious, but the risks boil |
| 80 | down to this: CSS is sufficiently powerful that if someone can apply |
| @@ -61,11 +85,11 @@ | |
| 85 | We do currently trust CSS checked into the repository as a file, but |
| 86 | that stance might be overly-trusting, so we might revoke it later, as we |
| 87 | do for JavaScript: |
| 88 | |
| 89 | |
| 90 | ### <a name="script"></a> script-src 'self' 'nonce-%s' |
| 91 | |
| 92 | This policy means HTML `<script>` tags are only allowed to be emitted |
| 93 | into the output HTML by Fossil C or TH1 code, because only code running |
| 94 | in those contexts can correctly apply the random “nonce” attribute to |
| 95 | the tag that matches the one declared in the CSP, which changes on each |
| @@ -77,11 +101,87 @@ | |
| 101 | behind the default [hamburger menu](./customskin.md#menu). |
| 102 | |
| 103 | We’re so restrictive about how we treat JavaScript because it can lead |
| 104 | to [difficult-to-avoid cross-site scripting attacks][xssci]. |
| 105 | |
| 106 | |
| 107 | |
| 108 | ## <a name="override"></a>Replacing the Default CSP |
| 109 | |
| 110 | If you wish to relax the default CSP’s restrictions or to tighten them |
| 111 | further, there are two ways to accomplish that: |
| 112 | |
| 113 | |
| 114 | ### <a name="th1"></a>TH1 Setup Hook |
| 115 | |
| 116 | The stock CSP text is hard-coded in the Fossil C source code, but it’s |
| 117 | only used to set the default value of one of [the TH1 skinning |
| 118 | variables](./customskin.md#vars), `$default_csp`. That means you can |
| 119 | override the default CSP by giving this variable a value before Fossil |
| 120 | sees that it’s undefined and uses this default. |
| 121 | |
| 122 | The best place to do that is from the [`th1-setup` |
| 123 | script](./th1-hooks.md), which runs before TH1 processing happens during |
| 124 | skin processing: |
| 125 | |
| 126 | $ fossil set th1-setup "set default_csp {default-src: 'self'}" |
| 127 | |
| 128 | This is the cleanest method, allowing you to set a custom CSP without |
| 129 | recompiling Fossil or providing a hand-written `<head>` section in the |
| 130 | Header section of a custom skin. |
| 131 | |
| 132 | You can’t remove the CSP entirely with this method, but you can get the |
| 133 | same effect by telling the browser there are no content restrictions: |
| 134 | |
| 135 | $ fossil set th1-setup 'set default_csp {default-src: *}' |
| 136 | |
| 137 | |
| 138 | ### <a name="header"></a>Custom Skin Header |
| 139 | |
| 140 | Fossil only inserts a CSP into the HTML pages it generates when the |
| 141 | [skin’s Header section](./customskin.md#headfoot) doesn’t contain a |
| 142 | `<head>` tag. None of the stock skins include a `<head>` tag,¹ so if you |
| 143 | haven’t [created a custom skin][cs], you should be getting Fossil’s |
| 144 | default CSP. |
| 145 | |
| 146 | We say “should” because long-time Fossil users may be hanging onto a |
| 147 | legacy behavior from before Fossil 2.5, when Fossil added this automatic |
| 148 | `<head>` insertion feature. Repositories created before that release |
| 149 | where the admin either defined a custom skin *or chose one of the stock |
| 150 | skins* (!) will effectively override this automatic HTML `<head>` |
| 151 | insertion feature because the skins from before that time did include |
| 152 | these elements. Unless the admin for such a repository updated the skin |
| 153 | to track this switch to automatic `<head>` insertion, the default CSP |
| 154 | added to the generated header text in Fossil 2.7 is probably being |
| 155 | overridden by the skin. |
| 156 | |
| 157 | If you want the protection of the default CSP in your custom skin, the |
| 158 | simplest method is to leave the `<html><head>...` elements out of the |
| 159 | skin’s Header section, starting it with the `<div class="head">` element |
| 160 | instead as described in the custom skinning guide. Alternately, you can |
| 161 | [make use of `$default_csp`](#th1). |
| 162 | |
| 163 | This then tells you one way to override Fossil’s default CSP: provide |
| 164 | your own HTML header in a custom skin. |
| 165 | |
| 166 | A useful combination is to entirely override the default CSP in the skin |
| 167 | but then provide a new CSP [in the front-end proxy layer](./server/) |
| 168 | using any of the many reverse proxy servers that can define custom HTTP |
| 169 | headers. |
| 170 | |
| 171 | |
| 172 | ------------ |
| 173 | |
| 174 | |
| 175 | **Asides and Digressions:** |
| 176 | |
| 177 | 1. The stock Bootstrap skin does actually include a `<head>` tag, but |
| 178 | from Fossil 2.7 through Fossil 2.9, it just repeated the same CSP |
| 179 | text that Fossil’s C code inserts into the HTML header for all other |
| 180 | stock skins. With Fossil 2.10, the stock Bootstrap skin uses |
| 181 | `$default_csp` instead, so you can [override it as above](#th1). |
| 182 | |
| 183 | |
| 184 | [cs]: ./customskin.md |
| 185 | [csp]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP |
| 186 | [de]: https://dopiaza.org/tools/datauri/index.php |
| 187 | [xssci]: https://fossil-scm.org/forum/forumpost/e7c386b21f |
| 188 |