vulnerability-explorer 1.0.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.
Files changed (43) hide show
  1. vulnerability_explorer-1.0.0/PKG-INFO +149 -0
  2. vulnerability_explorer-1.0.0/README.md +131 -0
  3. vulnerability_explorer-1.0.0/pyproject.toml +46 -0
  4. vulnerability_explorer-1.0.0/setup.cfg +4 -0
  5. vulnerability_explorer-1.0.0/src/vex/__init__.py +3 -0
  6. vulnerability_explorer-1.0.0/src/vex/commands/__init__.py +1 -0
  7. vulnerability_explorer-1.0.0/src/vex/commands/interactive.py +14 -0
  8. vulnerability_explorer-1.0.0/src/vex/commands/list.py +29 -0
  9. vulnerability_explorer-1.0.0/src/vex/commands/options.py +78 -0
  10. vulnerability_explorer-1.0.0/src/vex/commands/read.py +42 -0
  11. vulnerability_explorer-1.0.0/src/vex/commands/search.py +27 -0
  12. vulnerability_explorer-1.0.0/src/vex/commands/sync.py +50 -0
  13. vulnerability_explorer-1.0.0/src/vex/commands/tree.py +14 -0
  14. vulnerability_explorer-1.0.0/src/vex/core/__init__.py +1 -0
  15. vulnerability_explorer-1.0.0/src/vex/core/catalog.py +59 -0
  16. vulnerability_explorer-1.0.0/src/vex/core/models.py +24 -0
  17. vulnerability_explorer-1.0.0/src/vex/main.py +38 -0
  18. vulnerability_explorer-1.0.0/src/vex/utils/__init__.py +1 -0
  19. vulnerability_explorer-1.0.0/src/vex/utils/display.py +264 -0
  20. vulnerability_explorer-1.0.0/src/vex/utils/filesystem.py +49 -0
  21. vulnerability_explorer-1.0.0/src/vex_lint/__init__.py +5 -0
  22. vulnerability_explorer-1.0.0/src/vex_lint/core/context.py +25 -0
  23. vulnerability_explorer-1.0.0/src/vex_lint/core/engine.py +36 -0
  24. vulnerability_explorer-1.0.0/src/vex_lint/core/models.py +42 -0
  25. vulnerability_explorer-1.0.0/src/vex_lint/core/rule.py +30 -0
  26. vulnerability_explorer-1.0.0/src/vex_lint/linter.py +5 -0
  27. vulnerability_explorer-1.0.0/src/vex_lint/loaders/markdown.py +88 -0
  28. vulnerability_explorer-1.0.0/src/vex_lint/loaders/taxonomy.py +32 -0
  29. vulnerability_explorer-1.0.0/src/vex_lint/main.py +34 -0
  30. vulnerability_explorer-1.0.0/src/vex_lint/rules/cross_reference_rule.py +47 -0
  31. vulnerability_explorer-1.0.0/src/vex_lint/rules/frontmatter_schema_rule.py +68 -0
  32. vulnerability_explorer-1.0.0/src/vex_lint/rules/markdown_structure_rule.py +49 -0
  33. vulnerability_explorer-1.0.0/src/vex_lint/rules/path_convention_rule.py +72 -0
  34. vulnerability_explorer-1.0.0/src/vex_lint/rules/taxonomy_rule.py +30 -0
  35. vulnerability_explorer-1.0.0/src/vex_lint/schemas/frontmatter.py +111 -0
  36. vulnerability_explorer-1.0.0/src/vex_lint/schemas/sections.py +55 -0
  37. vulnerability_explorer-1.0.0/src/vex_lint/service.py +70 -0
  38. vulnerability_explorer-1.0.0/src/vulnerability_explorer.egg-info/PKG-INFO +149 -0
  39. vulnerability_explorer-1.0.0/src/vulnerability_explorer.egg-info/SOURCES.txt +41 -0
  40. vulnerability_explorer-1.0.0/src/vulnerability_explorer.egg-info/dependency_links.txt +1 -0
  41. vulnerability_explorer-1.0.0/src/vulnerability_explorer.egg-info/entry_points.txt +3 -0
  42. vulnerability_explorer-1.0.0/src/vulnerability_explorer.egg-info/requires.txt +11 -0
  43. vulnerability_explorer-1.0.0/src/vulnerability_explorer.egg-info/top_level.txt +2 -0
