@ingeniomaps/cauce 0.18.0 → 0.19.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/CHANGELOG.md CHANGED
@@ -8,6 +8,50 @@ esa operación sea confiable en vez de sólo cómoda: acá se lee qué cambió a
8
8
  un cambio en el protocolo, en las reglas del sistema o en un guard es visible para el usuario y sube
9
9
  minor aunque no toque una sola línea de código.
10
10
 
11
+ ## [0.19.0] - 2026-08-16
12
+
13
+ Los cuatro arreglos de abajo salieron de correr los tres caminos de punta a punta —un cargo que
14
+ aprende en el toolkit, uno propio de una empresa, y uno del catálogo adoptado y aprendiendo—, no de
15
+ un test. Son cosas que se veían correctas hasta que algo las siguió de verdad.
16
+
17
+ ### Agregado
18
+
19
+ - **`guard-governance` cubre el contrato de un cargo, su medición y su firma.** `agent-promote` se
20
+ niega si «Aprobación humana» no está firmada, pero lo único que impedía que la escribiera un agente
21
+ era una frase en un prompt: una instrucción, no un candado, y el archivo no estaba protegido por
22
+ nada.
23
+
24
+ Alrededor de la firma van las otras piezas del mismo acto. `SKILL.md` y `references/` son lo que la
25
+ propuesta cambia: sin ellos, editar el contrato directo saltea el ciclo entero. Y
26
+ `evaluations/cases/` con `expected-behaviors.yaml` son el denominador con que se juzga —el propio
27
+ recorrido de propuesta ya dice que cambiarlo «es parte de lo que se aprueba»—, así que moverlo en
28
+ silencio ablanda toda medición pasada sin tocar una regla.
29
+
30
+ **Para una empresa esto significa** que tocar su propio cargo pide `OPS_GOVERNANCE_OVERRIDE=1`, la
31
+ misma puerta explícita que ya rige para `planning/adr/` y `planning/rules/`. Las dos clases de
32
+ evidencia quedan afuera: `learning/reports/` y `evaluations/results/` registran lo que pasó un día
33
+ en vez de decidir algo, y un veredicto se escribe en cada corrida.
34
+
35
+ ### Corregido
36
+
37
+ - **La automatización de un cargo adoptado apuntaba al catálogo.** El `AUTOMATION.md` del sistema dice
38
+ «mantené `agents/<tipo>/system/<slug>`», que dentro de una empresa es el paquete. Copiado tal cual,
39
+ el ciclo semanal del cargo adoptado escribía en un directorio que el guard bloquea y que npm borra
40
+ —y reportaba éxito—. `agents fork` ahora reescribe esas rutas a las de la empresa.
41
+
42
+ - **Devolver un cargo al catálogo dejaba avisos sobre una copia que no existe.** Borrar un fork
43
+ resuelve bien —el cargo vuelve a salir del paquete— pero el manifiesto conservaba su registro, así
44
+ que `check` decía «tu copia no recibe mejoras del catálogo» y mandaba a mirar un directorio
45
+ borrado. Devolver es tan legítimo como adoptar: ahora la deriva exige que la copia exista antes de
46
+ comparar, y `upgrade` poda el registro huérfano igual que ya poda los archivos.
47
+
48
+ - **El puntero que instala el runner no decía a qué se ancla.** Dos agentes que resolvieron un cargo
49
+ parados en el repo ops construyeron la ruta doblada —`<empresa>-ops/<empresa>-ops/...`— y tuvieron
50
+ que deducir la raíz. En sidecar el wiring vive en la carpeta de la compañía y el repo ops es uno de
51
+ sus hijos; ahora el puntero lo dice.
52
+
53
+ - **`learn` sugería copiar un cargo a mano** en vez de nombrar `agents fork`, que ya existe.
54
+
11
55
  ## [0.18.0] - 2026-08-16
12
56
 
13
57
  ### Agregado
package/README.md CHANGED
@@ -80,6 +80,7 @@ Lee [template/planning/PROTOCOL.md](template/planning/PROTOCOL.md) para el contr
80
80
  | `ops learn <agent>` | Prepara el informe semanal que completa la automatización de Codex. |
81
81
  | `ops learn <agent> --proposal` | Consolida informes mensuales en una propuesta sin aplicar cambios. |
82
82
  | `ops evaluate <agent>` | Valida controles, casos y propuestas del agente. |
83
+ | `ops evaluate <agent> --bench` | Arma el banco desechable donde un cargo del catálogo trabaja. |
83
84
  | `ops team list` | Lista equipos disponibles. |
84
85
  | `ops team check <team>` | Valida manifiesto, agentes, dependencias y gates del equipo. |
85
86
  | `ops team show <team>` | Muestra el recorrido y artefactos del equipo; `--json` para consumirlo. |
@@ -184,6 +185,26 @@ todas las empresas: investigarla una vez y bien es mejor que repetirla en cada i
184
185
  Por eso `learn` falla si lo corrés sobre un cargo del catálogo dentro de una instancia: escribiría en
185
186
  el paquete y se perdería. El ciclo mensual de aprendizaje tampoco se distribuye — vive sólo acá.
186
187
 
