Skip to content

Gestion de Baselines

Guia completa para gestionar baselines de hallazgos aceptados.


Tabla de Contenidos

  1. Que es un Baseline
  2. Crear un Baseline
  3. Usar un Baseline
  4. Ver Contenido del Baseline
  5. Combinar Baselines
  6. Mantener el Baseline
  7. Estrategias de Gestion
  8. API Programatica

Que es un Baseline

Un baseline es un archivo que contiene hallazgos que han sido revisados y aceptados. Estos hallazgos se excluyen de futuros escaneos.

Casos de Uso

Caso Descripcion
Falsos positivos Hallazgos incorrectos que no son vulnerabilidades
Riesgo aceptado Vulnerabilidades conocidas con riesgo aceptado
Pendientes Issues que se arreglaran en el futuro
Legacy Codigo heredado que no se puede modificar
Excepciones Casos especiales documentados

Estructura del Baseline

{
  "version": "1.0",
  "created_at": "2026-01-23T10:00:00Z",
  "updated_at": "2026-01-23T10:00:00Z",
  "entries": [
    {
      "rule_id": "MCP-E001",
      "location_hash": "sha256:abc123...",
      "file": "src/config.py",
      "line": 47,
      "reason": "Development API key, not used in production",
      "accepted_by": "security@example.com",
      "timestamp": "2026-01-23T10:00:00Z"
    }
  ]
}

Campos de una Entrada

Campo Tipo Descripcion
rule_id string ID de la regla
location_hash string Hash de archivo + linea
file string Ruta del archivo
line int Numero de linea
reason string Razon de aceptacion
accepted_by string Quien acepto
timestamp string Cuando se acepto

Crear un Baseline

Metodo 1: Desde Resultados de Escaneo

# 1. Ejecutar escaneo y guardar resultados
mcp-scan scan . --output json > scan-results.json

# 2. Revisar resultados
cat scan-results.json | jq '.findings[] | "\(.rule_id): \(.title) @ \(.location.file):\(.location.line)"'

# 3. Generar baseline
mcp-scan baseline generate --from scan-results.json

# 4. El archivo baseline.json se crea

Metodo 2: Con Metadatos

mcp-scan baseline generate \
  --from scan-results.json \
  --output mi-baseline.json \
  --reason "Revision inicial de seguridad" \
  --accepted-by "security-team@example.com"

Metodo 3: Baseline Selectivo

Despues de generar, editar manualmente para incluir solo algunos hallazgos:

# Generar baseline completo
mcp-scan baseline generate --from scan-results.json

# Editar para dejar solo los aceptados
nano baseline.json

Opciones de Generacion

Opcion Alias Default Descripcion
--from -f - Archivo JSON de resultados
--output -o baseline.json Archivo de salida
--reason -r "" Razon para todas las entradas
--accepted-by -a "" Quien acepta

Usar un Baseline

En Escaneos

# Escaneo normal con baseline
mcp-scan scan . --baseline baseline.json

# Los hallazgos en baseline se filtran

Salida con Baseline

MCP-Scan v2.0.0
================================================================================

FINDINGS (excluding baselined)
================================================================================

HIGH: 1
  - MCP-C002: Unvalidated URL in request (NEW)
    File: src/api.py
    Line: 18

================================================================================

Total: 1 new finding (1 high)
Baselined: 3 findings filtered

Exit code: 1

En CI/CD

# .github/workflows/security.yml
- name: Security Scan
  run: |
    mcp-scan scan . \
      --baseline baseline.json \
      --fail-on high \
      --output sarif > results.sarif

Verificar Baseline Aplicado

# Ver cuantos hallazgos fueron filtrados
mcp-scan scan . --baseline baseline.json --output json | jq '.summary.baselined'

Ver Contenido del Baseline

Comando show

mcp-scan baseline show baseline.json

Salida de Ejemplo

BASELINE: baseline.json
=======================

Version: 1.0
Created: 2026-01-23T10:00:00Z
Updated: 2026-01-23T15:30:00Z

Total Entries: 5

BY RULE ID:
  MCP-E001: 3 entries
    - src/config.py:47 (Development API key)
    - src/config.py:48 (Development DB password)
    - src/legacy/old.py:12 (Legacy token)

  MCP-A003: 1 entry
    - scripts/admin.py:25 (Internal admin tool, not exposed)

  MCP-N001: 1 entry
    - . (Lockfile managed externally)

BY FILE:
  src/config.py: 2 entries
  src/legacy/old.py: 1 entry
  scripts/admin.py: 1 entry
  .: 1 entry

