@goodandready/dsh-clinebot 0.6.27 → 0.6.29
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/CHANGELOG.md +10 -0
- package/README.md +1 -1
- package/README.ru.md +1 -1
- package/README.zh.md +1 -1
- package/lib/access.js +8 -5
- package/lib/http.js +17 -7
- package/lib/transport/cline-transport.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,16 @@ All notable changes to `@goodandready/dsh-clinebot` will be documented in this f
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.6.29] - 2026-11-06
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **DSH Desktop key verification (#295, GitHub #19)**: `assertTrustedSettingsRequest()` now accepts a write after `connection.requestRejection()` has admitted it, even when Desktop strips `Origin`/`Referer`. Host auth is called once, and unauthenticated loopback writes still require Origin/Referer.
|
|
12
|
+
|
|
13
|
+
## [0.6.28] - 2026-11-05
|
|
14
|
+
|
|
15
|
+
### Security
|
|
16
|
+
- **Loopback hostname bypass (#294)**: URL hostnames are now matched exactly as `localhost`, `::1` or a valid `127.0.0.0/8` address; names such as `127.0.0.1.evil.example` no longer pass the HTTPS gate.
|
|
17
|
+
|
|
8
18
|
## [0.6.27] - 2026-11-05
|
|
9
19
|
|
|
10
20
|
### Security
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
<h3>Native ClineBot / ClinePass Provider Companion for DeepSeek Harness</h3>
|
|
6
6
|
|
|
7
|
-
<p><b>0.6.
|
|
7
|
+
<p><b>0.6.29</b> fixes DSH Desktop key verification when the host strips <code>Origin</code>/<code>Referer</code> but admits the request through <code>connection.requestRejection()</code>. It keeps the complete security boundary from 0.6.28, DSH <code>0.2.2-rc.1</code> support, automatic model/free/dated catalog backfill, model-scoped credit cooldowns and 402/413 errors. Chat runs on the native <code>ctx.llm</code> adapter; the legacy <code>llm-pi-ai</code>, loopback proxy and <code>llm/stream</code> path are removed.</p>
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
10
|
<a href="https://www.npmjs.com/package/@goodandready/dsh-clinebot"><img src="https://img.shields.io/npm/v/@goodandready/dsh-clinebot.svg?style=for-the-badge&color=6366f1&labelColor=1e1b4b" alt="npm version"></a>
|
package/README.ru.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
<h3>Нативное подключение провайдера ClineBot / ClinePass для DeepSeek Harness</h3>
|
|
6
6
|
|
|
7
|
-
<p><b>0.6.
|
|
7
|
+
<p><b>0.6.29</b> исправляет проверку ключа в DSH Desktop, когда host убирает <code>Origin</code>/<code>Referer</code>, но принимает запрос через <code>connection.requestRejection()</code>. Сохраняются полная security-граница из 0.6.28, поддержка DSH <code>0.2.2-rc.1</code>, автодобор каталога моделей/FREE/dated, cooldown недостатка кредитов для конкретной модели и ошибки 402/413. Чат работает через нативный адаптер <code>ctx.llm</code>; старые <code>llm-pi-ai</code>, loopback-прокси и <code>llm/stream</code> удалены.</p>
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
10
|
<a href="https://www.npmjs.com/package/@goodandready/dsh-clinebot"><img src="https://img.shields.io/npm/v/@goodandready/dsh-clinebot.svg?style=for-the-badge&color=6366f1&labelColor=1e1b4b" alt="npm version"></a>
|
package/README.zh.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
<h3>适用于 DeepSeek Harness 的 ClineBot / ClinePass 原生模型提供商伴侣插件</h3>
|
|
6
6
|
|
|
7
|
-
<p><b>0.6.
|
|
7
|
+
<p><b>0.6.29</b> 修复 DSH Desktop 在移除 <code>Origin</code>/<code>Referer</code> 后通过 <code>connection.requestRejection()</code> 认证时密钥验证失败的问题。保留 0.6.28 的完整安全边界、DSH <code>0.2.2-rc.1</code> 支持、模型/免费/日期变体自动补齐、按模型隔离的余额不足冷却和明确的 402/413 错误。聊天使用原生 <code>ctx.llm</code> 适配器;旧的 <code>llm-pi-ai</code>、回环代理和 <code>llm/stream</code> 已删除。</p>
|
|
8
8
|
|
|
9
9
|
<p align="center">
|
|
10
10
|
<a href="https://www.npmjs.com/package/@goodandready/dsh-clinebot"><img src="https://img.shields.io/npm/v/@goodandready/dsh-clinebot.svg?style=for-the-badge&color=6366f1&labelColor=1e1b4b" alt="npm version"></a>
|
package/lib/access.js
CHANGED
|
@@ -4,6 +4,7 @@ export { isTrustedSettingsRequest }
|
|
|
4
4
|
|
|
5
5
|
export function assertTrustedSettingsRequest(req, res, ctx = null) {
|
|
6
6
|
const conn = getConnection(ctx) || req?.connectionService
|
|
7
|
+
let hostAuthAccepted = false
|
|
7
8
|
if (conn && typeof conn.requestRejection === 'function') {
|
|
8
9
|
let rejection
|
|
9
10
|
try {
|
|
@@ -18,18 +19,20 @@ export function assertTrustedSettingsRequest(req, res, ctx = null) {
|
|
|
18
19
|
})
|
|
19
20
|
return false
|
|
20
21
|
}
|
|
22
|
+
hostAuthAccepted = true
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
if (!isTrustedSettingsRequest(req, ctx)) {
|
|
25
|
+
if (!isTrustedSettingsRequest(req, ctx, { skipConnectionAuth: hostAuthAccepted })) {
|
|
24
26
|
writeJson(res, 403, { ok: false, error: 'Forbidden' })
|
|
25
27
|
return false
|
|
26
28
|
}
|
|
27
29
|
|
|
28
|
-
// A loopback-only caller is not enough for state changes
|
|
29
|
-
//
|
|
30
|
-
//
|
|
30
|
+
// A loopback-only caller is not enough for state changes unless the host
|
|
31
|
+
// authentication service explicitly admitted the request. DSH Desktop
|
|
32
|
+
// authenticates with its session cookie and strips Origin/Referer while
|
|
33
|
+
// forwarding to the local host; requiring those headers breaks Desktop.
|
|
31
34
|
const method = String(req?.method || 'GET').toUpperCase()
|
|
32
|
-
if (method !== 'GET' && method !== 'HEAD' && method !== 'OPTIONS') {
|
|
35
|
+
if (!hostAuthAccepted && method !== 'GET' && method !== 'HEAD' && method !== 'OPTIONS') {
|
|
33
36
|
const origin = req?.headers?.origin || req?.headers?.referer
|
|
34
37
|
if (typeof origin !== 'string' || origin.length === 0) {
|
|
35
38
|
writeJson(res, 403, { ok: false, error: 'Forbidden' })
|
package/lib/http.js
CHANGED
|
@@ -53,11 +53,19 @@ function isLoopbackAddress(value) {
|
|
|
53
53
|
|| address.startsWith('::ffff:127.')
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
function isLoopbackHostname(value) {
|
|
57
|
+
const host = String(value || '').toLowerCase().replace(/^\[|\]$/g, '')
|
|
58
|
+
if (host === 'localhost' || host === 'localhost.' || host === '::1') return true
|
|
59
|
+
const parts = host.split('.')
|
|
60
|
+
if (parts.length !== 4 || parts[0] !== '127') return false
|
|
61
|
+
return parts.every((part) => /^\d{1,3}$/.test(part) && Number(part) <= 255)
|
|
62
|
+
}
|
|
63
|
+
|
|
56
64
|
export function isSecureRemoteBaseUrl(value) {
|
|
57
65
|
try {
|
|
58
66
|
const url = new URL(String(value || ''))
|
|
59
67
|
return url.protocol === 'https:'
|
|
60
|
-
|| (url.protocol === 'http:' &&
|
|
68
|
+
|| (url.protocol === 'http:' && isLoopbackHostname(url.hostname))
|
|
61
69
|
} catch {
|
|
62
70
|
return false
|
|
63
71
|
}
|
|
@@ -79,10 +87,12 @@ export function getConnection(ctx) {
|
|
|
79
87
|
}
|
|
80
88
|
|
|
81
89
|
/** Reject unauthenticated LAN/remote writes while allowing admitted DSH and local requests (#222). */
|
|
82
|
-
export function isTrustedSettingsRequest(request, ctx = null) {
|
|
83
|
-
// 1. Core DSH connection auth gate (#222)
|
|
90
|
+
export function isTrustedSettingsRequest(request, ctx = null, options = {}) {
|
|
91
|
+
// 1. Core DSH connection auth gate (#222). assertTrustedSettingsRequest may
|
|
92
|
+
// already have called it, so allow skipping the duplicate auth call safely.
|
|
84
93
|
const conn = getConnection(ctx) || request?.connectionService
|
|
85
|
-
|
|
94
|
+
const skipConnectionAuth = options?.skipConnectionAuth === true
|
|
95
|
+
if (!skipConnectionAuth && conn && typeof conn.requestRejection === 'function') {
|
|
86
96
|
let rejection
|
|
87
97
|
try {
|
|
88
98
|
rejection = conn.requestRejection(request)
|
|
@@ -97,7 +107,7 @@ export function isTrustedSettingsRequest(request, ctx = null) {
|
|
|
97
107
|
}
|
|
98
108
|
|
|
99
109
|
const remoteLoopback = isLoopbackAddress(request?.socket?.remoteAddress)
|
|
100
|
-
const hasConnectionAuth = Boolean(conn && typeof conn.requestRejection === 'function')
|
|
110
|
+
const hasConnectionAuth = skipConnectionAuth || Boolean(conn && typeof conn.requestRejection === 'function')
|
|
101
111
|
// Without the host auth service, a remote request cannot prove it is a
|
|
102
112
|
// same-origin browser request: Origin/Referer/Sec-Fetch are caller-controlled.
|
|
103
113
|
if (!hasConnectionAuth && !remoteLoopback) return false
|
|
@@ -113,7 +123,7 @@ export function isTrustedSettingsRequest(request, ctx = null) {
|
|
|
113
123
|
if (host && url.host.toLowerCase() === host.toLowerCase()) {
|
|
114
124
|
return true
|
|
115
125
|
}
|
|
116
|
-
if (
|
|
126
|
+
if (isLoopbackHostname(url.hostname) && remoteLoopback) {
|
|
117
127
|
return true
|
|
118
128
|
}
|
|
119
129
|
return false
|
|
@@ -129,7 +139,7 @@ export function isTrustedSettingsRequest(request, ctx = null) {
|
|
|
129
139
|
if (host && url.host.toLowerCase() === host.toLowerCase()) {
|
|
130
140
|
return true
|
|
131
141
|
}
|
|
132
|
-
if (
|
|
142
|
+
if (isLoopbackHostname(url.hostname) && remoteLoopback) {
|
|
133
143
|
return true
|
|
134
144
|
}
|
|
135
145
|
return false
|
|
@@ -10,7 +10,7 @@ import { createRequire } from 'node:module'
|
|
|
10
10
|
import { isSecureRemoteBaseUrl } from '../http.js'
|
|
11
11
|
|
|
12
12
|
const require = createRequire(import.meta.url)
|
|
13
|
-
let packageVersion = '0.6.
|
|
13
|
+
let packageVersion = '0.6.29'
|
|
14
14
|
try {
|
|
15
15
|
packageVersion = require('../../package.json').version || packageVersion
|
|
16
16
|
} catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@goodandready/dsh-clinebot",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.29",
|
|
4
4
|
"description": "DeepSeek Harness companion for ClineBot / ClinePass: dynamic subscription models sync, quota exhaustion warnings, session metrics, dedicated settings page, live usage limits, and /cline slash-command.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|