# Cómo una tarjeta de aprobación de API muestra el destino real

Un revisor no puede aprobar una acción HTTP a partir de una cadena que solo se parece a un destino. La tarjeta debe mostrar el destino de solicitud que el transporte utilizará realmente, después del análisis y antes de que las credenciales salgan del equipo.

Parece obvio hasta que un agente envía `HTTPS://API.EXAMPLE.TEST:443/%76%31/../admin`, un cliente lo acepta y la persona ve una etiqueta abreviada como `api.example.test`. Una tarjeta así no solicita un consentimiento informado. Pide al usuario que confíe en un elemento visual que puede no coincidir con la pila HTTP.

La solución no consiste en enseñar a los revisores todos los detalles de la sintaxis de las URL. Consiste en crear una única descripción canónica de la solicitud, mostrarla con claridad y garantizar que el ejecutor use esa misma descripción. El esquema, el host, el puerto efectivo, el método y la ruta son el mínimo. Los valores de consulta, las redirecciones, los encabezados controlados por el solicitante y la identidad del cuerpo suelen tener que aparecer junto a ellos, porque pueden cambiar la acción de forma igual de drástica.

## Cómo consigue un sí una tarjeta de aprobación

Una tarjeta de aprobación consigue un sí solo cuando nombra la acción de red en términos que el revisor pueda comprobar. Un nombre de host por sí solo es una afirmación de identidad, no una descripción de la solicitud. `POST https://billing.example.test/v1/invoices/481/refund` dice mucho más que `billing API` o `example.test`.

Coloca primero la línea de acción, en un orden fijo:

```text
POST https://billing.example.test/v1/invoices/481/refund
```

Después, coloca justo debajo los detalles que cambian el significado:

```text
Authorization: injected from vault entry "billing-production"
Query: dry_run=false
Body: JSON, 214 bytes, sha256: 7b1f...c0a9
```

No pongas el valor de la credencial, un marcador de autorización ni el nombre amistoso de una integración en el lugar que corresponde al destino. Esas etiquetas pueden ayudar al revisor a reconocer el contexto, pero no prueban cuál es el destino.

El método debe aparecer en la primera línea porque cambia las consecuencias de una misma ruta. `GET /exports/481` y `DELETE /exports/481` no son variantes de una misma acción. Son acciones distintas y nunca deben quedar reducidas a una sola etiqueta de aprobación.

La ruta debe aparecer en la primera línea porque normalmente ahí se encuentra el enrutamiento de la API. Una tarjeta que muestra `api.example.test` obliga al revisor a deducir si el agente está leyendo un perfil, creando un token de acceso o eliminando un proyecto. Es una mala forma de usar una interrupción de aprobación.

## La canonicalización es un contrato de presentación, no una coincidencia de permisos

La canonicalización responde a «¿Qué debe ver una persona para esta solicitud analizada?». No responde a «¿Qué destinos están permitidos?». Los equipos suelen mezclar ambas tareas y terminan creando una lista de permitidos frágil disfrazada de formato sencillo.

Para una tarjeta de aprobación, crea un registro estructurado del destino después de analizarlo:

```json
{
  "method": "POST",
  "scheme": "https",
  "host": "api.example.test",
  "port": 443,
  "port_display": null,
  "path": "/v1/invoices/481/refund",
  "query": "dry_run=false",
  "raw_url": "HTTPS://API.EXAMPLE.TEST:443/v1/invoices/481/refund?dry_run=false"
}
```

El ejecutor debe tomar esos mismos campos estructurados, o una URL serializada a partir de ellos. No analices una vez para la tarjeta y pases después la cadena original a otra biblioteca. Esa separación es lo que convierte una pantalla de revisión en puro teatro.

RFC 3986 distingue varias categorías de normalización seguras. Trata el esquema y el host como elementos que no distinguen mayúsculas de minúsculas, recomienda usar dígitos hexadecimales en mayúscula en los escapes porcentuales y describe la eliminación de segmentos de punto. También advierte que el código debe analizar los componentes de la URI antes de decodificar los octetos con escapes porcentuales, porque decodificar en el momento equivocado puede convertir los datos en delimitadores. Es un consejo práctico de ingeniería, no un detalle meramente teórico de una especificación.

