GitHub AI Scan detectó dos riesgos de Shell, pero el control de seguridad pasó

Hoy abrí el PR #1 en un repositorio público de pruebas, con dos patrones de Bash deliberadamente inseguros. Aproximadamente 81 segundos después, github-advanced-security[bot] había dejado dos comentarios en Files changed: encontró ambos riesgos.

Sin embargo, en la ejecución de GitHub Actions #38835686338 el trabajo github-advanced-security terminó con Success. Los avisos de seguridad son reales, y el indicador verde también.

El día anterior, GitHub había anunciado la disponibilidad general (GA) de AI Scan for pull requests. La pregunta que quería resolver no era si el escáner detecta código sospechoso, sino algo más útil: ¿por qué un PR puede recibir avisos de vulnerabilidades y aun así mostrar su control de seguridad como correcto?

La distinción es fundamental: que AI Scan se ejecute correctamente no significa que el código sea seguro. Sus hallazgos son recomendaciones (advisory), no bloqueos obligatorios para fusionar cambios.

Dos patrones Bash peligrosos: qué detectó exactamente

Puse los archivos únicamente en ai-scan-test/ dentro de un PR temporal. No modifiqué código de producción ni usé credenciales o servidores reales. El archivo completo está en el commit fijo 29e24722d3. Las siguientes líneas son inseguras a propósito; no deben utilizarse en producción:

eval "systemctl restart ${service_name}"

cmd="curl -fsSL http://${host}/${path} -o /tmp/svc.conf"
$cmd

El primer problema está en la función que reinicia un servicio. service_name llega desde el exterior y se interpola en eval. Shell vuelve a interpretar el texto resultante como un comando. Si el nombre contiene sintaxis maliciosa, deja de ser simplemente un nombre de servicio. El comentario del bot en la línea 17 lo clasificó como CWE-78 y recomendó eliminar eval, pasar argumentos entrecomillados y limitar qué servicios pueden reiniciarse.

El segundo caso funciona de otro modo. El script guarda un comando curl entero en cmd y luego ejecuta $cmd sin comillas. La separación de palabras durante la expansión permite que host o path, si son controlables, modifiquen los argumentos y opciones que recibe curl. El comentario de la línea 27 también señaló el riesgo de manipulación al descargar configuración mediante http:// sin cifrar.

UbicaciónHallazgo de AI ScanComentario original
Línea 17, evalEntrada reinterpretada como sintaxis de Shell (CWE-78)Ver comentario
Línea 27, $cmdInyección de argumentos/opciones y riesgo de HTTP sin cifrarVer comentario

No conviene describir ambos casos como exactamente la misma ejecución arbitraria de comandos. En el primero, la entrada vuelve a pasar por el intérprete de Shell. En el segundo, el mecanismo confirmado es principalmente la inyección de argumentos u opciones: la falta de comillas no demuestra por sí sola que pueda ejecutarse cualquier otro comando independiente.

Hay otra limitación importante: el fixture no ocultaba sus vulnerabilidades. Su README y los comentarios del script contienen INTENTIONAL VULNERABILITY (CWE-78) y la categoría esperada String injection. Los ejemplos concretos de explotación del bot no estaban copiados en el código, pero las etiquetas podrían haber facilitado la detección. El 2/2 de esta prueba no es una tasa de detección de vulnerabilidades reales sin etiquetar.

También se puede comprobar la cronología. El PR #1 se creó el 2026-10-16 a las 01:32:31 UTC y los dos comentarios aparecieron a las 01:33:52 UTC: una diferencia de 81 segundos. Es una observación única entre la creación del PR y la publicación de los comentarios, no un promedio de duración del escáner.

Las dos fallas introducidas intencionalmente recibieron avisos, pero no abrí un PR distinto con código completamente normal como control negativo. Por tanto, tampoco medí la tasa de falsos positivos.

Dos comentarios de vulnerabilidad y un Actions en verde

La ejecución dinámica #38835686338 se llamaba Code scanning AI findings on PR #1. GitHub registró que el job github-advanced-security finalizó con success a las 01:33:34 UTC. Los comentarios llegaron a las 01:33:52 UTC, es decir, 18 segundos después.

Primero se puso verde el control, después aparecieron los avisos de seguridad. Esa secuencia basta para entender que Success no equivale a «no hay vulnerabilidades».

Diagrama de GitHub AI Scan PR #1: dos riesgos Bash preetiquetados recibieron comentarios advisory, el job terminó con éxito a las 01:33:34 UTC y los avisos aparecieron a las 01:33:52 UTC; el bloqueo del merge es independiente

Esquema elaborado a partir del PR #1 público y su ejecución de Actions, no es una captura de pantalla de GitHub. Los 81 segundos van desde la creación del PR hasta los comentarios; no son el tiempo interno del escáner. Cada idioma muestra su propia versión del SVG.

