Este notebook ejecuta un análisis de seguridad estático sobre los repositorios clonados en data/repos/ utilizando CodeQL, la herramienta de análisis de código de GitHub.
¿Qué es CodeQL?
CodeQL es un motor de búsqueda de código que permite escribir consultas para encontrar vulnerabilidades, problemas de seguridad y defectos de codificación en el código fuente. Produce resultados en formato SARIF (Static Analysis Results Interchange Format).
Flujo de Ejecución
Descubrimiento de repositorios: Escanea data/repos/ para encontrar todos los subdirectorios
Creación de base de datos CodeQL: Para cada repositorio, CodeQL crea una base de datos indexada del código
Análisis: Se ejecutan consultas de seguridad predefinidas en la base de datos
Generación de resultados: Los hallazgos se convierten a formato JSON normalizado y se guardan en data/results/
Preparación: Importar scripts/generate_codeql.py
A continuación importaremos el módulo CodeQLAnalyzer desde el archivo scripts/generate_codeql.py.
import sysfrom pathlib import Pathimport jsonimport pandas as pd# Agregar el directorio scripts al path para importar el modulo generate_codeql# Este módulo contiene la clase CodeQLAnalyzer que orquesta todo el análisisproject_root = Path().cwd().parent.parentscripts_path = project_root /"scripts"ifstr(scripts_path) notin sys.path: sys.path.insert(0, str(scripts_path))print(f"Notebook working directory: {Path().cwd()}")print(f"Project root: {project_root}")print(f"Scripts path: {scripts_path}")print(f"Importing from: generate_codeql.py")
# Importar la clase CodeQLAnalyzer desde generate_codeql.pyfrom generate_codeql import CodeQLAnalyzerprint("✓ Importado correctamente: CodeQLAnalyzer desde scripts/generate_codeql.py")
✓ Importado correctamente: CodeQLAnalyzer desde scripts/generate_codeql.py
Configuración
# Configurar rutasrepos_path = project_root /"data"/"repos"output_path = project_root /"data"/"results"# Crear analizadoranalizador = CodeQLAnalyzer( repos_path=str(repos_path), output_path=str(output_path))# Configurar para ejecución normal (no dry-run)analizador.dry_run =Falseprint(f"Repos path: {repos_path}")print(f"Output path: {output_path}")print(f"Dry run: {analizador.dry_run}")
Ahora ejecutaremos el análisis CodeQL sobre todos los repositorios descubiertos. El script generate_codeql.py manejará automáticamente la orquestación completa.
# Descubrir repositorios usando el método del CodeQLAnalyzer# Este método viene de scripts/generate_codeql.pyrepositorios = analizador.discover_repositories()print(f"Repositorios encontrados: {len(repositorios)}")for repo in repositorios:print(f" - {repo}")
print("Iniciando análisis CodeQL con scripts/generate_codeql.py...")analizador.run()print("✓ Análisis completado")
INFO | Usando CodeQL CLI: /usr/local/bin/codeql
INFO | === Diagnóstico del Entorno CodeQL ===
Iniciando análisis CodeQL con scripts/generate_codeql.py...
INFO | ✓ CodeQL CLI: CodeQL command-line toolchain release 2.25.1.
INFO | ✓ Node.js: v20.20.2
INFO | ✓ npm: 10.8.2
INFO | Verificando query packs...
INFO | ✓ Query pack codeql/python-queries disponible
INFO | ✓ Query pack codeql/javascript-queries disponible
INFO | ✓ Query pack codeql/java-queries disponible
INFO | === Fin Diagnóstico ===
INFO | [1/3] Procesando repositorio data/repos/claude-code-action
INFO | Lenguaje detectado en claude-code-action: javascript
INFO | Directorio temporal CodeQL: /tmp/codeql_analysis
INFO | Creando base de datos CodeQL para claude-code-action (lenguaje: javascript)...
INFO | Usando suite compilada: /home/vscode/.codeql/packages/codeql/javascript-queries/2.3.6/codeql-suites/javascript-security-and-quality.qls
INFO | SARIF guardado en: /workspaces/id_2026/data/results/claude-code-action_temp.sarif (284762 bytes)
INFO | SARIF output length: 284762 bytes
INFO | parse_sarif: recibiendo 284762 bytes
INFO | parse_sarif: 1 runs encontrados
INFO | parse_sarif: 16 resultados en run[0]
INFO | parse_sarif: total_issues=16
INFO | save_analysis: claude-code-action con 16 issues
INFO | Analisis CodeQL guardado en data/results/claude-code-action-codeql.json
INFO | [2/3] Procesando repositorio data/repos/genai-code-review
INFO | Lenguaje detectado en genai-code-review: python
INFO | Creando base de datos CodeQL para genai-code-review (lenguaje: python)...
INFO | Usando suite compilada: /home/vscode/.codeql/packages/codeql/python-queries/1.7.11/codeql-suites/python-security-and-quality.qls
INFO | SARIF guardado en: /workspaces/id_2026/data/results/genai-code-review_temp.sarif (132815 bytes)
INFO | SARIF output length: 132815 bytes
INFO | parse_sarif: recibiendo 132815 bytes
INFO | parse_sarif: 1 runs encontrados
INFO | parse_sarif: 4 resultados en run[0]
INFO | parse_sarif: total_issues=4
INFO | save_analysis: genai-code-review con 4 issues
INFO | Analisis CodeQL guardado en data/results/genai-code-review-codeql.json
INFO | [3/3] Procesando repositorio data/repos/opencode
INFO | Lenguaje detectado en opencode: javascript
INFO | Creando base de datos CodeQL para opencode (lenguaje: javascript)...
INFO | Usando suite compilada: /home/vscode/.codeql/packages/codeql/javascript-queries/2.3.6/codeql-suites/javascript-security-and-quality.qls
INFO | SARIF guardado en: /workspaces/id_2026/data/results/opencode_temp.sarif (1454012 bytes)
INFO | SARIF output length: 1454012 bytes
INFO | parse_sarif: recibiendo 1454012 bytes
INFO | parse_sarif: 1 runs encontrados
INFO | parse_sarif: 218 resultados en run[0]
INFO | parse_sarif: total_issues=218
INFO | save_analysis: opencode con 218 issues
INFO | Analisis CodeQL guardado en data/results/opencode-codeql.json
INFO | Resumen final | total_repos=3 | repos_analizados=3 | archivos_generados=3 | omitidos=0 | errores=0
# Cargar y mostrar resumen de UN análisisresults_dir = project_root /"data"/"results"results_files =sorted(results_dir.glob("*-codeql.json"))print(f"📂 Buscando en: {results_dir}")print(f"📋 Archivos encontrados: {len(results_files)}")if results_files: result_file = results_files[0]print(f"📖 Leyendo: {result_file.name}")withopen(result_file, 'r', encoding='utf-8') as f: data_content = json.load(f) repo_name_clean = result_file.stem.replace('-codeql', '') total_found = data_content.get('total_issues', 0)print(f"\n📊 ANÁLISIS: {repo_name_clean}")print(f"{'='*50}")print(f"✓ Total de problemas encontrados: {total_found}")if total_found >0: severity_data = data_content.get('issues_by_severity', {})print(f"\n🔍 Problemas por severidad:")for severity_type, count in severity_data.items():if count >0: emoji ="🔴"if severity_type =="error"else"🟡"if severity_type =="warning"else"⚪"print(f" {emoji}{severity_type}: {count}")else:print("⚠️ No se encontraron problemas")else:print("❌ No se encontraron archivos de análisis CodeQL")
📂 Buscando en: /workspaces/id_2026/data/results
📋 Archivos encontrados: 3
📖 Leyendo: claude-code-action-codeql.json
📊 ANÁLISIS: claude-code-action
==================================================
✓ Total de problemas encontrados: 16
🔍 Problemas por severidad:
🟡 warning: 16
# Mostrar problemas detalladosoutput_path_det = project_root /"data"/"results"codeql_files_det =sorted(output_path_det.glob("*-codeql.json"))if codeql_files_det: archivo_det = codeql_files_det[0]withopen(archivo_det, 'r', encoding='utf-8') as f: analisis_det = json.load(f) issues_list = analisis_det.get('issues', [])print(f"📋 Leyendo: {archivo_det.name}")print(f"📊 Total de issues en archivo: {len(issues_list)}")if issues_list:# Crear DataFrame con los problemas df_issues = pd.DataFrame(issues_list)# Seleccionar y reordenar columnas cols_mostrar = ['rule_id', 'level', 'message', 'file'] cols_disponibles = [c for c in cols_mostrar if c in df_issues.columns] df_mostrar = df_issues[cols_disponibles].head(20)print(f"\n📋 Primeros 20 problemas encontrados:")print(f"{'='*100}")for idx, row in df_mostrar.iterrows():print(f"\n{idx+1}. [{row['level'].upper()}] {row['rule_id']}")print(f" 📝 {row['message'][:80]}")if'file'in row and pd.notna(row['file']):print(f" 📂 {row['file']}")iflen(issues_list) >20:print(f"\n... y {len(issues_list)-20} problemas más")else:print("⚠️ No hay detalles de problemas en el archivo")
📋 Leyendo: claude-code-action-codeql.json
📊 Total de issues en archivo: 16
📋 Primeros 20 problemas encontrados:
====================================================================================================
1. [WARNING] js/incomplete-multi-character-sanitization
📝 This string may still contain [<!--](1), which may cause an HTML element injecti
📂 src/github/utils/sanitizer.ts
2. [WARNING] js/xss-through-dom
📝 [DOM text](1) is reinterpreted as HTML without escaping meta-characters.
📂 docs/create-app.html
3. [WARNING] js/http-to-file-access
📝 Write to file system depends on [Untrusted data](1).
📂 src/github/utils/image-downloader.ts
4. [WARNING] js/file-access-to-http
📝 Outbound network request depends on [file data](1).
📂 src/entrypoints/post-buffered-inline-comments.ts
5. [WARNING] js/file-access-to-http
📝 Outbound network request depends on [file data](1).
📂 src/mcp/github-file-ops-server.ts
6. [WARNING] js/regex/missing-regexp-anchor
📝 When this is used as a regular expression on a URL, it may match anywhere, and a
📂 src/github/api/config.ts
7. [WARNING] js/insecure-temporary-file
📝 Insecure creation of file in [the os temp dir](1).
Insecure creation of file in
📂 base-action/src/prepare-prompt.ts
8. [WARNING] js/insecure-temporary-file
📝 Insecure creation of file in [the os temp dir](1).
📂 base-action/test/prepare-prompt.test.ts
9. [WARNING] js/insecure-temporary-file
📝 Insecure creation of file in [the os temp dir](1).
📂 base-action/test/prepare-prompt.test.ts
10. [WARNING] js/insecure-temporary-file
📝 Insecure creation of file in [the os temp dir](1).
📂 base-action/test/prepare-prompt.test.ts
11. [WARNING] js/insecure-temporary-file
📝 Insecure creation of file in [the os temp dir](1).
📂 base-action/test/setup-claude-code-settings.test.ts
12. [WARNING] js/insecure-temporary-file
📝 Insecure creation of file in [the os temp dir](1).
📂 src/github/utils/image-downloader.ts
13. [WARNING] js/insecure-temporary-file
📝 Insecure creation of file in [the os temp dir](1).
📂 test/github-file-ops-path-validation.test.ts
14. [WARNING] js/insecure-temporary-file
📝 Insecure creation of file in [the os temp dir](1).
📂 test/github-file-ops-path-validation.test.ts
15. [WARNING] js/insecure-temporary-file
📝 Insecure creation of file in [the os temp dir](1).
📂 test/github-file-ops-path-validation.test.ts
16. [WARNING] js/insecure-temporary-file
📝 Insecure creation of file in [the os temp dir](1).
📂 test/github-file-ops-path-validation.test.ts
Análisis Detallado de Problemas
# Compilar estadísticas consolidadasfrom collections import defaultdictoutput_path_stats = project_root /"data"/"results"codeql_files_stats =sorted(output_path_stats.glob("*-codeql.json"))stats_consolidadas = {'total_repositories': len(codeql_files_stats),'total_issues': 0,'issues_by_repo': {},'issues_by_severity': defaultdict(int),'top_rules': defaultdict(int)}for archivo in codeql_files_stats:withopen(archivo, 'r', encoding='utf-8') as f: data = json.load(f) repo_name = archivo.stem.replace('-codeql', '') total = data.get('total_issues', 0) stats_consolidadas['total_issues'] += total stats_consolidadas['issues_by_repo'][repo_name] = total# Contar por severidadfor severity, count in data.get('issues_by_severity', {}).items(): stats_consolidadas['issues_by_severity'][severity] += count# Contar top rulesfor issue in data.get('issues', []): rule = issue.get('rule_id', 'unknown') stats_consolidadas['top_rules'][rule] +=1# Convertir defaultdicts a dicts para visualizaciónstats_consolidadas['issues_by_severity'] =dict(stats_consolidadas['issues_by_severity'])stats_consolidadas['top_rules'] =dict(sorted( stats_consolidadas['top_rules'].items(), key=lambda x: x[1], reverse=True)[:10])print("\n📊 ESTADÍSTICAS CONSOLIDADAS DE CODEQL")print(f"{'='*60}")print(f"\n📁 Repositorios analizados: {stats_consolidadas['total_repositories']}")print(f"🔴 Total de problemas: {stats_consolidadas['total_issues']}")print(f"\n📋 Problemas por repositorio:")for repo, count in stats_consolidadas['issues_by_repo'].items():print(f" • {repo}: {count}")print(f"\n⚠️ Problemas por severidad:")for severity, count insorted(stats_consolidadas['issues_by_severity'].items()): emoji ="🔴"if severity =="error"else"🟡"if severity =="warning"else"⚪"print(f" {emoji}{severity}: {count}")print(f"\n🎯 Top 10 reglas más comunes:")for rule, count inlist(stats_consolidadas['top_rules'].items())[:10]:print(f" • {rule}: {count}")