Conserva un segundo registro para la entrada sin procesar del agente. Debe estar en el registro de actividad y en una vista de detalles para cuando alguien necesite investigar una solicitud extraña. No debe competir con la línea de acción canónica por la atención del revisor.

Una regla útil es sencilla: la tarjeta muestra una descripción normalizada, el diario conserva tanto la descripción como la entrada y las decisiones de autorización no usan ninguna de las dos como sustituto de reglas de alcance explícitas. Son productos de datos distintos.

## Analiza primero y rechaza lo que el transporte no pueda explicar

Un analizador de URL forma parte del límite de seguridad una vez que una persona aprueba su resultado. Elige un comportamiento de análisis para los esquemas de URL compatibles y conviértelo en la fuente de verdad tanto para la presentación como para la ejecución.

Para las API HTTP normales, rechaza las entradas que dejan dudas en lugar de intentar ser útil. Las referencias relativas necesitan una URL base explícita antes de tener un host. Los fragmentos no forman parte de una solicitud HTTP y no deben aparecer como si influyeran en el servidor. La información de usuario, como `https://alice@api.example.test/`, casi siempre induce a error en un flujo de aprobación de API y debe rechazarse, no ocultarse en silencio.

Usa un flujo de análisis con un punto de fallo claro:

1. Acepta solo una URL absoluta `http` o `https` para el canal de API saliente.
2. Analízala con la implementación de URL elegida por el ejecutor de la acción.
3. Rechaza la información de usuario, la ausencia de host, los valores de puerto mal formados, los esquemas no compatibles y los escapes porcentuales no válidos.
4. Construye la solicitud real a partir de los componentes analizados y los encabezados aprobados.
5. Muestra la tarjeta a partir de esos componentes y envía después esa solicitud exacta.

No intentes hacerlo con separaciones de cadenas escritas a mano. El primer `@`, `:`, `/`, `?` y `#` no significan lo mismo en todas las posiciones. Las autoridades IPv6 necesitan corchetes. Un signo de dos puntos después de un corchete puede introducir un puerto, mientras que los dos puntos dentro de los corchetes forman parte de la dirección. Un analizador conoce esa diferencia; una expresión regular corta normalmente no.

El estándar WHATWG URL define el comportamiento de análisis y serialización de URL, hosts, dominios y direcciones IP. Sus indicaciones de seguridad también señalan la confusión que el texto bidireccional puede crear entre un host y una ruta, y recomiendan mostrar solo el host en esa situación. Un producto de seguridad debe extraer una lección más estricta: mantén la autoridad separada visualmente de la ruta en todas las tarjetas, no solo cuando aparezcan cadenas poco habituales.

Si la capa de acciones tiene un cliente personalizado, demuestra que coincide con el analizador usando un corpus de pruebas. No des por hecho que dos bibliotecas maduras toleran exactamente igual los espacios, las barras invertidas, los nombres de host Unicode o las formas numéricas de IP poco habituales. La coincidencia es una propiedad que se prueba.

## Decodifica los escapes porcentuales sin cambiar la ruta

La codificación porcentual crea el tipo más peligroso de URL engañosa: una que parece inofensiva después de una decodificación superficial, pero que significa algo distinto para el enrutador, el proxy o el servicio ascendente.

Considera estas rutas:

```text
/v1/projects/%2E%2E/admin
/v1/projects/%252E%252E/admin
/v1/files/report%2Ffinal
```

La primera contiene puntos codificados porcentualmente. La segunda contiene un signo de porcentaje codificado seguido de `2E`, que no es la misma entrada. La tercera contiene una barra codificada dentro de un segmento de ruta. Si una capa de presentación decodifica las tres repetidamente hasta obtener signos de puntuación legibles, puede mostrar una estructura de ruta que el cliente no envió.

RFC 3986 ofrece un caso seguro limitado: los escapes porcentuales de caracteres no reservados pueden decodificarse durante la normalización. Los caracteres no reservados son letras, dígitos, guion, punto, guion bajo y tilde. Los caracteres reservados como `/`, `?`, `#`, `@` y `:` deben permanecer codificados cuando su decodificación cambie los límites de los componentes o los delimitadores. La RFC también indica que una implementación no debe codificar ni decodificar la misma cadena más de una vez.

De ahí sale una buena regla de presentación:

