@goodandready/dsh-model-sync 0.3.8 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -142,6 +142,19 @@ dsh plugin --profile web add @goodandready/dsh-model-sync
142
142
 
143
143
  ---
144
144
 
145
+ ## 🚀 Enhancements in v0.3.9
146
+
147
+ * 🔒 **Serialized Settings Mutations (`saveConfig`)**:
148
+ * Chained internal settings persistence via an async promise mutex (`saveQueue`).
149
+ * Eliminates race conditions between simultaneous sync runs and user UI interactions, ensuring clean atomic writes to `settings.yaml`.
150
+ * 🛡️ **Hardened Cross-Origin & CSRF Protections**:
151
+ * Enhanced `trusted(req)` checking to evaluate `sec-fetch-site`, `origin`, and `referer` against the HTTP `Host`.
152
+ * Blocks unauthorized cross-origin mutations while preserving legitimate local and reverse-proxy requests.
153
+ * 🎨 **Standardized Plugin Style Discovery**:
154
+ * Added `data-dsh-plugin="dsh-model-sync"` attribute to injected UI style tags in compliance with the DeepSeek Harness plugin authoring standard.
155
+
156
+ ---
157
+
145
158
  ## 🚀 Enhancements in v0.3.5
146
159
 
147
160
  * ⏱️ **Adaptive Rate Limit & Retry-After Handling**:
package/README.ru.md CHANGED
@@ -119,6 +119,19 @@ dsh plugin --profile web add @goodandready/dsh-model-sync
119
119
 
120
120
  ---
121
121
 
122
+ ## 🚀 Новые возможности в v0.3.9
123
+
124
+ * 🔒 **Атомарная очередь сохранения настроек (`saveConfig`)**:
125
+ * Сериализация внутренних мутаций конфигурации через цепочку промисов (`saveQueue`).
126
+ * Предотвращает состояние гонки между фоновой синхронизацией и действиями пользователя в интерфейсе, гарантируя целостность `settings.yaml`.
127
+ * 🛡️ **Усиленная защита от CSRF и cross-origin атак**:
128
+ * Расширенная валидация запросов `trusted(req)` с проверкой заголовков `sec-fetch-site`, `origin` и `referer` против `Host`.
129
+ * Блокировка межсайтовых вызовов мутаций без нарушения локальных обращений и проксирования.
130
+ * 🎨 **Стандартизация инжекции стилей интерфейса**:
131
+ * Добавлен атрибут `data-dsh-plugin="dsh-model-sync"` для тега стилей в полном соответствии со стандартом разработки плагинов DeepSeek Harness.
132
+
133
+ ---
134
+
122
135
  ## 🚀 Новые возможности в v0.3.5
123
136
 
124
137
  * ⏱️ **Адаптивная обработка Rate Limit и Retry-After**:
package/lib/client.js CHANGED
@@ -33,10 +33,11 @@ window.__ModuleLoader__.load({
33
33
  function ensureStyles() {
34
34
  if (typeof document === 'undefined' || !document.head) return
35
35
  const cssId = 'dsh-model-sync/settings.module.css'
36
- if (document.querySelector('style[data-plugin-css="' + cssId + '"]')) return
36
+ if (document.querySelector('style[data-plugin-css="' + cssId + '"]') || document.querySelector('style[data-dsh-plugin="dsh-model-sync"]')) return
37
37
  const tag = document.createElement('style')
38
38
  tag.textContent = CSS
39
39
  tag.setAttribute('data-plugin', 'dsh-model-sync')
40
+ tag.setAttribute('data-dsh-plugin', 'dsh-model-sync')
40
41
  tag.setAttribute('data-plugin-css', cssId)
41
42
  document.head.appendChild(tag)
42
43
  }
package/lib/http.js CHANGED
@@ -8,12 +8,24 @@ function json(res, status, body) {
8
8
  }
9
9
 
10
10
  function trusted(req) {
11
- if (req.headers?.['sec-fetch-site'] === 'cross-site') return false
12
- const origin = req.headers?.origin
13
- if (!origin) return true
11
+ const fetchSite = req.headers?.['sec-fetch-site']
12
+ if (fetchSite === 'cross-site') return false
14
13
  const host = req.headers?.host
15
- if (!host) return false
16
- try { return new URL(origin).host === host } catch { return false }
14
+ const origin = req.headers?.origin
15
+ if (origin) {
16
+ if (!host) return false
17
+ try { return new URL(origin).host === host } catch { return false }
18
+ }
19
+ const referer = req.headers?.referer
20
+ if (referer) {
21
+ if (!host) return false
22
+ try { return new URL(referer).host === host } catch { return false }
23
+ }
24
+ // If sec-fetch-site is same-origin or same-site, allow
25
+ if (fetchSite === 'same-origin' || fetchSite === 'same-site') return true
26
+ // If sec-fetch-mode is present but origin/referer missing on a cross-site request, reject
27
+ if (req.headers?.['sec-fetch-mode'] && !host) return false
28
+ return true
17
29
  }
18
30
 
19
31
  function readJson(req) {
package/lib/index.js CHANGED
@@ -115,10 +115,15 @@ export function apply(ctx, config) {
115
115
  liveConfig = resolveConfig(next || baseConfig)
116
116
  reconfigure()
117
117
  })
118
- saveConfigImpl = async (patch) => {
119
- const result = await scope.update(patch)
120
- liveConfig = resolveConfig(scope.get() || baseConfig)
121
- return result
118
+ let saveQueue = Promise.resolve()
119
+ saveConfigImpl = (patch) => {
120
+ const nextTask = saveQueue.then(async () => {
121
+ const result = await scope.update(patch)
122
+ liveConfig = resolveConfig(scope.get() || baseConfig)
123
+ return result
124
+ })
125
+ saveQueue = nextTask.catch(() => {})
126
+ return nextTask
122
127
  }
123
128
  ownerCtx.effect(() => () => {
124
129
  settingsScope = undefined
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-model-sync",
3
- "version": "0.3.8",
3
+ "version": "0.3.9",
4
4
  "description": "Automatic model catalog synchronization for API-key DeepSeek Harness providers.",
5
5
  "keywords": [
6
6
  "deepseek-harness",