RECENT ENTRIES (last 5):
  1. MCP-E001 @ src/config.py:47
     Reason: Development API key, not used in production
     Accepted by: security@example.com
     Date: 2026-01-23

  2. MCP-E001 @ src/config.py:48
     Reason: Development DB password
     Accepted by: security@example.com
     Date: 2026-01-23

  ...

Salida JSON

mcp-scan baseline show baseline.json --output json
{
  "file": "baseline.json",
  "version": "1.0",
  "created_at": "2026-01-23T10:00:00Z",
  "updated_at": "2026-01-23T15:30:00Z",
  "total_entries": 5,
  "stats": {
    "by_rule_id": {
      "MCP-E001": 3,
      "MCP-A003": 1,
      "MCP-N001": 1
    },
    "by_file": {
      "src/config.py": 2,
      "src/legacy/old.py": 1,
      "scripts/admin.py": 1,
      ".": 1
    }
  },
  "entries": [...]
}

Combinar Baselines

Escenario

Tienes multiples baselines de diferentes equipos o ramas:

baseline-dev.json      # Desarrollo
baseline-qa.json       # QA
baseline-legacy.json   # Codigo heredado

Comando merge

mcp-scan baseline merge \
  baseline-dev.json \
  baseline-qa.json \
  baseline-legacy.json \
  --output baseline-combined.json

Comportamiento del Merge

  1. Deduplicacion: Entradas duplicadas se eliminan
  2. Conflictos: Se mantiene la entrada mas reciente
  3. Metadatos: Se combinan razones y aceptantes
Merging baselines...
  baseline-dev.json: 10 entries
  baseline-qa.json: 5 entries
  baseline-legacy.json: 8 entries

Result:
  Total entries: 18 (5 duplicates removed)
  Written to: baseline-combined.json

Ejemplo de Merge

baseline-dev.json:

{
  "entries": [
    {"rule_id": "MCP-E001", "file": "src/config.py", "line": 47}
  ]
}

baseline-qa.json:

{
  "entries": [
    {"rule_id": "MCP-E001", "file": "src/config.py", "line": 47},  // Duplicado
    {"rule_id": "MCP-B002", "file": "src/handlers.py", "line": 33}
  ]
}

baseline-combined.json (resultado):

{
  "entries": [
    {"rule_id": "MCP-E001", "file": "src/config.py", "line": 47},
    {"rule_id": "MCP-B002", "file": "src/handlers.py", "line": 33}
  ]
}


Mantener el Baseline

Eliminar Entradas Especificas

Editar manualmente el archivo JSON:

# Abrir para edicion
nano baseline.json

# O con jq (eliminar entrada especifica)
jq 'del(.entries[] | select(.rule_id == "MCP-E001" and .file == "src/config.py"))' \
  baseline.json > baseline-updated.json

Limpiar Entradas Obsoletas

Si el codigo ha cambiado, algunas entradas pueden ser obsoletas:

# Escanear y ver que entradas ya no coinciden
mcp-scan scan . --baseline baseline.json --output json | \
  jq '.summary.baselined'

# Si es 0, el baseline puede necesitar limpieza

Actualizar Razones

# Editar manualmente
nano baseline.json

# Actualizar timestamp
jq '.updated_at = now | todate' baseline.json > baseline-updated.json

Auditar Baseline

# Ver estadisticas
mcp-scan baseline show baseline.json

# Exportar para revision
mcp-scan baseline show baseline.json --output json > baseline-audit.json

# Revisar entradas antiguas
jq '.entries | sort_by(.timestamp) | .[0:5]' baseline.json

Estrategias de Gestion

Estrategia 1: Baseline por Ambiente

baselines/
  baseline-dev.json       # Desarrollo permisivo
  baseline-staging.json   # Staging moderado
  baseline-prod.json      # Produccion estricto
# Desarrollo
mcp-scan scan . --baseline baselines/baseline-dev.json

# CI para staging
mcp-scan scan . --baseline baselines/baseline-staging.json --fail-on high

# Release a produccion
mcp-scan scan . --baseline baselines/baseline-prod.json --fail-on medium

Estrategia 2: Baseline por Tipo

baselines/
  baseline-false-positives.json  # Solo falsos positivos
  baseline-accepted-risk.json    # Riesgo aceptado
  baseline-legacy.json           # Codigo heredado
# Combinar para escaneo
mcp-scan baseline merge \
  baselines/baseline-false-positives.json \
  baselines/baseline-accepted-risk.json \
  --output .mcp-scan-baseline.json

mcp-scan scan . --baseline .mcp-scan-baseline.json

Estrategia 3: Revision Periodica

