Saltar al contenido
Ingeniería AI-native

eMudhra en macOS: qué actualizar en curl, Node, Python, Go, Java, Rust y Docker

curl, Node, pip, Java y contenedores (Docker) usan sus raíces propias, no el bloqueo de macOS a eMudhra: qué actualizar y cómo probarlo.

Publicado Actualizado 11 min de lectura
securityTLScertificatesDevOpsmacOS

Este artículo es educativo y no sustituye una auditoría formal de smart contracts, asesoría legal, asesoría financiera ni una revisión de seguridad específica del protocolo.

Ocho CA intermedias de eMudhra, seis de TLS y dos de firma de código, se generaron con claves privadas que cualquiera puede recuperar a partir del propio certificado; con las seis de TLS se puede firmar un certificado válido para cualquier dominio. Chrome y Firefox las bloquean desde el 17 de octubre de 2026, Microsoft desde el 20 y, según la página de Dennis Jackson, Apple desde el 22. Muchas herramientas de desarrollo no consultan esos bloqueos y deciden con su propia lista de raíces.

En un Mac, el bloqueo de Apple cubre lo que usa el verificador del sistema, como Safari, Go y rustls-platform-verifier. No cubre el curl y el python3 del sistema, Node, pip, Java ni los contenedores Linux de Docker, que llevan sus propias raíces. Actualiza cada herramienta a su versión corregida, recompila lo que lleve las raíces dentro y prueba contra el servidor público. Lo correcto es un error de certificado; un 200 significa que esa herramienta aún confía en las intermedias.

Siete bloques de cristal en fila sobre una base de mármol: cinco en cobalto, ya verificados; uno ámbar que una mano comprueba con una sonda, y uno transparente, sin revisar.
Ilustración conceptual.

Qué pasó y por qué el navegador no basta

El 17 de octubre de 2026 un investigador avisó a los programas de raíces de Apple, Google, Microsoft y Mozilla de que las claves de ocho intermedias de eMudhra eran débiles. Google identifica emSign ECC Root CA - G3 como la raíz que firmó las intermedias comprometidas, y existe además un camino alternativo a través de una firma cruzada de la raíz AAA Certificate Services de Sectigo, que siguen incluyendo las listas de raíces antiguas.

Chrome bloqueó las claves públicas de esas intermedias con una actualización de CRLSet a las 19:45 UTC del mismo día (21:45 en Madrid). Mozilla completó su bloqueo con OneCRL a las 22:59 UTC (00:59 del 18 en Madrid), publicó Firefox 158.0.1 y quitó la raíz de NSS en la versión 3.131.1. Google retirará la raíz de su almacén el martes 27 de octubre por la mañana, hora del Pacífico.

Según eMudhra, solo una de las ocho intermedias estaba en producción y ninguna emitió certificados finales. Google y Dennis Jackson, de Mozilla, coinciden en que no hay certificados de esas intermedias en los registros de Certificate Transparency (CT), así que los navegadores que exigen CT no pudieron ser atacados. El riesgo está en los programas que no exigen CT ni consultan revocaciones: curl, los runtimes de lenguajes, pip y las imágenes de contenedor. Para que esos programas dejen de confiar en las intermedias hace falta actualizar su lista de raíces o su código.

Una intermedia comprometida no basta para atacar. Hace falta además que el atacante controle la red, el DNS o un proxy entre la herramienta y el servidor. Los vectores concretos y cómo frenarlos están en un artículo aparte.

Cómo leer la prueba

Dennis Jackson, de Mozilla, mantiene un servidor de comprobación con una cadena firmada por una de las intermedias afectadas, EM DV TLS CA - G3B. Si una herramienta conecta con éxito, todavía confía en ella. Si devuelve un error de certificado, no.

El mensaje exacto cambia según la herramienta y la versión: «unable to get local issuer certificate», «certificate revoked», «no valid chains built», «CertDistrusted» o «PKIX path building failed» significan lo mismo. Si la prueba se queda sin respuesta o falla el DNS, el resultado no dice nada: repítela.