```text
Raw path:       /v1/%75sers/alice%7Eops/report%2Ffinal
Card path:      /v1/users/alice~ops/report%2Ffinal
Wire path:      /v1/users/alice~ops/report%2Ffinal
```

La tarjeta hace legibles `%75` y `%7E` porque representan caracteres no reservados. Mantiene visible `%2F` porque una barra cambiaría la estructura de segmentos de la ruta. La forma de la tarjeta y la forma enviada pueden diferir de manera inofensiva, pero deben conservar el mismo significado de enrutamiento.

No elimines los segmentos de punto después de decodificar la ruta de forma indiscriminada. Analiza la ruta según su estructura codificada, aplica un procedimiento de normalización definido y conserva los escapes que transporten caracteres reservados como datos. Si el servicio descendente aplica un orden de decodificación distinto, es un problema de compatibilidad y seguridad que conviene exponer en las pruebas, no una razón para que la tarjeta adivine.

## Un nombre de host no es toda la autoridad

La autoridad de un destino HTTP incluye el host y, cuando no es el predeterminado, el puerto. Omitir el puerto hace que una tarjeta de revisión mienta por omisión.

Trata estos casos como distintos:

```text
https://api.example.test/v1/keys
https://api.example.test:8443/v1/keys
http://api.example.test/v1/keys
```

El primero normalmente usa el puerto 443. El segundo usa el puerto 8443. El tercero usa un esquema distinto y normalmente el puerto 80. Un revisor puede aceptar una llamada a una API de producción mediante HTTPS y rechazar una solicitud a un servicio de pruebas en un puerto personalizado. La tarjeta debe permitirle tomar esa decisión.

Normaliza el esquema y el nombre de host a minúsculas. Omite el puerto solo cuando sea el predeterminado para el esquema analizado: 80 para `http` y 443 para `https`. No omitas un puerto porque un registro DNS conduzca casualmente a un lugar conocido.

Los nombres de dominio internacionalizados requieren el mismo cuidado. Una forma Unicode fácil de leer puede resultar más clara para una persona, mientras que la forma que usa DNS en el cable emplea etiquetas ASCII. Si muestras Unicode, muestra también la forma ASCII en los detalles y usa un analizador que aplique un algoritmo definido para procesar hosts. No inventes tu propia conversión a punycode ni compares cadenas de presentación para decidir si son equivalentes.

Los literales IP merecen un tratamiento propio. Muestra corchetes alrededor de las direcciones IPv6, conserva los puertos no predeterminados y etiqueta una dirección literal como literal IP. Una solicitud a `https://[2001:db8::9]/v1/keys` no debe parecer un servicio de producción identificado por nombre solo porque el agente haya incluido un alias agradable en un campo de notas.

Los alias plantean un problema distinto. `api.internal`, `api` y `10.0.0.9` pueden terminar hoy en el mismo servidor y divergir después de un cambio DNS. No reescribas uno silenciosamente como otro para la aprobación. Muestra la autoridad analizada que solicitó el cliente. Si el sistema resuelve DNS antes de abrir una conexión, muestra la dirección seleccionada como contexto de conexión y regístrala en la pista de auditoría. La autoridad sigue siendo aquello que nombra la solicitud HTTP.

HTTP hace explícita esa distinción. RFC 9110 indica que el campo `Host` proporciona la información de host y puerto de la URI de destino, mientras que HTTP/2 y HTTP/3 pueden transportar esa información en `:authority`. RFC 9113 indica que un intermediario que genere `Host` a partir de la autoridad de HTTP/2 debe usar `:authority`, salvo que cambie el destino de la solicitud. Por tanto, una tarjeta debe tratar un campo de autoridad proporcionado por el solicitante como material de enrutamiento, no como metadatos decorativos.

## El método y la ruta necesitan un peso visual propio

Coloca juntos el método HTTP, la autoridad y la ruta porque los revisores leen la acción como una frase. Después, da al método y a los segmentos de ruta peligrosos suficiente contraste para que una mirada rápida no los reduzca a una URL larga.

Esta disposición funciona porque mantiene las partes en un orden estable:

```text
DELETE
https://api.example.test/v1/projects/acme/production
```

Para una solicitud que modifica un objeto, incluye su identificador en la ruta visible. Recortar el final de `/v1/projects/acme/production` para que quepa en una tarjeta es contraproducente. Si falta espacio, recorta primero los valores de consulta largos o las vistas previas del cuerpo, nunca el segmento final de la ruta que identifica el destino.

