Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add custom Content Security Policy support #1987

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions internal/database/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -846,4 +846,9 @@ var migrations = []func(tx *sql.Tx) error{
_, err = tx.Exec(sql)
return err
},
func(tx *sql.Tx) (err error) {
sql := `ALTER TABLE users ADD COLUMN content_security_policy text default ''`
_, err = tx.Exec(sql)
return err
},
}
2 changes: 2 additions & 0 deletions internal/locale/translations/de_DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,8 @@
"form.prefs.fieldset.application_settings": "Anwendungseinstellungen",
"form.prefs.fieldset.authentication_settings": "Authentifizierungseinstellungen",
"form.prefs.fieldset.reader_settings": "Reader-Einstellungen",
"form.prefs.label.content_security_policy": "Content Security Policy",
"form.prefs.label.content_security_policy.default": "Default",
"form.import.label.file": "OPML Datei",
"form.import.label.url": "URL",
"form.integration.fever_activate": "Fever API aktivieren",
Expand Down
2 changes: 2 additions & 0 deletions internal/locale/translations/el_EL.json
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@
"form.prefs.label.default_home_page": "Προεπιλεγμένη αρχική σελίδα",
"form.prefs.label.categories_sorting_order": "Ταξινόμηση κατηγοριών",
"form.prefs.label.mark_read_on_view": "Αυτόματη επισήμανση καταχωρήσεων ως αναγνωσμένων κατά την προβολή",
"form.prefs.label.content_security_policy": "Content Security Policy",
"form.prefs.label.content_security_policy.default": "Default",
"form.prefs.fieldset.application_settings": "Application Settings",
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
"form.prefs.fieldset.reader_settings": "Reader Settings",
Expand Down
2 changes: 2 additions & 0 deletions internal/locale/translations/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@
"form.prefs.label.default_home_page": "Default home page",
"form.prefs.label.categories_sorting_order": "Categories sorting",
"form.prefs.label.mark_read_on_view": "Automatically mark entries as read when viewed",
"form.prefs.label.content_security_policy": "Content Security Policy",
"form.prefs.label.content_security_policy.default": "Default",
"form.prefs.fieldset.application_settings": "Application Settings",
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
"form.prefs.fieldset.reader_settings": "Reader Settings",
Expand Down
2 changes: 2 additions & 0 deletions internal/locale/translations/es_ES.json
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@
"form.prefs.label.default_home_page": "Página de inicio por defecto",
"form.prefs.label.categories_sorting_order": "Clasificación por categorías",
"form.prefs.label.mark_read_on_view": "Marcar automáticamente las entradas como leídas cuando se vean",
"form.prefs.label.content_security_policy": "Content Security Policy",
"form.prefs.label.content_security_policy.default": "Default",
"form.prefs.fieldset.application_settings": "Application Settings",
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
"form.prefs.fieldset.reader_settings": "Reader Settings",
Expand Down
2 changes: 2 additions & 0 deletions internal/locale/translations/fi_FI.json
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@
"form.prefs.label.default_home_page": "Oletusarvoinen etusivu",
"form.prefs.label.categories_sorting_order": "Kategorioiden lajittelu",
"form.prefs.label.mark_read_on_view": "Merkitse kohdat automaattisesti luetuiksi, kun niitä tarkastellaan",
"form.prefs.label.content_security_policy": "Content Security Policy",
"form.prefs.label.content_security_policy.default": "Default",
"form.prefs.fieldset.application_settings": "Application Settings",
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
"form.prefs.fieldset.reader_settings": "Reader Settings",
Expand Down
2 changes: 2 additions & 0 deletions internal/locale/translations/fr_FR.json
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@
"form.prefs.label.default_home_page": "Page d'accueil par défaut",
"form.prefs.label.categories_sorting_order": "Colonne de tri des catégories",
"form.prefs.label.mark_read_on_view": "Marquer automatiquement les entrées comme lues lorsqu'elles sont consultées",
"form.prefs.label.content_security_policy": "Content Security Policy",
"form.prefs.label.content_security_policy.default": "Default",
"form.prefs.fieldset.application_settings": "Paramètres de l'application",
"form.prefs.fieldset.authentication_settings": "Paramètres d'authentification",
"form.prefs.fieldset.reader_settings": "Paramètres du lecteur",
Expand Down
2 changes: 2 additions & 0 deletions internal/locale/translations/hi_IN.json
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@
"form.prefs.label.default_home_page": "डिफ़ॉल्ट होमपेज़",
"form.prefs.label.categories_sorting_order": "श्रेणियाँ छँटाई",
"form.prefs.label.mark_read_on_view": "देखे जाने पर स्वचालित रूप से प्रविष्टियों को पढ़ने के रूप में चिह्नित करें",
"form.prefs.label.content_security_policy": "Content Security Policy",
"form.prefs.label.content_security_policy.default": "Default",
"form.prefs.fieldset.application_settings": "Application Settings",
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
"form.prefs.fieldset.reader_settings": "Reader Settings",
Expand Down
2 changes: 2 additions & 0 deletions internal/locale/translations/id_ID.json
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,8 @@
"form.prefs.label.default_home_page": "Beranda Baku",
"form.prefs.label.categories_sorting_order": "Pengurutan Kategori",
"form.prefs.label.mark_read_on_view": "Secara otomatis menandai entri sebagai telah dibaca saat dilihat",
"form.prefs.label.content_security_policy": "Content Security Policy",
"form.prefs.label.content_security_policy.default": "Default",
"form.prefs.fieldset.application_settings": "Application Settings",
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
"form.prefs.fieldset.reader_settings": "Reader Settings",
Expand Down
2 changes: 2 additions & 0 deletions internal/locale/translations/it_IT.json
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@
"form.prefs.label.default_home_page": "Pagina iniziale predefinita",
"form.prefs.label.categories_sorting_order": "Ordinamento delle categorie",
"form.prefs.label.mark_read_on_view": "Contrassegna automaticamente le voci come lette quando visualizzate",
"form.prefs.label.content_security_policy": "Content Security Policy",
"form.prefs.label.content_security_policy.default": "Default",
"form.prefs.fieldset.application_settings": "Application Settings",
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
"form.prefs.fieldset.reader_settings": "Reader Settings",
Expand Down
2 changes: 2 additions & 0 deletions internal/locale/translations/ja_JP.json
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@
"form.prefs.label.default_home_page": "デフォルトのトップページ",
"form.prefs.label.categories_sorting_order": "カテゴリの表示順",
"form.prefs.label.mark_read_on_view": "表示時にエントリを自動的に既読としてマークします",
"form.prefs.label.content_security_policy": "Content Security Policy",
"form.prefs.label.content_security_policy.default": "Default",
"form.prefs.fieldset.application_settings": "Application Settings",
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
"form.prefs.fieldset.reader_settings": "Reader Settings",
Expand Down
2 changes: 2 additions & 0 deletions internal/locale/translations/nl_NL.json
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@
"form.prefs.label.default_home_page": "Standaard startpagina",
"form.prefs.label.categories_sorting_order": "Categorieën sorteren",
"form.prefs.label.mark_read_on_view": "Items automatisch markeren als gelezen wanneer ze worden bekeken",
"form.prefs.label.content_security_policy": "Content Security Policy",
"form.prefs.label.content_security_policy.default": "Default",
"form.prefs.fieldset.application_settings": "Application Settings",
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
"form.prefs.fieldset.reader_settings": "Reader Settings",
Expand Down
2 changes: 2 additions & 0 deletions internal/locale/translations/pl_PL.json
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,8 @@
"form.prefs.label.default_home_page": "Domyślna strona główna",
"form.prefs.label.categories_sorting_order": "Sortowanie kategorii",
"form.prefs.label.mark_read_on_view": "Automatycznie oznaczaj wpisy jako przeczytane podczas przeglądania",
"form.prefs.label.content_security_policy": "Content Security Policy",
"form.prefs.label.content_security_policy.default": "Default",
"form.prefs.fieldset.application_settings": "Application Settings",
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
"form.prefs.fieldset.reader_settings": "Reader Settings",
Expand Down
2 changes: 2 additions & 0 deletions internal/locale/translations/pt_BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@
"form.prefs.label.default_home_page": "Página inicial predefinida",
"form.prefs.label.categories_sorting_order": "Classificação das categorias",
"form.prefs.label.mark_read_on_view": "Marcar automaticamente as entradas como lidas quando visualizadas",
"form.prefs.label.content_security_policy": "Content Security Policy",
"form.prefs.label.content_security_policy.default": "Default",
"form.prefs.fieldset.application_settings": "Application Settings",
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
"form.prefs.fieldset.reader_settings": "Reader Settings",
Expand Down
2 changes: 2 additions & 0 deletions internal/locale/translations/ru_RU.json
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,8 @@
"form.prefs.label.default_home_page": "Домашняя страница по умолчанию",
"form.prefs.label.categories_sorting_order": "Сортировка категорий",
"form.prefs.label.mark_read_on_view": "Автоматически отмечать записи как прочитанные при просмотре",
"form.prefs.label.content_security_policy": "Content Security Policy",
"form.prefs.label.content_security_policy.default": "Default",
"form.prefs.fieldset.application_settings": "Application Settings",
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
"form.prefs.fieldset.reader_settings": "Reader Settings",
Expand Down
2 changes: 2 additions & 0 deletions internal/locale/translations/tr_TR.json
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,8 @@
"form.prefs.label.default_home_page": "Varsayılan ana sayfa",
"form.prefs.label.categories_sorting_order": "Kategoriler sıralama",
"form.prefs.label.mark_read_on_view": "Girişleri görüntülendiğinde otomatik olarak okundu olarak işaretle",
"form.prefs.label.content_security_policy": "Content Security Policy",
"form.prefs.label.content_security_policy.default": "Default",
"form.prefs.fieldset.application_settings": "Application Settings",
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
"form.prefs.fieldset.reader_settings": "Reader Settings",
Expand Down
2 changes: 2 additions & 0 deletions internal/locale/translations/uk_UA.json
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,8 @@
"form.prefs.label.default_home_page": "Домашня сторінка за умовчанням",
"form.prefs.label.categories_sorting_order": "Сортування за категоріями",
"form.prefs.label.mark_read_on_view": "Автоматично позначати записи як прочитані під час перегляду",
"form.prefs.label.content_security_policy": "Content Security Policy",
"form.prefs.label.content_security_policy.default": "Default",
"form.prefs.fieldset.application_settings": "Application Settings",
"form.prefs.fieldset.authentication_settings": "Authentication Settings",
"form.prefs.fieldset.reader_settings": "Reader Settings",
Expand Down
2 changes: 2 additions & 0 deletions internal/locale/translations/zh_CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,8 @@
"form.prefs.fieldset.application_settings": "应用设置",
"form.prefs.fieldset.authentication_settings": "用户认证设置",
"form.prefs.fieldset.reader_settings": "阅读器设置",
"form.prefs.label.content_security_policy": "Content Security Policy",
"form.prefs.label.content_security_policy.default": "Default",
"form.import.label.file": "OPML 文件",
"form.import.label.url": "URL",
"form.integration.fever_activate": "启用 Fever API",
Expand Down
2 changes: 2 additions & 0 deletions internal/locale/translations/zh_TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,8 @@
"form.prefs.fieldset.application_settings": "應用程式設定",
"form.prefs.fieldset.authentication_settings": "使用者認證設定",
"form.prefs.fieldset.reader_settings": "閱讀器設定",
"form.prefs.label.content_security_policy": "Content Security Policy",
"form.prefs.label.content_security_policy.default": "Default",
"form.import.label.file": "OPML 檔案",
"form.import.label.url": "URL",
"form.integration.fever_activate": "啟用 Fever API",
Expand Down
6 changes: 6 additions & 0 deletions internal/model/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type User struct {
DefaultHomePage string `json:"default_home_page"`
CategoriesSortingOrder string `json:"categories_sorting_order"`
MarkReadOnView bool `json:"mark_read_on_view"`
ContentSecurityPolicy string `json:"content_security_policy"`
}

// UserCreationRequest represents the request to create a user.
Expand Down Expand Up @@ -70,6 +71,7 @@ type UserModificationRequest struct {
DefaultHomePage *string `json:"default_home_page"`
CategoriesSortingOrder *string `json:"categories_sorting_order"`
MarkReadOnView *bool `json:"mark_read_on_view"`
ContentSecurityPolicy *string `json:"content_security_policy"`
}

// Patch updates the User object with the modification request.
Expand Down Expand Up @@ -161,6 +163,10 @@ func (u *UserModificationRequest) Patch(user *User) {
if u.MarkReadOnView != nil {
user.MarkReadOnView = *u.MarkReadOnView
}

if u.ContentSecurityPolicy != nil {
user.ContentSecurityPolicy = *u.ContentSecurityPolicy
}
}

// UseTimezone converts last login date to the given timezone.
Expand Down
33 changes: 23 additions & 10 deletions internal/storage/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ func (s *Storage) CreateUser(userCreationRequest *model.UserCreationRequest) (*m
cjk_reading_speed,
default_home_page,
categories_sorting_order,
mark_read_on_view
mark_read_on_view,
content_security_policy
`

tx, err := s.db.Begin()
Expand Down Expand Up @@ -130,6 +131,7 @@ func (s *Storage) CreateUser(userCreationRequest *model.UserCreationRequest) (*m
&user.DefaultHomePage,
&user.CategoriesSortingOrder,
&user.MarkReadOnView,
&user.ContentSecurityPolicy,
)
if err != nil {
tx.Rollback()
Expand Down Expand Up @@ -186,9 +188,10 @@ func (s *Storage) UpdateUser(user *model.User) error {
cjk_reading_speed=$19,
default_home_page=$20,
categories_sorting_order=$21,
mark_read_on_view=$22
mark_read_on_view=$22,
content_security_policy=$23
WHERE
id=$23
id=$24
`

_, err = s.db.Exec(
Expand All @@ -215,6 +218,7 @@ func (s *Storage) UpdateUser(user *model.User) error {
user.DefaultHomePage,
user.CategoriesSortingOrder,
user.MarkReadOnView,
user.ContentSecurityPolicy,
user.ID,
)
if err != nil {
Expand Down Expand Up @@ -243,9 +247,10 @@ func (s *Storage) UpdateUser(user *model.User) error {
cjk_reading_speed=$18,
default_home_page=$19,
categories_sorting_order=$20,
mark_read_on_view=$21
mark_read_on_view=$21,
content_security_policy=$22
WHERE
id=$22
id=$23
`

_, err := s.db.Exec(
Expand All @@ -271,6 +276,7 @@ func (s *Storage) UpdateUser(user *model.User) error {
user.DefaultHomePage,
user.CategoriesSortingOrder,
user.MarkReadOnView,
user.ContentSecurityPolicy,
user.ID,
)

Expand Down Expand Up @@ -318,7 +324,8 @@ func (s *Storage) UserByID(userID int64) (*model.User, error) {
cjk_reading_speed,
default_home_page,
categories_sorting_order,
mark_read_on_view
mark_read_on_view,
content_security_policy
FROM
users
WHERE
Expand Down Expand Up @@ -353,7 +360,8 @@ func (s *Storage) UserByUsername(username string) (*model.User, error) {
cjk_reading_speed,
default_home_page,
categories_sorting_order,
mark_read_on_view
mark_read_on_view,
content_security_policy
FROM
users
WHERE
Expand Down Expand Up @@ -388,7 +396,8 @@ func (s *Storage) UserByField(field, value string) (*model.User, error) {
cjk_reading_speed,
default_home_page,
categories_sorting_order,
mark_read_on_view
mark_read_on_view,
content_security_policy
FROM
users
WHERE
Expand Down Expand Up @@ -430,7 +439,8 @@ func (s *Storage) UserByAPIKey(token string) (*model.User, error) {
u.cjk_reading_speed,
u.default_home_page,
u.categories_sorting_order,
u.mark_read_on_view
u.mark_read_on_view,
u.content_security_policy
FROM
users u
LEFT JOIN
Expand Down Expand Up @@ -467,6 +477,7 @@ func (s *Storage) fetchUser(query string, args ...interface{}) (*model.User, err
&user.DefaultHomePage,
&user.CategoriesSortingOrder,
&user.MarkReadOnView,
&user.ContentSecurityPolicy,
)

if err == sql.ErrNoRows {
Expand Down Expand Up @@ -574,7 +585,8 @@ func (s *Storage) Users() (model.Users, error) {
cjk_reading_speed,
default_home_page,
categories_sorting_order,
mark_read_on_view
mark_read_on_view,
content_security_policy
FROM
users
ORDER BY username ASC
Expand Down Expand Up @@ -612,6 +624,7 @@ func (s *Storage) Users() (model.Users, error) {
&user.DefaultHomePage,
&user.CategoriesSortingOrder,
&user.MarkReadOnView,
&user.ContentSecurityPolicy,
)

if err != nil {
Expand Down
7 changes: 6 additions & 1 deletion internal/template/templates/common/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,12 @@

<link rel="stylesheet" type="text/css" href="{{ route "stylesheet" "name" .theme "checksum" .theme_checksum }}">

{{ if and .user .user.Stylesheet }}
{{ if and .user .user.ContentSecurityPolicy }}
<meta http-equiv="Content-Security-Policy" content="{{ .user.ContentSecurityPolicy }}">
{{ if .user.Stylesheet }}
<style>{{ .user.Stylesheet | safeCSS }}</style>
{{ end }}
{{ else if and .user .user.Stylesheet }}
{{ $stylesheetNonce := nonce }}
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src * data:; media-src *; frame-src *; style-src 'self' 'nonce-{{ $stylesheetNonce }}'">
<style nonce="{{ $stylesheetNonce }}">{{ .user.Stylesheet | safeCSS }}</style>
Expand Down
4 changes: 4 additions & 0 deletions internal/template/templates/views/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,10 @@ <h1>{{ t "page.settings.title" }}</h1>

<label><input type="checkbox" name="entry_swipe" value="1" {{ if .form.EntrySwipe }}checked{{ end }}> {{ t "form.prefs.label.entry_swipe" }}</label>

<label for="form-content-security-policy">{{ t "form.prefs.label.content_security_policy" }}</label>
<input type="text" name="content_security_policy" id="form-content-security-policy" value="{{ .form.ContentSecurityPolicy }}" spellcheck="false">
<p class="form-help">{{ t "form.prefs.label.content_security_policy.default" }}: <code>default-src 'self'; img-src * data:; media-src *; frame-src *; style-src 'self' 'nonce-{{ "{{" }} $$stylesheetNonce {{ "}}" }}'</code></p>

<label>{{t "form.prefs.label.custom_css" }}</label><textarea name="custom_css" cols="40" rows="8" spellcheck="false">{{ .form.CustomCSS }}</textarea>
<div class="buttons">
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
Expand Down
3 changes: 3 additions & 0 deletions internal/ui/form/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type SettingsForm struct {
DefaultHomePage string
CategoriesSortingOrder string
MarkReadOnView bool
ContentSecurityPolicy string
}

// Merge updates the fields of the given user.
Expand All @@ -55,6 +56,7 @@ func (s *SettingsForm) Merge(user *model.User) *model.User {
user.DefaultHomePage = s.DefaultHomePage
user.CategoriesSortingOrder = s.CategoriesSortingOrder
user.MarkReadOnView = s.MarkReadOnView
user.ContentSecurityPolicy = s.ContentSecurityPolicy

if s.Password != "" {
user.Password = s.Password
Expand Down Expand Up @@ -122,5 +124,6 @@ func NewSettingsForm(r *http.Request) *SettingsForm {
DefaultHomePage: r.FormValue("default_home_page"),
CategoriesSortingOrder: r.FormValue("categories_sorting_order"),
MarkReadOnView: r.FormValue("mark_read_on_view") == "1",
ContentSecurityPolicy: r.FormValue("content_security_policy"),
}
}
1 change: 1 addition & 0 deletions internal/ui/settings_show.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ func (h *handler) showSettingsPage(w http.ResponseWriter, r *http.Request) {
DefaultHomePage: user.DefaultHomePage,
CategoriesSortingOrder: user.CategoriesSortingOrder,
MarkReadOnView: user.MarkReadOnView,
ContentSecurityPolicy: user.ContentSecurityPolicy,
}

timezones, err := h.store.Timezones()
Expand Down
Loading
Loading