La misma página documenta variantes del servidor. La que lleva «-aaa-» entra por la firma cruzada de Sectigo y la que lleva «-noct-» usa un certificado que nunca se registró en CT. Si una herramienta acepta la variante sin CT, confía en las intermedias y además no exige CT.

Cloudflare recuerda un matiz importante: la prueba solo vale para el camino de verificación que se prueba. Que el navegador o curl rechacen la cadena no dice nada de una aplicación que usa otra biblioteca TLS.

curl

El curl de macOS, igual que git, python3, perl, ruby y openssl del sistema, verifica contra /etc/ssl/cert.pem sin consultar revocaciones, y ese archivo no se ha actualizado. La página de Jackson publica un procedimiento con sudo para quitar las dos raíces afectadas de ese archivo. Toca un archivo del sistema, así que conviene leerlo entero y decidir antes de aplicarlo.

El curl de Homebrew usa el OpenSSL de Homebrew, que toma las raíces de la fórmula ca-certificates. La versión corregida es 2026-09-25_1 o posterior. Actualízalas y prueba con ese curl en concreto:

bash
brew update
brew upgrade ca-certificates curl
brew list --versions ca-certificates curl
"$(brew --prefix curl)/bin/curl" -v -o /dev/null https://dv-g3b-emudhra.we-have-a-problem.com/
echo "exit: $?"

Lo esperado es «SSL certificate problem: unable to get local issuer certificate» y el código de salida 60. Un código 0 con «HTTP/1.1 200 OK» significa que ese curl todavía confía en la cadena. curl no exige CT, así que es justo el tipo de cliente que describe la página de Jackson.

Node.js

Node lleva su propia copia de las raíces de Mozilla dentro del binario, así que no le afecta lo que haga macOS. Las versiones corregidas son 26.12.1, 24.22.1 y 22.23.4: actualizan las raíces a NSS 3.131.1 y añaden el rechazo de las claves públicas bloqueadas. Si usas nvm, fnm o Volta, actualiza cada versión que tengas instalada, no solo la activa.

bash
node --version
node <<'NODE'
const https = require('node:https');

const req = https.get({
  hostname: 'dv-g3b-emudhra.we-have-a-problem.com',
  timeout: 10000,
  rejectUnauthorized: true
}, res => {
  console.log('WARNING: TLS ACCEPTED:', res.statusCode);
  res.resume();
});

req.on('error', err => {
  console.log('REQUEST REJECTED:', err.code, err.message);
});

req.on('timeout', () => {
  console.log('INCONCLUSIVE: timeout');
  req.destroy();
});
NODE

Con Node 24.22.1 la salida es «REQUEST REJECTED: CERT_REVOKED certificate revoked». Con una versión anterior sin corregir, como la 20.19.2, sale «WARNING: TLS ACCEPTED: 200». Revisa también NODE_EXTRA_CA_CERTS, NODE_TLS_REJECT_UNAUTHORIZED=0 y cualquier rejectUnauthorized: false en el código, porque cambian lo que Node acepta.

Python, pip y uv

En Python hay dos listas de raíces que actualizar. La del intérprete depende de cómo lo instalaste. La de pip va dentro de pip: según el hilo oficial de Python sobre el incidente, pip carga siempre su copia de certifi además del almacén del sistema, y opciones como REQUESTS_CA_BUNDLE o --cert solo añaden raíces. La corrección fiable es actualizar pip.

Las versiones mínimas que publicó la Python Software Foundation son certifi 2026.10.19, pip 26.2.2 y uv 0.13.4. Python 3.15.1, 3.14.9 y 3.13.17 ya incluyen pip 26.2.2. Con un pip antiguo, la propia actualización viaja con las raíces antiguas, así que la PSF recomienda fijar el hash del paquete en el comando. Ejecuta esto dentro de cada entorno virtual:

