cwe-explorer 0.1.0__tar.gz
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.
Potentially problematic release.
This version of cwe-explorer might be problematic. Click here for more details.
- cwe_explorer-0.1.0/LICENSE +21 -0
- cwe_explorer-0.1.0/MANIFEST.in +4 -0
- cwe_explorer-0.1.0/PKG-INFO +311 -0
- cwe_explorer-0.1.0/README.md +337 -0
- cwe_explorer-0.1.0/cwe_explorer/__init__.py +3 -0
- cwe_explorer-0.1.0/cwe_explorer/__main__.py +3 -0
- cwe_explorer-0.1.0/cwe_explorer/cli.py +181 -0
- cwe_explorer-0.1.0/cwe_explorer/data.py +159 -0
- cwe_explorer-0.1.0/cwe_explorer/hierarchy.py +297 -0
- cwe_explorer-0.1.0/cwe_explorer/render_html.py +69 -0
- cwe_explorer-0.1.0/cwe_explorer/render_term.py +291 -0
- cwe_explorer-0.1.0/cwe_explorer/tui.py +268 -0
- cwe_explorer-0.1.0/cwe_explorer/web/logo.png +0 -0
- cwe_explorer-0.1.0/cwe_explorer/web/viewer.css +617 -0
- cwe_explorer-0.1.0/cwe_explorer/web/viewer.html +60 -0
- cwe_explorer-0.1.0/cwe_explorer/web/viewer.js +815 -0
- cwe_explorer-0.1.0/cwe_explorer.egg-info/PKG-INFO +311 -0
- cwe_explorer-0.1.0/cwe_explorer.egg-info/SOURCES.txt +25 -0
- cwe_explorer-0.1.0/cwe_explorer.egg-info/dependency_links.txt +1 -0
- cwe_explorer-0.1.0/cwe_explorer.egg-info/entry_points.txt +3 -0
- cwe_explorer-0.1.0/cwe_explorer.egg-info/top_level.txt +1 -0
- cwe_explorer-0.1.0/docs/pypi.md +281 -0
- cwe_explorer-0.1.0/pyproject.toml +55 -0
- cwe_explorer-0.1.0/setup.cfg +4 -0
- cwe_explorer-0.1.0/tests/fixtures/mini_cwe.xml +94 -0
- cwe_explorer-0.1.0/tests/test_cwe_explorer.py +246 -0
- cwe_explorer-0.1.0/tools/pypi_readme.py +36 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Emi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cwe-explorer
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Explora la jerarquía CWE de MITRE (vista 1000): árbol en la terminal, explorador interactivo, grafo web y consulta de ascendientes y descendientes
|
|
5
|
+
Author-email: Emi <edm@unex.es>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: cwe,mitre,weakness,vulnerability,security,hierarchy,cli,tui,graph
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Environment :: Console :: Curses
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Information Technology
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: Natural Language :: Spanish
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Topic :: Security
|
|
25
|
+
Classifier: Topic :: Utilities
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
<!-- Generado por tools/pypi_readme.py a partir de README.md: no editar a mano. -->
|
|
32
|
+
|
|
33
|
+
# CWE Explorer
|
|
34
|
+
|
|
35
|
+
Explora la jerarquía de debilidades de la **vista 1000 de MITRE** (*Complete
|
|
36
|
+
Weakness Hierarchy*, antes *Research Concepts*) a partir de un identificador CWE.
|
|
37
|
+
Muestra el linaje hasta el pilar del que desciende y los descendientes que cuelgan
|
|
38
|
+
de ella, y lista sus ascendientes o descendientes, directos o por niveles, también
|
|
39
|
+
en JSON.
|
|
40
|
+
|
|
41
|
+
Al seleccionar cualquier nodo se muestra su **ID**, su **descripción**, su **tipo**
|
|
42
|
+
(Pillar, Class, Base, Variant o Compound), su estructura y estado, y su **linaje**
|
|
43
|
+
completo: el camino principal hasta el pilar y cualquier otro camino alternativo
|
|
44
|
+
cuando la debilidad tiene varios padres.
|
|
45
|
+
|
|
46
|
+
Funciona en dos sitios:
|
|
47
|
+
|
|
48
|
+
- **Terminal**: árbol a color, explorador interactivo (`--tui`) que se maneja con el
|
|
49
|
+
ratón o el teclado, y listados de ascendientes y descendientes (`--ancestors`,
|
|
50
|
+
`--descendants`).
|
|
51
|
+
- **Navegador**: una página HTML/CSS autocontenida (`--web`) con el grafo
|
|
52
|
+
interactivo.
|
|
53
|
+
|
|
54
|
+
## Instalación
|
|
55
|
+
|
|
56
|
+
Requiere Python 3.9 o superior y no tiene dependencias externas.
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
pip install cwe-explorer # o: pipx install cwe-explorer / uv tool install cwe-explorer
|
|
60
|
+
cwex 79
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Se instalan dos comandos equivalentes: `cwex`, el corto, y `cwe-explorer`. En
|
|
64
|
+
Windows, el explorador interactivo (`--tui`) necesita además `pip install
|
|
65
|
+
windows-curses`; el resto funciona sin nada más.
|
|
66
|
+
|
|
67
|
+
También se puede ejecutar como módulo, ya sea instalado o desde una copia del código:
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
python3 -m cwe_explorer 79
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
La primera ejecución descarga el catálogo oficial (`cwec_latest.xml.zip`, unos 2 MB)
|
|
74
|
+
desde cwe.mitre.org y lo guarda en caché.
|
|
75
|
+
|
|
76
|
+
## Uso
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
cwex 79 # árbol en la terminal (linaje + 2 niveles de descendientes)
|
|
80
|
+
cwex CWE-79 --tui # explorador interactivo en la terminal
|
|
81
|
+
cwex 79 --web # grafo interactivo en el navegador
|
|
82
|
+
cwex 664 --depth all # todos los descendientes (también --depth 0, 1, 2...)
|
|
83
|
+
cwex --search "race" # buscar debilidades por nombre
|
|
84
|
+
cwex 79 --ancestors # modo 1: ascendientes directos (padres)
|
|
85
|
+
cwex 79 --descendants # modo 2: descendientes directos (hijos)
|
|
86
|
+
cwex 79 --web -o xss.html --no-open # guardar el HTML sin abrirlo
|
|
87
|
+
cwex --update # descargar de nuevo el catálogo más reciente
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
El identificador se acepta como `79`, `CWE-79` o `cwe79`. Si el ID corresponde a una
|
|
91
|
+
agrupación (las antiguas *Categories*), a una vista o a una entrada obsoleta, el
|
|
92
|
+
programa explica por qué no aparece en la vista 1000.
|
|
93
|
+
|
|
94
|
+
### Terminal
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
Ba CWE-79 Improper Neutralization of Input During Web Page Generation
|
|
98
|
+
Tipo Base: debilidad todavía abstracta, pero con el detalle suficiente
|
|
99
|
+
para deducir métodos concretos de detección y prevención.
|
|
100
|
+
Estructura Simple
|
|
101
|
+
Estado Stable
|
|
102
|
+
Descripción The product does not neutralize or incorrectly neutralizes ...
|
|
103
|
+
Linaje CWE-707 › CWE-74 › CWE-79
|
|
104
|
+
Hijos CWE-80, CWE-81, CWE-83, CWE-84, CWE-85, CWE-86, CWE-87
|
|
105
|
+
...
|
|
106
|
+
|
|
107
|
+
Jerarquía en la vista 1000 (CWE 5.0, profundidad 2)
|
|
108
|
+
Pi CWE-707 Improper Neutralization
|
|
109
|
+
└─ Cl CWE-74 Improper Neutralization of Special Elements in Output Used by ...
|
|
110
|
+
└─ Ba CWE-79 Improper Neutralization of Input During Web Page Generation ◀
|
|
111
|
+
├─ Va CWE-80 Improper Neutralization of Script-Related HTML Tags in ...
|
|
112
|
+
├─ Va CWE-83 Improper Neutralization of Script in Attributes in a Web Page
|
|
113
|
+
│ └─ Va CWE-82 Improper Neutralization of Script in Attributes of IMG ...
|
|
114
|
+
...
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Cuando una debilidad tiene varios linajes, todos se dibujan en el árbol y el
|
|
118
|
+
subárbol de descendientes se muestra una sola vez.
|
|
119
|
+
|
|
120
|
+
Explorador interactivo (`--tui`):
|
|
121
|
+
|
|
122
|
+
| Acción | Ratón | Teclado |
|
|
123
|
+
| --- | --- | --- |
|
|
124
|
+
| Ver la ficha de un nodo | clic | `↑` `↓` / `j` `k` |
|
|
125
|
+
| Centrar la jerarquía en ese nodo | doble clic | `Enter` / `→` |
|
|
126
|
+
| Volver al nodo anterior | | `⌫` / `b` / `←` |
|
|
127
|
+
| Más o menos niveles de descendientes | | `+` / `-` |
|
|
128
|
+
| Ir a otra CWE o buscar por nombre | | `/` |
|
|
129
|
+
| Desplazar la ficha | rueda sobre la ficha | `J` / `K` |
|
|
130
|
+
| Abrir la ficha en cwe.mitre.org | | `o` |
|
|
131
|
+
| Abrir el nodo en el grafo web | | `w` |
|
|
132
|
+
| Salir | | `q` |
|
|
133
|
+
|
|
134
|
+
### Navegador
|
|
135
|
+
|
|
136
|
+
- **Clic** en un nodo: muestra su ficha en el panel lateral y resalta su linaje
|
|
137
|
+
hasta el pilar (trazo grueso: linaje principal).
|
|
138
|
+
- **Doble clic**, o el botón *Centrar la jerarquía*: reconstruye el grafo alrededor
|
|
139
|
+
de ese nodo.
|
|
140
|
+
- **`+N` / `−`** en un nodo: despliega u oculta sus hijos. En los ancestros sirve
|
|
141
|
+
para ver las debilidades hermanas.
|
|
142
|
+
- Buscador por ID o por nombre, selector de niveles de descendientes, zoom con la
|
|
143
|
+
rueda y arrastre para desplazarse.
|
|
144
|
+
- **Pantalla completa** (botón o tecla `F`): el grafo ocupa todo el monitor y la
|
|
145
|
+
ficha aparece como un panel flotante al hacer clic en un nodo (en el móvil, desde
|
|
146
|
+
abajo). Se cierra con `×` o `Esc`; `Esc` también sale de la pantalla completa.
|
|
147
|
+
- Aristas continuas para el padre primario y discontinuas para los padres
|
|
148
|
+
secundarios.
|
|
149
|
+
- La página incluye toda la vista 1000, así que se puede navegar a cualquier otra
|
|
150
|
+
CWE sin volver a generarla. El enlace `#cwe-89` abre directamente esa debilidad.
|
|
151
|
+
Respeta el modo oscuro del sistema y se adapta al móvil.
|
|
152
|
+
|
|
153
|
+
Por defecto el HTML se guarda en `~/.cache/cwe-explorer/html/`. Usa `-o` para
|
|
154
|
+
elegir otra ruta.
|
|
155
|
+
|
|
156
|
+
### Ascendientes y descendientes (modos 1 y 2)
|
|
157
|
+
|
|
158
|
+
Dada una CWE, lista sus ascendientes (modo 1) o sus descendientes (modo 2) en la
|
|
159
|
+
vista 1000. Por defecto sólo los directos, es decir, sus padres o sus hijos:
|
|
160
|
+
|
|
161
|
+
```sh
|
|
162
|
+
cwex 79 --ancestors # padres de CWE-79
|
|
163
|
+
cwex 79 --descendants # hijos de CWE-79
|
|
164
|
+
cwex 425 --ancestors --depth all # todos los ascendientes, hasta los pilares
|
|
165
|
+
cwex 664 --descendants --depth 2 # hijos y nietos
|
|
166
|
+
cwex 664 --descendants -d all --json > cwe-664.json
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
`--ascendientes` y `--descendientes` son alias de las mismas opciones. Con
|
|
170
|
+
`--depth N` se recorren N niveles y con `--depth all`, todos. Cuando hay más de un
|
|
171
|
+
nivel, el resultado se agrupa por distancia:
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
Ba CWE-425 Direct Request
|
|
175
|
+
Todos los ascendientes en la vista 1000 (CWE 5.0): 11 (3 Pillar, 6 Class, 2 Base)
|
|
176
|
+
|
|
177
|
+
Nivel 1 (padres)
|
|
178
|
+
Cl CWE-862 Missing Authorization (padre primario)
|
|
179
|
+
Ba CWE-288 Authentication Bypass Using an Alternate Path or Channel (padre secundario)
|
|
180
|
+
Cl CWE-424 Improper Protection of Alternate Path (padre secundario)
|
|
181
|
+
|
|
182
|
+
Nivel 2
|
|
183
|
+
Cl CWE-285 Improper Authorization (vía CWE-862 · linaje principal)
|
|
184
|
+
Ba CWE-306 Missing Authentication for Critical Functionality (vía CWE-288)
|
|
185
|
+
Cl CWE-638 Not Using Complete Mediation (vía CWE-424)
|
|
186
|
+
Pi CWE-693 Protection Mechanism Failure (vía CWE-424)
|
|
187
|
+
...
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Las notas entre paréntesis explican el parentesco:
|
|
191
|
+
|
|
192
|
+
- **Padres**: `padre primario` o `padre secundario`, según el atributo
|
|
193
|
+
`Ordinal="Primary"` del catálogo.
|
|
194
|
+
- **Hijos**: `vínculo secundario` cuando la CWE consultada no es su padre primario,
|
|
195
|
+
junto con cuál es ese padre primario.
|
|
196
|
+
- **Niveles siguientes**: `vía CWE-…`, que son los nodos del nivel anterior por los
|
|
197
|
+
que se llega. Además, `linaje principal` en los ascendientes que están en el
|
|
198
|
+
camino de padres primarios, y `fuera de su linaje principal` en los descendientes
|
|
199
|
+
que sólo llegan a la CWE consultada a través de algún padre secundario.
|
|
200
|
+
|
|
201
|
+
Con `--json` la salida incluye la CWE consultada (`cwe`, `name`, `abstraction`), el
|
|
202
|
+
modo (`mode`, `depth`), la versión del catálogo (`catalog`), `count` y una lista
|
|
203
|
+
`results` con un objeto por nodo:
|
|
204
|
+
|
|
205
|
+
| Campo | Significado |
|
|
206
|
+
| --- | --- |
|
|
207
|
+
| `id`, `name`, `abstraction`, `status`, `url` | Datos de la debilidad |
|
|
208
|
+
| `distance` | 1 = padre o hijo, 2 = abuelo o nieto… Si hay varios caminos, se usa el más corto |
|
|
209
|
+
| `via` | IDs del nivel anterior por los que se llega (en distancia 1, la propia CWE) |
|
|
210
|
+
| `primary_parent` | Sólo en distancia 1: si el `ChildOf` entre ambas es el primario. `null` en el resto |
|
|
211
|
+
| `primary_lineage` | En ascendientes: si está en el linaje principal de la CWE. En descendientes: si la CWE está en el linaje principal del nodo |
|
|
212
|
+
|
|
213
|
+
El campo `note` explica un resultado vacío o un linaje incompleto, y vale `null` en
|
|
214
|
+
el resto de casos. Desde Python se obtiene lo mismo con
|
|
215
|
+
`Hierarchy.relatives(cwe_id, "ancestors" | "descendants", depth=1)`. Con
|
|
216
|
+
`depth=None` se recorren todos los niveles:
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
from cwe_explorer.data import load_catalog
|
|
220
|
+
from cwe_explorer.hierarchy import Hierarchy
|
|
221
|
+
|
|
222
|
+
h = Hierarchy(load_catalog())
|
|
223
|
+
[r.id for r in h.relatives(79, "ancestors")] # [74]
|
|
224
|
+
[(r.id, r.distance) for r in h.relatives(83, "ancestors", depth=None)]
|
|
225
|
+
# [(79, 1), (74, 2), (707, 3)]
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Limitaciones de los ascendientes y descendientes
|
|
229
|
+
|
|
230
|
+
Los dos modos se pueden calcular con los datos que publica MITRE, pero la vista 1000
|
|
231
|
+
no es un árbol simple, y eso condiciona cómo leer el resultado. Las cifras son del
|
|
232
|
+
catálogo CWE 5.0 (2026-10-28).
|
|
233
|
+
|
|
234
|
+
1. **Es un grafo con varios padres, no un árbol.**
|
|
235
|
+
- 152 de las 957 debilidades tienen más de un padre directo, hasta 3 (CWE-396,
|
|
236
|
+
CWE-397, CWE-415, CWE-425 y CWE-1299). De los 1106 vínculos `ChildOf`, 160 son
|
|
237
|
+
secundarios.
|
|
238
|
+
- Por eso el modo 1 puede devolver varios padres, y la lista completa de
|
|
239
|
+
ascendientes puede abarcar varias ramas: 279 debilidades tienen más de un
|
|
240
|
+
linaje y 235 llegan a más de un pilar (CWE-425 llega a tres).
|
|
241
|
+
- MITRE sólo indica cuál es el padre primario. Entre los demás padres no hay
|
|
242
|
+
ningún orden de importancia.
|
|
243
|
+
2. **"Directo" significa un enlace `ChildOf`, no el nivel de abstracción
|
|
244
|
+
contiguo.** Los enlaces no siempre bajan un escalón Pillar → Class → Base →
|
|
245
|
+
Variant:
|
|
246
|
+
- 83 Base y 3 Variant (CWE-580, CWE-581 y CWE-1077) cuelgan directamente de un
|
|
247
|
+
Pillar, y 85 Variant cuelgan de una Class.
|
|
248
|
+
- 177 enlaces unen debilidades del mismo tipo: 88 Base–Base, 62 Class–Class y
|
|
249
|
+
27 Variant–Variant.
|
|
250
|
+
- Hay 2 Class que cuelgan de una Base: CWE-114 de CWE-73 y CWE-240 de CWE-237.
|
|
251
|
+
|
|
252
|
+
La herramienta devuelve lo que publica el catálogo; no deduce padres a partir
|
|
253
|
+
del tipo.
|
|
254
|
+
3. **Un mismo ascendiente puede estar a varias distancias.**
|
|
255
|
+
- En 25 debilidades hay caminos de distinta longitud hacia un mismo ascendiente.
|
|
256
|
+
Por ejemplo, CWE-102 llega a CWE-707 en 3 niveles y también en 4.
|
|
257
|
+
- En 4 casos, un padre directo es además un ascendiente más lejano (CWE-240,
|
|
258
|
+
CWE-415, CWE-425 y CWE-1262).
|
|
259
|
+
|
|
260
|
+
Cada nodo aparece una sola vez, a la distancia más corta, y `via` dice por dónde
|
|
261
|
+
se llega. `--depth N` también cuenta los niveles por el camino más corto. Los
|
|
262
|
+
demás caminos se ven con `cwex <id>`, que dibuja todos los linajes, o en el
|
|
263
|
+
grafo web.
|
|
264
|
+
4. **Sólo la vista 1000 y sólo `ChildOf`.**
|
|
265
|
+
- Otras vistas tienen su propia jerarquía. Algunas usan `ChildOf` con otro
|
|
266
|
+
`View_ID` (por ejemplo la 1003, la que usa NVD, o la 1305). Otras se organizan
|
|
267
|
+
con agrupaciones y `Has_Member` (por ejemplo la 699). Este programa no carga
|
|
268
|
+
ninguna de ellas.
|
|
269
|
+
- Las 431 agrupaciones (antes *Categories*) y las 62 vistas no son debilidades.
|
|
270
|
+
Si se consulta una, el programa explica qué es y termina con error en lugar de
|
|
271
|
+
listar sus miembros.
|
|
272
|
+
- El XML no publica relaciones `ParentOf`: los hijos se obtienen invirtiendo
|
|
273
|
+
los `ChildOf`.
|
|
274
|
+
5. **Las demás relaciones no son parentesco.** `CanPrecede` (148), `PeerOf` (103),
|
|
275
|
+
`CanAlsoBe` (28), `Requires` (13), `StartsWith` (3) y `Overlaps` (3) no son
|
|
276
|
+
jerárquicas y no se recorren. Aparecen en la ficha de cada CWE (`cwex <id>`).
|
|
277
|
+
6. **Los Compound no tienen hijos.** Las 7 debilidades Compound (por ejemplo
|
|
278
|
+
CWE-352) son hojas de la vista 1000, porque sus componentes se enlazan con
|
|
279
|
+
`Requires` o `StartsWith`. El modo 2 lo explica y nombra esos componentes, pero
|
|
280
|
+
no los cuenta como descendientes.
|
|
281
|
+
7. **Hay resultados vacíos esperables.** Los 10 Pillar no tienen ascendientes, y 692
|
|
282
|
+
debilidades no tienen descendientes: 390 Base, 274 Variant, 21 Class y las 7
|
|
283
|
+
Compound. En esos casos el programa añade una nota (`note` en JSON) y termina con
|
|
284
|
+
código 0.
|
|
285
|
+
8. **Entradas obsoletas.** Las 27 entradas obsoletas que no forman parte de la vista
|
|
286
|
+
no se pueden consultar. La excepción es CWE-377: está obsoleta, pero CWE-378 y
|
|
287
|
+
CWE-379 siguen colgando de ella. Por eso sus ascendientes terminan en CWE-377 con
|
|
288
|
+
un aviso de linaje incompleto.
|
|
289
|
+
9. **Los resultados dependen de la versión del catálogo.** MITRE cambia relaciones de
|
|
290
|
+
una versión a otra. El programa usa la copia en caché hasta que se ejecuta
|
|
291
|
+
`cwex --update`.
|
|
292
|
+
|
|
293
|
+
## Datos
|
|
294
|
+
|
|
295
|
+
- Fuente: <https://cwe.mitre.org/data/xml/cwec_latest.xml.zip>. Solo se usan las
|
|
296
|
+
relaciones `ChildOf` con `View_ID="1000"`, y el padre marcado como
|
|
297
|
+
`Ordinal="Primary"` define el linaje principal.
|
|
298
|
+
- Caché: `~/.cache/cwe-explorer/`, o `$XDG_CACHE_HOME/cwe-explorer`, o la ruta
|
|
299
|
+
de `CWEX_CACHE`.
|
|
300
|
+
- Para trabajar sin conexión con un XML concreto: `cwex 79 --xml cwec_v5.0.xml`
|
|
301
|
+
(también acepta el `.zip`).
|
|
302
|
+
- El parser no depende del namespace del esquema (`cwe-7`, `cwe-8`...), así que
|
|
303
|
+
sirve para catálogos antiguos y nuevos.
|
|
304
|
+
|
|
305
|
+
> En CWE 5.0, CWE-378 y CWE-379 siguen colgando de CWE-377, que está marcada como
|
|
306
|
+
> obsoleta. La herramienta conserva ese enlace tal como lo publica MITRE, marca el
|
|
307
|
+
> nodo como obsoleto y avisa de que ese linaje no llega a un pilar.
|
|
308
|
+
|
|
309
|
+
## Licencia
|
|
310
|
+
|
|
311
|
+
MIT © 2026 Emi
|
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/logo.jpeg" alt="CWE Explorer" width="220">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# CWE Explorer
|
|
6
|
+
|
|
7
|
+
Explora la jerarquía de debilidades de la **vista 1000 de MITRE** (*Complete
|
|
8
|
+
Weakness Hierarchy*, antes *Research Concepts*) a partir de un identificador CWE.
|
|
9
|
+
Muestra el linaje hasta el pilar del que desciende y los descendientes que cuelgan
|
|
10
|
+
de ella, y lista sus ascendientes o descendientes, directos o por niveles, también
|
|
11
|
+
en JSON.
|
|
12
|
+
|
|
13
|
+
Al seleccionar cualquier nodo se muestra su **ID**, su **descripción**, su **tipo**
|
|
14
|
+
(Pillar, Class, Base, Variant o Compound), su estructura y estado, y su **linaje**
|
|
15
|
+
completo: el camino principal hasta el pilar y cualquier otro camino alternativo
|
|
16
|
+
cuando la debilidad tiene varios padres.
|
|
17
|
+
|
|
18
|
+
Funciona en dos sitios:
|
|
19
|
+
|
|
20
|
+
- **Terminal**: árbol a color, explorador interactivo (`--tui`) que se maneja con el
|
|
21
|
+
ratón o el teclado, y listados de ascendientes y descendientes (`--ancestors`,
|
|
22
|
+
`--descendants`).
|
|
23
|
+
- **Navegador**: una página HTML/CSS autocontenida (`--web`) con el grafo
|
|
24
|
+
interactivo.
|
|
25
|
+
|
|
26
|
+

|
|
27
|
+
|
|
28
|
+
## Instalación
|
|
29
|
+
|
|
30
|
+
Requiere Python 3.9 o superior y no tiene dependencias externas.
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
pip install cwe-explorer # o: pipx install cwe-explorer / uv tool install cwe-explorer
|
|
34
|
+
cwex 79
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Se instalan dos comandos equivalentes: `cwex`, el corto, y `cwe-explorer`. En
|
|
38
|
+
Windows, el explorador interactivo (`--tui`) necesita además `pip install
|
|
39
|
+
windows-curses`; el resto funciona sin nada más.
|
|
40
|
+
|
|
41
|
+
También se puede ejecutar como módulo, ya sea instalado o desde una copia del código:
|
|
42
|
+
|
|
43
|
+
```sh
|
|
44
|
+
python3 -m cwe_explorer 79
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
La primera ejecución descarga el catálogo oficial (`cwec_latest.xml.zip`, unos 2 MB)
|
|
48
|
+
desde cwe.mitre.org y lo guarda en caché.
|
|
49
|
+
|
|
50
|
+
## Uso
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
cwex 79 # árbol en la terminal (linaje + 2 niveles de descendientes)
|
|
54
|
+
cwex CWE-79 --tui # explorador interactivo en la terminal
|
|
55
|
+
cwex 79 --web # grafo interactivo en el navegador
|
|
56
|
+
cwex 664 --depth all # todos los descendientes (también --depth 0, 1, 2...)
|
|
57
|
+
cwex --search "race" # buscar debilidades por nombre
|
|
58
|
+
cwex 79 --ancestors # modo 1: ascendientes directos (padres)
|
|
59
|
+
cwex 79 --descendants # modo 2: descendientes directos (hijos)
|
|
60
|
+
cwex 79 --web -o xss.html --no-open # guardar el HTML sin abrirlo
|
|
61
|
+
cwex --update # descargar de nuevo el catálogo más reciente
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
El identificador se acepta como `79`, `CWE-79` o `cwe79`. Si el ID corresponde a una
|
|
65
|
+
agrupación (las antiguas *Categories*), a una vista o a una entrada obsoleta, el
|
|
66
|
+
programa explica por qué no aparece en la vista 1000.
|
|
67
|
+
|
|
68
|
+
### Terminal
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
Ba CWE-79 Improper Neutralization of Input During Web Page Generation
|
|
72
|
+
Tipo Base: debilidad todavía abstracta, pero con el detalle suficiente
|
|
73
|
+
para deducir métodos concretos de detección y prevención.
|
|
74
|
+
Estructura Simple
|
|
75
|
+
Estado Stable
|
|
76
|
+
Descripción The product does not neutralize or incorrectly neutralizes ...
|
|
77
|
+
Linaje CWE-707 › CWE-74 › CWE-79
|
|
78
|
+
Hijos CWE-80, CWE-81, CWE-83, CWE-84, CWE-85, CWE-86, CWE-87
|
|
79
|
+
...
|
|
80
|
+
|
|
81
|
+
Jerarquía en la vista 1000 (CWE 5.0, profundidad 2)
|
|
82
|
+
Pi CWE-707 Improper Neutralization
|
|
83
|
+
└─ Cl CWE-74 Improper Neutralization of Special Elements in Output Used by ...
|
|
84
|
+
└─ Ba CWE-79 Improper Neutralization of Input During Web Page Generation ◀
|
|
85
|
+
├─ Va CWE-80 Improper Neutralization of Script-Related HTML Tags in ...
|
|
86
|
+
├─ Va CWE-83 Improper Neutralization of Script in Attributes in a Web Page
|
|
87
|
+
│ └─ Va CWE-82 Improper Neutralization of Script in Attributes of IMG ...
|
|
88
|
+
...
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Cuando una debilidad tiene varios linajes, todos se dibujan en el árbol y el
|
|
92
|
+
subárbol de descendientes se muestra una sola vez.
|
|
93
|
+
|
|
94
|
+
Explorador interactivo (`--tui`):
|
|
95
|
+
|
|
96
|
+
| Acción | Ratón | Teclado |
|
|
97
|
+
| --- | --- | --- |
|
|
98
|
+
| Ver la ficha de un nodo | clic | `↑` `↓` / `j` `k` |
|
|
99
|
+
| Centrar la jerarquía en ese nodo | doble clic | `Enter` / `→` |
|
|
100
|
+
| Volver al nodo anterior | | `⌫` / `b` / `←` |
|
|
101
|
+
| Más o menos niveles de descendientes | | `+` / `-` |
|
|
102
|
+
| Ir a otra CWE o buscar por nombre | | `/` |
|
|
103
|
+
| Desplazar la ficha | rueda sobre la ficha | `J` / `K` |
|
|
104
|
+
| Abrir la ficha en cwe.mitre.org | | `o` |
|
|
105
|
+
| Abrir el nodo en el grafo web | | `w` |
|
|
106
|
+
| Salir | | `q` |
|
|
107
|
+
|
|
108
|
+
### Navegador
|
|
109
|
+
|
|
110
|
+
- **Clic** en un nodo: muestra su ficha en el panel lateral y resalta su linaje
|
|
111
|
+
hasta el pilar (trazo grueso: linaje principal).
|
|
112
|
+
- **Doble clic**, o el botón *Centrar la jerarquía*: reconstruye el grafo alrededor
|
|
113
|
+
de ese nodo.
|
|
114
|
+
- **`+N` / `−`** en un nodo: despliega u oculta sus hijos. En los ancestros sirve
|
|
115
|
+
para ver las debilidades hermanas.
|
|
116
|
+
- Buscador por ID o por nombre, selector de niveles de descendientes, zoom con la
|
|
117
|
+
rueda y arrastre para desplazarse.
|
|
118
|
+
- **Pantalla completa** (botón o tecla `F`): el grafo ocupa todo el monitor y la
|
|
119
|
+
ficha aparece como un panel flotante al hacer clic en un nodo (en el móvil, desde
|
|
120
|
+
abajo). Se cierra con `×` o `Esc`; `Esc` también sale de la pantalla completa.
|
|
121
|
+
- Aristas continuas para el padre primario y discontinuas para los padres
|
|
122
|
+
secundarios.
|
|
123
|
+
- La página incluye toda la vista 1000, así que se puede navegar a cualquier otra
|
|
124
|
+
CWE sin volver a generarla. El enlace `#cwe-89` abre directamente esa debilidad.
|
|
125
|
+
Respeta el modo oscuro del sistema y se adapta al móvil.
|
|
126
|
+
|
|
127
|
+
Por defecto el HTML se guarda en `~/.cache/cwe-explorer/html/`. Usa `-o` para
|
|
128
|
+
elegir otra ruta.
|
|
129
|
+
|
|
130
|
+
### Ascendientes y descendientes (modos 1 y 2)
|
|
131
|
+
|
|
132
|
+
Dada una CWE, lista sus ascendientes (modo 1) o sus descendientes (modo 2) en la
|
|
133
|
+
vista 1000. Por defecto sólo los directos, es decir, sus padres o sus hijos:
|
|
134
|
+
|
|
135
|
+
```sh
|
|
136
|
+
cwex 79 --ancestors # padres de CWE-79
|
|
137
|
+
cwex 79 --descendants # hijos de CWE-79
|
|
138
|
+
cwex 425 --ancestors --depth all # todos los ascendientes, hasta los pilares
|
|
139
|
+
cwex 664 --descendants --depth 2 # hijos y nietos
|
|
140
|
+
cwex 664 --descendants -d all --json > cwe-664.json
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
`--ascendientes` y `--descendientes` son alias de las mismas opciones. Con
|
|
144
|
+
`--depth N` se recorren N niveles y con `--depth all`, todos. Cuando hay más de un
|
|
145
|
+
nivel, el resultado se agrupa por distancia:
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
Ba CWE-425 Direct Request
|
|
149
|
+
Todos los ascendientes en la vista 1000 (CWE 5.0): 11 (3 Pillar, 6 Class, 2 Base)
|
|
150
|
+
|
|
151
|
+
Nivel 1 (padres)
|
|
152
|
+
Cl CWE-862 Missing Authorization (padre primario)
|
|
153
|
+
Ba CWE-288 Authentication Bypass Using an Alternate Path or Channel (padre secundario)
|
|
154
|
+
Cl CWE-424 Improper Protection of Alternate Path (padre secundario)
|
|
155
|
+
|
|
156
|
+
Nivel 2
|
|
157
|
+
Cl CWE-285 Improper Authorization (vía CWE-862 · linaje principal)
|
|
158
|
+
Ba CWE-306 Missing Authentication for Critical Functionality (vía CWE-288)
|
|
159
|
+
Cl CWE-638 Not Using Complete Mediation (vía CWE-424)
|
|
160
|
+
Pi CWE-693 Protection Mechanism Failure (vía CWE-424)
|
|
161
|
+
...
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Las notas entre paréntesis explican el parentesco:
|
|
165
|
+
|
|
166
|
+
- **Padres**: `padre primario` o `padre secundario`, según el atributo
|
|
167
|
+
`Ordinal="Primary"` del catálogo.
|
|
168
|
+
- **Hijos**: `vínculo secundario` cuando la CWE consultada no es su padre primario,
|
|
169
|
+
junto con cuál es ese padre primario.
|
|
170
|
+
- **Niveles siguientes**: `vía CWE-…`, que son los nodos del nivel anterior por los
|
|
171
|
+
que se llega. Además, `linaje principal` en los ascendientes que están en el
|
|
172
|
+
camino de padres primarios, y `fuera de su linaje principal` en los descendientes
|
|
173
|
+
que sólo llegan a la CWE consultada a través de algún padre secundario.
|
|
174
|
+
|
|
175
|
+
Con `--json` la salida incluye la CWE consultada (`cwe`, `name`, `abstraction`), el
|
|
176
|
+
modo (`mode`, `depth`), la versión del catálogo (`catalog`), `count` y una lista
|
|
177
|
+
`results` con un objeto por nodo:
|
|
178
|
+
|
|
179
|
+
| Campo | Significado |
|
|
180
|
+
| --- | --- |
|
|
181
|
+
| `id`, `name`, `abstraction`, `status`, `url` | Datos de la debilidad |
|
|
182
|
+
| `distance` | 1 = padre o hijo, 2 = abuelo o nieto… Si hay varios caminos, se usa el más corto |
|
|
183
|
+
| `via` | IDs del nivel anterior por los que se llega (en distancia 1, la propia CWE) |
|
|
184
|
+
| `primary_parent` | Sólo en distancia 1: si el `ChildOf` entre ambas es el primario. `null` en el resto |
|
|
185
|
+
| `primary_lineage` | En ascendientes: si está en el linaje principal de la CWE. En descendientes: si la CWE está en el linaje principal del nodo |
|
|
186
|
+
|
|
187
|
+
El campo `note` explica un resultado vacío o un linaje incompleto, y vale `null` en
|
|
188
|
+
el resto de casos. Desde Python se obtiene lo mismo con
|
|
189
|
+
`Hierarchy.relatives(cwe_id, "ancestors" | "descendants", depth=1)`. Con
|
|
190
|
+
`depth=None` se recorren todos los niveles:
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
from cwe_explorer.data import load_catalog
|
|
194
|
+
from cwe_explorer.hierarchy import Hierarchy
|
|
195
|
+
|
|
196
|
+
h = Hierarchy(load_catalog())
|
|
197
|
+
[r.id for r in h.relatives(79, "ancestors")] # [74]
|
|
198
|
+
[(r.id, r.distance) for r in h.relatives(83, "ancestors", depth=None)]
|
|
199
|
+
# [(79, 1), (74, 2), (707, 3)]
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Limitaciones de los ascendientes y descendientes
|
|
203
|
+
|
|
204
|
+
Los dos modos se pueden calcular con los datos que publica MITRE, pero la vista 1000
|
|
205
|
+
no es un árbol simple, y eso condiciona cómo leer el resultado. Las cifras son del
|
|
206
|
+
catálogo CWE 5.0 (2026-10-28).
|
|
207
|
+
|
|
208
|
+
1. **Es un grafo con varios padres, no un árbol.**
|
|
209
|
+
- 152 de las 957 debilidades tienen más de un padre directo, hasta 3 (CWE-396,
|
|
210
|
+
CWE-397, CWE-415, CWE-425 y CWE-1299). De los 1106 vínculos `ChildOf`, 160 son
|
|
211
|
+
secundarios.
|
|
212
|
+
- Por eso el modo 1 puede devolver varios padres, y la lista completa de
|
|
213
|
+
ascendientes puede abarcar varias ramas: 279 debilidades tienen más de un
|
|
214
|
+
linaje y 235 llegan a más de un pilar (CWE-425 llega a tres).
|
|
215
|
+
- MITRE sólo indica cuál es el padre primario. Entre los demás padres no hay
|
|
216
|
+
ningún orden de importancia.
|
|
217
|
+
2. **"Directo" significa un enlace `ChildOf`, no el nivel de abstracción
|
|
218
|
+
contiguo.** Los enlaces no siempre bajan un escalón Pillar → Class → Base →
|
|
219
|
+
Variant:
|
|
220
|
+
- 83 Base y 3 Variant (CWE-580, CWE-581 y CWE-1077) cuelgan directamente de un
|
|
221
|
+
Pillar, y 85 Variant cuelgan de una Class.
|
|
222
|
+
- 177 enlaces unen debilidades del mismo tipo: 88 Base–Base, 62 Class–Class y
|
|
223
|
+
27 Variant–Variant.
|
|
224
|
+
- Hay 2 Class que cuelgan de una Base: CWE-114 de CWE-73 y CWE-240 de CWE-237.
|
|
225
|
+
|
|
226
|
+
La herramienta devuelve lo que publica el catálogo; no deduce padres a partir
|
|
227
|
+
del tipo.
|
|
228
|
+
3. **Un mismo ascendiente puede estar a varias distancias.**
|
|
229
|
+
- En 25 debilidades hay caminos de distinta longitud hacia un mismo ascendiente.
|
|
230
|
+
Por ejemplo, CWE-102 llega a CWE-707 en 3 niveles y también en 4.
|
|
231
|
+
- En 4 casos, un padre directo es además un ascendiente más lejano (CWE-240,
|
|
232
|
+
CWE-415, CWE-425 y CWE-1262).
|
|
233
|
+
|
|
234
|
+
Cada nodo aparece una sola vez, a la distancia más corta, y `via` dice por dónde
|
|
235
|
+
se llega. `--depth N` también cuenta los niveles por el camino más corto. Los
|
|
236
|
+
demás caminos se ven con `cwex <id>`, que dibuja todos los linajes, o en el
|
|
237
|
+
grafo web.
|
|
238
|
+
4. **Sólo la vista 1000 y sólo `ChildOf`.**
|
|
239
|
+
- Otras vistas tienen su propia jerarquía. Algunas usan `ChildOf` con otro
|
|
240
|
+
`View_ID` (por ejemplo la 1003, la que usa NVD, o la 1305). Otras se organizan
|
|
241
|
+
con agrupaciones y `Has_Member` (por ejemplo la 699). Este programa no carga
|
|
242
|
+
ninguna de ellas.
|
|
243
|
+
- Las 431 agrupaciones (antes *Categories*) y las 62 vistas no son debilidades.
|
|
244
|
+
Si se consulta una, el programa explica qué es y termina con error en lugar de
|
|
245
|
+
listar sus miembros.
|
|
246
|
+
- El XML no publica relaciones `ParentOf`: los hijos se obtienen invirtiendo
|
|
247
|
+
los `ChildOf`.
|
|
248
|
+
5. **Las demás relaciones no son parentesco.** `CanPrecede` (148), `PeerOf` (103),
|
|
249
|
+
`CanAlsoBe` (28), `Requires` (13), `StartsWith` (3) y `Overlaps` (3) no son
|
|
250
|
+
jerárquicas y no se recorren. Aparecen en la ficha de cada CWE (`cwex <id>`).
|
|
251
|
+
6. **Los Compound no tienen hijos.** Las 7 debilidades Compound (por ejemplo
|
|
252
|
+
CWE-352) son hojas de la vista 1000, porque sus componentes se enlazan con
|
|
253
|
+
`Requires` o `StartsWith`. El modo 2 lo explica y nombra esos componentes, pero
|
|
254
|
+
no los cuenta como descendientes.
|
|
255
|
+
7. **Hay resultados vacíos esperables.** Los 10 Pillar no tienen ascendientes, y 692
|
|
256
|
+
debilidades no tienen descendientes: 390 Base, 274 Variant, 21 Class y las 7
|
|
257
|
+
Compound. En esos casos el programa añade una nota (`note` en JSON) y termina con
|
|
258
|
+
código 0.
|
|
259
|
+
8. **Entradas obsoletas.** Las 27 entradas obsoletas que no forman parte de la vista
|
|
260
|
+
no se pueden consultar. La excepción es CWE-377: está obsoleta, pero CWE-378 y
|
|
261
|
+
CWE-379 siguen colgando de ella. Por eso sus ascendientes terminan en CWE-377 con
|
|
262
|
+
un aviso de linaje incompleto.
|
|
263
|
+
9. **Los resultados dependen de la versión del catálogo.** MITRE cambia relaciones de
|
|
264
|
+
una versión a otra. El programa usa la copia en caché hasta que se ejecuta
|
|
265
|
+
`cwex --update`.
|
|
266
|
+
|
|
267
|
+
## Datos
|
|
268
|
+
|
|
269
|
+
- Fuente: <https://cwe.mitre.org/data/xml/cwec_latest.xml.zip>. Solo se usan las
|
|
270
|
+
relaciones `ChildOf` con `View_ID="1000"`, y el padre marcado como
|
|
271
|
+
`Ordinal="Primary"` define el linaje principal.
|
|
272
|
+
- Caché: `~/.cache/cwe-explorer/`, o `$XDG_CACHE_HOME/cwe-explorer`, o la ruta
|
|
273
|
+
de `CWEX_CACHE`.
|
|
274
|
+
- Para trabajar sin conexión con un XML concreto: `cwex 79 --xml cwec_v5.0.xml`
|
|
275
|
+
(también acepta el `.zip`).
|
|
276
|
+
- El parser no depende del namespace del esquema (`cwe-7`, `cwe-8`...), así que
|
|
277
|
+
sirve para catálogos antiguos y nuevos.
|
|
278
|
+
|
|
279
|
+
> En CWE 5.0, CWE-378 y CWE-379 siguen colgando de CWE-377, que está marcada como
|
|
280
|
+
> obsoleta. La herramienta conserva ese enlace tal como lo publica MITRE, marca el
|
|
281
|
+
> nodo como obsoleto y avisa de que ese linaje no llega a un pilar.
|
|
282
|
+
|
|
283
|
+
## Desarrollo
|
|
284
|
+
|
|
285
|
+
```sh
|
|
286
|
+
python3 -m unittest discover -s tests
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
```
|
|
290
|
+
cwe_explorer/
|
|
291
|
+
data.py descarga, parseo del XML y caché JSON
|
|
292
|
+
hierarchy.py grafo de la vista 1000: ancestros, descendientes, linajes
|
|
293
|
+
render_term.py árbol y ficha para la terminal
|
|
294
|
+
tui.py explorador interactivo (curses)
|
|
295
|
+
render_html.py genera la página HTML autocontenida
|
|
296
|
+
web/ plantilla, estilos, JavaScript del grafo (layout por capas propio) y logo
|
|
297
|
+
cli.py argumentos de línea de comandos
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Publicar una versión
|
|
301
|
+
|
|
302
|
+
1. Sube `__version__` en `cwe_explorer/__init__.py`; `pyproject.toml` la toma de ahí.
|
|
303
|
+
2. Si has cambiado el README, regenera la descripción de PyPI (`docs/pypi.md`),
|
|
304
|
+
que es el README sin imágenes, sin enlaces relativos y sin esta sección,
|
|
305
|
+
porque el repositorio es privado:
|
|
306
|
+
|
|
307
|
+
```sh
|
|
308
|
+
python3 tools/pypi_readme.py
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
3. Genera y comprueba el paquete:
|
|
312
|
+
|
|
313
|
+
```sh
|
|
314
|
+
rm -rf dist && uv build
|
|
315
|
+
uvx twine check dist/*
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
4. Pruébalo primero en TestPyPI, con un token de <https://test.pypi.org> en la
|
|
319
|
+
variable `UV_PUBLISH_TOKEN`:
|
|
320
|
+
|
|
321
|
+
```sh
|
|
322
|
+
uv publish --index testpypi
|
|
323
|
+
uvx --index-url https://test.pypi.org/simple/ --from cwe-explorer cwex 79 --ancestors
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
5. Publica en PyPI, con un token de <https://pypi.org> en `UV_PUBLISH_TOKEN`:
|
|
327
|
+
|
|
328
|
+
```sh
|
|
329
|
+
uv publish
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Una versión publicada no se puede reemplazar: para corregir algo hay que subir un
|
|
333
|
+
número de versión nuevo.
|
|
334
|
+
|
|
335
|
+
## Licencia
|
|
336
|
+
|
|
337
|
+
[MIT](LICENSE) © 2026 Emi
|