@@ -0,0 +1,149 @@
1
+ Metadata-Version: 2.4
2
+ Name: vulnerability-explorer
3
+ Version: 1.0.0
4
+ Summary: A searchable catalog of software vulnerabilities with detailed technical documentation and a CLI explorer.
5
+ License: MIT
6
+ Requires-Python: >=3.10
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: pyyaml>=6.0
9
+ Requires-Dist: types-pyyaml>=6.0.12.20260724
10
+ Requires-Dist: typer>=0.9.0
11
+ Requires-Dist: rich>=13.0
12
+ Requires-Dist: questionary>=2.0.0
13
+ Requires-Dist: pydantic>=2.0.0
14
+ Requires-Dist: markdown-it-py>=3.0.0
15
+ Provides-Extra: dev
16
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
17
+ Requires-Dist: mypy>=1.5.0; extra == "dev"
18
+
19
+ # Vulnerability Explorer (`vex`)
20
+
21
+ > **A git-native, interactive terminal reference and structured knowledge base for software vulnerabilities.**
22
+
23
+ ## What is Vulnerability Explorer?
24
+
25
+ Imagine `man` pages built specifically for application security. **Vulnerability Explorer** is a curated, structured knowledge base of software vulnerabilities combined with a versatile command-line tool (`vex`).
26
+
27
+ Each entry in the catalog is an **atomic Markdown file** enriched with structured YAML frontmatter. Entries document concrete vulnerability mechanisms across languages, frameworks, and architectures, detailing:
28
+
29
+ - **White-box identification** (Vulnerable vs. Safe code snippets, Source/Sink taint patterns)
30
+ - **Gray/Black-box behavior** (Observable indicators, protocol tells, timing characteristics)
31
+ - **Remediation & Fixes** (Primary patches, alternative approaches, common mistakes)
32
+
33
+ > [!NOTE] **This is a knowledge base, not a scanner.** It is designed for security engineers reviewing code, developers learning secure coding, AI agents generating structured security documentation, and CLI lovers looking for fast terminal references.
34
+
35
+ ## Key Features
36
+
37
+ - **Interactive Terminal CLI (`vex`)**: Built-in arrow-key interactive menu for seamless browsing.
38
+ - **Built-in Pager Reading**: Read rich formatted documentation in the terminal with built-in scrolling (`less`-style).
39
+ - **Multi-dimensional Filtering**: Filter entries by category, programming language, or analysis mode (white-box / black-box).
40
+ - **Full-Text Search**: Instantly query across titles, tags, code snippets, and methodologies.
41
+ - **AI-Native Workflow**: Pre-built LLM prompts in `.github/prompts/` to easily author new entries with strict quality standards.
42
+ - **Structural Linter (`vex-lint`)**: Strict schema, taxonomy, and reference validation tool for contributors and CI/CD pipelines.
43
+
44
+ ## Who is this for?
45
+
46
+ | Audience | How They Use Vulnerability Explorer |
47
+ | :----------------------- | :-------------------------------------------------------------------------------------- |
48
+ | **Security Engineers** | Fast reference during penetration tests, code audits, and report writing. |
49
+ | **Developers** | Learn vulnerability root causes and compare side-by-side vulnerable vs. safe code. |
50
+ | **AI Agents & LLMs** | Consume structured YAML/Markdown entries or generate new ones using `.github/prompts/`. |
51
+ | **Educators & Students** | Study standardized vulnerability taxonomy and recognition signals. |
52
+
53
+ ## Quick Start
54
+
55
+ ### 1. Installation
56
+
57
+ #### From PyPI
58
+
59
+ ```bash
60
+ # Using pip
61
+ pip install vulnerability-explorer
62
+
63
+ # Or using uv
64
+ uv pip install vulnerability-explorer
65
+ ```
66
+
67
+ #### From Source (For Contributors)
68
+
69
+ Clone the repository and install in editable mode:
70
+
71
+ ```bash
72
+ git clone https://github.com/othonhugo/vulnerability-explorer.git
73
+ cd vulnerability-explorer
74
+
75
+ # Using uv (recommended)
76
+ uv pip install -e .
77
+
78
+ # Or standard pip
79
+ pip install -e .
80
+ ```
81
+
82
+ ### 2. Launch Interactive Mode
83
+
84
+ Simply type `vex` to launch the interactive arrow-key navigation menu:
85
+
86
+ ```bash
87
+ vex
88
+ ```
89
+
90
+ ## CLI Usage Overview
91
+
92
+ | Task | Command |
93
+ | :-------------------------------- | :-------------------------------------------- |
94
+ | **Interactive Menu** | `vex` or `vex interactive` |
95
+ | **View Catalog Hierarchy** | `vex tree` |
96
+ | **Filter by Category** | `vex list --category injection` |
97
+ | **Filter by Language & Mode** | `vex list --language python --mode white-box` |
98
+ | **Read Specific Entry** | `vex read injection.sql-injection` |
99
+ | **Search Catalog** | `vex search "prepared statement"` |
100
+ | **Plain Text Output (No Colors)** | `vex list --no-rich` |
101
+
102
+ > For full CLI options, flags, and advanced usage, see the **[CLI Commands Reference](docs/commands/README.md)**.
103
+
104
+ ## Catalog Structure At A Glance
105
+
106
+ The catalog is organized logically under `data/`:
107
+
108
+ ```text
109
+ data/
110
+ ├── catalog/ # Vulnerability classes & manifestations
111
+ │ ├── access-control/
112
+ │ ├── injection/
113
+ │ │ ├── sql-injection/
114
+ │ │ │ ├── README.md # Concept entry (e.g. injection.sql-injection)
115
+ │ │ │ └── ... # Manifestation entries
116
+ │ │ └── ...
117
+ │ └── ...
118
+ └── signatures/ # Runtime behavioral signatures
119
+ └── ...
120
+ ```
121
+
122
+ ## Developer & Contributor Tools
123
+
124
+ If you are authoring entries or developing the `vex` CLI:
125
+
126
+ ```bash
127
+ # Run catalog integrity linter
128
+ vex-lint --root .
129
+ # or via Makefile
130
+ make lint
131
+
132
+ # Run Python code linters (Ruff & Mypy)
133
+ make lint-py
134
+
135
+ # Format Markdown/YAML (Prettier) and Python (Ruff)
136
+ make format
137
+ make format-py
138
+
139
+ # Run all CI checks
140
+ make check
141
+ ```
142
+
143
+ - **Authoring Guide**: See [CONTRIBUTING.md](CONTRIBUTING.md) and `docs/contributing/` for detailed authoring guidelines.
144
+ - **CLI Reference**: See [docs/commands/README.md](docs/commands/README.md).
145
+
146
+ ## Non-Goals
147
+
148
+ This repository **does not contain weaponized exploits** or automated scanning engines. Entries document recognition signals, root causes, and exploitation _methodology_ necessary to understand, confirm, and fix vulnerabilities responsibly. See `docs/contributing/guides/authoring.md` for our ethical
149
+ boundary guidelines.
@@ -0,0 +1,131 @@
1
+ # Vulnerability Explorer (`vex`)
2
+
3
+ > **A git-native, interactive terminal reference and structured knowledge base for software vulnerabilities.**
4
+
5
+ ## What is Vulnerability Explorer?
6
+
7
+ Imagine `man` pages built specifically for application security. **Vulnerability Explorer** is a curated, structured knowledge base of software vulnerabilities combined with a versatile command-line tool (`vex`).
8
+
9
+ Each entry in the catalog is an **atomic Markdown file** enriched with structured YAML frontmatter. Entries document concrete vulnerability mechanisms across languages, frameworks, and architectures, detailing:
10
+
11
+ - **White-box identification** (Vulnerable vs. Safe code snippets, Source/Sink taint patterns)
12
+ - **Gray/Black-box behavior** (Observable indicators, protocol tells, timing characteristics)
13
+ - **Remediation & Fixes** (Primary patches, alternative approaches, common mistakes)
14
+
15
+ > [!NOTE] **This is a knowledge base, not a scanner.** It is designed for security engineers reviewing code, developers learning secure coding, AI agents generating structured security documentation, and CLI lovers looking for fast terminal references.
16
+
17
+ ## Key Features
18
+
19
+ - **Interactive Terminal CLI (`vex`)**: Built-in arrow-key interactive menu for seamless browsing.
20
+ - **Built-in Pager Reading**: Read rich formatted documentation in the terminal with built-in scrolling (`less`-style).
21
+ - **Multi-dimensional Filtering**: Filter entries by category, programming language, or analysis mode (white-box / black-box).
22
+ - **Full-Text Search**: Instantly query across titles, tags, code snippets, and methodologies.
23
+ - **AI-Native Workflow**: Pre-built LLM prompts in `.github/prompts/` to easily author new entries with strict quality standards.
24
+ - **Structural Linter (`vex-lint`)**: Strict schema, taxonomy, and reference validation tool for contributors and CI/CD pipelines.
25
+
26
+ ## Who is this for?
27
+
28
+ | Audience | How They Use Vulnerability Explorer |
29
+ | :----------------------- | :-------------------------------------------------------------------------------------- |
30
+ | **Security Engineers** | Fast reference during penetration tests, code audits, and report writing. |
31
+ | **Developers** | Learn vulnerability root causes and compare side-by-side vulnerable vs. safe code. |
32
+ | **AI Agents & LLMs** | Consume structured YAML/Markdown entries or generate new ones using `.github/prompts/`. |
33
+ | **Educators & Students** | Study standardized vulnerability taxonomy and recognition signals. |
34
+
35
+ ## Quick Start
36
+
37
+ ### 1. Installation
38
+
39
+ #### From PyPI
40
+
41
+ ```bash
42
+ # Using pip
43
+ pip install vulnerability-explorer
44
+
45
+ # Or using uv
46
+ uv pip install vulnerability-explorer
47
+ ```
48
+
49
+ #### From Source (For Contributors)
50
+
51
+ Clone the repository and install in editable mode:
52
+
53
+ ```bash
54
+ git clone https://github.com/othonhugo/vulnerability-explorer.git
55
+ cd vulnerability-explorer
56
+
57
+ # Using uv (recommended)
58
+ uv pip install -e .
59
+
60
+ # Or standard pip
61
+ pip install -e .
62
+ ```
63
+
64
+ ### 2. Launch Interactive Mode
65
+
66
+ Simply type `vex` to launch the interactive arrow-key navigation menu:
67
+
68
+ ```bash
69
+ vex
70
+ ```
71
+
72
+ ## CLI Usage Overview
73
+
74
+ | Task | Command |
75
+ | :-------------------------------- | :-------------------------------------------- |
76
+ | **Interactive Menu** | `vex` or `vex interactive` |
77
+ | **View Catalog Hierarchy** | `vex tree` |
78
+ | **Filter by Category** | `vex list --category injection` |
79
+ | **Filter by Language & Mode** | `vex list --language python --mode white-box` |
80
+ | **Read Specific Entry** | `vex read injection.sql-injection` |
81
+ | **Search Catalog** | `vex search "prepared statement"` |
82
+ | **Plain Text Output (No Colors)** | `vex list --no-rich` |
83
+
84
+ > For full CLI options, flags, and advanced usage, see the **[CLI Commands Reference](docs/commands/README.md)**.
85
+
86
+ ## Catalog Structure At A Glance
87
+
88
+ The catalog is organized logically under `data/`:
89
+
90
+ ```text
91
+ data/
92
+ ├── catalog/ # Vulnerability classes & manifestations
93
+ │ ├── access-control/
94
+ │ ├── injection/
95
+ │ │ ├── sql-injection/
96
+ │ │ │ ├── README.md # Concept entry (e.g. injection.sql-injection)
97
+ │ │ │ └── ... # Manifestation entries
98
+ │ │ └── ...
99
+ │ └── ...
100
+ └── signatures/ # Runtime behavioral signatures
101
+ └── ...
102
+ ```
103
+
104
+ ## Developer & Contributor Tools
105
+
106
+ If you are authoring entries or developing the `vex` CLI:
107
+
108
+ ```bash
109
+ # Run catalog integrity linter
110
+ vex-lint --root .
111
+ # or via Makefile
112
+ make lint
113
+
114
+ # Run Python code linters (Ruff & Mypy)
115
+ make lint-py
116
+
117
+ # Format Markdown/YAML (Prettier) and Python (Ruff)
118
+ make format
119
+ make format-py
120
+
121
+ # Run all CI checks
122
+ make check
123
+ ```
124
+
125
+ - **Authoring Guide**: See [CONTRIBUTING.md](CONTRIBUTING.md) and `docs/contributing/` for detailed authoring guidelines.
126
+ - **CLI Reference**: See [docs/commands/README.md](docs/commands/README.md).
127
+
128
+ ## Non-Goals
129
+
130
+ This repository **does not contain weaponized exploits** or automated scanning engines. Entries document recognition signals, root causes, and exploitation _methodology_ necessary to understand, confirm, and fix vulnerabilities responsibly. See `docs/contributing/guides/authoring.md` for our ethical
131
+ boundary guidelines.
@@ -0,0 +1,46 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "vulnerability-explorer"
7
+ version = "1.0.0"
8
+ description = "A searchable catalog of software vulnerabilities with detailed technical documentation and a CLI explorer."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ dependencies = [
13
+ "pyyaml>=6.0",
14
+ "types-pyyaml>=6.0.12.20260724",
15
+ "typer>=0.9.0",
16
+ "rich>=13.0",
17
+ "questionary>=2.0.0",
18
+ "pydantic>=2.0.0",
19
+ "markdown-it-py>=3.0.0",
20
+ ]
21
+
22
+ [project.scripts]
23
+ vex = "vex.main:app"
24
+ vex-lint = "vex_lint.main:app"
25
+
26
+ [project.optional-dependencies]
27
+ dev = [
28
+ "ruff>=0.1.0",
29
+ "mypy>=1.5.0",
30
+ ]
31
+
32
+ [tool.setuptools.packages.find]
33
+ where = ["src"]
34
+
35
+ [tool.ruff]
36
+ line-length = 200
37
+
38
+ [tool.ruff.lint]
39
+ ignore = ["B008"]
40
+
41
+ [tool.mypy]
42
+ python_version = "3.10"
43
+ warn_return_any = true
44
+ warn_unused_configs = true
45
+ strict = false
46
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ """Vulnerability Explorer Package."""
2
+
3
+ __version__ = "1.0.0"
@@ -0,0 +1 @@
1
+ """Typer command definitions for vex CLI."""
@@ -0,0 +1,14 @@
1
+ """Command to navigate the catalog interactively."""
2
+
3
+ from vex.commands.options import OptRich
4
+ from vex.core.catalog import collect_entries
5
+ from vex.utils.display import interactive_navigation
6
+ from vex.utils.filesystem import find_repo_root
7
+
8
+
9
+ def cmd_interactive(rich: OptRich = True) -> None:
10
+ """Interactive navigation menu."""
11
+
12
+ root = find_repo_root()
13
+ entries = collect_entries(root)
14
+ interactive_navigation(entries, use_rich=rich)
@@ -0,0 +1,29 @@
1
+ """Command to list catalog entries with optional filters."""
2
+
3
+ from vex.commands.options import OptCategory, OptLanguage, OptMode, OptRich, OptType
4
+ from vex.core.catalog import collect_entries
5
+ from vex.utils.display import display_list
6
+ from vex.utils.filesystem import find_repo_root
7
+
8
+
9
+ def cmd_list(category: OptCategory = None, language: OptLanguage = None, mode: OptMode = None, entry_type: OptType = None, rich: OptRich = True) -> None:
10
+ """List entries with optional filters."""
11
+
12
+ root = find_repo_root()
13
+ entries = collect_entries(root)
14
+
15
+ filtered = entries
16
+
17
+ if category:
18
+ filtered = [e for e in filtered if e["fm"].get("category") == category]
19
+
20
+ if language:
21
+ filtered = [e for e in filtered if language in (e["fm"].get("languages") or [])]
22
+
23
+ if mode:
24
+ filtered = [e for e in filtered if mode in (e["fm"].get("analysis_modes") or [])]
25
+
26
+ if entry_type:
27
+ filtered = [e for e in filtered if e["fm"].get("type") == entry_type]
28
+
29
+ display_list(filtered, use_rich=rich)
@@ -0,0 +1,78 @@
1
+ """Reusable Typer options and arguments definitions using Annotated."""
2
+
3
+ from pathlib import Path
4
+ from typing import Annotated
5
+
6
+ import typer
7
+
8
+ # Global / Shared Options
9
+ OptRich = Annotated[
10
+ bool,
11
+ typer.Option("--rich/--no-rich", help="Enable or disable Rich formatted output", rich_help_panel="Global Options"),
12
+ ]
13
+
14
+ OptVersion = Annotated[
15
+ bool | None,
16
+ typer.Option(
17
+ "--version",
18
+ "-V",
19
+ help="Print version and exit.",
20
+ rich_help_panel="Global Options",
21
+ ),
22
+ ]
23
+
24
+ # Explore Command Options & Arguments
25
+ OptCategory = Annotated[
26
+ str | None,
27
+ typer.Option("--category", "-c", help="Filter by category slug"),
28
+ ]
29
+
30
+ OptLanguage = Annotated[
31
+ str | None,
32
+ typer.Option("--language", "-l", help="Filter by language slug"),
33
+ ]
34
+
35
+ OptMode = Annotated[
36
+ str | None,
37
+ typer.Option("--mode", "-m", help="Filter by analysis mode"),
38
+ ]
39
+
40
+ OptType = Annotated[
41
+ str | None,
42
+ typer.Option("--type", "-t", help="Filter by entry type (concept, manifestation, runtime-signature)"),
43
+ ]
44
+
45
+ OptNoPager = Annotated[
46
+ bool,
47
+ typer.Option("--no-pager", help="Disable pager and print directly to terminal"),
48
+ ]
49
+
50
+ ArgEntryId = Annotated[
51
+ str,
52
+ typer.Argument(help="Entry ID (exact or prefix match)"),
53
+ ]
54
+
55
+ ArgQuery = Annotated[
56
+ str,
57
+ typer.Argument(help="Search query string"),
58
+ ]
59
+
60
+ # Linter Options
61
+ OptRoot = Annotated[
62
+ Path | None,
63
+ typer.Option("--root", "-r", help="Repository root path (default: auto-detected)"),
64
+ ]
65
+
66
+ # Sync Command Options
67
+ DEFAULT_REMOTE_URL = "https://github.com/othonhugo/vulnerability-explorer.git"
68
+ DEFAULT_BRANCH = "unstable"
69
+
70
+ OptRemote = Annotated[
71
+ str,
72
+ typer.Option("--remote", "-r", help="Remote Git repository URL"),
73
+ ]
74
+
75
+ OptBranch = Annotated[
76
+ str,
77
+ typer.Option("--branch", "-b", help="Git branch to pull/clone"),
78
+ ]
@@ -0,0 +1,42 @@
1
+ """Command to read a specific catalog entry by ID."""
2
+
3
+ import typer
4
+
5
+ from vex.commands.options import ArgEntryId, OptNoPager, OptRich
6
+ from vex.core.catalog import collect_entries
7
+ from vex.core.models import CatalogEntry
8
+ from vex.utils.display import display_read
9
+ from vex.utils.filesystem import find_repo_root
10
+
11
+
12
+ def cmd_read(entry_id: ArgEntryId, no_pager: OptNoPager = False, rich: OptRich = True) -> None:
13
+ """Read an entry by ID using Pager."""
14
+
15
+ root = find_repo_root()
16
+ entries = collect_entries(root)
17
+
18
+ entry_match: CatalogEntry | None = None
19
+
20
+ for e in entries:
21
+ if e["fm"].get("id") == entry_id:
22
+ entry_match = e
23
+ break
24
+
25
+ if not entry_match:
26
+ candidates = [e for e in entries if str(e["fm"].get("id", "")).startswith(entry_id)]
27
+
28
+ if len(candidates) == 1:
29
+ entry_match = candidates[0]
30
+ elif len(candidates) > 1:
31
+ typer.echo(f"Ambiguous ID '{entry_id}'. Matches:")
32
+
33
+ for c in candidates:
34
+ typer.echo(f" {c['fm'].get('id')}")
35
+
36
+ raise typer.Exit(code=1)
37
+ else:
38
+ typer.echo(f"No entry found with ID '{entry_id}'.")
39
+
40
+ raise typer.Exit(code=1)
41
+
42
+ display_read(entry_match, use_pager=not no_pager, use_rich=rich)
@@ -0,0 +1,27 @@
1
+ """Command to search across the vulnerability catalog."""
2
+
3
+ from vex.commands.options import ArgQuery, OptRich
4
+ from vex.core.catalog import collect_entries
5
+ from vex.core.models import CatalogEntry
6
+ from vex.utils.display import display_search
7
+ from vex.utils.filesystem import find_repo_root
8
+
9
+
10
+ def cmd_search(query: ArgQuery, rich: OptRich = True) -> None:
11
+ """Search entries for query term."""
12
+
13
+ root = find_repo_root()
14
+ entries = collect_entries(root)
15
+ q = query.lower()
16
+
17
+ results: list[tuple[int, CatalogEntry]] = []
18
+
19
+ for e in entries:
20
+ full_text = e["path"].read_text(encoding="utf-8").lower()
21
+
22
+ if q in full_text:
23
+ count = full_text.count(q)
24
+ results.append((count, e))
25
+
26
+ results.sort(key=lambda x: x[0], reverse=True)
27
+ display_search(results, query, use_rich=rich)
@@ -0,0 +1,50 @@
1
+ """Command for syncing/updating global vulnerability catalog from remote Git repository."""
2
+
3
+ import subprocess
4
+
5
+ import typer
6
+ from rich.console import Console
7
+
8
+ from vex.commands.options import OptBranch, OptRemote
9
+ from vex.utils.filesystem import get_global_cache_dir
10
+
11
+ console = Console()
12
+ stderr_console = Console(stderr=True)
13
+
14
+
15
+ def cmd_sync(
16
+ remote: OptRemote,
17
+ branch: OptBranch,
18
+ ) -> None:
19
+ """Download or update the global vulnerability catalog cache."""
20
+ cache_dir = get_global_cache_dir()
21
+
22
+ # Check if git is available in PATH
23
+ try:
24
+ subprocess.run(["git", "--version"], capture_output=True, check=True)
25
+ except (subprocess.SubprocessError, FileNotFoundError):
26
+ stderr_console.print("[bold red]Error:[/] `git` command not found. Please install Git to use `vex sync`.")
27
+ raise typer.Exit(code=1)
28
+
29
+ cache_dir.parent.mkdir(parents=True, exist_ok=True)
30
+
31
+ if (cache_dir / ".git").is_dir():
32
+ console.print(f"[bold cyan]Updating global catalog cache at:[/] {cache_dir}")
33
+ res = subprocess.run(["git", "pull", "origin", branch], cwd=cache_dir, capture_output=True, text=True, check=False)
34
+
35
+ if res.returncode != 0:
36
+ stderr_console.print(f"[bold red]Failed to update catalog:[/] {res.stderr.strip()}")
37
+ raise typer.Exit(code=1)
38
+
39
+ console.print("[bold green]✔ Catalog successfully updated![/]")
40
+ else:
41
+ console.print(f"[bold cyan]Cloning catalog from:[/] {remote}")
42
+ console.print(f"[bold cyan]Target directory:[/] {cache_dir}")
43
+
44
+ res = subprocess.run(["git", "clone", "--depth", "1", "-b", branch, remote, str(cache_dir)], capture_output=True, text=True, check=False)
45
+
46
+ if res.returncode != 0:
47
+ stderr_console.print(f"[bold red]Failed to clone catalog:[/] {res.stderr.strip()}")
48
+ raise typer.Exit(code=1)
49
+
50
+ console.print("[bold green]✔ Catalog successfully downloaded![/]")
@@ -0,0 +1,14 @@
1
+ """Command to show the catalog tree structure."""
2
+
3
+ from vex.commands.options import OptRich
4
+ from vex.core.catalog import collect_entries
5
+ from vex.utils.display import display_tree
6
+ from vex.utils.filesystem import find_repo_root
7
+
8
+
9
+ def cmd_tree(rich: OptRich = True) -> None:
10
+ """Show the catalog tree structure."""
11
+
12
+ root = find_repo_root()
13
+ entries = collect_entries(root)
14
+ display_tree(entries, use_rich=rich)
@@ -0,0 +1 @@
1
+ """Core catalog models and parsing logic."""
@@ -0,0 +1,59 @@
1
+ """Catalog loading and parsing utilities."""
2
+
3
+ import re
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+ import yaml
8
+
9
+ from vex.core.models import CatalogEntry
10
+
11
+ FRONTMATTER_RE = re.compile(r"\A---\n(.*?\n)---\n?(.*)\Z", re.DOTALL)
12
+
13
+
14
+ def parse_file(path: Path) -> tuple[dict[str, Any] | None, str | None]:
15
+ """Parse a catalog file, returning (frontmatter_dict, body_str) or (None, None)."""
16
+
17
+ text = path.read_text(encoding="utf-8")
18
+ m = FRONTMATTER_RE.match(text)
19
+
20
+ if not m:
21
+ return None, None
22
+
23
+ try:
24
+ fm: Any = yaml.safe_load(m.group(1))
25
+ except yaml.YAMLError:
26
+ return None, None
27
+
28
+ if not isinstance(fm, dict):
29
+ return None, None
30
+
31
+ return fm, m.group(2)
32
+
33
+
34
+ def collect_entries(root: Path) -> list[CatalogEntry]:
35
+ """Collect all catalog entries and runtime-signatures."""
36
+
37
+ entries: list[CatalogEntry] = []
38
+ catalog_dir = root / "data" / "catalog"
39
+ signatures_dir = root / "data" / "signatures"
40
+
41
+ dirs: list[Path] = []
42
+
43
+ if catalog_dir.exists():
44
+ dirs.append(catalog_dir)
45
+
46
+ if signatures_dir.exists():
47
+ dirs.append(signatures_dir)
48
+
49
+ for d in dirs:
50
+ for path in sorted(d.rglob("*.md")):
51
+ if path.name == "README.md" and (path.parent == signatures_dir or path.parent == catalog_dir or path.parent.parent == catalog_dir):
52
+ continue # Skip documentation READMEs
53
+
54
+ fm, body = parse_file(path)
55
+
56
+ if fm is not None and body is not None and "id" in fm:
57
+ entries.append({"path": path, "fm": fm, "body": body})
58
+
59
+ return entries