ddeck-remote 0.0.0-stage → 0.9.7-beta
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 -0
- package/NOTICE +40 -0
- package/README.md +96 -2
- package/THIRD-PARTY-LICENSES.md +69 -0
- package/cordis.patch.yml +22 -0
- package/icon.svg +6 -0
- package/install.cmd +4 -0
- package/install.ps1 +119 -0
- package/install.sh +95 -0
- package/lib/admin-trust.js +64 -0
- package/lib/client.js +751 -0
- package/lib/constants.js +11 -0
- package/lib/gateway-bundle.js +7063 -0
- package/lib/gateway.js +275 -0
- package/lib/index.js +1088 -0
- package/lib/mobile/index.js +82 -0
- package/lib/mobile/skin.css +1904 -0
- package/lib/mobile/skin.js +3614 -0
- package/lib/node-forge-bundle.js +17841 -0
- package/lib/qr-bundle.js +1990 -0
- package/lib/tls.js +173 -0
- package/locale/en.json +4 -0
- package/locale/ru.json +4 -0
- package/package.json +63 -4
- package/uninstall.ps1 +60 -0
- package/uninstall.sh +31 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Проверка «это админ-запрос с компьютера».
|
|
3
|
+
*
|
|
4
|
+
* Одного loopback-адреса мало: DNS-rebinding даёт чужой странице доступ к
|
|
5
|
+
* loopback-API, а `/dsh-remote/pairing` отдаёт токен сопряжения — то есть контроль
|
|
6
|
+
* над компьютером. Поэтому проверяются Host (локальный/приватный), `Sec-Fetch-Site`
|
|
7
|
+
* и Origin, как это делает вендорный `assertLocalAdminTrust` в шлюзе.
|
|
8
|
+
*
|
|
9
|
+
* @module dsh-remote/admin-trust
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {string} address адрес сокета.
|
|
14
|
+
* @returns {boolean} это петля?
|
|
15
|
+
*/
|
|
16
|
+
export function isLoopbackAddress(address) {
|
|
17
|
+
return address === '127.0.0.1' || address === '::1' || address === '::ffff:127.0.0.1'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {string} hostname имя хоста без скобок.
|
|
22
|
+
* @returns {boolean} локальное/приватное имя, которому можно отдавать админ-API.
|
|
23
|
+
*/
|
|
24
|
+
export function isLocalAdminHostname(hostname) {
|
|
25
|
+
if (hostname === 'localhost' || hostname === '::1') return true
|
|
26
|
+
const parts = hostname.split('.').map(Number)
|
|
27
|
+
if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) return false
|
|
28
|
+
const [a, b] = parts
|
|
29
|
+
return a === 127 || a === 10 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168) || (a === 169 && b === 254)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {import('node:http').IncomingMessage} request запрос.
|
|
34
|
+
* @returns {boolean} можно ли отдавать админ-ответ.
|
|
35
|
+
*/
|
|
36
|
+
export function isTrustedAdminRequest(request) {
|
|
37
|
+
if (request?.socket === undefined || !isLoopbackAddress(request.socket.remoteAddress ?? '')) return false
|
|
38
|
+
const hostHeader = request.headers?.host
|
|
39
|
+
if (typeof hostHeader !== 'string' || /[/?#@\\]/u.test(hostHeader)) return false
|
|
40
|
+
let authority
|
|
41
|
+
let hostname
|
|
42
|
+
try {
|
|
43
|
+
const parsed = new URL(`http://${hostHeader}`)
|
|
44
|
+
if (parsed.pathname !== '/' || parsed.username !== '' || parsed.password !== '') return false
|
|
45
|
+
authority = parsed.host.toLowerCase()
|
|
46
|
+
hostname = parsed.hostname.replace(/^\[|\]$/gu, '').toLowerCase()
|
|
47
|
+
} catch {
|
|
48
|
+
return false
|
|
49
|
+
}
|
|
50
|
+
if (!isLocalAdminHostname(hostname)) return false
|
|
51
|
+
const site = request.headers['sec-fetch-site']
|
|
52
|
+
if (site !== undefined && site !== 'same-origin' && site !== 'none') return false
|
|
53
|
+
const origin = request.headers.origin
|
|
54
|
+
if (origin !== undefined) {
|
|
55
|
+
try {
|
|
56
|
+
const parsed = new URL(origin)
|
|
57
|
+
if (parsed.host.toLowerCase() !== authority) return false
|
|
58
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return false
|
|
59
|
+
} catch {
|
|
60
|
+
return false
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return true
|
|
64
|
+
}
|