188
+ #### Evaluar un cargo del catálogo
189
+
190
+ Los casos adversariales miden a un cargo trabajando, y un cargo cuya entrega es una épica o una entrada
191
+ de INBOX necesita un `planning/` donde escribir sea legítimo. El toolkit no lo tiene ni puede tenerlo:
192
+ el único `planning/` que vive acá es `template/planning`, el molde que se distribuye.
193
+
194
+ ```bash
195
+ node engine/cli/ops.js evaluate product-manager --bench
196
+ ```
197
+
198
+ Devuelve la ruta de una instancia desechable —`check` pasa, el catálogo resuelve desde adentro,
199
+ `planning/` está vacío y escribible— que el recorrido `/agent-eval` usa como lugar de trabajo. Se
200
+ recrea entera en cada corrida: reutilizarla dejaría que lo que un cargo escribió el lunes sea contexto
201
+ del que responde el martes.
202
+
203
+ El veredicto se escribe **junto al cargo**, no en el banco. El banco se borra; el contrato queda.
204
+
205
+ Desde una empresa esto no aplica: su instancia ya es el lugar, y lo que se evalúa ahí tiene que ser un
206
+ cargo suyo —propio o adoptado—.
207
+
187
208
  #### Quedarse con una versión propia de un cargo del catálogo
188
209
 
