Contribuir
Guia para contribuir al desarrollo de vigil.
Setup de desarrollo
Requisitos
- Python 3.12 o superior
- git
- pip
Clonar y configurar
git clone https://github.com/org/vigil.git
cd vigil
# Crear entorno virtual
python3.12 -m venv .venv
source .venv/bin/activate # Linux/macOS
# .venv\Scripts\activate # Windows
# Instalar en modo desarrollo con dependencias de dev
pip install -e ".[dev]"
# Verificar instalacion
vigil --version
Verificar que todo funciona
# Ejecutar tests
pytest
# Ejecutar vigil
vigil scan src/
vigil rules
Estructura del proyecto
src/vigil/
cli.py # Comandos Click
config/ # Configuracion (schema, loader, rules)
core/ # Modelos y motor (finding, engine, file_collector)
analyzers/ # Analyzers (logica de deteccion)
base.py # BaseAnalyzer Protocol
deps/ # DependencyAnalyzer (parsers, registry, similarity)
auth/ # AuthAnalyzer (endpoints, middleware, patterns)
secrets/ # SecretsAnalyzer (placeholders, entropy, env tracing)
tests/ # TestQualityAnalyzer (assertions, mocks, coverage heuristics)
reports/ # Formateadores de salida
logging/ # Setup de structlog
tests/
test_cli.py # Tests del CLI
test_core/ # Tests del core
test_config/ # Tests de configuracion
test_reports/ # Tests de formateadores
test_analyzers/ # Tests de analyzers
test_deps/ # Tests del DependencyAnalyzer
test_auth/ # Tests del AuthAnalyzer
test_secrets/ # Tests del SecretsAnalyzer
test_tests/ # Tests del TestQualityAnalyzer
fixtures/ # Archivos de prueba
deps/ # Fixtures de dependencias
auth/ # Fixtures de auth
secrets/ # Fixtures de secrets
tests/ # Fixtures de test quality
docs/ # Documentacion
Convenciones de codigo
Python
- Version: Python 3.12+. Usar
str | Noneen lugar deOptional[str]. - Type hints: Todas las funciones publicas deben tener type hints completos.
- Linter: ruff con target
py312y line-length 100. - Formatter: ruff format.
# Lint
ruff check src/ tests/
# Format
ruff format src/ tests/
Nomenclatura
- Clases:
PascalCase(ScanEngine,HumanFormatter). - Funciones y variables:
snake_case(collect_files,rule_id). - Constantes:
UPPER_SNAKE_CASE(SEVERITY_SORT_ORDER,LANGUAGE_EXTENSIONS). - Archivos:
snake_case.py.
Imports
Orden: stdlib, third-party, local. Ruff se encarga de ordenarlos.
import json
from pathlib import Path
import structlog
from pydantic import BaseModel
from vigil.core.finding import Finding, Severity
Logging
- Usar
structlogsiempre. Nuncaprint(). - Logs van a stderr (nunca a stdout).
- Mensajes como keys snake_case:
logger.info("files_collected", count=42).
Modelos
- Pydantic v2: Para configuracion del usuario (requiere validacion).
- dataclasses: Para modelos internos (Finding, Location, RuleDefinition).
- Protocol: Para interfaces (BaseAnalyzer, BaseFormatter).
Como implementar un analyzer
Los analyzers son la logica de deteccion de vigil. Cada analyzer implementa el protocolo BaseAnalyzer.
Paso 1: Crear el modulo
Para analyzers con multiples componentes, crear un subpackage:
# Ejemplo: analyzer de secrets
mkdir -p src/vigil/analyzers/secrets
touch src/vigil/analyzers/secrets/__init__.py
touch src/vigil/analyzers/secrets/analyzer.py
Para analyzers simples, un archivo individual es suficiente.
Paso 2: Implementar el protocolo
Referencia: ver src/vigil/analyzers/deps/analyzer.py para un ejemplo completo.
"""Analyzer de secrets y credenciales."""
import structlog
from vigil.config.schema import ScanConfig
from vigil.core.finding import Category, Finding, Location, Severity
logger = structlog.get_logger()
class SecretsAnalyzer:
"""Detecta secrets hardcodeados y placeholders."""
@property
def name(self) -> str:
return "secrets"
@property
def category(self) -> Category:
return Category.SECRETS
def analyze(self, files: list[str], config: ScanConfig) -> list[Finding]:
findings: list[Finding] = []
for file_path in files:
if not file_path.endswith(".py"):
continue
try:
with open(file_path) as f:
content = f.read()
findings.extend(self._check_file(file_path, content, config))
except OSError:
logger.warning("file_read_error", file=file_path)
return findings
def _check_file(
self, file_path: str, content: str, config: ScanConfig
) -> list[Finding]:
findings: list[Finding] = []
# ... implementar checks ...
return findings
Paso 3: Registrar en el engine
En cli.py, agregar el analyzer a _register_analyzers():
def _register_analyzers(engine: ScanEngine) -> None:
from vigil.analyzers.deps import DependencyAnalyzer
from vigil.analyzers.auth import AuthAnalyzer
from vigil.analyzers.secrets import SecretsAnalyzer
from vigil.analyzers.tests import TestQualityAnalyzer
engine.register_analyzer(DependencyAnalyzer())
engine.register_analyzer(AuthAnalyzer())
engine.register_analyzer(SecretsAnalyzer())
engine.register_analyzer(TestQualityAnalyzer())
Paso 4: Escribir tests
# tests/test_analyzers/test_secrets/test_analyzer.py
from vigil.analyzers.secrets import SecretsAnalyzer
from vigil.config.schema import ScanConfig
def test_detects_placeholder_secret(tmp_path):
# Crear archivo de prueba
test_file = tmp_path / "app.py"
test_file.write_text('SECRET_KEY = "your-api-key-here"\n')
analyzer = SecretsAnalyzer()
config = ScanConfig()
findings = analyzer.analyze([str(test_file)], config)
assert len(findings) == 1
assert findings[0].rule_id == "SEC-001"
assert findings[0].severity.value == "critical"
Reglas del analyzer
- Determinista: Mismo input = mismo output. Sin aleatoridad.
- Sin side effects: No modificar archivos, no escribir a stdout.
- Robusto: Capturar errores de I/O por archivo, no por scan completo.
- Eficiente: Leer cada archivo una sola vez, usar early returns.
- Configurable: Respetar thresholds y opciones de
ScanConfig.
Como agregar una regla
Paso 1: Definir en el catalogo
En src/vigil/config/rules.py, agregar a la lista RULES_V0:
RuleDefinition(
id="SEC-007",
name="My new rule",
description="Description of what this rule detects.",
category=Category.SECRETS,
default_severity=Severity.HIGH,
owasp_ref="LLM02",
cwe_ref="CWE-798",
),
Paso 2: Implementar la deteccion
En el analyzer correspondiente, agregar la logica que crea Finding con rule_id="SEC-007".
Paso 3: Agregar tests
- Test positivo: codigo vulnerable es detectado.
- Test negativo: codigo seguro no genera findings.
- Test de configuracion: la regla respeta overrides.
Paso 4: Documentar
Agregar la regla a docs/reglas.md con:
- Severidad y referencias OWASP/CWE.
- Que detecta.
- Ejemplo de codigo vulnerable.
- Como corregirlo.
Tests
Ejecutar
# Todos los tests
pytest
# Con cobertura
pytest --cov=vigil --cov-report=term-missing
# Un archivo especifico
pytest tests/test_core/test_engine.py
# Un test especifico
pytest tests/test_core/test_engine.py::test_engine_no_analyzers
# Verbose
pytest -v
Estructura de tests
- Cada modulo tiene su directorio de tests correspondiente.
- Fixtures globales en
tests/conftest.py. - Archivos de prueba en
tests/fixtures/.
Convenciones de tests
- Nombres descriptivos:
test_detects_placeholder_secret, notest_1. - Un assert logico por test (puede ser multiples
assertsi verifican la misma cosa). - Usar
tmp_pathde pytest para archivos temporales. - Usar fixtures para configuraciones reutilizables.
- No usar mocks excepto para I/O externo (HTTP, filesystem).
Cobertura actual
| Modulo | Tests |
|---|---|
| CLI | 53 |
| Core (finding) | 28 |
| Core (engine) | 29 |
| Core (file_collector) | 36 |
| Config (schema) | 25 |
| Config (loader) | 39 |
| Config (rules) | 34 |
| Reports (formatters) | 47 |
| Reports (formatters edge cases) | ~50 |
| Reports (FASE 4 features) | 77 |
| Reports (FASE 4 QA) | 89 |
| Analyzers (deps) | 120 |
| Analyzers (deps) QA | 126 |
| Analyzers (auth) | ~130 |
| Analyzers (secrets) | ~130 |
| Analyzers (test-quality) | ~128 |
| Analyzers (test-quality) QA | 81 |
| Integration | 14 |
| Integration QA (deps) | 13 |
| Integration (auth+secrets) | ~70 |
| Logging | 3 |
| Total | ~1336 |
Cobertura global: ~98% (99% en modulo reports)
Pull requests
Proceso
- Crear un branch desde
develop:git checkout -b feature/mi-feature develop - Hacer los cambios.
- Ejecutar tests:
pytest - Ejecutar linter:
ruff check src/ tests/ - Crear PR hacia
develop.
Checklist del PR
- Los tests pasan:
pytest - El linter pasa:
ruff check src/ tests/ - Se agregaron tests para la nueva funcionalidad.
- La documentacion se actualizo si aplica.
- El CHANGELOG.md se actualizo.
Commits
- Mensajes claros y descriptivos en ingles.
- Usar verbos en imperativo: “Add”, “Fix”, “Remove”, no “Added”, “Fixed”.
- Prefijos utiles:
feat:,fix:,docs:,test:,refactor:.
feat: add secrets analyzer with SEC-001 and SEC-002
fix: handle empty requirements.txt in dependency analyzer
docs: add architecture documentation
test: add tests for SARIF formatter edge cases
Fases de desarrollo
vigil se desarrolla en fases incrementales:
| Fase | Descripcion | Estado |
|---|---|---|
| FASE 0 | Scaffolding, config, engine, CLI, formatters, rule catalog | Completada (QA done) |
| FASE 1 | Dependency analyzer (DEP-001, 002, 003, 005, 007) | Completada (QA done) |
| FASE 2 | Auth + Secrets analyzers (AUTH-001..007, SEC-001..006) | Completada (QA done) |
| FASE 3 | Test quality analyzer (TEST-001..006) | Completada (QA done) |
| FASE 4 | Reports polish (formatters, summary, icons, SARIF 2.1.0) | Completada (QA done) |
| FASE 5 | Integracion, fixtures realistas, docs | Pendiente |
| FASE 6 | Popular packages corpus, polish final | Pendiente |
Consultar SEGUIMIENTO-V0.md para el estado detallado de cada fase.
Contacto
- Issues: Reportar bugs y sugerir features en el repositorio de GitHub.
- PRs: Siempre bienvenidos. Para cambios grandes, abrir un issue primero para discutir el enfoque.