bash
python3 -m pip --version
python3 -m pip install "pip @ https://files.pythonhosted.org/packages/3f/06/3e4420302363fbb71be3c7f35ce042f54bfca330e3e66b3a2e3c4cca579f/pip-26.2.2-py3-none-any.whl#sha256=fd7f4dfa8190e014013b9f08f3e973f4e4c26ffd179a204895533a7254813a70"
python3 -m pip install --upgrade "certifi>=2026.10.19"
python3 -m pip debug --verbose | grep certifi

La última línea debe mostrar certifi==2026.10.19, la copia que lleva pip. Si usas uv instalado con su instalador propio, la PSF indica «UV_SYSTEM_CERTS=true uv self update»; si lo instalaste con Homebrew, «brew upgrade uv».

Para comprobar las dos listas de un entorno, ejecuta esta prueba con el python3 de ese entorno:

python
import ssl
import socket
import certifi

host = "dv-g3b-emudhra.we-have-a-problem.com"

contexts = {
    "default": ssl.create_default_context(),
    "certifi": ssl.create_default_context(cafile=certifi.where()),
}

for name, ctx in contexts.items():
    try:
        with socket.create_connection((host, 443), timeout=10) as sock:
            with ctx.wrap_socket(sock, server_hostname=host):
                print(name, "WARNING: TLS ACCEPTED")
    except ssl.SSLCertVerificationError as exc:
        print(name, "TLS REJECTED:", exc.verify_message)
    except Exception as exc:
        print(name, "INCONCLUSIVE:", repr(exc))

Con certifi 2026.10.19, la línea certifi debe decir «TLS REJECTED: unable to get local issuer certificate». La línea default depende del almacén que use ese intérprete: si dice ACCEPTED, actualiza el almacén del sistema o el del instalador.

Go

En macOS, Go delega la verificación en las API del sistema, así que un programa de Go en el Mac hereda el bloqueo de Apple. El problema está en los binarios que compilas en el Mac para Linux, por ejemplo para un contenedor: ahí Go usa su propio verificador. Go 1.27.3 y 1.26.10 añaden a crypto/x509 una lista de claves públicas bloqueadas (CVE-2026-111943). Actualiza Go con «brew upgrade go» o el instalador oficial y vuelve a compilar y desplegar cada binario.

Para probarlo, ejecuta esto en Linux o dentro del contenedor donde corre el binario:

bash
go version
mkdir -p /tmp/emudhra-go && cd /tmp/emudhra-go
cat > main.go <<'EOF'
package main

import (
	"fmt"
	"net/http"
	"time"
)

func main() {
	client := &http.Client{Timeout: 10 * time.Second}
	resp, err := client.Get("https://dv-g3b-emudhra.we-have-a-problem.com/")
	if err != nil {
		fmt.Println("TLS REJECTED:", err)
		return
	}
	resp.Body.Close()
	fmt.Println("WARNING: TLS ACCEPTED:", resp.Status)
}
EOF
go run main.go

Con Go 1.27.3 en Linux, la salida termina en «x509: no valid chains built». Con 1.27.2 sobre un almacén de raíces sin actualizar, en «200 OK». Un binario ya compilado lleva dentro la versión con la que se compiló; «go version» seguido de la ruta del binario la muestra.

Java

Los JDK publicados antes del 21 de octubre NO bloquean estas intermedias por defecto, tampoco el que ya tengas instalado si es anterior. Actualiza a una versión corregida (Oracle u OpenJDK 27.0.1.0.2 / 25.0.5.0.2 / 21.0.13.0.2 / 17.0.21.0.2 / 11.0.33.0.2, Oracle JDK 8u511 b32, Corretto 25.0.5.0.9 / 21.0.13.0.10 / 8.512.08.1, o Homebrew openjdk / @21 / @17 en esas mismas versiones). Si no puedes, copia lib/security/blocked.certs (en JDK 8, jre/lib/security/blacklisted.certs) de un JDK ya corregido, comprueba que contiene las 16 huellas SHA-256 y reinicia las aplicaciones Java. Oracle publica también el archivo en ops.java/emudhra, pero el 23 de octubre esa página no estaba disponible.