Las mayúsculas y minúsculas deben conservarse en la ruta. RFC 3986 indica que la sintaxis URI genérica trata los componentes distintos del esquema y el host como sensibles a mayúsculas y minúsculas, salvo que el esquema indique lo contrario. Muchos frameworks enrutan teniendo en cuenta las mayúsculas incluso cuando una API concreta no lo hace. Convertir `/Admin/DeleteUser` en `/admin/deleteuser` crea una afirmación sobre una solicitud que nunca se hizo.

Una ruta de solicitud también puede parecer inofensiva mientras la consulta cambia el efecto:

```text
POST https://api.example.test/v1/invoices/481/refund?dry_run=false
POST https://api.example.test/v1/invoices/481/refund?dry_run=true
```

Muestra un resumen breve de la consulta debajo de la línea de acción cuando los parámetros cambien el alcance, el comportamiento o la identidad. Para consultas no estructuradas o muy largas, muestra la consulta codificada completa en un área de detalles desplegable y un resumen decodificado y redactado en la tarjeta principal. Nunca decodifiques un token hasta convertirlo en un secreto legible solo porque la tarjeta quiera resultar más amable.

El cuerpo de la solicitud puede importar incluso más que la ruta. Una aprobación para `PATCH /v1/users/alice` dice poco si el cuerpo puede conceder un rol de administrador. Muestra como mínimo el tipo de contenido, la longitud en bytes y un resumen estable. Para formatos estructurados como JSON, una pequeña vista previa de los campos modificados puede ayudar, siempre que la vista previa proceda de los mismos bytes que se enviarán. Volver a serializar un objeto para mostrarlo después de firmar o calcular un hash sobre otra secuencia de bytes crea el mismo problema de divergencia que volver a analizar las URL.

## Los encabezados y las redirecciones pueden cambiar el destino

Una URL canónica no salva un flujo de aprobación si otro campo de la solicitud puede dirigir la conexión. La tarjeta debe restringir esos campos o mostrar sus efectos.

Empieza por `Host` y `:authority`. Un cliente HTTP normalmente los deriva de la URL de destino. Si una interfaz de acciones permite al solicitante sobrescribirlos, rechaza la sobrescritura salvo que el transporte tenga una razón documentada para admitirla. Si la admites, la línea de aprobación debe mostrar tanto el destino de conexión como la autoridad solicitada, de forma que una persona pueda compararlos.

La configuración del proxy merece el mismo tratamiento. Un proxy cambia el interlocutor inmediato, pero no necesariamente el destino de origen. No sustituyas el origen por la dirección del proxy en la tarjeta. Muestra el origen como la acción que se aprueba y el proxy como contexto de transporte. Si el proxy puede reescribir campos de destino, trátalo como un componente del ejecutor con pruebas, registros y una decisión de confianza independiente.

Las redirecciones son acciones nuevas cuando cambia su destino. Un `POST` puede convertirse en un `GET` según el comportamiento de la redirección, o reenviarse a una nueva autoridad. La aprobación original debe cubrir solo el destino original. Antes de seguir una redirección, analiza el valor de `Location`, construye la siguiente solicitud propuesta, compara su esquema, autoridad, método, ruta, consulta y comportamiento del cuerpo, y vuelve a preguntar si cambia algo relevante.

Evita el atajo popular de aprobar un «sitio» durante toda una ejecución y tratar como inofensiva cualquier redirección dentro de él. Resulta menos molesto, pero convierte el análisis de URL y la política de redirecciones en una ampliación invisible de privilegios. Si la ejecución necesita permisos amplios, expresa ese alcance de forma explícita en el texto de aprobación en lugar de dejar que las redirecciones lo introduzcan a escondidas.

## Pon la entrada sin procesar en el registro, no en la línea de decisión

Un registro de auditoría debe contener suficientes detalles para responder a dos preguntas distintas: qué solicitó el agente y qué intentó ejecutar el sistema. Una sola cadena URL no siempre puede responder a ambas.

Registra exactamente la cadena URL original recibida, sujeta a las reglas de redacción de secretos. Registra por separado el destino canónico. Añade la autoridad de conexión final y la dirección resuelta si el ejecutor realizó la resolución. Para HTTP/2 o HTTP/3, registra la `:authority` efectiva; para HTTP/1.1, registra el valor efectivo de `Host`. Registra los saltos de redirección como solicitudes intentadas individuales, no como una nota al pie de la primera llamada.