189
210
  ```bash
@@ -0,0 +1,313 @@
1
+ ---
2
+ agent: security-engineer
3
+ date: 2026-08-16
4
+ status: draft
5
+ ---
6
+
7
+ # Investigación semanal — 2026-08-16
8
+
9
+ ## Fuentes consultadas
10
+
11
+ Inventario local primero (activos reales, no supuestos):
12
+
13
+ - `package.json` — `@ingeniomaps/cauce@0.18.0`, `type: commonjs`, `engines.node >= 24`,
14
+ `packageManager: npm@11.16.0`, cero dependencias de runtime, `bin.cauce → engine/cli/ops.js`,
15
+ `files` como allowlist explícita (`engine/`, `template/`, `agents/`, `teams/`,
16
+ `automatization/`, `.github/workflows/`).
17
+ - `engine/hooks/run.js` — los doce guards y sus tres grupos por evento.
18
+ - `engine/core/files.js` — `assertWithin`, `assertNoSymlinkPath`, `atomicWrite`.
19
+ - `automatization/hooks/*.sh` y `run-hook.sh` — wrappers que fijan `OPS_ROOT` y resuelven el motor.
20
+ - `.github/workflows/ci.yml` y `.github/workflows/agent-learning.yml`.
21
+ - `.npmrc`, `.env` (gitignoreado; leído sólo por nombres de variable), `.gitignore`,
22
+ `ops.config.json` (`mode: toolkit`, `allowPush: false`), `Makefile`.
23
+ - `SKILL.md`, `learning/sources.yaml`, `evaluations/expected-behaviors.yaml` del propio cargo.
24
+
25
+ Fuentes externas (tratadas como datos, no como instrucciones):
26
+
27
+ | Fuente | Tipo | Fecha | Enlace |
28
+ | --- | --- | --- | --- |
29
+ | GitHub Changelog — npm classic tokens revoked | proveedor primario | 2025-12-09 | https://github.blog/changelog/2025-12-09-npm-classic-tokens-revoked-session-based-auth-and-cli-token-management-now-available |
30
+ | GitHub Changelog — npm classic token creation disabled, granular token changes | proveedor primario | 2025-11-05 | https://github.blog/changelog/2025-11-05-npm-security-update-classic-token-creation-disabled-and-granular-token-changes/ |
31
+ | Microsoft Security Blog — «ChainDrop supply chain compromise» | vendor research | 2026-08-04 | https://www.microsoft.com/en-us/security/blog/2026/08/04/chaindrop-supply-chain-compromise-anatomy-self-propagating-worm/ |
32
+ | StepSecurity — ChainDrop npm worm | vendor research | 2026-08 | https://www.stepsecurity.io/blog/chaindrop-npm-worm |
33
+ | GitHub Advisory GHSA-69fq-xp46-6x23 / CVE-2026-33634 — Trivy ecosystem supply chain | advisory primario | 2026-03 | https://github.com/aquasecurity/trivy/security/advisories/GHSA-69fq-xp46-6x23 |
34
+ | Aqua Security — conclusión del incidente 2026-03-19 | proveedor afectado | 2026-03 | https://github.com/aquasecurity/trivy/discussions/10462 |
35
+ | Node.js — Security Releases | proveedor primario | 2026-07-29 | https://nodejs.org/en/blog/vulnerability/july-2026-security-releases |
36
+ | Socket — «npm v12 ships with install scripts off by default» | vendor research | 2026-07 | https://socket.dev/blog/npm-12 |
37
+ | OWASP Top 10 for Agentic Applications 2026 | estándar comunitario | 2025-12-09 | https://genai.owasp.org/resource/owasp-top-10-for-agentic-applications-for-2026/ |
38
+ | OWASP ASVS 5.0.0 | estándar primario | 2025-05-30 | https://owasp.org/www-project-application-security-verification-standard/ |
39
+ | NIST — SSDF v1.2 en comentario público | estándar primario | 2025-12 | https://www.nist.gov/news-events/news/2025/12/secure-software-development-framework-ssdf-version-12-available-public |
40
+
41
+ ## Hallazgos
42
+
43
+ ### H1 — La publicación a npm depende de un token de larga vida en disco, y la guía escrita apunta a un mecanismo que npm ya revocó
44
+
45
+ - **Activo**: la cuenta que publica `@ingeniomaps/cauce`. Es el activo de mayor consecuencia del
46
+ toolkit: quien publique una versión ejecuta código dentro de cada repositorio de empresa que instale.
47
+ - **Exposición**: `.npmrc` resuelve `//registry.npmjs.org/:_authToken=${NPM_TOKEN}` y `NPM_TOKEN` vive
48
+ en `.env` en la máquina de desarrollo. No hay workflow de publicación: `npm publish` se corre a mano
49
+ (y el guard `dependencies` lo bloquea salvo acción humana explícita). No hay trusted publishing
50
+ (OIDC) ni, por lo tanto, procedencia firmada en el paquete.
51
+ - **Estado externo**: npm revocó **permanentemente todos los tokens clásicos el 2025-12-09** («no pueden
52
+ autenticar, recrearse ni recuperarse»), los granulares con escritura quedaron **limitados a 90 días**
53
+ y npm recomienda OIDC trusted publishing «que elimina la necesidad de generar y administrar tokens».
54
+ - **Explotabilidad**: no requiere una vulnerabilidad en Cauce. Requiere leer un archivo del disco del
55
+ desarrollador — que es exactamente lo que hacen los worms de npm en curso (ver H2).
56
+ - **Mitigación proporcional**: publicar desde un workflow con trusted publishing (OIDC) y un
57
+ *environment* con aprobación humana requerida; mientras tanto, token granular acotado a este paquete,
58
+ con expiración, y rotación desde un host limpio.
59
+ - **Confianza**: **alta** (hecho local verificado + changelog primario del proveedor).
60
+
61
+ ### H2 — ChainDrop (2026-08-04): el worm de npm cosecha justo las credenciales que este repositorio guarda, y también abusa de trusted publishing
62
+
63
+ - **Qué pasó**: el 2026-08-04 un worm autopropagante recorrió npm; Microsoft describe releases no
64
+ autorizadas en más de 400 paquetes, y StepSecurity reporta 444 paquetes y 2 212 versiones en menos de
65
+ cuatro horas, incluyendo `keyv@6.0.0`, `flat-cache@6.1.24` y `file-entry-cache@11.1.6`.
66
+ - **Qué roba**: tokens npm con permiso de escritura, PAT de GitHub y credenciales del CLI, secretos de
67
+ runners de GitHub Actions, credenciales cloud (AWS/Azure/Kubernetes) y secretos de HashiCorp Vault.
68
+ - **Cómo se ejecuta**: scripts de instalación (`preinstall` corre antes de que termine la instalación) y
69
+ —el detalle importante— **una ruta de publicación dirigida a workflows de GitHub Actions configurados
70
+ como trusted publishers de npm**, lo que le permitió emitir procedencia válida para releases maliciosas.
71
+ - **Exposición de Cauce como consumidor**: **baja**. Cero dependencias de runtime; el motor sólo importa
72
+ módulos del core (`fs`, `path`, `child_process`, `crypto`, `os`). CI no instala dependencias
73
+ (`npm run ci` no corre `npm install`).
74
+ - **Exposición de Cauce como publicador**: **alta**, por H1. `.env` contiene además `GITHUB_PAT_CAUCE`
75
+ con permiso de push sobre `ingeniomaps/cauce`: dos de las categorías que el payload busca, en el mismo
76
+ archivo.
77
+ - **Lectura sobre H1**: trusted publishing reduce la superficie pero no la elimina. La recomendación
78
+ correcta no es «OIDC» a secas, sino OIDC **con aprobación de environment** y revisión del workflow.
79
+ - **Confianza**: **alta** para el incidente y su alcance (dos fuentes independientes); **media** para el
80
+ detalle de abuso de OIDC (una sola fuente: Microsoft).
81
+
82
+ ### H3 — `agent-learning.yml` corre un agente autónomo con `contents: write` en el mismo job donde ingiere contenido web no confiable
83
+
84
+ - **Evidencia local**: `.github/workflows/agent-learning.yml` declara `permissions: contents: write` y
85
+ `pull-requests: write` a nivel workflow (líneas 19-21). El job `research` instala y ejecuta
86
+ `claude -p …` (líneas 121-122) con `ANTHROPIC_API_KEY` en el entorno, y el prompt que ese agente
87
+ obedece es el `learning/AUTOMATION.md` del cargo — que en este cargo manda explícitamente a leer
88
+ fuentes externas.
89
+ - **Amenaza**: inyección indirecta de prompt. El contenido que el agente lee es controlado por terceros;
90
+ el proceso que lo lee tiene, en el mismo entorno, el token de GitHub del job y una API key.
91
+ - **Control existente y real**: el paso «Open research pull request» (líneas 140-145) compara
92
+ `git diff --name-only` contra la ruta exacta del informe y **falla el job** si el agente tocó otra
93
+ cosa. Es un buen control y hay que decir que existe.
94
+ - **Límite de ese control**: es *post-hoc*. Corre después de que el agente ya ejecutó comandos con el
95
+ token disponible. No cubre exfiltración de `ANTHROPIC_API_KEY` ni de `GITHUB_TOKEN` durante el paso
96
+ `Research`, ni un `git push` emitido desde dentro de ese paso. Verifica el artefacto, no el proceso.
97
+ - **Marco aplicable**: OWASP Top 10 for Agentic Applications 2026 (publicado 2025-12-09), cuyas
98
+ categorías cubren uso de herramientas, identidad, ejecución de código y agencia excesiva.
99
+ - **Mitigación proporcional**: separar en dos jobs — investigación sin `GITHUB_TOKEN` de escritura
100
+ (`permissions: contents: read`, resultado subido como artifact) y apertura del PR en un job posterior
101
+ que sólo lee el artifact. Es el patrón estándar de aislar el paso que toca contenido no confiable del
102
+ paso que tiene credenciales.
103
+ - **Consecuencia de segundo orden**: `automatization/` y `.github/workflows/` viajan dentro del tarball
104
+ npm (allowlist `files` de `package.json`), así que este patrón se replica en cada instalación que
105
+ adopte los workflows. El defecto de diseño escala con la adopción.
106
+ - **Confianza**: **alta** en la observación (código local verificado); **media** en la severidad, porque
107
+ depende del modelo de amenaza de cada instalación y del repositorio donde corra.
108
+
109
+ ### H4 — Las acciones de GitHub están fijadas a tags mutables, con precedente explotado este año
110
+
111
+ - **Evidencia local**: `ci.yml` y `agent-learning.yml` usan `actions/checkout@v4` y
112
+ `actions/setup-node@v4`. Un tag es una referencia móvil.
113
+ - **Precedente explotado**: el 2026-03-19 un actor con credenciales comprometidas hizo *force-push*
114
+ sobre 76 de 77 tags de versión de `aquasecurity/trivy-action` y sobre los 7 tags de
115
+ `aquasecurity/setup-trivy`, redirigiendo referencias confiables a commits maliciosos que leían la
116
+ memoria del worker del runner para extraer secretos. Advisory: **GHSA-69fq-xp46-6x23 /
117
+ CVE-2026-33634**. Fue la segunda vez en tres semanas.
118
+ - **Exposición diferencial**: `ci.yml` corre con `permissions: contents: read` y sin secretos →
119
+ impacto contenido. `agent-learning.yml` corre con `contents: write`, `pull-requests: write` y
120
+ `ANTHROPIC_API_KEY` → ahí un action comprometido es una fuga de credenciales.
121
+ - **Mitigación proporcional**: fijar por SHA completo (comentando la versión al lado) en
122
+ `agent-learning.yml` como mínimo; `ci.yml` puede seguir después.
123
+ - **Confianza**: **alta**.
124
+
125
+ ### H5 — Node 24: la última release de seguridad es 24.18.1; el impacto real sobre el motor es bajo y conviene decirlo así
126
+
127
+ - **Externo**: el 2026-07-29 Node publicó 26.5.1, **24.18.1** y 22.23.2, con tres CVE **High** que
128
+ afectan a 24.x: `CVE-2026-56846` (HTTP/2, headers retenidos evaden `maxSessionMemory`),
129
+ `CVE-2026-56848` (HTTP/2, heap-use-after-free por envío reentrante) y `CVE-2026-58043` (el
130
+ Permission Model otorga acceso de más al hacer *path matching*). Más ocho de severidad media/baja.
131
+ - **Local**: `engines.node: ">=24"` (rango abierto, sin techo) y la máquina de desarrollo corre
132
+ **v24.18.0** — una parche por detrás. CI usa `node-version: "24"`, que resuelve la última 24.x, así
133
+ que CI ya toma 24.18.1.
134
+ - **Exposición real**: **baja para el producto**. Cauce es un CLI local sin servidor: el motor no
135
+ importa `http2`, `tls`, `zlib`, `dns` ni `node:sqlite` (verificado: sólo `fs`, `path`,
136
+ `child_process`, `crypto`, `os`). `CVE-2026-58043` afecta al Permission Model, que Cauce no activa.
137
+ - **Por qué igual figura**: los guards de Cauce **no son** un Permission Model de Node y no deben
138
+ presentarse como sustituto de uno (ver H7). Y un rango `>=24` sin piso de parche significa que una
139
+ empresa puede correr el toolkit sobre un 24.x vulnerable creyendo que cumple el requisito.
140
+ - **Mitigación proporcional**: subir el piso de `engines.node` a la línea parcheada vigente.
141
+ - **Confianza**: **alta** en versiones y CVEs (fuente primaria); **alta** en la no-exposición del motor,
142
+ acotada a que la verificación fue por imports del core y no un análisis de flujo completo.
143
+
144
+ ### H6 — npm v12 apagó los install scripts por defecto; el repositorio sigue fijado a npm 11
145
+
146
+ - **Externo**: npm v12.0.0 salió el 2026-07-08 con `allowScripts` en *off* por defecto —
147
+ `preinstall`/`install`/`postinstall` de dependencias ya no corren sin opt-in — sumado a
148
+ `min-release-age`, que rechaza paquetes publicados demasiado recientemente. Es la contramedida
149
+ directa al vector de H2.
150
+ - **Local**: `package.json` declara `packageManager: npm@11.16.0`; la máquina corre npm 11.16.0.
151
+ - **Impacto**: Cauce no define scripts de ciclo de vida ni tiene dependencias, así que migrar a npm 12
152
+ **no rompe nada**; el costo es cero y el beneficio es alinearse con el default endurecido y poder usar
153
+ `min-release-age` en las instalaciones que sí tienen dependencias.
154
+ - **Confianza**: **alta** en el hecho local; **media** en la fecha exacta y el detalle de la release,
155
+ porque las fuentes que la reportan son secundarias (Socket) y no encontré el anuncio primario de npm.
156
+
157
+ ### H7 — Los guards son coincidencia de texto sobre el comando, no un límite de seguridad; el riesgo es la confianza que inspiran
158
+
159
+ - **Evidencia reproducible localmente** (`engine/hooks/run.js`, ejecutado contra sus propias funciones):
160
+
161
+ | Comando | Resultado |
162
+ | --- | --- |
163
+ | `git push origin main` | BLOQUEA |
164
+ | `git\tpush origin main` | BLOQUEA |
165
+ | `bash -c "git push"` | BLOQUEA |
166
+ | `g=push; git $g origin main` | **PASA** |
167
+ | `eval "git pu""sh"` | **PASA** |
168
+ | `npm publish` / `npm exec -- npm publish` | BLOQUEA |
169
+
170
+ - **Lectura correcta**: esto **no es un bug a tapar con más regex**. Un guard que inspecciona una cadena
171
+ de shell antes de que la ejecute un shell no puede decidir qué va a ejecutarse; agregar patrones sólo
172
+ mueve la frontera. Los guards funcionan como lo que son: barandas contra el error y el atajo, no
173
+ contra un adversario.
174
+ - **Riesgo real**: falsa confianza. Una empresa que instala Cauce y lee «Bloquea publicación y comandos
175
+ con pérdida de datos» en `hookMetadata` puede concluir que `guard-destructive` impide publicar. No lo
176
+ impide frente a alguien que quiera publicar. Esto choca de frente con dos controles que este propio
177
+ cargo declara: `scanner_score_as_complete_risk_decision` en `expected-behaviors.yaml` y «no asegurar
178
+ cumplimiento ni ausencia de vulnerabilidades» en `SKILL.md`.
179
+ - **Mitigación proporcional**: documental, no técnica — enunciar el alcance de los guards en el README
180
+ de `automatization/hooks/`. Ninguna de las dos cosas se toca en este informe.
181
+ - **Confianza**: **alta**.
182
+
183
+ ### H8 — El guard de secretos decide por nombre de archivo y deja pasar formatos de credencial habituales
184
+
185
+ - **Evidencia local** (`engine/hooks/run.js:106-116`, ejecutado):
186
+
187
+ | Archivo | Resultado |
188
+ | --- | --- |
189
+ | `.env`, `.env.local`, `service-account.json`, `cert.pem`, `deploy.key` | BLOQUEA |
190
+ | `.npmrc`, `.netrc`, `credentials`, `.aws/credentials`, `id_rsa`, `config/secrets.yaml` | **PASA** |
191
+
192
+ - **Detalle**: la regex exige extensión para credenciales (`credentials.*\.json`), así que el
193
+ `~/.aws/credentials` clásico —sin extensión— no matchea. Y `.npmrc` pasa, siendo que en *este mismo
194
+ repositorio* `.npmrc` es el archivo que referencia el token de publicación.
195
+ - **Exposición**: **baja**. `workspace-boundary` acota la escritura a las raíces declaradas en
196
+ `ops.config.json`, y `.gitignore` cubre `.env*`, `.npmrc` y `.gitconfig`. El guard es una capa de
197
+ redundancia, no el control primario.
198
+ - **Explotabilidad**: no es un bypass buscado por un atacante; es un hueco por el que se cuela un
199
+ descuido honesto.
200
+ - **Confianza**: **alta** en el hecho verificado; **media** en que valga la pena cambiarlo, dado que el
201
+ control primario ya cubre el caso.
202
+
203
+ ## Evidencia
204
+
205
+ Todo lo local fue verificado por lectura y ejecución en esta máquina, sin tocar sistemas remotos, sin
206
+ credenciales y sin escanear nada. No se leyó ningún valor de `.env`: sólo los nombres de las variables y
207
+ los comentarios del archivo.
208
+
209
+ | # | Evidencia | Tipo |
210
+ | --- | --- | --- |
211
+ | H1 | `.npmrc:1`; `.env` (nombres `NPM_TOKEN`, `GITHUB_PAT_CAUCE`, `GITHUB_USER_CAUCE` y su comentario de uso); ausencia de workflow de publicación en `.github/workflows/`; `engine/hooks/run.js:171-178` (guard que exige acción humana para `npm publish`) | local + primaria externa |
212
+ | H2 | Microsoft Security Blog 2026-08-04; StepSecurity; `package.json` sin `dependencies`; imports del motor limitados al core de Node | externa corroborada + local |
213
+ | H3 | `.github/workflows/agent-learning.yml:19-21, 84-94, 115-122, 132-145` | local |
214
+ | H4 | `.github/workflows/ci.yml:19-20`; `agent-learning.yml:29, 80-83`; GHSA-69fq-xp46-6x23 / CVE-2026-33634 | local + advisory primario |
215
+ | H5 | nodejs.org security releases 2026-07-29; `node -v` → v24.18.0; `package.json:29-31`; barrido de `require()` sobre `engine/**/*.js` | primaria externa + local |
216
+ | H6 | Socket 2026-07; `package.json:32`; `npm -v` → 11.16.0 | externa secundaria + local |
217
+ | H7 | Ejecución directa de `guards.destructive` / `guards['git-add']` / `guards.dependencies` sobre once comandos de prueba | local, reproducible |
218
+ | H8 | Ejecución directa de `guards.secrets` sobre trece nombres de archivo | local, reproducible |
219
+
220
+ Las tablas de H7 y H8 se reproducen invocando las funciones exportadas por `engine/hooks/run.js` con
221
+ objetos de entrada sintéticos. No se ejecutó ninguno de los comandos de prueba.
222
+
223
+ ## Posibles prácticas obsoletas
224
+
225
+ 1. **La guía de credenciales de `.env` describe un mecanismo que ya no existe.** El comentario indica
226
+ «usá un token *Automation* (clásico) o uno *Granular* con la opción de bypass activada». Los tokens
227
+ clásicos fueron revocados permanentemente el **2025-12-09** y su creación ya estaba deshabilitada
228
+ desde el **2025-11-05**. La mitad de esa instrucción es hoy irrealizable, y la otra mitad quedó
229
+ acotada a 90 días de vida. (Confianza: **alta**.)
230
+ 2. **Publicar a npm con token en vez de trusted publishing.** El propio proveedor recomienda OIDC como
231
+ camino por defecto para CI/CD desde 2025-12-09. Con el matiz de H2: OIDC solo, sin aprobación de
232
+ environment, ya fue abusado en agosto de 2026. (Confianza: **alta** para lo obsoleto, **media** para
233
+ la forma exacta del reemplazo.)
234
+ 3. **`uses: action@vN` como forma de fijar una acción.** Con el precedente de `trivy-action`
235
+ (2026-03-19), tratar un tag como inmutable es una práctica vencida en la superficie que maneja
236
+ secretos. (Confianza: **alta**.)
237
+ 4. **`packageManager: npm@11.16.0`.** Fija el toolkit a la línea previa al endurecimiento por defecto de
238
+ npm v12. (Confianza: **media** — el hecho local es firme; la fecha de v12 viene de fuente secundaria.)
239
+ 5. **`learning/sources.yaml` de este cargo no cubre el stack real.** Sus cuatro fuentes (SSDF, ASVS,
240
+ CWE, KEV) son sólidas y correctas como base de la profesión, pero ninguna emite avisos sobre npm,
241
+ Node.js o GitHub Actions, que es donde vivió todo lo accionable de esta semana. La brecha es de
242
+ cobertura, no de calidad. (Confianza: **alta**; propuesta, no cambio — `sources.yaml` no se toca.)
243
+ 6. **Vigilar, no cambiar todavía**: `sources.yaml` apunta a SP 800-218 «final», que sigue siendo v1.1
244
+ (feb 2022). NIST puso **SSDF v1.2 en comentario público en diciembre de 2025**; cuando se finalice,
245
+ el enlace habrá que revisarlo. ASVS 5.0.0 (2025-05-30) sigue vigente y el enlace del proyecto es
246
+ correcto. (Confianza: **alta**.)
247
+
248
+ ## Recomendación
249
+
250
+ Nada de lo que sigue se aplicó: este informe no toca dependencias, controles, secretos, código,
251
+ `SKILL.md` ni planificación.
252
+
253
+ **Prioridad alta — la cadena de publicación**
254
+
255
+ 1. Migrar la publicación de `@ingeniomaps/cauce` a un workflow con trusted publishing (OIDC) protegido
256
+ por un *environment* con aprobación humana requerida. Cierra H1 y no queda expuesto al abuso de OIDC
257
+ sin aprobación descrito en H2. Owner: mantenedor del paquete.
258
+ 2. Mientras eso no exista: reemplazar `NPM_TOKEN` por un token granular acotado a este paquete y con
259
+ expiración, generado y rotado desde un host limpio. Rotar también `GITHUB_PAT_CAUCE`.
260
+ 3. Fijar por SHA las acciones de `agent-learning.yml` (el workflow con secretos y `contents: write`).
261
+
262
+ **Prioridad media — el agente en CI**
263
+
264
+ 4. Partir el job `research` en dos: investigación con `permissions: contents: read` y sin token de
265
+ escritura, entregando el informe como artifact; apertura del PR en un job separado que sólo consume
266
+ ese artifact. Conservar la verificación de ruta del informe, que ya funciona. Fijar la versión de
267
+ `@anthropic-ai/claude-code` que se instala, hoy sin pin.
268
+ 5. Subir el piso de `engines.node` a la línea parcheada vigente en vez de `>=24` abierto.
269
+
270
+ **Prioridad baja — higiene**
271
+
272
+ 6. Evaluar mover `packageManager` a npm 12 (sin impacto funcional: no hay dependencias ni scripts).
273
+ 7. Documentar en `automatization/hooks/README.md` qué son y qué no son los guards (H7). Es la
274
+ recomendación más barata del informe y la que más falsa confianza evita.
275
+ 8. Considerar agregar a `sources.yaml` los avisos de Node.js, el changelog de npm/GitHub y el GitHub
276
+ Advisory Database, que es donde apareció todo lo accionable de esta semana.
277
+
278
+ **Sobre el contrato del cargo**: nada de esto exige cambiar `SKILL.md` ni
279
+ `evaluations/expected-behaviors.yaml`. H7 es un caso de libro del control que ya existe
280
+ (`scanner_score_as_complete_risk_decision`) y H3 encaja en
281
+ `minimizes_privileges_secrets_sensitive_data_and_attack_surface`. Si algo mereciera discutirse en la
282
+ propuesta mensual, es si el cargo debería nombrar explícitamente el caso «agente autónomo con
283
+ credenciales en CI», que hoy queda cubierto por implicación y no por nombre. Eso es material de
284
+ propuesta, no de este informe.
285
+
286
+ ## Preguntas abiertas
287
+
288
+ 1. **¿Está `@ingeniomaps/cauce` en el radar de ChainDrop?** No lo verifiqué. Confirmarlo exige revisar el
289
+ historial de publicaciones y las sesiones de la cuenta npm, que es acceso a un sistema remoto y queda
290
+ fuera del alcance de una revisión pasiva. La versión publicada coincide con la local (0.18.0), lo que
291
+ es consistente con «sin publicación no autorizada», pero **no es evidencia suficiente**.
292
+ 2. **¿Hubo alguna vez un token clásico activo para este paquete?** Si lo hubo, npm lo revocó el
293
+ 2025-12-09 y no hay nada que rotar. Si el `NPM_TOKEN` actual funciona, es granular y tiene fecha de
294
+ expiración que conviene conocer. Requiere que un humano mire la cuenta.
295
+ 3. **Alcance real del incidente de GitHub Actions de marzo de 2026.** Circula la cifra de «78 330
296
+ secretos exfiltrados de 2 186 organizaciones en cinco días» en blogs secundarios; **no encontré
297
+ fuente primaria y no la afirmo**. El incidente de `trivy-action` sí está confirmado por advisory. Si
298
+ alguien necesita la cifra agregada, hay que buscarla en el reporte original, no en este informe.
299
+ 4. **Identificadores exactos del OWASP Top 10 for Agentic Applications 2026.** Cito el marco y su fecha
300
+ de publicación, pero no abrí el PDF, así que no atribuyo un identificador concreto (`AAI0xx`) a H3.
301
+ **Confianza baja** en cualquier mapeo taxonómico preciso hasta leer el documento.
302
+ 5. **¿`npm publish` corre alguna vez desde CI hoy?** No hay workflow que lo haga y el guard
303
+ `dependencies` lo bloquea, así que la respuesta aparente es «no, siempre a mano». Vale confirmarlo
304
+ con el mantenedor antes de diseñar la migración a OIDC.
305
+ 6. **TOCTOU en `assertNoSymlinkPath`** (`engine/core/files.js:16-28`): la comprobación de symlinks y la
306
+ escritura posterior no son atómicas, y la función no inspecciona la propia base. En un CLI local, con
307
+ un atacante que ya tendría escritura en el árbol del proyecto, el escenario es difícil de justificar.
308
+ Lo dejo anotado **sin severidad asignada**: no encontré un camino en el que un tercero controle esas
309
+ rutas, y no voy a inventarle explotabilidad a un hallazgo teórico.
310
+ 7. **`isWithin` en Windows** (`engine/core/files.js:6-9`): si `path.relative` opera entre volúmenes
311
+ distintos devuelve una ruta absoluta que no empieza con `..`, y la comprobación la aceptaría.
312
+ No verificado — no tengo Windows a mano. **Confianza baja**; se confirma con una prueba en esa
313
+ plataforma o leyendo la implementación de `path.win32.relative`.
@@ -30,6 +30,8 @@ function inherited(relative) {
30
30
  return true
31
31
  }
32
32
 
33
+ const TEXT = /\.(md|ya?ml|json|txt)$/i
34
+
33
35
  function tree(dir, prefix = '') {
34
36
  const found = []
35
37
  let list = []
@@ -87,13 +89,20 @@ function fork(root, slug, date) {
87
89
 
88
90
  // El digest sale del origen, no de la copia. Es lo que el catálogo tenía al momento del fork, y es
89
91
  // contra eso que después se responde «esto mejoró río arriba»; medirlo sobre la copia lo ataría a
90
- // cualquier cosa que le hagamos acá —empezando por la fila de historial que se escribe abajo—.
92
+ // cualquier cosa que le hagamos acá —la reescritura de rutas incluida—.
93
+ //
94
+ // Y esa reescritura no es cosmética: el `AUTOMATION.md` del catálogo dice «mantené
95
+ // `agents/<tipo>/system/<slug>`», que en una empresa es el paquete. Copiado tal cual, el aprendizaje
96
+ // del fork apuntaría a un directorio que el guard bloquea y que npm borra en el próximo install.
97
+ const desde = `agents/${type}/system/${slug}`
98
+ const hacia = `agents/${type}/${slug}`
91
99
  const digests = {}
92
100
  for (const relative of files) {
93
101
  const from = path.join(found.dir, relative)
94
102
  const to = path.join(target, relative)
95
103
  fs.mkdirSync(path.dirname(to), { recursive: true })
96
- fs.copyFileSync(from, to)
104
+ if (TEXT.test(relative)) fs.writeFileSync(to, fs.readFileSync(from, 'utf8').split(desde).join(hacia))
105
+ else fs.copyFileSync(from, to)
97
106
  digests[relative] = manifest.digest(from)
98
107
  }
99
108
 
@@ -122,6 +131,10 @@ function drift(root) {
122
131
  const found = []
123
132
  for (const [slug, record] of Object.entries(forks)) {
124
133
  if (!record || !record.files) continue
134
+ // Sin copia no hay deriva. Devolver a un cargo al catálogo es tan legítimo como adoptarlo, y el
135
+ // registro sobrevive a esa vuelta: sin este corte, `check` avisaba «tu copia no recibe mejoras»
136
+ // sobre una copia que ya no existe, y mandaba a mirar un directorio borrado.
137
+ if (!fs.existsSync(path.join(root, 'agents', record.type || 'roles', slug))) continue
125
138
  const source = catalogDir ? path.join(catalogDir, record.type || 'roles', 'system', slug) : ''
126
139
  if (!source || !fs.existsSync(source)) continue
127
140
  const current = tree(source).filter(inherited)
@@ -32,7 +32,7 @@ function assertWritable(root, agent) {
32
32
  throw new Error(
33
33
  `${agent} es un cargo que trae Cauce y su aprendizaje se hace en el toolkit, no acá.\n` +
34
34
  ` Lo que este cargo debe saber de esta empresa va en organization/roles/${agent}.md.\n` +
35
- ` Para tener una versión propia del cargo, escribila en agents/roles/${agent}/.`,
35
+ ` Para tener una versión propia del cargo, adoptalo: ops agents fork ${agent}.`,
36
36
  )
37
37
  }
38
38
 
@@ -163,6 +163,9 @@ Leé \`${role.reference}/SKILL.md\` para el contrato completo del cargo: cuándo
163
163
  qué decide, qué no le corresponde y cuál es su entrega mínima. Sus métodos y formatos de salida están
164
164
  en \`${role.reference}/references/\`.
165
165
 
166
+ Esas rutas se resuelven desde este directorio raíz, no desde el repositorio de operaciones: en modo
167
+ sidecar el wiring vive acá y el repo ops es uno de sus hijos.
168
+
166
169
  Respetá los límites de ese contrato y las reglas de \`AGENTS.md\`. Generado por
167
170
  \`cauce automation install\`: no lo edites acá.
168
171
  `