Java usa su propio almacén de raíces, cacerts, dentro de cada JDK, y no consulta el bloqueo de macOS. Si ese cacerts incluye emSign ECC Root CA - G3 o AAA Certificate Services, el JDK acepta las intermedias mientras no estén en su lista de certificados bloqueados. El aviso de OpenJDK del 21 de octubre (CVE-2026-91403) da como afectadas las versiones 27.0.1, 25.0.5, 21.0.13, 17.0.21, 11.0.32.1 y 8u504, y las anteriores.

La corrección se llama JDK-8395142 en OpenJDK y JDK-8395555 en las notas de versión de Oracle. No quita ninguna raíz: añade a la lista de bloqueo las 8 intermedias de eMudhra y 8 certificados con firma cruzada de Sectigo. Ya está publicada en Oracle JDK 27.0.1.0.2, 25.0.5.0.2, 21.0.13.0.2, 17.0.21.0.2, 11.0.33.0.2 y 8u511 b32, en Corretto 25.0.5.0.9, 21.0.13.0.10 y 8.512.08.1, y en las fórmulas openjdk, openjdk@21 y openjdk@17 de Homebrew. A 23 de octubre siguen sin ella openjdk@11 y openjdk@8 de Homebrew, Zulu y Temurin 8 y 11. Temurin 21 y 25 tampoco la traen, pero rechazan la cadena porque no incluyen ni G3 ni AAA.

En Oracle JDK 8, la versión 8u511 publicada el 20 de octubre (1.8.0_511-b09) sigue afectada; la corregida es 8u511 b32 (1.8.0_511-b32, del 21 de octubre). Compruébalo con «java -version»: si no muestra «b32», actualiza. En OpenJDK 8 (Temurin, Zulu, etc.), 8u504 y las anteriores están afectadas y, a 23 de octubre, Temurin no ha publicado corrección; Corretto 8.512.08.1 sí la trae.

La vía principal es actualizar. En los JDK de Homebrew:

bash
/usr/libexec/java_home -V
brew list --versions | grep openjdk
brew upgrade openjdk
brew upgrade openjdk@21
brew upgrade openjdk@17
for f in openjdk openjdk@21 openjdk@17; do
  J="$(brew --prefix "$f")/bin/java"
  [ -x "$J" ] && echo "$f:" && "$J" -version
done

Actualiza solo las fórmulas que tengas instaladas. La primera línea no lista todos los JDK: solo los que tienen un enlace en /Library/Java/JavaVirtualMachines o ~/Library/Java/JavaVirtualMachines. Las fórmulas keg-only de Homebrew, como openjdk@21 u openjdk@17, solo aparecen con «brew list --versions» o «brew --prefix openjdk@21», y hay que revisar cada una por separado. El nombre no basta: compara la versión real de cada JDK con la lista de arriba. «brew list --versions» debe mostrar openjdk 27.0.1.0.2, openjdk@21 21.0.13.0.2 u openjdk@17 17.0.21.0.2; openjdk 27 u openjdk@21 21.0.13 siguen afectadas. El bucle final imprime «java -version» de cada fórmula. Los JDK de un IDE o de SDKMAN! se actualizan con su propio instalador.

Este fragmento guarda las 16 huellas SHA-256 en /tmp/emudhra16.txt y cuenta cuántas tiene la lista de bloqueo de cada JDK del Mac. En JDK 11 y posteriores el archivo se llama blocked.certs; en JDK 8, blacklisted.certs, dentro de la carpeta jre:

bash
cat > /tmp/emudhra16.txt <<'EOF'
0164D7CEAE5BB909C272AF151FF44B1177AE9B47D9424B94F1D9558C30934D19
42C4ACCD12C4F59597925E1A08A7A90102608543809308D50EC427E118BA1493
432EC25F0D28DE9006D97BF752F17BBB2CD41F669933FCB072D9A1148D0261F3
45A883F349AEDFD5F3A649B5498FFB121712BA8B448F3F2A91DBC5A766FCDDBE
4B2460A466F95FD40113BCDCCA2CEB29B3B093921DE45474DA7DF48874C630B1
579480AEA166290BD3C643589E795FC0D0E54E86505D180F1DFF99EFD3199512
5F1DED4DD590AF74631290CF073B122A2AAC6A1FA479778A0BD43579BD832699
62B7F9868263F72F6911FC78CEAF72C1979158B7368BF648683BB2AA39B02E72
7485B51C0ED192878FA1D2F948FB529AE1842CC5086C4B67F7BE5B2039DE9489
84112972B8E59D14560AE7E7119654474AAD119990CF3722BFC1E3B91615E1FD
98B896625C0C230ADEEC3EB2CF6AAD5001ECF9156A68644E82374CE5833BC25B
9E39DE2881273ACC225A149C72BA3E0F51424963669AB633EE1DC7DF5DB067B4
A496E23AC0CAB8155FC9B117B97EAE368EFCBB9041AEC8804D5A308D2D01C2D8
CB6E15165AC802E9B3F993F16C25D41F4E81C21EBDE71AE1B012AC00BF9C7C3F
E7568FE6E12331F4FD7DE61DA4C7559D0F8EB192A218D4741A3BA75184333838
FD9D5C2DBBD4995AB69B8D50C439D9527B00529199C2FD01B2174EB58F07021F
EOF

for JDK in /Library/Java/JavaVirtualMachines/*/Contents/Home \
           "$HOME"/Library/Java/JavaVirtualMachines/*/Contents/Home \
           /opt/homebrew/opt/openjdk*/libexec/openjdk.jdk/Contents/Home \
           /usr/local/opt/openjdk*/libexec/openjdk.jdk/Contents/Home; do
  [ -x "$JDK/bin/java" ] || continue
  F="$JDK/lib/security/blocked.certs"
  [ -f "$F" ] || F="$JDK/jre/lib/security/blacklisted.certs"
  N="$(grep -c -x -F -f /tmp/emudhra16.txt "$F" 2>/dev/null)"
  echo "$JDK: ${N:-0}/16 eMudhra fingerprints in $(basename "$F")"
done

Un JDK corregido muestra 16/16. Con menos de 16 sigue sin estar protegido.

Para probar un JDK concreto, ejecuta esto con su java y su jshell en el PATH:

bash
java -version
cat > /tmp/emudhra-java.jsh <<'EOF'
import java.net.*;
import javax.net.ssl.*;

var url = URI.create("https://dv-g3b-emudhra.we-have-a-problem.com/").toURL();

try {
    var c = (HttpsURLConnection) url.openConnection();
    c.setConnectTimeout(10000);
    c.setReadTimeout(10000);
    System.out.println("WARNING: TLS ACCEPTED: " + c.getResponseCode());
} catch (SSLHandshakeException e) {
    System.out.println("TLS REJECTED: " + e.getMessage());
} catch (Exception e) {
    System.out.println("INCONCLUSIVE: " + e);
}
/exit
EOF
jshell /tmp/emudhra-java.jsh

Con la lista de bloqueo aplicada, la salida es «TLS REJECTED: (certificate_unknown) PKIX path validation failed», seguida de «Untrusted certificate: CN=EM DV TLS CA - G3B». En Temurin 21 y 25 sale «PKIX path building failed», porque no tienen la raíz. El texto varía entre distribuciones de Java; lo que importa es que no aparezca «TLS ACCEPTED».

Si un JDK no se puede actualizar, como openjdk@11 u openjdk@8, hay que añadir las huellas a su lista de bloqueo. Sirven el blocked.certs de un JDK ya corregido de la misma versión o las 16 huellas de /tmp/emudhra16.txt, que coinciden con las del PEM de JDK-8395142 en OpenJDK. Oracle publica también el archivo en ops.java/emudhra, pero el 23 de octubre esa página no estaba disponible. El archivo empieza por la línea Algorithm=SHA-256 y sigue con una huella en hexadecimal por línea. Este fragmento valida el archivo de origen, exige las 16 huellas, conserva las entradas que ya tiene cada JDK, guarda una copia .pre-emudhra que no sobrescribe y recorre los mismos JDK que la comprobación:

bash
SRC=/tmp/emudhra16.txt
(
  [ -s "$SRC" ] || { echo "ERROR: missing $SRC"; exit 1; }
  grep -Evx 'Algorithm=SHA-256|[0-9A-Fa-f]{64}' "$SRC" | grep -q . && { echo "ERROR: unexpected format"; exit 1; }
  [ "$(tr 'a-f' 'A-F' < "$SRC" | grep -c -x -F -f /tmp/emudhra16.txt)" = 16 ] || { echo "ERROR: missing eMudhra fingerprints"; exit 1; }
  for JDK in /Library/Java/JavaVirtualMachines/*/Contents/Home \
             "$HOME"/Library/Java/JavaVirtualMachines/*/Contents/Home \
             /opt/homebrew/opt/openjdk*/libexec/openjdk.jdk/Contents/Home \
             /usr/local/opt/openjdk*/libexec/openjdk.jdk/Contents/Home; do
    [ -x "$JDK/bin/java" ] || continue
    F="$JDK/lib/security/blocked.certs"
    [ -f "$F" ] || F="$JDK/jre/lib/security/blacklisted.certs"
    [ "$(head -n 1 "$F" 2>/dev/null)" = "Algorithm=SHA-256" ] || { echo "SKIPPED: $JDK"; continue; }
    [ -e "$F.pre-emudhra" ] || cp -p "$F" "$F.pre-emudhra" || exit 1
    {
      echo "Algorithm=SHA-256"
      { tail -n +2 "$F"; grep -Ex '[0-9A-Fa-f]{64}' "$SRC"; } | tr 'a-f' 'A-F' | sort -u
    } > "$F.tmp" && cat "$F.tmp" > "$F" && rm "$F.tmp" || exit 1
    echo "$JDK: $(grep -c -x -F -f /tmp/emudhra16.txt "$F")/16; entries before: $(($(wc -l < "$F.pre-emudhra") - 1)); after: $(($(wc -l < "$F") - 1))"
  done
  echo "Restart all running Java applications: blocked.certs is only read when the JVM starts."
)

Para usar el archivo de Oracle o el de un JDK corregido, cambia la primera línea por su ruta. Cada JDK debe quedar en 16/16. Los JDK de /Library/Java/JavaVirtualMachines necesitan permisos de administrador: ejecuta el fragmento en un shell abierto con sudo -s. Java lee la lista solo al arrancar, así que hay que reiniciar las aplicaciones Java, el IDE y los servidores que estén en marcha. Una actualización de Homebrew sustituye el archivo por el de la nueva versión: si esa versión está corregida no pasa nada; si no, vuelve a ejecutar el fragmento.

El aviso de OpenJDK propone otra alternativa temporal: activar la comprobación de revocación con «-Dcom.sun.net.ssl.checkRevocation=true -Dcom.sun.security.enableCRLDP=true». Borrar las raíces con keytool -delete no es lo que hace la corrección, que mantiene las raíces y bloquea los 16 certificados. Quitar AAA Certificate Services afecta además a todo lo que depende de ella, y la página de Dennis Jackson indica que desconfiar de estas raíces no se recomienda a usuarios normales.

Rust

En Rust la corrección no depende de la versión del compilador, sino del Cargo.lock de cada proyecto. rustls-webpki 0.103.16 rechaza las claves bloqueadas (RUSTSEC-2026-0382) y webpki-roots 1.0.10 quita la raíz emSign ECC Root CA - G3. En macOS, rustls-platform-verifier usa el verificador del sistema, pero en Linux usa webpki, así que el riesgo vuelve a estar en los binarios que despliegas en contenedores. En cada proyecto:

bash
cargo tree -i rustls-webpki
cargo update -p rustls-webpki
cargo tree -i rustls-webpki

Después de la actualización, «cargo tree» debe mostrar rustls-webpki v0.103.16 o posterior. Si el proyecto usa webpki-roots, haz lo mismo con «cargo update -p webpki-roots» hasta la 1.0.10. Vuelve a compilar y desplegar. Si usas rustup, actualízalo a la 1.29.2 con «rustup self update».

Docker

Docker Desktop 4.96.2, del 22 de octubre, retira la confianza en las intermedias comprometidas. Eso cubre las conexiones de la CLI y el daemon de Docker, pero no las imágenes: cada imagen lleva su propia lista de raíces y sus propias versiones de Python, Node, Java o Go.

Actualiza Docker Desktop desde la aplicación o con «brew upgrade --cask docker-desktop» y vuelve a construir las imágenes sin caché, para que no reutilicen una capa con las raíces antiguas:

bash
docker version --format '{{.Server.Platform.Name}}'
docker build --pull --no-cache -t my-image:emudhra .
docker run --rm my-image:emudhra python3 -m pip --version

La última línea es un ejemplo para imágenes con Python: dentro de la imagen tiene que salir pip 26.2.2 o posterior. Para las demás, ejecuta dentro del contenedor la prueba del lenguaje que corresponda. Si la imagen base no ha publicado aún una versión corregida, actualiza el paquete ca-certificates de la distribución o el runtime en tu propio Dockerfile.

Lista de comprobación

  • Homebrew: ca-certificates 2026-09-25_1 o posterior y curl de Homebrew probado contra el servidor.
  • curl, git y python3 del sistema: decidida y documentada la opción para /etc/ssl/cert.pem.
  • Node: 26.12.1, 24.22.1 o 22.23.4 en cada versión instalada.
  • Python: pip 26.2.2 y certifi 2026.10.19 en cada entorno virtual; uv 0.13.4.
  • Go: 1.27.3 o 1.26.10, y binarios recompilados y desplegados.
  • Java: cada JDK, también las fórmulas keg-only de Homebrew, en una versión corregida (número completo, no solo el nombre) o, si no se puede, con las 16 huellas en blocked.certs (16/16) o la revocación activa, y las aplicaciones Java reiniciadas.
  • Rust: rustls-webpki 0.103.16 en cada Cargo.lock y binarios recompilados.
  • Docker: Desktop 4.96.2 o posterior e imágenes reconstruidas sin caché.
  • Revisión de variables y opciones que desactivan la verificación o añaden raíces.

Qué no prueba un rechazo

Un error de certificado en la prueba indica que esa herramienta, en esa versión, ya no confía en las intermedias. No dice nada de otra herramienta del mismo Mac, de otro entorno virtual ni de un contenedor. Tampoco sustituye a revisar si alguna conexión pudo pasar por una red que no controlas mientras el problema seguía abierto.

Para cerrar el incidente de eMudhra en macOS y en CI, actualiza las 7 herramientas una a una y guarda la salida de cada prueba con la fecha y la versión. Es la evidencia más simple de que la corrección se aplicó donde importa.

Fuentes

Fuentes consultadas el 23 de octubre de 2026. Ejecutado en Linux (Debian 13), con la salida guardada: las pruebas de curl, Node, Python, Go, Java y Rust, la actualización de pip con hash, y la comprobación 16/16 y la unión de blocked.certs sobre copias de JDK en rutas de prueba. Comprobado en un Mac real (macOS 27.0.1, arm64) en solo lectura el 23 de octubre: /usr/libexec/java_home -V, /Library/Java/JavaVirtualMachines y brew list --versions. No ejecutado en macOS: brew upgrade, brew --prefix, sudo -s, las rutas de Homebrew de los scripts y /etc/ssl/cert.pem. No ejecutado: Docker.

¿Necesitas aplicar esto a tu lanzamiento o workflow?

Solicita una revisión técnica y mapeamos el primer paso seguro.

Posts relacionados