@linxin666/dsh-remote-web-ui 0.2.7 → 0.2.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/LICENSE +202 -28
- package/README.i18n.yaml +2 -2
- package/README.md +14 -7
- package/README.zh.md +3 -2
- package/lib/client.js +40 -8
- package/lib/client.js.map +1 -1
- package/lib/index.js +34 -24
- package/lib/mobile.js +39 -21
- package/lib/mobile.js.map +1 -1
- package/lib/types/client/RemoteEntry.js +3 -3
- package/lib/types/client/UpdateEntry.d.ts.map +1 -1
- package/lib/types/client/UpdateEntry.js +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +28 -14
- package/lib/types/client/pair-api.d.ts +6 -0
- package/lib/types/client/pair-api.d.ts.map +1 -1
- package/lib/types/client/pair-api.js +11 -0
- package/lib/types/client/remote-channel.d.ts +10 -0
- package/lib/types/client/remote-channel.d.ts.map +1 -1
- package/lib/types/client/remote-channel.js +11 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +16 -5
- package/lib/types/mobile/messages.d.ts.map +1 -1
- package/lib/types/mobile/messages.js +5 -3
- package/lib/types/mobile/messages.test.js +20 -0
- package/lib/types/mobile/mux.d.ts +7 -4
- package/lib/types/mobile/mux.d.ts.map +1 -1
- package/lib/types/mobile/mux.js +43 -15
- package/lib/types/mobile/mux.test.js +57 -0
- package/lib/types/remote-api.d.ts +13 -4
- package/lib/types/remote-api.d.ts.map +1 -1
- package/lib/types/remote-api.js +27 -17
- package/lib/types/routes.d.ts +2 -0
- package/lib/types/routes.d.ts.map +1 -1
- package/lib/types/routes.js +5 -2
- package/package.json +15 -12
- package/src/client/RemoteEntry.tsx +3 -2
- package/src/client/UpdateEntry.tsx +1 -0
- package/src/client/index.ts +30 -13
- package/src/client/pair-api.ts +16 -0
- package/src/client/remote-channel.ts +21 -0
- package/src/index.ts +16 -5
- package/src/mobile/messages.test.ts +23 -0
- package/src/mobile/messages.ts +5 -3
- package/src/mobile/mux.test.ts +64 -0
- package/src/mobile/mux.ts +44 -17
- package/src/remote-api.ts +34 -17
- package/src/routes.ts +7 -2
package/src/remote-api.ts
CHANGED
|
@@ -7,8 +7,10 @@
|
|
|
7
7
|
* and no per-plugin pairing consult.
|
|
8
8
|
*
|
|
9
9
|
* Security model:
|
|
10
|
-
* -
|
|
11
|
-
* any bytes are forwarded and
|
|
10
|
+
* - While `requirePairingForLan` is on (default), every request must carry a
|
|
11
|
+
* live paired-device cookie, enforced before any bytes are forwarded and
|
|
12
|
+
* before any host call. With the policy off, the cookie gate is skipped
|
|
13
|
+
* (the loopback-only denials below still apply).
|
|
12
14
|
* - The SDK's loopback-only privileged methods (native dialogs, the settings
|
|
13
15
|
* plane, credentials — the `PRIVILEGED_METHODS` set of client-connection)
|
|
14
16
|
* are denied here. The set is pinned by tests/remote-contract.spec.ts.
|
|
@@ -68,6 +70,13 @@ export interface RemoteApiDeps {
|
|
|
68
70
|
service: PairingService
|
|
69
71
|
/** The local webServer port the loopback proxy connects to. */
|
|
70
72
|
port: number
|
|
73
|
+
/**
|
|
74
|
+
* Live policy: whether the paired-device cookie gates the /remote channel.
|
|
75
|
+
* When false, requests are proxied without a cookie (the loopback-only
|
|
76
|
+
* denials still apply). A function is re-read per request, so a settings
|
|
77
|
+
* edit takes effect without a restart. Defaults to true.
|
|
78
|
+
*/
|
|
79
|
+
requirePairingForLan?: boolean | (() => boolean)
|
|
71
80
|
}
|
|
72
81
|
|
|
73
82
|
/** One SDK-shaped error envelope (keeps the desktop client's parse path intact). */
|
|
@@ -125,20 +134,25 @@ export function loopbackOnlyDenial(innerPath: string): string | undefined {
|
|
|
125
134
|
|
|
126
135
|
/**
|
|
127
136
|
* Build the remote desktop channel HTTP routes.
|
|
128
|
-
* @param deps - pairing service + local port.
|
|
137
|
+
* @param deps - pairing service + local port + live pairing policy.
|
|
129
138
|
* @returns the routes to register on webServer.
|
|
130
139
|
*/
|
|
131
140
|
export function makeRemoteApiRoutes(deps: RemoteApiDeps): WebRoute[] {
|
|
132
|
-
const { service, port } = deps
|
|
141
|
+
const { service, port, requirePairingForLan = true } = deps
|
|
133
142
|
|
|
134
143
|
const handler = (req: IncomingMessage, res: ServerResponse): void => {
|
|
135
144
|
// Cookie gate first — same order as /m/api. Do not buffer an unpaired body.
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
145
|
+
// With the live policy off, untrusted-but-policy-open callers are proxied
|
|
146
|
+
// (a stale client rewrite must not 403); loopback-only denials stay below.
|
|
147
|
+
const require = typeof requirePairingForLan === 'function' ? requirePairingForLan() : requirePairingForLan
|
|
148
|
+
if (require) {
|
|
149
|
+
const deviceId = readCookie(req.headers.cookie, service.config.cookieName)
|
|
150
|
+
const paired = deviceId !== undefined && service.touchDevice(deviceId)
|
|
151
|
+
if (!paired) {
|
|
152
|
+
req.resume()
|
|
153
|
+
envelopeError(res, 403, 'invalid-request', 'unpaired', 'this device is not paired with the desktop')
|
|
154
|
+
return
|
|
155
|
+
}
|
|
142
156
|
}
|
|
143
157
|
|
|
144
158
|
const method = req.method ?? 'GET'
|
|
@@ -187,18 +201,21 @@ export function upgradeInnerPath(reqUrl: string | undefined, fallbackPath: strin
|
|
|
187
201
|
/**
|
|
188
202
|
* Build the WebSocket upgrade routes for the event streams and known plugin
|
|
189
203
|
* sockets. webServer matches upgrades by exact path.
|
|
190
|
-
* @param deps - pairing service + local port.
|
|
204
|
+
* @param deps - pairing service + local port + live pairing policy.
|
|
191
205
|
* @returns the upgrade routes to register on webServer.
|
|
192
206
|
*/
|
|
193
207
|
export function makeRemoteApiUpgradeRoutes(deps: RemoteApiDeps): WebUpgradeRoute[] {
|
|
194
|
-
const { service, port } = deps
|
|
208
|
+
const { service, port, requirePairingForLan = true } = deps
|
|
195
209
|
|
|
196
210
|
const handlerFor = (fallbackPath: string) => (req: IncomingMessage, socket: Duplex, head: Buffer): void => {
|
|
197
|
-
const
|
|
198
|
-
if (
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
211
|
+
const require = typeof requirePairingForLan === 'function' ? requirePairingForLan() : requirePairingForLan
|
|
212
|
+
if (require) {
|
|
213
|
+
const deviceId = readCookie(req.headers.cookie, service.config.cookieName)
|
|
214
|
+
if (deviceId === undefined || !service.touchDevice(deviceId)) {
|
|
215
|
+
socket.write('HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n')
|
|
216
|
+
socket.destroy()
|
|
217
|
+
return
|
|
218
|
+
}
|
|
202
219
|
}
|
|
203
220
|
const inner = upgradeInnerPath(req.url, fallbackPath)
|
|
204
221
|
const denied = loopbackOnlyDenial(inner.split('?')[0] ?? inner)
|
package/src/routes.ts
CHANGED
|
@@ -195,6 +195,8 @@ export interface PairRoutesDeps {
|
|
|
195
195
|
service: PairingService
|
|
196
196
|
/** The LAN IP literals the fence accepts (derived from the bind host). */
|
|
197
197
|
lanAddresses: readonly string[]
|
|
198
|
+
/** Current desktop gate policy, re-read for every status response. */
|
|
199
|
+
requirePairingForLan?: boolean | (() => boolean)
|
|
198
200
|
}
|
|
199
201
|
|
|
200
202
|
/**
|
|
@@ -203,7 +205,10 @@ export interface PairRoutesDeps {
|
|
|
203
205
|
* @returns the exact routes to register on webServer.
|
|
204
206
|
*/
|
|
205
207
|
export function makeRoutes(deps: PairRoutesDeps): WebRoute[] {
|
|
206
|
-
const { service, lanAddresses } = deps
|
|
208
|
+
const { service, lanAddresses, requirePairingForLan = true } = deps
|
|
209
|
+
const pairingRequired = (): boolean => typeof requirePairingForLan === 'function'
|
|
210
|
+
? requirePairingForLan()
|
|
211
|
+
: requirePairingForLan
|
|
207
212
|
const events = new PairingEventsStream(service)
|
|
208
213
|
|
|
209
214
|
/** Loopback-only fence: the desktop panel's control endpoints. */
|
|
@@ -404,7 +409,7 @@ export function makeRoutes(deps: PairRoutesDeps): WebRoute[] {
|
|
|
404
409
|
const visible = paired
|
|
405
410
|
? rest
|
|
406
411
|
: { phase: snapshot.phase, lanAvailable: snapshot.lanAvailable, lanAddresses: snapshot.lanAddresses }
|
|
407
|
-
writeJson(res, 200, { ok: true, paired, ...visible })
|
|
412
|
+
writeJson(res, 200, { ok: true, paired, requirePairingForLan: pairingRequired(), ...visible })
|
|
408
413
|
}
|
|
409
414
|
|
|
410
415
|
const handleEvents = (req: IncomingMessage, res: ServerResponse): void => {
|