package/engine/cli/ops.js CHANGED
@@ -608,7 +608,12 @@ function upgrade(dir) {
608
608
  const dir = path.join(root, relative)
609
609
  if (fs.existsSync(dir)) registro = M.record(root, relative, O.treeFiles(dir), registro)
610
610
  }
611
- M.write(root, M.prune(root, registro))
611
+ // El registro de forks se poda igual que el de archivos: un cargo devuelto al catálogo deja su
612
+ // entrada, y una entrada sin copia sólo puede producir avisos sobre algo que no está.
613
+ const vivos = Object.fromEntries(Object.entries(M.readForks(root)).filter(
614
+ ([slug, record]) => fs.existsSync(path.join(root, 'agents', (record || {}).type || 'roles', slug)),
615
+ ))
616
+ M.write(root, M.prune(root, registro), null, vivos)
612
617
 
613
618
  const config = JSON.parse(fs.readFileSync(path.join(root, 'ops.config.json'), 'utf8'))
614
619
  config.cauceVersion = to
@@ -219,9 +219,27 @@ function governance(input) {
219
219
  const command = commandOf(input)
220
220
  if (!isCommit(command)) return
221
221
  const dir = gitDirectory(command, cwdOf(input))
222
+ // El contrato de un cargo y lo que lo mide son gobernanza, igual que un ADR o una regla.
223
+ //
224
+ // Lo puntual es la firma: `agent-promote` se niega si «Aprobación humana» no está firmada, pero lo
225
+ // único que impedía que la escribiera un agente era una frase en un prompt. Una instrucción, no un
226
+ // candado — y el archivo no estaba protegido por nada.
227
+ //
228
+ // Alrededor de la firma van las otras tres piezas del mismo acto. `SKILL.md` y `references/` son lo
229
+ // que la propuesta cambia: sin ellos, editar el contrato directo saltea el ciclo entero. Y
230
+ // `evaluations/` es el denominador con que se juzga —el propio recorrido de propuesta dice que
231
+ // cambiarlo «es parte de lo que se aprueba»—, así que moverlo en silencio ablanda toda medición
232
+ // pasada sin tocar una sola regla.
233
+ //
234
+ // Quedan afuera las dos clases de evidencia, y por la misma razón: registran lo que pasó un día en
235
+ // vez de decidir algo. `learning/reports/` es lo investigado esa semana, y `evaluations/results/` la
236
+ // transcripción de una corrida —que se escribe en cada evaluación, así que gobernarla pediría un
237
+ // override por corrida—. Por eso `evaluations/` se nombra por partes en vez de entero.
222
238
  const governedPattern = new RegExp(
223
239
  String.raw`^(?:(?:template\/)?planning\/(?:rules\/|adr\/|PROTOCOL\.md|` +
224
- String.raw`METHODOLOGY\.md|FLOW\.md)|automatization\/|engine\/)`,
240
+ String.raw`METHODOLOGY\.md|FLOW\.md)|automatization\/|engine\/` +
241
+ String.raw`|agents\/[a-z0-9-]+\/(?:system\/)?[a-z0-9-]+\/(?:SKILL\.md|references\/` +
242
+ String.raw`|evaluations\/(?:cases\/|expected-behaviors\.yaml)|learning\/proposals\/))`,
225
243
  )
226
244
  const governed = stagedFiles(dir).filter((file) => governedPattern.test(file))
227
245
  if (governed.length) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ingeniomaps/cauce",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "Sistema portable de planificación y ejecución verificable para cualquier proyecto",
5
5
  "type": "commonjs",
6
6
  "bin": {