# Cada trimestre, revisar baseline
mcp-scan baseline show baseline.json > baseline-review-Q1-2026.txt

# Limpiar entradas antiguas (> 6 meses)
jq --arg cutoff "2025-07-01T00:00:00Z" \
  '.entries |= map(select(.timestamp > $cutoff))' \
  baseline.json > baseline-cleaned.json

Estrategia 4: Aprobacion Requerida

Proceso de revision para agregar al baseline:

  1. PR con hallazgos: Scan falla con nuevos hallazgos
  2. Revision de seguridad: Equipo de seguridad revisa
  3. Aprobacion documentada: Se documenta razon
  4. Actualizacion de baseline: Se agrega con metadatos
# Agregar con documentacion
mcp-scan baseline generate \
  --from new-findings.json \
  --reason "Reviewed in JIRA-1234, accepted risk" \
  --accepted-by "security-lead@example.com"

API Programatica

Usando la Libreria Go

package main

import (
    "github.com/mcphub/mcp-scan/internal/baseline"
    "github.com/mcphub/mcp-scan/pkg/scanner"
)

func main() {
    // Cargar baseline existente
    bl, err := baseline.Load("baseline.json")
    if err != nil {
        log.Fatal(err)
    }

    // Ejecutar escaneo
    s := scanner.New(scanner.Config{Mode: "fast"})
    result, err := s.Scan(context.Background(), "./src")
    if err != nil {
        log.Fatal(err)
    }

    // Aplicar baseline
    filtered := s.ApplyBaseline(result, bl)

    // Ver hallazgos no baselined
    fmt.Printf("New findings: %d\n", len(filtered.Findings))

    // Generar nuevo baseline
    newBaseline := s.GenerateBaseline(result)

    // Combinar con existente
    merged := baseline.Merge(bl, newBaseline)

    // Guardar
    err = baseline.Save(merged, "baseline-updated.json")
}

Estadisticas del Baseline

// Obtener estadisticas
stats := bl.GetStats()

fmt.Printf("Total: %d\n", stats.Total)
fmt.Printf("By Rule:\n")
for rule, count := range stats.ByRuleID {
    fmt.Printf("  %s: %d\n", rule, count)
}

Eliminar Entradas

// Eliminar por regla y ubicacion
bl.Remove("MCP-E001", "src/config.py", 47)

// Eliminar por ID de finding
bl.RemoveFinding(finding)

Verificar si Esta Baselined

// Verificar si un finding esta en baseline
if bl.Contains(finding) {
    fmt.Println("Finding is baselined")
}

Buenas Practicas

1. Documentar Siempre

{
  "rule_id": "MCP-E001",
  "file": "src/config.py",
  "line": 47,
  "reason": "Development API key for local testing. Production uses env vars. See JIRA-1234.",
  "accepted_by": "jane.doe@example.com"
}

2. Revisar Periodicamente

  • Trimestral: Revisar todas las entradas
  • Mensual: Revisar entradas criticas
  • Cada release: Validar que baseline sigue siendo relevante

3. Versionar Baseline

# Mantener baseline en control de versiones
git add baseline.json
git commit -m "Update baseline: add accepted risk for MCP-E001 in config.py"

4. Separar por Criticidad

{
  "entries": [
    {
      "rule_id": "MCP-A003",
      "severity_at_baseline": "critical",
      "requires_review_by": "2026-04-01"
    }
  ]
}

5. Automatizar Validacion

# CI: Verificar que baseline no crece sin control
- name: Check baseline size
  run: |
    ENTRIES=$(jq '.entries | length' baseline.json)
    if [ $ENTRIES -gt 50 ]; then
      echo "Baseline has too many entries ($ENTRIES). Please review."
      exit 1
    fi

Troubleshooting

Baseline No Se Aplica

Sintoma: Los hallazgos no se filtran.

Causas: 1. Archivo movido/renombrado 2. Linea cambiada 3. Formato de baseline incorrecto

Solucion:

# Verificar que el hash coincide
mcp-scan scan . --output json | jq '.findings[0].id'
jq '.entries[0].location_hash' baseline.json

Muchos Falsos Positivos

Sintoma: El baseline crece demasiado.

Soluciones: 1. Usar reglas custom para casos especificos 2. Ajustar configuracion de allowlist 3. Revisar si las reglas son apropiadas

Baseline Corrupto

Sintoma: Error al cargar baseline.

Solucion:

# Validar JSON
jq . baseline.json

# Verificar estructura
jq 'has("entries")' baseline.json


Anterior: Formatos de Salida | Siguiente: Integracion CI/CD