@saulwade/swl-ses 1.3.7 → 1.4.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/CLAUDE.md +12 -4
- package/README.md +1 -1
- package/bin/swl-mcp-server.js +187 -187
- package/bin/swl-webhook-server.js +198 -0
- package/comandos/swl/.evolved.json +22 -22
- package/comandos/swl/adoptar-proyecto.md +21 -1
- package/comandos/swl/claudemd.md +14 -1
- package/comandos/swl/contribuir.md +233 -233
- package/comandos/swl/exportar-vault.md +207 -7
- package/comandos/swl/nuevo-proyecto.md +24 -2
- package/gateway/adapters/base.js +109 -0
- package/gateway/adapters/discord.js +167 -0
- package/gateway/adapters/email.js +221 -0
- package/gateway/adapters/slack.js +192 -0
- package/gateway/adapters/telegram.js +183 -0
- package/gateway/adapters/webhook.js +113 -0
- package/gateway/adapters/whatsapp.js +214 -0
- package/gateway/agent-executor.js +322 -0
- package/gateway/command-relay.js +271 -0
- package/gateway/cron/jobs.js +263 -0
- package/gateway/cron/scheduler.js +322 -0
- package/gateway/cron/store.js +335 -0
- package/gateway/index.js +320 -0
- package/gateway/lib/event-channel.js +191 -0
- package/gateway/session.js +131 -0
- package/gateway/webhook-server.js +324 -0
- package/habilidades/backend-production-resilience/SKILL.md +288 -288
- package/habilidades/benchmark-memoria/SKILL.md +186 -186
- package/habilidades/build-errors-nextjs/SKILL.md +55 -1
- package/habilidades/diagrama-arquitectura/assets/template.html +276 -276
- package/habilidades/doubt-driven-review/SKILL.md +171 -171
- package/habilidades/doubt-driven-review/recursos/EXAMPLES.md +130 -130
- package/habilidades/eval-framework/SKILL.md +212 -212
- package/habilidades/extractor-de-aprendizajes/SKILL.md +24 -10
- package/habilidades/harness-claude-code/SKILL.md +299 -299
- package/habilidades/infra-github-actions/SKILL.md +166 -166
- package/habilidades/legacy-code-rescue/SKILL.md +267 -267
- package/habilidades/manejo-errores/.evolved.json +8 -8
- package/habilidades/meta-skills-estandar/recursos/convencion-examples.md +93 -93
- package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -163
- package/habilidades/nextjs-testing/SKILL.md +89 -5
- package/habilidades/node-experto/SKILL.md +37 -1
- package/habilidades/patrones-python/SKILL.md +229 -229
- package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -469
- package/habilidades/planear-fase/SKILL.md +319 -319
- package/habilidades/react-experto/SKILL.md +45 -4
- package/habilidades/release-semver/.evolved.json +8 -8
- package/habilidades/swl-claudemd/SKILL.md +15 -1
- package/habilidades/tdd-workflow/SKILL.md +36 -4
- package/habilidades/testing-python/SKILL.md +340 -340
- package/hooks/claudemd-bloat-detector.js +161 -161
- package/hooks/inyeccion-contexto.js +8 -3
- package/hooks/lib/agent-routing.js +107 -107
- package/hooks/lib/auto-consolidator.js +335 -335
- package/hooks/lib/error-classifier.js +308 -308
- package/hooks/lib/merkle-audit.js +96 -96
- package/hooks/lib/provenance-tracker.js +191 -191
- package/hooks/lib/rate-limit-ip.js +177 -0
- package/hooks/lib/rate-limit-tracker.js +253 -253
- package/hooks/lib/resource-quota.js +122 -122
- package/hooks/lib/retry-jitter.js +165 -165
- package/hooks/lib/skill-auditor.js +588 -588
- package/hooks/lib/sync-status.js +228 -228
- package/hooks/lib/taint-tracker.js +107 -107
- package/hooks/lib/text-similarity.js +241 -241
- package/hooks/lib/toon-compressor.js +245 -245
- package/hooks/lib/webhook-dedup.js +184 -0
- package/hooks/lib/webhook-verify.js +123 -0
- package/hooks/proteccion-rutas.js +120 -15
- package/hooks/registro-turnos.js +209 -209
- package/hooks/sugerir-regenerar-inventario.js +170 -170
- package/hooks/validar-formato-post-subagente.js +140 -140
- package/hooks/validar-memoria-hook.js +218 -218
- package/instintos/prompt-appendices.yaml +57 -57
- package/manifiestos/agent-output-schemas.json +57 -57
- package/manifiestos/modulos.json +1 -0
- package/manifiestos/skills-lock.json +37 -37
- package/package.json +5 -3
- package/plantillas/auditor-veto-template.md +105 -105
- package/plantillas/github-workflows/README.md +47 -47
- package/plantillas/github-workflows/release-please.yml +44 -44
- package/plantillas/github-workflows/swl-ci.yml +107 -107
- package/plantillas/github-workflows/swl-security.yml +51 -51
- package/plugin.json +1 -1
- package/reglas/analisis-previo-tareas-grandes.md +172 -172
- package/reglas/arreglar-al-detectar.md +147 -147
- package/reglas/fragmentos-compartidos.md +152 -152
- package/reglas/harness-claude-code.md +213 -213
- package/reglas/usar-context7.md +226 -226
- package/reglas/usar-sistema-swl.md +251 -0
- package/schemas/diary-entry.schema.json +80 -80
- package/scripts/benchmark-memoria.js +167 -167
- package/scripts/comandos/skills.js +251 -2
- package/scripts/configurar-branch-protection.js +418 -418
- package/scripts/detectar-aprendizajes-duplicados.js +151 -151
- package/scripts/field-report.js +199 -199
- package/scripts/generar-checklists-consolidados.js +273 -273
- package/scripts/generar-inventario.js +420 -420
- package/scripts/generar-matriz-lenguajes.js +271 -271
- package/scripts/lib/artefactos-python.js +43 -43
- package/scripts/lib/benchmark-metrics.js +160 -160
- package/scripts/lib/budget-enforcer.js +252 -252
- package/scripts/lib/configurar-ci.js +380 -380
- package/scripts/lib/contadores-inventario.js +217 -217
- package/scripts/lib/detectar-stack-detallado.js +307 -307
- package/scripts/lib/diary-entry.js +234 -234
- package/scripts/lib/eval-metrics-store.js +218 -218
- package/scripts/lib/eval-quality.js +171 -171
- package/scripts/lib/eval-schemas.js +144 -144
- package/scripts/lib/eval-self-correct.js +106 -106
- package/scripts/lib/eval-validator.js +185 -185
- package/scripts/lib/jaccard-similarity.js +98 -98
- package/scripts/lib/longmemeval-runner.js +125 -125
- package/scripts/lib/npm-version.js +261 -261
- package/scripts/lib/paquetes-conocidos.js +50 -50
- package/scripts/lib/prompt-builder.js +264 -264
- package/scripts/lib/rrf-fusion.js +175 -175
- package/scripts/lib/scoring-instintos.js +277 -277
- package/scripts/lib/semantic-search.js +252 -252
- package/scripts/limpiar-artefactos-python.js +131 -131
- package/scripts/mcp-server/README.md +128 -128
- package/scripts/mcp-server/handlers.js +206 -206
- package/scripts/migrar-csv-a-array.js +168 -168
- package/scripts/migrar-fase-dominio.js +201 -201
- package/scripts/publicar.js +511 -511
- package/scripts/run-eval.js +141 -141
- package/scripts/validar-manifest.js +195 -195
- package/scripts/validar-userland-vacio.js +110 -110
- package/scripts/verificar-release.js +110 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* webhook-verify.js — Verificación de firmas HMAC en webhooks entrantes.
|
|
5
|
+
*
|
|
6
|
+
* Provee dos funciones independientes:
|
|
7
|
+
*
|
|
8
|
+
* - verifyGithubSignature(payloadBody, signatureHeader, secret)
|
|
9
|
+
* GitHub envía la firma como header `X-Hub-Signature-256: sha256=<hex>`.
|
|
10
|
+
* Calcula HMAC SHA-256 sobre el body crudo y compara timing-safe.
|
|
11
|
+
*
|
|
12
|
+
* - verifyBearer(authorizationHeader, secret)
|
|
13
|
+
* Genérico: header `Authorization: Bearer <token>` comparado timing-safe
|
|
14
|
+
* contra el secreto compartido.
|
|
15
|
+
*
|
|
16
|
+
* Ambas funciones:
|
|
17
|
+
* - Aceptan body como Buffer o string. Buffer es lo correcto para HMAC
|
|
18
|
+
* porque GitHub firma los bytes literales del request, no la cadena
|
|
19
|
+
* decodificada.
|
|
20
|
+
* - Retornan boolean. Nunca lanzan excepciones — falla = false.
|
|
21
|
+
* - Usan `crypto.timingSafeEqual` para evitar ataques de timing.
|
|
22
|
+
* - Zero-deps (solo `crypto` nativo).
|
|
23
|
+
*
|
|
24
|
+
* Origen: port de `temp/claude-code-telegram-main/src/api/auth.py` (Python, 61 LOC).
|
|
25
|
+
*
|
|
26
|
+
* @module hooks/lib/webhook-verify
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
const crypto = require('crypto');
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Verifica firma HMAC SHA-256 al estilo GitHub.
|
|
33
|
+
*
|
|
34
|
+
* @param {Buffer|string} payloadBody Body crudo del request.
|
|
35
|
+
* @param {string|undefined|null} signatureHeader Valor del header X-Hub-Signature-256.
|
|
36
|
+
* @param {string} secret Secreto compartido configurado en GitHub.
|
|
37
|
+
* @returns {boolean} true si la firma es válida; false en cualquier otro caso.
|
|
38
|
+
*/
|
|
39
|
+
function verifyGithubSignature(payloadBody, signatureHeader, secret) {
|
|
40
|
+
if (!signatureHeader || typeof signatureHeader !== 'string') {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
if (!signatureHeader.startsWith('sha256=')) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
if (!secret || typeof secret !== 'string') {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
if (payloadBody === undefined || payloadBody === null) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const bodyBuffer = Buffer.isBuffer(payloadBody)
|
|
54
|
+
? payloadBody
|
|
55
|
+
: Buffer.from(String(payloadBody), 'utf8');
|
|
56
|
+
|
|
57
|
+
const expectedDigest = crypto
|
|
58
|
+
.createHmac('sha256', secret)
|
|
59
|
+
.update(bodyBuffer)
|
|
60
|
+
.digest('hex');
|
|
61
|
+
const expectedHeader = 'sha256=' + expectedDigest;
|
|
62
|
+
|
|
63
|
+
return constantTimeEqual(expectedHeader, signatureHeader);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Verifica un Bearer token simple en el header Authorization.
|
|
68
|
+
*
|
|
69
|
+
* @param {string|undefined|null} authorizationHeader Valor del header Authorization.
|
|
70
|
+
* @param {string} secret Token esperado (sin el prefijo "Bearer ").
|
|
71
|
+
* @returns {boolean} true si el token coincide; false en cualquier otro caso.
|
|
72
|
+
*/
|
|
73
|
+
function verifyBearer(authorizationHeader, secret) {
|
|
74
|
+
if (!authorizationHeader || typeof authorizationHeader !== 'string') {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
if (!authorizationHeader.startsWith('Bearer ')) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
if (!secret || typeof secret !== 'string') {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const token = authorizationHeader.slice('Bearer '.length);
|
|
85
|
+
return constantTimeEqual(token, secret);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Comparación timing-safe de dos strings.
|
|
90
|
+
*
|
|
91
|
+
* `crypto.timingSafeEqual` requiere buffers de la misma longitud. Si las
|
|
92
|
+
* longitudes difieren, el comparador termina temprano y eso filtra
|
|
93
|
+
* información de longitud por timing. Para evitarlo:
|
|
94
|
+
*
|
|
95
|
+
* 1. Si longitudes difieren, retornar false sin invocar timingSafeEqual.
|
|
96
|
+
* 2. Si coinciden, comparar.
|
|
97
|
+
*
|
|
98
|
+
* El filtrado de longitud es aceptable aquí porque la longitud de la firma
|
|
99
|
+
* HMAC SHA-256 hex es fija (71 caracteres con prefijo "sha256=") y la
|
|
100
|
+
* longitud del Bearer es controlada por el secreto del servidor — ninguna
|
|
101
|
+
* es información sensible derivable del input del atacante.
|
|
102
|
+
*
|
|
103
|
+
* @param {string} a
|
|
104
|
+
* @param {string} b
|
|
105
|
+
* @returns {boolean}
|
|
106
|
+
*/
|
|
107
|
+
function constantTimeEqual(a, b) {
|
|
108
|
+
if (typeof a !== 'string' || typeof b !== 'string') {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
if (a.length !== b.length) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
const bufA = Buffer.from(a, 'utf8');
|
|
115
|
+
const bufB = Buffer.from(b, 'utf8');
|
|
116
|
+
return crypto.timingSafeEqual(bufA, bufB);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
module.exports = {
|
|
120
|
+
verifyGithubSignature,
|
|
121
|
+
verifyBearer,
|
|
122
|
+
constantTimeEqual,
|
|
123
|
+
};
|
|
@@ -208,8 +208,10 @@ function verificarDentroDeCwd(rutaRaw, cwdActual) {
|
|
|
208
208
|
|
|
209
209
|
/**
|
|
210
210
|
* Rutas permitidas fuera del CWD.
|
|
211
|
-
* Estas rutas son legítimas para el funcionamiento de Claude Code
|
|
212
|
-
* (memoria persistente, configuración
|
|
211
|
+
* Estas rutas son legítimas para el funcionamiento de Claude Code y SWL
|
|
212
|
+
* (memoria persistente, configuración global, skills/agentes/comandos/reglas
|
|
213
|
+
* del usuario que SWL instala globalmente y que se modifican durante backports
|
|
214
|
+
* entre proyectos).
|
|
213
215
|
*
|
|
214
216
|
* Se comparan con startsWith case-insensitive en Windows.
|
|
215
217
|
*/
|
|
@@ -218,21 +220,100 @@ const RUTAS_PERMITIDAS_EXTERNAS = [
|
|
|
218
220
|
path.join(os.homedir(), '.claude', 'projects'),
|
|
219
221
|
// Configuración global de Claude Code
|
|
220
222
|
path.join(os.homedir(), '.claude', 'settings.json'),
|
|
223
|
+
// Skills globales del usuario (instaladas por SWL, modificables durante backport)
|
|
224
|
+
path.join(os.homedir(), '.claude', 'skills'),
|
|
225
|
+
// Agentes globales del usuario
|
|
226
|
+
path.join(os.homedir(), '.claude', 'agents'),
|
|
227
|
+
// Comandos globales del usuario
|
|
228
|
+
path.join(os.homedir(), '.claude', 'commands'),
|
|
229
|
+
// Reglas globales del usuario (modificables durante consolidación cross-proyecto)
|
|
230
|
+
path.join(os.homedir(), '.claude', 'rules'),
|
|
221
231
|
];
|
|
222
232
|
|
|
223
233
|
/**
|
|
224
|
-
*
|
|
234
|
+
* Parsea la variable de entorno SWL_PROTECCION_RUTAS_PERMITIR (CSV de rutas
|
|
235
|
+
* absolutas) y devuelve un array de paths normalizados.
|
|
236
|
+
*
|
|
237
|
+
* Patrón de uso: el usuario define la variable ANTES de iniciar Claude Code
|
|
238
|
+
* cuando necesita permitir writes legítimos a destinos fuera del CWD que no
|
|
239
|
+
* están cubiertos por el whitelist por defecto. Ej:
|
|
240
|
+
*
|
|
241
|
+
* SWL_PROTECCION_RUTAS_PERMITIR="D:\Python\sigaf,F:\vault\notas"
|
|
242
|
+
*
|
|
243
|
+
* Cada ruta se resuelve con path.resolve para normalizar separadores y
|
|
244
|
+
* eliminar referencias relativas. Rutas vacías o solo whitespace se descartan.
|
|
245
|
+
*
|
|
246
|
+
* @returns {string[]} array de paths absolutos resueltos (puede estar vacío)
|
|
247
|
+
*/
|
|
248
|
+
function parsearRutasPermitidasUsuario() {
|
|
249
|
+
const raw = process.env.SWL_PROTECCION_RUTAS_PERMITIR;
|
|
250
|
+
if (!raw || typeof raw !== 'string') return [];
|
|
251
|
+
return raw
|
|
252
|
+
.split(',')
|
|
253
|
+
.map(r => r.trim())
|
|
254
|
+
.filter(r => r.length > 0)
|
|
255
|
+
.map(r => path.resolve(r));
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Cache módulo-level del parseo de SWL_PROTECCION_RUTAS_PERMITIR.
|
|
260
|
+
*
|
|
261
|
+
* `process.env` no cambia durante la vida del proceso (los hooks se ejecutan
|
|
262
|
+
* como subproceso de Claude Code, un proceso nuevo por cada Write/Edit), por
|
|
263
|
+
* lo que el parseo solo necesita hacerse una vez al cargar el módulo. Esto
|
|
264
|
+
* evita re-parsear el CSV y volver a llamar `path.resolve` en cada call a
|
|
265
|
+
* `esRutaExternaPermitida()`, que se ejecuta por cada operación de escritura.
|
|
266
|
+
*
|
|
267
|
+
* Para tests que necesitan re-parsear (cambian la variable después de cargar
|
|
268
|
+
* el módulo), exportar la función `_resetCacheRutasPermitidas()` permitiría
|
|
269
|
+
* forzar el refresh — los tests existentes usan spawnSync, por lo que cada
|
|
270
|
+
* subproceso carga el módulo fresco con su propio entorno.
|
|
271
|
+
*/
|
|
272
|
+
const RUTAS_PERMITIDAS_USUARIO = parsearRutasPermitidasUsuario();
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Normaliza separadores y elimina trailing slash para comparación de prefijo
|
|
276
|
+
* con boundary check. Garantiza que la comparación sea sobre componentes de
|
|
277
|
+
* path completos, no sobre substrings.
|
|
278
|
+
*
|
|
279
|
+
* Ejemplo: ambas variantes de `~/.claude/skills/` y `~/.claude/skills` se
|
|
280
|
+
* normalizan a `.../.claude/skills` (sin trailing).
|
|
281
|
+
*
|
|
282
|
+
* @param {string} p
|
|
283
|
+
* @returns {string}
|
|
284
|
+
*/
|
|
285
|
+
function normalizarParaComparacion(p) {
|
|
286
|
+
return normalizarSeparadores(p).replace(/\/+$/, '');
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Verifica si una ruta resuelta está en la lista de rutas externas permitidas
|
|
291
|
+
* (whitelist por defecto + rutas opt-in del usuario vía variable de entorno).
|
|
292
|
+
*
|
|
293
|
+
* **Boundary check obligatorio**: usa match exacto O prefijo seguido de '/'
|
|
294
|
+
* para evitar que paths siblings con el mismo prefijo textual pasen la
|
|
295
|
+
* verificación. Ejemplos de hostiles que DEBEN ser bloqueados:
|
|
296
|
+
* - whitelist `~/.claude/skills` → `~/.claude/skills-evil/x.md` (no es subdir)
|
|
297
|
+
* - whitelist `~/.claude/settings.json` → `~/.claude/settings.json.bak`
|
|
298
|
+
* - whitelist `D:/Proyecto` → `D:/Proyecto-otro/file.md`
|
|
299
|
+
*
|
|
225
300
|
* @param {string} rutaResuelta
|
|
226
301
|
* @returns {boolean}
|
|
227
302
|
*/
|
|
228
303
|
function esRutaExternaPermitida(rutaResuelta) {
|
|
229
|
-
const rutaNorm =
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
304
|
+
const rutaNorm = normalizarParaComparacion(rutaResuelta);
|
|
305
|
+
const rutaCmp = process.platform === 'win32' ? rutaNorm.toLowerCase() : rutaNorm;
|
|
306
|
+
|
|
307
|
+
const combinadas = RUTAS_PERMITIDAS_EXTERNAS.concat(RUTAS_PERMITIDAS_USUARIO);
|
|
308
|
+
for (const permitida of combinadas) {
|
|
309
|
+
const permitidaNorm = normalizarParaComparacion(permitida);
|
|
310
|
+
const permitidaCmp = process.platform === 'win32' ? permitidaNorm.toLowerCase() : permitidaNorm;
|
|
311
|
+
|
|
312
|
+
// Match exacto (cubre archivos como settings.json) O prefijo seguido de '/'
|
|
313
|
+
// (cubre subdirectorios como skills/foo dentro del directorio skills).
|
|
314
|
+
if (rutaCmp === permitidaCmp || rutaCmp.startsWith(permitidaCmp + '/')) {
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
236
317
|
}
|
|
237
318
|
return false;
|
|
238
319
|
}
|
|
@@ -270,7 +351,8 @@ function extraerRuta(toolInput) {
|
|
|
270
351
|
* @returns {string}
|
|
271
352
|
*/
|
|
272
353
|
function construirMensajeBloqueo(tipoViolacion, rutaRaw, rutaResuelta, cwdActual) {
|
|
273
|
-
|
|
354
|
+
const esTraversal = /traversal/i.test(tipoViolacion);
|
|
355
|
+
const lineas = [
|
|
274
356
|
`Escritura bloqueada por política de seguridad de rutas.`,
|
|
275
357
|
``,
|
|
276
358
|
`Violación: ${tipoViolacion}`,
|
|
@@ -282,10 +364,33 @@ function construirMensajeBloqueo(tipoViolacion, rutaRaw, rutaResuelta, cwdActual
|
|
|
282
364
|
` - La ruta de destino debe estar dentro del directorio de trabajo actual.`,
|
|
283
365
|
` - No se permiten secuencias de path traversal (../, ~/, %2e%2e, etc.).`,
|
|
284
366
|
` - No se permiten rutas absolutas fuera del proyecto.`,
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
367
|
+
];
|
|
368
|
+
|
|
369
|
+
if (!esTraversal) {
|
|
370
|
+
lineas.push(
|
|
371
|
+
``,
|
|
372
|
+
`Rutas externas permitidas por defecto:`,
|
|
373
|
+
` - ~/.claude/projects, settings.json, skills, agents, commands, rules`,
|
|
374
|
+
``,
|
|
375
|
+
`Para destinos legítimos no cubiertos (otros proyectos del usuario, vault,`,
|
|
376
|
+
`backports cross-proyecto), define la variable de entorno ANTES de iniciar`,
|
|
377
|
+
`Claude Code con un CSV de rutas absolutas:`,
|
|
378
|
+
``,
|
|
379
|
+
` PowerShell: $env:SWL_PROTECCION_RUTAS_PERMITIR = "D:\\Python\\sigaf,F:\\vault"`,
|
|
380
|
+
` Bash: export SWL_PROTECCION_RUTAS_PERMITIR="/d/Python/sigaf,/f/vault"`,
|
|
381
|
+
``,
|
|
382
|
+
`Alternativa: usar Bash con cp/heredoc (no pasa por este hook) cuando el`,
|
|
383
|
+
`destino es seguro y el cambio es puntual.`,
|
|
384
|
+
);
|
|
385
|
+
} else {
|
|
386
|
+
lineas.push(
|
|
387
|
+
``,
|
|
388
|
+
`Path traversal NO se levanta con SWL_PROTECCION_RUTAS_PERMITIR — la variable`,
|
|
389
|
+
`solo afecta el check de "dentro del CWD", no las secuencias ../, ~/, %2e%2e.`,
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return lineas.join('\n');
|
|
289
394
|
}
|
|
290
395
|
|
|
291
396
|
// ---------------------------------------------------------------------------
|