@dotrino/vault 0.85.0 → 0.86.0
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 +32 -32
- package/package.json +16 -4
- package/src/browser.js +167 -0
- package/src/core/approvals.js +204 -0
- package/src/core/blocked.js +68 -0
- package/src/core/browserDisk.js +109 -0
- package/src/core/commKey.js +68 -0
- package/src/core/disk.js +65 -0
- package/src/core/libVersion.js +8 -0
- package/src/core/machines.js +66 -0
- package/src/core/notices.js +71 -0
- package/src/core/notifiable.js +73 -0
- package/src/core/passwords.js +125 -0
- package/src/core/sealKey.js +77 -0
- package/src/core/sealer.js +172 -0
- package/src/core/sealers.js +159 -0
- package/src/core/secretsStore.js +1326 -0
- package/src/core/sha256.js +69 -0
- package/src/core/store.js +68 -0
- package/src/core/subacta.js +101 -0
- package/src/core/threadStore.js +273 -0
- package/src/core/transport.js +270 -0
- package/src/core/vault.js +4414 -0
- package/src/proc.js +2 -84
- package/src/procContext.js +97 -0
package/src/proc.js
CHANGED
|
@@ -29,19 +29,10 @@
|
|
|
29
29
|
*/
|
|
30
30
|
import fs from 'node:fs'
|
|
31
31
|
import os from 'node:os'
|
|
32
|
-
import crypto from 'node:crypto'
|
|
33
32
|
import { cleanMachine } from '@dotrino/identity/machine'
|
|
33
|
+
import { MAX_PATH_BYTES, clip, trimArgv, sanitizeContext, commandFingerprint, sameCommand, commandLine } from './procContext.js'
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
export const MAX_ARGV_BYTES = 4096
|
|
37
|
-
export const MAX_PATH_BYTES = 1024
|
|
38
|
-
/** Cuántos argumentos como mucho: más que esto no lo lee nadie. */
|
|
39
|
-
export const MAX_ARGV_ITEMS = 64
|
|
40
|
-
|
|
41
|
-
const clip = (s, max) => {
|
|
42
|
-
if (typeof s !== 'string') return ''
|
|
43
|
-
return Buffer.byteLength(s) <= max ? s : Buffer.from(s).subarray(0, max).toString('utf8')
|
|
44
|
-
}
|
|
35
|
+
export { MAX_ARGV_BYTES, MAX_PATH_BYTES, MAX_ARGV_ITEMS, sanitizeContext, commandFingerprint, sameCommand, commandLine } from './procContext.js'
|
|
45
36
|
|
|
46
37
|
/** ¿Hay `/proc`? Es lo que separa poder medir de tener que creer. */
|
|
47
38
|
export const hasProc = () => { try { return fs.statSync('/proc/self').isDirectory() } catch (_) { return false } }
|
|
@@ -54,24 +45,6 @@ function readCmdline (pid) {
|
|
|
54
45
|
return parts
|
|
55
46
|
}
|
|
56
47
|
|
|
57
|
-
/**
|
|
58
|
-
* Recorta el `argv` a lo que se puede enseñar y transportar. Devuelve también si sobró
|
|
59
|
-
* algo: un comando cortado que no dice que está cortado se lee como otro comando.
|
|
60
|
-
*/
|
|
61
|
-
function trimArgv (argv) {
|
|
62
|
-
const list = (Array.isArray(argv) ? argv : []).map((a) => String(a))
|
|
63
|
-
let cut = list.length > MAX_ARGV_ITEMS
|
|
64
|
-
const kept = list.slice(0, MAX_ARGV_ITEMS)
|
|
65
|
-
let total = 0
|
|
66
|
-
const out = []
|
|
67
|
-
for (const a of kept) {
|
|
68
|
-
const b = Buffer.byteLength(a)
|
|
69
|
-
if (total + b > MAX_ARGV_BYTES) { cut = true; break }
|
|
70
|
-
total += b
|
|
71
|
-
out.push(a)
|
|
72
|
-
}
|
|
73
|
-
return { argv: out, truncated: cut }
|
|
74
|
-
}
|
|
75
48
|
|
|
76
49
|
/**
|
|
77
50
|
* LA HORA DE ARRANQUE del proceso (campo 22 de `/proc/<pid>/stat`, en ticks). Sirve para
|
|
@@ -146,59 +119,4 @@ export function selfMachine () {
|
|
|
146
119
|
const safeCwd = () => { try { return process.cwd() } catch (_) { return '' } }
|
|
147
120
|
const userName = () => { try { return os.userInfo().username } catch (_) { return '' } }
|
|
148
121
|
|
|
149
|
-
/**
|
|
150
|
-
* LO QUE LLEGA POR LA RED NO SE CREE NI EN LA FORMA. Este dato lo manda otro proceso, se
|
|
151
|
-
* guarda en memoria y se pinta en un teléfono: se recorta a lo que cabe y se tira lo que no
|
|
152
|
-
* tiene el tipo que debería. Devuelve `null` si no queda nada que enseñar — que no es lo
|
|
153
|
-
* mismo que un contexto vacío, y por eso no se devuelve un objeto a medias.
|
|
154
|
-
*/
|
|
155
|
-
export function sanitizeContext (raw) {
|
|
156
|
-
if (!raw || typeof raw !== 'object') return null
|
|
157
|
-
const argvRaw = Array.isArray(raw.argv) ? raw.argv.filter((a) => typeof a === 'string') : []
|
|
158
|
-
const { argv, truncated } = trimArgv(argvRaw)
|
|
159
|
-
const exe = clip(typeof raw.exe === 'string' ? raw.exe : '', MAX_PATH_BYTES)
|
|
160
|
-
const cwd = clip(typeof raw.cwd === 'string' ? raw.cwd : '', MAX_PATH_BYTES)
|
|
161
|
-
if (!exe && !argv.length) return null
|
|
162
|
-
return {
|
|
163
|
-
pid: Number.isInteger(raw.pid) && raw.pid > 0 ? raw.pid : null,
|
|
164
|
-
uid: Number.isInteger(raw.uid) ? raw.uid : null,
|
|
165
|
-
exe,
|
|
166
|
-
cwd,
|
|
167
|
-
argv,
|
|
168
|
-
truncated: truncated || raw.truncated === true,
|
|
169
|
-
start: Number.isFinite(raw.start) ? raw.start : null,
|
|
170
|
-
user: clip(typeof raw.user === 'string' ? raw.user : '', 64),
|
|
171
|
-
host: clip(typeof raw.host === 'string' ? raw.host : '', 64)
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* LA HUELLA DEL COMANDO: qué binario, desde qué carpeta y con qué argumentos. Es la llave
|
|
177
|
-
* de la concesión por tiempo — aprobar `node server.js` en `/srv/app` no aprueba otra cosa
|
|
178
|
-
* ni la misma cosa en otro sitio.
|
|
179
|
-
*
|
|
180
|
-
* El pid NO entra: cambia en cada arranque y la gracia es justamente que el mismo comando
|
|
181
|
-
* se reconozca la próxima vez.
|
|
182
|
-
*/
|
|
183
|
-
export function commandFingerprint (ctx) {
|
|
184
|
-
if (!ctx || typeof ctx !== 'object') return null
|
|
185
|
-
const argv = Array.isArray(ctx.argv) ? ctx.argv : []
|
|
186
|
-
if (!ctx.exe && !argv.length) return null
|
|
187
|
-
const material = [ctx.exe || '', ctx.cwd || '', ...argv].join('\0')
|
|
188
|
-
return crypto.createHash('sha256').update(material).digest('hex')
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/** ¿Son el mismo comando? Mismo binario, misma carpeta, mismos argumentos. */
|
|
192
|
-
export const sameCommand = (a, b) => {
|
|
193
|
-
const fa = commandFingerprint(a)
|
|
194
|
-
return !!fa && fa === commandFingerprint(b)
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/** El comando en una línea, para un log o una pantalla estrecha. */
|
|
198
|
-
export function commandLine (ctx, max = 160) {
|
|
199
|
-
const argv = Array.isArray(ctx?.argv) ? ctx.argv : []
|
|
200
|
-
const linea = argv.length ? argv.join(' ') : (ctx?.exe || '')
|
|
201
|
-
return linea.length > max ? linea.slice(0, max - 1) + '…' : linea
|
|
202
|
-
}
|
|
203
|
-
|
|
204
122
|
export default { selfContext, readProcContext, sanitizeContext, commandFingerprint, sameCommand, commandLine, hasProc }
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LA PARTE PORTABLE DE `proc.js`: limpiar el contexto que declara quien pide, su huella y su
|
|
3
|
+
* línea. La usan el binario y la bóveda del navegador (que no tiene `/proc` y nunca mide,
|
|
4
|
+
* así que todo lo que le llega se queda como declarado). Leer `/proc` sigue en `proc.js`,
|
|
5
|
+
* que es solo de Node.
|
|
6
|
+
*/
|
|
7
|
+
import { sha256Hex } from './core/sha256.js'
|
|
8
|
+
|
|
9
|
+
/** Topes. Un `argv` puede ser enorme y esto viaja en cada pedido y se pinta en un teléfono. */
|
|
10
|
+
export const MAX_ARGV_BYTES = 4096
|
|
11
|
+
export const MAX_PATH_BYTES = 1024
|
|
12
|
+
/** Cuántos argumentos como mucho: más que esto no lo lee nadie. */
|
|
13
|
+
export const MAX_ARGV_ITEMS = 64
|
|
14
|
+
|
|
15
|
+
const enc = new TextEncoder()
|
|
16
|
+
const byteLength = (s) => enc.encode(s).length
|
|
17
|
+
export const clip = (s, max) => {
|
|
18
|
+
if (typeof s !== 'string') return ''
|
|
19
|
+
const b = enc.encode(s)
|
|
20
|
+
return b.length <= max ? s : new TextDecoder().decode(b.subarray(0, max))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Recorta el `argv` a lo que se puede enseñar y transportar. Devuelve también si sobró
|
|
25
|
+
* algo: un comando cortado que no dice que está cortado se lee como otro comando.
|
|
26
|
+
*/
|
|
27
|
+
export function trimArgv (argv) {
|
|
28
|
+
const list = (Array.isArray(argv) ? argv : []).map((a) => String(a))
|
|
29
|
+
let cut = list.length > MAX_ARGV_ITEMS
|
|
30
|
+
const kept = list.slice(0, MAX_ARGV_ITEMS)
|
|
31
|
+
let total = 0
|
|
32
|
+
const out = []
|
|
33
|
+
for (const a of kept) {
|
|
34
|
+
const b = byteLength(a)
|
|
35
|
+
if (total + b > MAX_ARGV_BYTES) { cut = true; break }
|
|
36
|
+
total += b
|
|
37
|
+
out.push(a)
|
|
38
|
+
}
|
|
39
|
+
return { argv: out, truncated: cut }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* LO QUE LLEGA POR LA RED NO SE CREE NI EN LA FORMA. Este dato lo manda otro proceso, se
|
|
44
|
+
* guarda en memoria y se pinta en un teléfono: se recorta a lo que cabe y se tira lo que no
|
|
45
|
+
* tiene el tipo que debería. Devuelve `null` si no queda nada que enseñar — que no es lo
|
|
46
|
+
* mismo que un contexto vacío, y por eso no se devuelve un objeto a medias.
|
|
47
|
+
*/
|
|
48
|
+
export function sanitizeContext (raw) {
|
|
49
|
+
if (!raw || typeof raw !== 'object') return null
|
|
50
|
+
const argvRaw = Array.isArray(raw.argv) ? raw.argv.filter((a) => typeof a === 'string') : []
|
|
51
|
+
const { argv, truncated } = trimArgv(argvRaw)
|
|
52
|
+
const exe = clip(typeof raw.exe === 'string' ? raw.exe : '', MAX_PATH_BYTES)
|
|
53
|
+
const cwd = clip(typeof raw.cwd === 'string' ? raw.cwd : '', MAX_PATH_BYTES)
|
|
54
|
+
if (!exe && !argv.length) return null
|
|
55
|
+
return {
|
|
56
|
+
pid: Number.isInteger(raw.pid) && raw.pid > 0 ? raw.pid : null,
|
|
57
|
+
uid: Number.isInteger(raw.uid) ? raw.uid : null,
|
|
58
|
+
exe,
|
|
59
|
+
cwd,
|
|
60
|
+
argv,
|
|
61
|
+
truncated: truncated || raw.truncated === true,
|
|
62
|
+
start: Number.isFinite(raw.start) ? raw.start : null,
|
|
63
|
+
user: clip(typeof raw.user === 'string' ? raw.user : '', 64),
|
|
64
|
+
host: clip(typeof raw.host === 'string' ? raw.host : '', 64)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* LA HUELLA DEL COMANDO: qué binario, desde qué carpeta y con qué argumentos. Es la llave
|
|
70
|
+
* de la concesión por tiempo — aprobar `node server.js` en `/srv/app` no aprueba otra cosa
|
|
71
|
+
* ni la misma cosa en otro sitio.
|
|
72
|
+
*
|
|
73
|
+
* El pid NO entra: cambia en cada arranque y la gracia es justamente que el mismo comando
|
|
74
|
+
* se reconozca la próxima vez.
|
|
75
|
+
*/
|
|
76
|
+
export function commandFingerprint (ctx) {
|
|
77
|
+
if (!ctx || typeof ctx !== 'object') return null
|
|
78
|
+
const argv = Array.isArray(ctx.argv) ? ctx.argv : []
|
|
79
|
+
if (!ctx.exe && !argv.length) return null
|
|
80
|
+
const material = [ctx.exe || '', ctx.cwd || '', ...argv].join('\0')
|
|
81
|
+
return sha256Hex(material)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** ¿Son el mismo comando? Mismo binario, misma carpeta, mismos argumentos. */
|
|
85
|
+
export const sameCommand = (a, b) => {
|
|
86
|
+
const fa = commandFingerprint(a)
|
|
87
|
+
return !!fa && fa === commandFingerprint(b)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** El comando en una línea, para un log o una pantalla estrecha. */
|
|
91
|
+
export function commandLine (ctx, max = 160) {
|
|
92
|
+
const argv = Array.isArray(ctx?.argv) ? ctx.argv : []
|
|
93
|
+
const linea = argv.length ? argv.join(' ') : (ctx?.exe || '')
|
|
94
|
+
return linea.length > max ? linea.slice(0, max - 1) + '…' : linea
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export default { sanitizeContext, commandFingerprint, sameCommand, commandLine, trimArgv }
|