From 69d3f865e819d8301d4f8ab57f4956890328d004 Mon Sep 17 00:00:00 2001 From: GabriVG Date: Sun, 23 Aug 2026 17:42:38 +0200 Subject: [PATCH] Harden Attack Surface Mapper release quality --- .github/workflows/ci.yml | 24 ++++++++++ CHANGELOG.md | 48 +++++++++++-------- pyproject.toml | 11 +++-- .../validators/api_validator.py | 8 ++-- .../validators/auth_validator.py | 2 +- tests/test_source_encoding.py | 19 ++++++++ 6 files changed, 84 insertions(+), 28 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 tests/test_source_encoding.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2f51236 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,24 @@ +name: CI + +on: + push: + pull_request: + +permissions: + contents: read + +jobs: + quality: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + cache: pip + - name: Install project and test dependencies + run: python -m pip install -e ".[dev]" + - name: Compile source + run: python -m compileall -q main.py src + - name: Run tests + run: python -m pytest -q diff --git a/CHANGELOG.md b/CHANGELOG.md index fee140a..ae3b652 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,12 @@ -# attack_surface_mapper_project_v10.22 +# Attack Surface Mapper + +## v10.22.9 quality hardening + +- Repairs corrupted UTF-8 text in API/auth validation evidence so generated Spanish reports remain readable and professional. +- Adds a minimal GitHub Actions quality gate for source compilation and the full pytest suite. +- Adds a regression test that rejects mojibake markers in Python source files. +- Aligns the package name, version and description with the public project identity. +- Repairs legacy changelog encoding so the project history is readable. ## v10.22.8 polish @@ -6,14 +14,14 @@ - Genera mappings estables, NDJSON `_bulk` y helpers de ingesta para las tres vías pedidas por el tutor: manual/Kibana Dev Tools, `curl` y Python. - Reutiliza el contrato actual del hallazgo (`finding_id`, `correlation_id`, `priority_score`, `finding_role`, `validated`, `validation_basis`, etc.) y evita exportar `raw` completo para mantener los índices más estables. -- Introduce un scoring estructurado (`scoring_version`, `priority_score`) basado en severidad, confianza, rol del hallazgo y base de validación, manteniendo la salida `low/medium/high/critical` pero haciéndola más estable y auditable. -- Expone el score numérico y su razón en reportes, CSV, agregados y matriz de revisión para facilitar comparativas futuras e ingest estructurado. +- Introduce un scoring estructurado (`scoring_version`, `priority_score`) basado en severidad, confianza, rol del hallazgo y base de validación, manteniendo la salida `low/medium/high/critical` pero haciéndola más estable y auditable. +- Expone el score numérico y su razón en reportes, CSV, agregados y matriz de revisión para facilitar comparativas futuras e ingest estructurado. - Ajusta `comparison.json` para detectar cambios en `priority_score` aunque la etiqueta de prioridad no cambie, mejorando el seguimiento fino entre runs. -- Introduce una capa de validación explícita en el modelo de hallazgo con `finding_role`, `validated` y `validation_basis`, separando mejor descubrimiento, candidatos y evidencia validada. -- Propaga esa semántica a `report.summary.json`, `aggregate_summary.json`, `comparison.json` y la matriz de revisión para dejar el output estructurado más estable y más honesto. -- Mantiene compatibilidad con hallazgos previos o JSON legacy: reporting, agregado y comparación infieren el rol de validación cuando el campo nuevo todavía no existe. -- Amplía el golden set y la exportación de revisión con `finding_role`, `validated` y `validation_basis` para afinar falsos positivos con una semántica más clara. +- Introduce una capa de validación explícita en el modelo de hallazgo con `finding_role`, `validated` y `validation_basis`, separando mejor descubrimiento, candidatos y evidencia validada. +- Propaga esa semántica a `report.summary.json`, `aggregate_summary.json`, `comparison.json` y la matriz de revisión para dejar el output estructurado más estable y más honesto. +- Mantiene compatibilidad con hallazgos previos o JSON legacy: reporting, agregado y comparación infieren el rol de validación cuando el campo nuevo todavía no existe. +- Amplía el golden set y la exportación de revisión con `finding_role`, `validated` y `validation_basis` para afinar falsos positivos con una semántica más clara. - Conserva el `debug_http_trace` de browser discovery y validación pasiva en una misma ejecución para facilitar análisis de ruido y troubleshooting. - Ajusta el resumen ejecutivo para que la nota sobre hallazgos altos/críticos confirmados dependa de los datos reales del run. @@ -27,21 +35,21 @@ - Estabiliza `report.summary.json`, `aggregate_summary.json` y `run_manifest.json` con secciones y claves más predecibles para futuro consumo estructurado. - Endurece `scripts/validate_labs.ps1` para validar artefactos generados, IDs estables y un mínimo configurable de hallazgos. - Reduce falsos positivos de `APIValidator` descartando pantallas de login servidas desde rutas como `/swagger` o `/graphql`. -- Enriquece `comparison.json` y la sección de comparativa en reportes con promociones, regresiones y cambios de confianza/verificación. -- Alinea mejor `verification_status`, `needs_manual_validation` y prioridad para que un hallazgo `confirmed` no siga marcado como revisión manual por inercia de categoría. -- Añade una matriz de revisión exportable (`reviews/lab_findings_review.csv`) para etiquetar hallazgos de labs como `verdadero`, `falso` o `dudoso` durante el afinado de falsos positivos. -- Ajusta la priorización de correlación para dar más peso a evidencia `confirmed` y evita que la mera multiplicidad de fuentes infle hallazgos todavía `likely`. +- Enriquece `comparison.json` y la sección de comparativa en reportes con promociones, regresiones y cambios de confianza/verificación. +- Alinea mejor `verification_status`, `needs_manual_validation` y prioridad para que un hallazgo `confirmed` no siga marcado como revisión manual por inercia de categoría. +- Añade una matriz de revisión exportable (`reviews/lab_findings_review.csv`) para etiquetar hallazgos de labs como `verdadero`, `falso` o `dudoso` durante el afinado de falsos positivos. +- Ajusta la priorización de correlación para dar más peso a evidencia `confirmed` y evita que la mera multiplicidad de fuentes infle hallazgos todavía `likely`. - Permite validar `passive-recon-enum` dentro del flujo repetible de labs con un override local (`config/examples/lab-passive-recon-enum.yml`) que desactiva Nuclei y mantiene el perfil comparable en Docker. -- Evita que headers confirmados de severidad media, como CSP ausente, escalen a prioridad `high` solo por estar confirmados; la prioridad se reserva mejor para evidencia aplicativa o impacto más claro. -- Endurece `APIValidator`, `PanelsValidator` y `SensitiveFilesValidator` contra superficies de login servidas desde rutas de docs, respuestas HTML que simulan ficheros y respuestas GraphQL demasiado débiles o indistinguibles del fallback. -- Reordena `top_findings` y el agregado para que hallazgos confirmados de aplicación queden por delante de inventario, fingerprints y headers higiénicos cuando comparten prioridad similar. - -- Acota mejor la prioridad de documentación y superficies API: `Swagger UI Exposed` deja de escalar a `critical`, `GraphQL Endpoint Accessible Without Authentication` se mantiene en `medium` cuando sigue en `likely`, y el inventario `Multiple API Endpoints Exposed` no compite como si fuese una confirmación de impacto. -- Ajusta la matriz de revisión para que headers de higiene y superficies de inventario API queden en `revisar` o `descubrimiento`, evitando priorizar por defecto hallazgos que todavía son de contexto o endurecimiento. -- Separa mejor el reporting entre riesgo de aplicación, higiene/endurecimiento y descubrimiento: `report.summary.json` expone listas dedicadas (`top_risk_findings`, `top_hygiene_findings`, `top_discovery_findings`) y el markdown mueve headers/TLS a una sección propia para que no compitan visualmente con acceso indebido real. -- Ajusta `AuthValidator` para que rutas de superficie API como `/graphql`, `/swagger` o `/api-docs` no se expresen por defecto como fallo de autorización: ahora se reportan como `api` (`GraphQL Surface Exposed`, `Swagger UI Exposed`, etc.) y se acotan a `likely/medium` salvo evidencia más fuerte. -- Filtra `top_risk_findings` para dejar fuera señales de baja prioridad como CORS amplio `likely` cuando ya existen hallazgos medios/altos más accionables, manteniendo el summary centrado en lo que primero merece revisión. +- Evita que headers confirmados de severidad media, como CSP ausente, escalen a prioridad `high` solo por estar confirmados; la prioridad se reserva mejor para evidencia aplicativa o impacto más claro. +- Endurece `APIValidator`, `PanelsValidator` y `SensitiveFilesValidator` contra superficies de login servidas desde rutas de docs, respuestas HTML que simulan ficheros y respuestas GraphQL demasiado débiles o indistinguibles del fallback. +- Reordena `top_findings` y el agregado para que hallazgos confirmados de aplicación queden por delante de inventario, fingerprints y headers higiénicos cuando comparten prioridad similar. + +- Acota mejor la prioridad de documentación y superficies API: `Swagger UI Exposed` deja de escalar a `critical`, `GraphQL Endpoint Accessible Without Authentication` se mantiene en `medium` cuando sigue en `likely`, y el inventario `Multiple API Endpoints Exposed` no compite como si fuese una confirmación de impacto. +- Ajusta la matriz de revisión para que headers de higiene y superficies de inventario API queden en `revisar` o `descubrimiento`, evitando priorizar por defecto hallazgos que todavía son de contexto o endurecimiento. +- Separa mejor el reporting entre riesgo de aplicación, higiene/endurecimiento y descubrimiento: `report.summary.json` expone listas dedicadas (`top_risk_findings`, `top_hygiene_findings`, `top_discovery_findings`) y el markdown mueve headers/TLS a una sección propia para que no compitan visualmente con acceso indebido real. +- Ajusta `AuthValidator` para que rutas de superficie API como `/graphql`, `/swagger` o `/api-docs` no se expresen por defecto como fallo de autorización: ahora se reportan como `api` (`GraphQL Surface Exposed`, `Swagger UI Exposed`, etc.) y se acotan a `likely/medium` salvo evidencia más fuerte. +- Filtra `top_risk_findings` para dejar fuera señales de baja prioridad como CORS amplio `likely` cuando ya existen hallazgos medios/altos más accionables, manteniendo el summary centrado en lo que primero merece revisión. ## v10.22.6 false-positive tuning diff --git a/pyproject.toml b/pyproject.toml index 8faad3d..22a3b41 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,9 +3,9 @@ requires = ["setuptools>=68", "wheel"] build-backend = "setuptools.build_meta" [project] -name = "nuclei-mvp-project-v10" -version = "0.10.3" -description = "Nuclei + validaciones propias + correlación + reporting + Nmap + Scrapling por modos (fetcher/dynamic)" +name = "attack-surface-mapper" +version = "10.22.9" +description = "Pipeline-based attack-surface discovery, validation, correlation, and reporting." requires-python = ">=3.11" dependencies = [ "requests>=2.31.0", @@ -14,6 +14,11 @@ dependencies = [ "playwright>=1.52.0", ] +[project.optional-dependencies] +dev = [ + "pytest>=8.3.0,<9", +] + [tool.pytest.ini_options] testpaths = ["tests"] pythonpath = ["."] diff --git a/src/attack_surface_mapper/validators/api_validator.py b/src/attack_surface_mapper/validators/api_validator.py index 34df6f7..a548bf2 100644 --- a/src/attack_surface_mapper/validators/api_validator.py +++ b/src/attack_surface_mapper/validators/api_validator.py @@ -169,17 +169,17 @@ def _classify_path(self, path: str, response, preview: str, content_type: str, b graphql_signature = '' if looks_like_login_surface(response, preview): title = 'API Surface Exposed' - description = 'Se ha detectado una superficie de API accesible públicamente.' + description = 'Se ha detectado una superficie de API accesible públicamente.' if path in {'/swagger', '/swagger-ui', '/api-docs'}: title = 'Swagger UI Exposed' - description = 'Se ha detectado una interfaz de documentación Swagger accesible sin restricciones claras.' + description = 'Se ha detectado una interfaz de documentación Swagger accesible sin restricciones claras.' elif path == '/openapi.json': title = 'OpenAPI Specification Exposed' - description = 'Se ha detectado un documento OpenAPI/Swagger accesible públicamente.' + description = 'Se ha detectado un documento OpenAPI/Swagger accesible públicamente.' elif path.startswith('/graphql'): title = 'GraphQL Surface Exposed' description = 'Se ha detectado un endpoint o interfaz GraphQL accesible.' - return False, 'low', 'respuesta parece una superficie de login pública', 'discarded', title, description, 'medium' + return False, 'low', 'respuesta parece una superficie de login pública', 'discarded', title, description, 'medium' score = 0 reasons: list[str] = [] if response.status_code in (200, 201, 202, 204): diff --git a/src/attack_surface_mapper/validators/auth_validator.py b/src/attack_surface_mapper/validators/auth_validator.py index 66e9311..fae6bd8 100644 --- a/src/attack_surface_mapper/validators/auth_validator.py +++ b/src/attack_surface_mapper/validators/auth_validator.py @@ -173,7 +173,7 @@ def _check_protected_paths(self, session, target: str, host: str | None, port: s description=description, severity=severity, target=response.url, - evidence=f'GET {response.url} devolvió {response.status_code}; validación={reason}', + evidence=f'GET {response.url} devolvió {response.status_code}; validación={reason}', cwe=['CWE-200'], tags=['api', 'exposure'], template_id=f"custom-auth-open-{path.strip('/') or 'root'}", diff --git a/tests/test_source_encoding.py b/tests/test_source_encoding.py new file mode 100644 index 0000000..0ccf817 --- /dev/null +++ b/tests/test_source_encoding.py @@ -0,0 +1,19 @@ +from pathlib import Path + + +def test_public_project_text_has_no_mojibake_markers() -> None: + root = Path(__file__).resolve().parents[1] + source_files = [ + root / "main.py", + root / "README.md", + root / "CHANGELOG.md", + root / "pyproject.toml", + *sorted((root / "src").rglob("*.py")), + ] + offenders = [ + str(path.relative_to(root)) + for path in source_files + if any(marker in path.read_text(encoding="utf-8") for marker in ("Ã", "Â", "â€", "�")) + ] + + assert offenders == []