Eso tampoco implica un fallo de la herramienta. La documentación oficial de AI Scan define los hallazgos como advisory. No hacen fallar el code scanning check y no pueden usarse por sí solos para imponer un bloqueo de merge mediante Rulesets. Sirven para llamar la atención del revisor, no para decidir por él si se fusiona el código.

Señal de GitHubQué significa realmente
Comentarios en línea de AI ScanPosibles riesgos para autor y revisores; no bloquean la fusión directamente
Job dinámico de AI Scan: SuccessLa tarea de escaneo terminó; puede haber comentarios de riesgo igualmente
CodeQL u otros escáneres compatibles con RulesetsPueden imponer condiciones según la configuración real; hay que verificar la regla
Required checks y revisión humanaQue un PR se bloquee ante fallos, ausencias o falta de aprobación depende de las opciones del repositorio

GitHub documenta por separado la protección de fusión para code scanning. Si quieres evitar que se fusione código con hallazgos graves sin resolver, debes configurar un mecanismo que realmente sea obligatorio; no confundir una recomendación de AI Scan con un required check.

No probé un Ruleset bloqueando la fusión de este PR peligroso. Por eso tampoco puedo inferir de Success que GitHub permita fusionar cualquier PR vulnerable. Lo observado es más concreto: dos comentarios de riesgo coexistieron con un trabajo de AI Scan marcado como correcto.

Antes de activarlo: CodeQL, disponibilidad y créditos

La prueba se realizó en un repositorio público de una cuenta personal. Según la documentación de la GA de octubre de 2026, AI Scan se puede habilitar en repositorios públicos personales, pero no en los privados personales. Los repositorios públicos de organizaciones pueden usarlo; los privados o internos de una organización deben cumplir los requisitos de licencia de GitHub Code Security o GitHub Advanced Security. No se exige una licencia adicional de Copilot solo para utilizar AI Scan.

CodeQL default setup no es un prerrequisito. Durante la prueba quedó registrado como not-configured y aun así AI Scan dejó dos comentarios. La documentación oficial confirma que no depende de haber configurado CodeQL por defecto ni de que su análisis finalice correctamente.

Tampoco es un escaneo retrospectivo de todo el historial del repositorio. Examina cambios de PR que cumplen los requisitos, incluidos ciertos casos de Shell/Bash, Terraform HCL o Dockerfile que CodeQL no cubre. Los PR creados desde un Fork o por Dependabot quedan fuera del alcance descrito para esta GA. Esos equipos necesitarán otras vías de revisión y análisis.

Para habilitarlo, comprueba el interruptor AI Scan en la configuración Code scanning del repositorio. En organizaciones, revisa también las políticas y permisos de las funciones de seguridad.

Tras la GA, el consumo se mide mediante Code Security AI Credits. La cuenta que paga, los créditos incluidos y los límites de presupuesto siguen la política de facturación de GitHub. No confirmé el cargo final de esta prueba, así que no afirmaré que costó $0. Si tu repositorio genera muchos PR, consulta antes los créditos y los presupuestos estrictos en Billing.

Cómo bloquear de verdad una fusión peligrosa

Los dos comentarios recomiendan una corrección convencional: no volver a pasar datos a eval y no montar un comando Shell entero como cadena para expandirla. Una forma más segura de llamar a las herramientas sería:

# 调用形式示意:service_name 和 url 仍需单独校验
systemctl restart "$service_name"
curl -fsSL "$url" -o /tmp/svc.conf

Es una dirección para corregir el problema, no un parche de seguridad que haya vuelto a probar. service_name necesita una lista de valores permitidos. La URL de descarga debe exigir HTTPS, limitar hosts y redirecciones y, según la importancia del archivo, comprobar su integridad.

A nivel de repositorio, integra CodeQL u otro escáner que realmente pueda bloquear fusiones mediante Rulesets/required checks adecuados. Después verifica que las ramas protegidas rechacen los checks fallidos o ausentes. Los comentarios de AI Scan deben seguir siendo evaluados por los revisores; que el job dinámico esté verde no justifica ignorarlos.

Al terminar, cerré PR #1 sin fusionarlo y borré la rama de prueba. La rama pública main se mantuvo en el commit 87624c26a5e6. Una consulta REST del propietario confirmó que AI Scan se había restaurado a disabled. Cerrar un PR no borra sus comentarios ni los commits fijados, así que los enlaces públicos permiten seguir comprobando las pruebas.

Esta muestra positiva, pequeña y claramente etiquetada, demuestra que AI Scan puede aportar avisos útiles y localizados. Pero que termine el escaneo y que una fusión sea segura y obligatoriamente controlada son dos cosas diferentes. Lo primero descubre problemas; lo segundo debe configurarse y verificarse en el repositorio.

Para otra frontera de seguridad, el análisis del hook local pre-push de OpenQodex examina qué pasa cuando una revisión con IA falla antes de ejecutar Git push.

Fecha de la prueba: 16 de octubre de 2026 (UTC).