Skip to content

SDK de Python y CLI

Mantenido por: Aether365 Team Audiencia: Desarrolladores e ingenieros DevOps Alcance: Instalación y uso del SDK de Python oficial aether365 y de su interfaz de línea de comandos

El paquete aether365 es el SDK de Python oficial y la interfaz de línea de comandos de la API de Aether365. Envuelve https://api.aether365.io, gestiona la autenticación, desempaqueta el envelope de respuesta y reintenta automáticamente los fallos transitorios. Todo lo que puedes hacer con curl puedes hacerlo con el SDK o la CLI.

El SDK y la CLI usan una clave de API (ak_live_...) y apuntan al endpoint unificado: las peticiones se enrutan automáticamente a la región de origen de tu tenant. Consulta Autenticación para saber cómo funcionan las claves.

Instalación

El paquete se distribuye internamente (no está en PyPI). Instálalo directamente desde el repositorio o desde un checkout local.

bash
# From a local checkout of the monorepo
pip install ./packages/cli

# Or with Poetry, from a path
poetry add ./packages/cli

Se requiere Python 3.12 o superior. Al instalar el paquete, el comando aether365 queda además disponible en tu PATH.

Configuración

Tanto el SDK como la CLI leen las mismas variables de entorno:

VariablePropósitoPor defecto
AETHER365_API_KEYTu clave de API (ak_live_...). Obligatoria.-
AETHER365_API_URLSobrescribe la URL base (por ejemplo, el endpoint de dev).https://api.aether365.io
AETHER365_OUTPUTFormato de salida de la CLI: table o json.table
bash
export AETHER365_API_KEY="ak_live_..."

La región es automática: la API resuelve tu tenant a partir de la clave y reenvía la petición a la región correcta, así que nunca tienes que configurar ni pasar una región.

Inicio rápido: SDK

python
from aether365 import Aether365Client

with Aether365Client() as client:          # reads AETHER365_API_KEY
    tenant = client.tenants.me()
    print(tenant["name"])

    # Trigger a scan and wait for it to finish
    scan = client.scans.create("compliance")
    for snapshot in client.scans.wait(scan["id"]):
        print(snapshot["status"])

    # Read the findings
    findings = client.scans.results(scan["id"], result="Failed")
    print(f"{len(findings)} failed checks")

Inicio rápido: CLI

bash
# Show the authenticated tenant
aether365 tenant me

# Trigger a scan and follow progress until it finishes
aether365 scan run --type compliance --watch

# List scans as JSON
aether365 --output json scan list

# Fail a CI job when a scan has failed findings (exit code 2)
aether365 scan results <SCAN_ID> --fail-on-findings

Qué puede y qué no puede hacer una clave de API

Una clave de API está limitada a una superficie segura, de lectura más operaciones. Puede leer tus datos, lanzar y gestionar scans, generar informes y gestionar los objetivos de superficie de ataque. No puede realizar acciones de control de la cuenta ni de bloqueo del directorio: gestión de claves de API, facturación, miembros del equipo, alta/baja de conexiones, SSO, aplicar un plan de remediación o las escrituras de conditional-access/break-glass de AI Pilot. Esas acciones siguen disponibles solo para una sesión iniciada en el dashboard. Una petición a una ruta no permitida devuelve 403 AUTH_INSUFFICIENT_SCOPE.

Referencia de comandos y métodos

Cada fila muestra la misma operación de tres formas: curl en crudo, el método del SDK y el comando de la CLI.

Tenant y cuenta

OperacióncurlSDKCLI
Obtener el perfil del tenantGET /tenants/meclient.tenants.me()aether365 tenant me
Listar conexionesGET /tenants/me/connectionsclient.connections.list()aether365 tenant connections
Preferencias de notificaciónGET /tenants/me/notificationsclient.notifications.get()aether365 tenant notifications
Scans programadosGET /tenants/me/scheduled-scansclient.scheduled_scans.list()aether365 tenant scheduled-scans

Scans

OperacióncurlSDKCLI
Listar scansGET /tenants/me/scansclient.scans.list()aether365 scan list
Lanzar un scanPOST /tenants/me/scansclient.scans.create("compliance")aether365 scan run --type compliance
Obtener un scanGET /scans/{id}client.scans.get(id)aether365 scan get <id>
Leer resultadosGET /scans/{id}/resultsclient.scans.results(id)aether365 scan results <id>
Cancelar un scanPOST /scans/{id}/cancelclient.scans.cancel(id)aether365 scan cancel <id>
Ocultar un scanPATCH /scans/{id}/hideclient.scans.hide(id)aether365 scan hide <id>
Mostrar un scan ocultoPATCH /scans/{id}/unhideclient.scans.unhide(id)aether365 scan unhide <id>
Eliminar un scanDELETE /scans/{id}client.scans.delete(id)aether365 scan delete <id>

El curl para lanzar un scan:

bash
curl -X POST https://api.aether365.io/tenants/me/scans \
  -H "Authorization: Bearer $AETHER365_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"scanType": "compliance"}'

Informes

OperacióncurlSDKCLI
Estado del informeGET /scans/{id}/reportclient.reports.status(id)aether365 report status <id>
Generar un informePOST /scans/{id}/report/generateclient.reports.generate(id)aether365 report generate <id>
Listar informesGET /tenants/me/reportsclient.reports.list()aether365 report list
Descargar un informeGET /scans/{id}/report (presigned)client.reports.download(id, path)aether365 report download <id> -o out.pdf

El estado de un informe es uno de ready, rendering, orgNameRequired o locked. Generar un informe puede consumir un crédito de informe (un cargo único cuando no hay asignación incluida): el propietario de la clave lo acepta al llamar a generate.

El listado de informes usa paginación por offset: devuelve meta["nextCursor"] (un offset de fila entero, o None en la última página) como cursor. client.reports.iter_all() sigue el cursor por ti.

Superficie de ataque

OperacióncurlSDKCLI
ResumenGET /tenants/me/attack-surfaceclient.attack_surface.summary()aether365 eas summary
HistorialGET /tenants/me/attack-surface/historyclient.attack_surface.history()aether365 eas history
Listar objetivosGET /tenants/me/attack-surface/targetsclient.attack_surface.targets()aether365 eas targets list
Añadir un objetivoPOST /tenants/me/attack-surface/targetsclient.attack_surface.add_target(host)aether365 eas targets add <host>
Actualizar un objetivoPATCH /tenants/me/attack-surface/targets/{id}client.attack_surface.update_target(id, ...)aether365 eas targets update <id> --label ...
Eliminar un objetivoDELETE /tenants/me/attack-surface/targets/{id}client.attack_surface.remove_target(id)aether365 eas targets remove <id>
Verificar un objetivoPOST /tenants/me/attack-surface/targets/{id}/verifyclient.attack_surface.verify_target(id)aether365 eas targets verify <id>
Ejecutar un scan EASPOST /tenants/me/attack-surface/scanclient.attack_surface.scan()aether365 eas scan

Postura, remediación y AI Pilot

OperacióncurlSDKCLI
Vista de amenazas / riesgosGET /tenants/me/threatsclient.threats.list()aether365 threats
Postura de políticasGET /tenants/me/policiesclient.policies.list()aether365 policies
Capacidades de remediaciónGET /tenants/me/remediation/capabilitiesclient.remediation.capabilities()aether365 remediation capabilities
Listar planes de remediaciónGET /tenants/me/remediation-plansclient.remediation.plans()aether365 remediation plans
Obtener un plan de remediaciónGET /tenants/me/remediation-plans/{id}client.remediation.plan(id)aether365 remediation plan <id>
Remediables de AI PilotGET /tenants/me/ai-pilot/remediableclient.ai_pilot.remediable()aether365 ai-pilot remediable
Break-glass de AI PilotGET /tenants/me/ai-pilot/break-glassclient.ai_pilot.break_glass()aether365 ai-pilot break-glass
Políticas CA de AI PilotGET /tenants/me/ai-pilot/conditional-accessclient.ai_pilot.conditional_access()aether365 ai-pilot conditional-access

La remediación es de solo lectura para las claves de API. Aplicar un plan no está expuesto: los elementos del action-registry de un plan pueden incluir parches de Conditional-Access / authorization-policy capaces de dejar a un tenant fuera de su propio directorio (AADSTS50097), así que la aplicación se queda en el dashboard con un operador supervisando. Las vistas de conditional-access y break-glass de AI Pilot se exponen igualmente en solo lectura por el mismo motivo.

Gestión de errores y reintentos

Cada error de la API se corresponde con una excepción tipada, subclase de Aether365Error:

python
from aether365 import Aether365Client
from aether365.exceptions import PermissionError_, PlanLimitError, NotFoundError

with Aether365Client() as client:
    try:
        client.scans.create("compliance")
    except PlanLimitError:
        print("Scan quota reached for this plan.")
    except PermissionError_:
        print("Route not allowed for this key, or plan lacks API access.")
    except NotFoundError:
        print("Resource not found.")

El cliente reintenta automáticamente 429 RATE_LIMITED y 503 SERVICE_STARTING (el arranque en frío de la base de datos de dev) con backoff exponencial, respetando la cabecera Retry-After. Los errores de cuota como SCAN_PLAN_LIMIT_REACHED se lanzan de inmediato: no se reintentan.

Uso de la CLI en CI

aether365 scan results <id> --fail-on-findings termina con código 2 cuando existe algún hallazgo fallido, de modo que un pipeline puede condicionar su resultado a los resultados del scan:

bash
aether365 scan run --type compliance --watch
aether365 scan results "$SCAN_ID" --fail-on-findings

La CLI escribe los errores en stderr y termina con 1 ante cualquier error de la API y con 2 en la puerta de hallazgos.

¿Te resultó útil esta página?