Una entrada del diario puede tener esta forma:

```json
{
  "request_id": "req_01J...",
  "agent_input_url": "HTTPS://API.EXAMPLE.TEST:443/v1/%75sers/alice%7Eops",
  "approved_target": "GET https://api.example.test/v1/users/alice~ops",
  "effective_authority": "api.example.test",
  "effective_port": 443,
  "connection_ip": "203.0.113.42",
  "result": "200"
}
```

El ejemplo usa un rango de direcciones reservado para documentación como IP de conexión. En un registro real, protege los secretos de las consultas, el material de autorización y los cuerpos sensibles antes de que los datos lleguen a un registro de larga duración. Un resumen ayuda a relacionar el contenido aprobado con el contenido ejecutado sin copiar cargas privadas en todas las pantallas.

La distinción entre la entrada original y el destino canónico resulta muy útil durante una investigación. Si una tarjeta mostraba una ruta normal, pero la entrada original contenía varias capas de codificación, puedes determinar si no coincidieron el analizador, el elemento visual o el cliente HTTP. Si solo guardaste una URL bonita, eliminaste las pruebas necesarias para encontrar el defecto.

Los diarios Activity y Sessions de Sallyport se proyectan a partir de un único registro de auditoría cifrado y encadenado mediante hashes, así que la representación del destino debe escribirse una vez como parte del registro de la acción, en lugar de reconstruirse después a partir de cadenas de la interfaz. Su comprobación sin conexión `sp audit verify` solo es útil si los campos registrados de la acción eran honestos en el momento de la ejecución.

## Prueba las discrepancias que las URL normales nunca revelan

Las pruebas unitarias importantes no son diez ejemplos normales de `https://api.example.test/v1/users`. Son los casos en que una cadena original, un elemento de presentación y una biblioteca de transporte podrían no coincidir.

Crea un corpus basado en tablas que compruebe los campos analizados, el destino visible, el destino enviado y la decisión. Incluye como mínimo estas familias:

- cambios de mayúsculas y minúsculas en el esquema y el host, con puertos predeterminados y no predeterminados;
- segmentos de punto y escapes porcentuales tanto de caracteres no reservados como reservados;
- signos de porcentaje y barras codificados, además de secuencias de escape mal formadas;
- literales IPv6, entradas de host Unicode e información de usuario que deba rechazarse;
- valores de consulta que cambien el comportamiento de la acción, además de destinos de redirección en otra autoridad.

Un caso de prueba debe hacer explícita la representación esperada:

```json
{
  "input": "HTTPS://API.EXAMPLE.TEST:443/v1/%75sers/alice%7Eops?role=viewer",
  "decision": "approve",
  "card": "GET https://api.example.test/v1/users/alice~ops?role=viewer",
  "wire_url": "https://api.example.test/v1/users/alice~ops?role=viewer"
}
```

Después añade casos negativos que deban fallar antes de la aprobación:

```json
{
  "input": "https://alice@api.example.test/v1/users",
  "decision": "reject",
  "reason": "userinfo is not supported for outbound API actions"
}
```

Ejecuta el corpus con el código exacto del cliente que abre la conexión. Una suite de pruebas limitada al analizador detecta errores de presentación, pero no comportamientos del transporte como que una biblioteca normalice una ruta vacía, inyecte una autoridad predeterminada o aplique sus propias reglas de redirección.

Por último, prueba la interfaz tal como la utiliza un revisor. Confirma que el método completo, el host, el puerto cuando esté presente y el segmento final de la ruta sigan visibles en tamaños de ventana normales. El texto de seguridad falla cuando la persona tiene que pasar el cursor, expandir o desplazarse para descubrir que una solicitud elimina datos de producción. La tarjeta debe hacer visible la diferencia importante antes de que el botón de aprobación reciba el foco.

Una aprobación humana puede ser un control sólido, pero solo será tan sólida como la descripción de la solicitud que se ponga delante de la persona. Construye esa descripción a partir de componentes analizados, ejecuta esos componentes, conserva la entrada original para examinarla después y rechaza la ambigüedad en lugar de decorarla.
