mirror of
https://github.com/miniflux/v2.git
synced 2025-08-26 18:21:01 +00:00
feat(js): tighten the trusted types policy
- Implement a better/simpler polyfill for web browsers that don't supported trusted types yet - Use two separate policies: one to create HTML, another to create/use script urls - Instead of having the policy live in the top-level scope, they're now declared at the lowest possible scope, right before they're used, making them inaccessible outside of it. This puts their usage completely out of reach of an attacker unable to gain some control outside of those two (small) scopes, and thus removes the need to tighten the policies. - Remove the now-unused tt.js file This has been tested on Firefox (doesn't support trusted types) and on Chromium (does support trusted types).
This commit is contained in:
parent
8e1d0bb693
commit
da9c3a4032
4 changed files with 15 additions and 18 deletions
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
{{ if .user }}
|
{{ if .user }}
|
||||||
{{ $cspNonce := nonce }}
|
{{ $cspNonce := nonce }}
|
||||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src * data:; media-src *; frame-src *; {{ if .user.ExternalFontHosts }}font-src {{ .user.ExternalFontHosts }}; {{ end }}style-src 'self'{{ if .user.Stylesheet }}{{ if .user.ExternalFontHosts }} {{ .user.ExternalFontHosts }}{{ end }} 'nonce-{{ $cspNonce }}'{{ end }}{{ if .user.CustomJS }}; script-src 'self' 'nonce-{{ $cspNonce }}'{{ end }}; require-trusted-types-for 'script'; trusted-types ttpolicy;">
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src * data:; media-src *; frame-src *; {{ if .user.ExternalFontHosts }}font-src {{ .user.ExternalFontHosts }}; {{ end }}style-src 'self'{{ if .user.Stylesheet }}{{ if .user.ExternalFontHosts }} {{ .user.ExternalFontHosts }}{{ end }} 'nonce-{{ $cspNonce }}'{{ end }}{{ if .user.CustomJS }}; script-src 'self' 'nonce-{{ $cspNonce }}'{{ end }}; require-trusted-types-for 'script'; trusted-types html url;">
|
||||||
|
|
||||||
{{ if .user.Stylesheet -}}
|
{{ if .user.Stylesheet -}}
|
||||||
<style nonce="{{ $cspNonce }}">{{ .user.Stylesheet | safeCSS }}</style>
|
<style nonce="{{ $cspNonce }}">{{ .user.Stylesheet | safeCSS }}</style>
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
<script type="module" nonce="{{ $cspNonce }}">{{ .user.CustomJS | safeJS }}</script>
|
<script type="module" nonce="{{ $cspNonce }}">{{ .user.CustomJS | safeJS }}</script>
|
||||||
{{ end -}}
|
{{ end -}}
|
||||||
{{ else -}}
|
{{ else -}}
|
||||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src * data:; media-src *; frame-src *; require-trusted-types-for 'script'; trusted-types ttpolicy;">
|
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src * data:; media-src *; frame-src *; require-trusted-types-for 'script'; trusted-types html url;">
|
||||||
{{ end -}}
|
{{ end -}}
|
||||||
|
|
||||||
<script src="{{ route "javascript" "name" "app" "checksum" .app_js_checksum }}" type="module"></script>
|
<script src="{{ route "javascript" "name" "app" "checksum" .app_js_checksum }}" type="module"></script>
|
||||||
|
|
|
@ -2,6 +2,17 @@
|
||||||
const TOP = 9999;
|
const TOP = 9999;
|
||||||
const BOTTOM = -9999;
|
const BOTTOM = -9999;
|
||||||
|
|
||||||
|
// Simple Polyfill for browsers that don't support Trusted Types
|
||||||
|
// See https://caniuse.com/?search=trusted%20types
|
||||||
|
if (!window.trustedTypes || !trustedTypes.createPolicy) {
|
||||||
|
window.trustedTypes = {
|
||||||
|
createPolicy: (name, policy) => ({
|
||||||
|
createScriptURL: src => src,
|
||||||
|
createHTML: html => html,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a POST request to the specified URL with the given body.
|
* Send a POST request to the specified URL with the given body.
|
||||||
*
|
*
|
||||||
|
@ -746,6 +757,7 @@ function handleFetchOriginalContentAction() {
|
||||||
|
|
||||||
response.json().then((data) => {
|
response.json().then((data) => {
|
||||||
if (data.content && data.reading_time) {
|
if (data.content && data.reading_time) {
|
||||||
|
const ttpolicy = trustedTypes.createPolicy('html', {createHTML: html => html});
|
||||||
document.querySelector(".entry-content").innerHTML = ttpolicy.createHTML(data.content);
|
document.querySelector(".entry-content").innerHTML = ttpolicy.createHTML(data.content);
|
||||||
const entryReadingtimeElement = document.querySelector(".entry-reading-time");
|
const entryReadingtimeElement = document.querySelector(".entry-reading-time");
|
||||||
if (entryReadingtimeElement) {
|
if (entryReadingtimeElement) {
|
||||||
|
@ -1081,6 +1093,7 @@ function initializeServiceWorker() {
|
||||||
if ("serviceWorker" in navigator) {
|
if ("serviceWorker" in navigator) {
|
||||||
const serviceWorkerURL = document.body.dataset.serviceWorkerUrl;
|
const serviceWorkerURL = document.body.dataset.serviceWorkerUrl;
|
||||||
if (serviceWorkerURL) {
|
if (serviceWorkerURL) {
|
||||||
|
const ttpolicy = trustedTypes.createPolicy('url', {createScriptURL: src => src});
|
||||||
navigator.serviceWorker.register(ttpolicy.createScriptURL(serviceWorkerURL), {
|
navigator.serviceWorker.register(ttpolicy.createScriptURL(serviceWorkerURL), {
|
||||||
type: "module"
|
type: "module"
|
||||||
}).catch((error) => {
|
}).catch((error) => {
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
let ttpolicy;
|
|
||||||
if (window.trustedTypes && trustedTypes.createPolicy) {
|
|
||||||
//TODO: use an allow-list for `createScriptURL`
|
|
||||||
if (!ttpolicy) {
|
|
||||||
ttpolicy = trustedTypes.createPolicy('ttpolicy', {
|
|
||||||
createScriptURL: src => src,
|
|
||||||
createHTML: html => html,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ttpolicy = {
|
|
||||||
createScriptURL: src => src,
|
|
||||||
createHTML: html => html,
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -121,7 +121,6 @@ func GenerateStylesheetsBundles() error {
|
||||||
func GenerateJavascriptBundles(webauthnEnabled bool) error {
|
func GenerateJavascriptBundles(webauthnEnabled bool) error {
|
||||||
var bundles = map[string][]string{
|
var bundles = map[string][]string{
|
||||||
"app": {
|
"app": {
|
||||||
"js/tt.js", // has to be first
|
|
||||||
"js/touch_handler.js",
|
"js/touch_handler.js",
|
||||||
"js/keyboard_handler.js",
|
"js/keyboard_handler.js",
|
||||||
"js/modal_handler.js",
|
"js/modal_handler.js",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue