From dcc339a2b4a1e96d2d87ee9a3029d05453cf3aa1 Mon Sep 17 00:00:00 2001 From: "Abderraouf Ghrissi (abgh)" Date: Tue, 14 Apr 2026 18:45:57 +0200 Subject: [PATCH] [SEC] restrict CORS to authorized extension IDs and integrate with settings Fixes a security issue where any Firefox extension could access the server without restriction. Granular control is now provided via a dedicated CORS configuration modal. Also removes legacy 'cors_origins' from the main settings store and deletes the obsolete 'ServerSettings.vue', as CORS configuration is now managed through a dedicated model and storage namespace. --- src/components/CorsConfigModal.vue | 107 ++++++++++++++++++++++++++ src/stores/cors.ts | 67 ++++++++++++++++ src/stores/settings.ts | 3 - src/views/settings/ServerSettings.vue | 33 -------- src/views/settings/Settings.vue | 11 +-- 5 files changed, 180 insertions(+), 41 deletions(-) create mode 100644 src/components/CorsConfigModal.vue create mode 100644 src/stores/cors.ts delete mode 100644 src/views/settings/ServerSettings.vue diff --git a/src/components/CorsConfigModal.vue b/src/components/CorsConfigModal.vue new file mode 100644 index 00000000..bb93ac6b --- /dev/null +++ b/src/components/CorsConfigModal.vue @@ -0,0 +1,107 @@ + + + diff --git a/src/stores/cors.ts b/src/stores/cors.ts new file mode 100644 index 00000000..269b47aa --- /dev/null +++ b/src/stores/cors.ts @@ -0,0 +1,67 @@ +import { defineStore } from 'pinia'; +import { getClient } from '~/util/awclient'; + +export interface CorsConfig { + cors: string[]; + cors_regex: string[]; + cors_allow_aw_chrome_extension: boolean; + cors_allow_all_mozilla_extension: boolean; + in_file: string[]; + needs_restart: boolean; +} + +export type MutableCorsConfig = Pick; + +interface State { + config: CorsConfig | null; + loading: boolean; + error: string | null; +} + +export const useCorsStore = defineStore('cors', { + state: (): State => ({ + config: null, + loading: false, + error: null, + }), + actions: { + async load() { + this.loading = true; + this.error = null; + try { + const client = getClient(); + const response = await client.req.get('/0/cors-config'); + this.config = response.data; + } catch (e: any) { + this.error = e.response?.data?.message || e.message || 'Failed to load CORS config'; + } finally { + this.loading = false; + } + }, + async save(newConfig: MutableCorsConfig) { + this.loading = true; + this.error = null; + try { + const client = getClient(); + // Only send the mutable subset to the server + const payload: MutableCorsConfig = { + cors: newConfig.cors, + cors_regex: newConfig.cors_regex, + cors_allow_aw_chrome_extension: newConfig.cors_allow_aw_chrome_extension, + cors_allow_all_mozilla_extension: newConfig.cors_allow_all_mozilla_extension, + }; + await client.req.post('/0/cors-config', payload); + + // Update local state if successful + if (this.config) { + this.config = { ...this.config, ...payload, needs_restart: true }; + } + } catch (e: any) { + this.error = e.response?.data?.message || e.message || 'Failed to save CORS config'; + throw e; + } finally { + this.loading = false; + } + } + } +}); diff --git a/src/stores/settings.ts b/src/stores/settings.ts index 8222587e..ce8552c0 100644 --- a/src/stores/settings.ts +++ b/src/stores/settings.ts @@ -45,8 +45,6 @@ interface State { useMultidevice: boolean; requestTimeout: number; // Server configuration - cors_origins: string; - // Set to true if settings loaded _loaded: boolean; } @@ -85,7 +83,6 @@ export const useSettingsStore = defineStore('settings', { showYearly: false, useMultidevice: false, requestTimeout: 30, - cors_origins: '', _loaded: false, }), diff --git a/src/views/settings/ServerSettings.vue b/src/views/settings/ServerSettings.vue deleted file mode 100644 index 3d29e4ce..00000000 --- a/src/views/settings/ServerSettings.vue +++ /dev/null @@ -1,33 +0,0 @@ - - - diff --git a/src/views/settings/Settings.vue b/src/views/settings/Settings.vue index 14b193ab..2a55989d 100644 --- a/src/views/settings/Settings.vue +++ b/src/views/settings/Settings.vue @@ -37,10 +37,11 @@ div hr DeveloperSettings + div.mt-2 + b-btn(v-b-modal.cors-config-modal, variant="outline-primary", size="sm") + | Configure CORS - hr - - ServerSettings + CorsConfigModal