SDK Python e CLI
Maintainer: Aether365 Team Destinatari: Sviluppatori e ingegneri DevOps Ambito: Installazione e utilizzo dell'SDK Python ufficiale aether365 e della relativa interfaccia a riga di comando
Il pacchetto aether365 è l'SDK Python ufficiale e l'interfaccia a riga di comando per l'API di Aether365. Incapsula https://api.aether365.io, gestisce l'autenticazione, estrae l'envelope di risposta e riprova automaticamente in caso di errori transitori. Tutto ciò che puoi fare con curl lo puoi fare anche con l'SDK o la CLI.
L'SDK e la CLI usano una chiave API (ak_live_...) e puntano all'endpoint unificato: le richieste vengono instradate automaticamente verso la region di origine del tuo tenant. Consulta Autenticazione per capire come funzionano le chiavi.
Installazione
Il pacchetto è distribuito internamente (non su PyPI). Installalo direttamente dal repository o da un checkout locale.
bash
# From a local checkout of the monorepo
pip install ./packages/cli
# Or with Poetry, from a path
poetry add ./packages/cliÈ richiesto Python 3.12 o superiore. L'installazione del pacchetto rende inoltre disponibile il comando aether365 nel tuo PATH.
Configurazione
Sia l'SDK sia la CLI leggono le stesse variabili d'ambiente:
| Variabile | Scopo | Predefinito |
|---|---|---|
AETHER365_API_KEY | La tua chiave API (ak_live_...). Obbligatoria. | - |
AETHER365_API_URL | Sovrascrive l'URL di base (ad esempio l'endpoint di dev). | https://api.aether365.io |
AETHER365_OUTPUT | Formato di output della CLI: table o json. | table |
bash
export AETHER365_API_KEY="ak_live_..."La region è automatica: l'API risolve il tuo tenant dalla chiave e inoltra la richiesta alla region corretta, quindi non devi mai configurare o passare una region.
Avvio rapido: 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")Avvio rapido: 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-findingsCosa può e cosa non può fare una chiave API
Una chiave API è limitata a una superficie sicura, di lettura più operatività. Può leggere i tuoi dati, avviare e gestire scan, generare report e gestire i target della superficie di attacco. Non può eseguire azioni di controllo dell'account o di blocco della directory: gestione delle chiavi API, fatturazione, membri del team, onboarding/offboarding delle connessioni, SSO, applicazione di un piano di remediation o le scritture conditional-access/break-glass di AI Pilot. Queste azioni restano disponibili solo per una sessione autenticata nella dashboard. Una richiesta verso una route non consentita restituisce 403 AUTH_INSUFFICIENT_SCOPE.
Riferimento a comandi e metodi
Ogni riga mostra la stessa operazione in tre modi: curl puro, il metodo dell'SDK e il comando della CLI.
Tenant e account
| Operazione | curl | SDK | CLI |
|---|---|---|---|
| Profilo del tenant | GET /tenants/me | client.tenants.me() | aether365 tenant me |
| Elencare le connessioni | GET /tenants/me/connections | client.connections.list() | aether365 tenant connections |
| Preferenze di notifica | GET /tenants/me/notifications | client.notifications.get() | aether365 tenant notifications |
| Scan pianificati | GET /tenants/me/scheduled-scans | client.scheduled_scans.list() | aether365 tenant scheduled-scans |
Scan
| Operazione | curl | SDK | CLI |
|---|---|---|---|
| Elencare gli scan | GET /tenants/me/scans | client.scans.list() | aether365 scan list |
| Avviare uno scan | POST /tenants/me/scans | client.scans.create("compliance") | aether365 scan run --type compliance |
| Ottenere uno scan | GET /scans/{id} | client.scans.get(id) | aether365 scan get <id> |
| Leggere i risultati | GET /scans/{id}/results | client.scans.results(id) | aether365 scan results <id> |
| Annullare uno scan | POST /scans/{id}/cancel | client.scans.cancel(id) | aether365 scan cancel <id> |
| Nascondere uno scan | PATCH /scans/{id}/hide | client.scans.hide(id) | aether365 scan hide <id> |
| Mostrare di nuovo uno scan | PATCH /scans/{id}/unhide | client.scans.unhide(id) | aether365 scan unhide <id> |
| Eliminare uno scan | DELETE /scans/{id} | client.scans.delete(id) | aether365 scan delete <id> |
Il curl per avviare uno 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"}'Report
| Operazione | curl | SDK | CLI |
|---|---|---|---|
| Stato del report | GET /scans/{id}/report | client.reports.status(id) | aether365 report status <id> |
| Generare un report | POST /scans/{id}/report/generate | client.reports.generate(id) | aether365 report generate <id> |
| Elencare i report | GET /tenants/me/reports | client.reports.list() | aether365 report list |
| Scaricare un report | GET /scans/{id}/report (presigned) | client.reports.download(id, path) | aether365 report download <id> -o out.pdf |
Lo stato di un report è uno tra ready, rendering, orgNameRequired o locked. Generare un report può consumare un credito report (un addebito una tantum quando nessuna quota è inclusa): il titolare della chiave lo accetta chiamando generate.
L'elenco dei report è paginato per offset: ripassa meta["nextCursor"] (un offset di riga intero, oppure None sull'ultima pagina) come cursor. client.reports.iter_all() segue il cursore per te.
Superficie di attacco
| Operazione | curl | SDK | CLI |
|---|---|---|---|
| Panoramica | GET /tenants/me/attack-surface | client.attack_surface.summary() | aether365 eas summary |
| Cronologia | GET /tenants/me/attack-surface/history | client.attack_surface.history() | aether365 eas history |
| Elencare i target | GET /tenants/me/attack-surface/targets | client.attack_surface.targets() | aether365 eas targets list |
| Aggiungere un target | POST /tenants/me/attack-surface/targets | client.attack_surface.add_target(host) | aether365 eas targets add <host> |
| Aggiornare un target | PATCH /tenants/me/attack-surface/targets/{id} | client.attack_surface.update_target(id, ...) | aether365 eas targets update <id> --label ... |
| Eliminare un target | DELETE /tenants/me/attack-surface/targets/{id} | client.attack_surface.remove_target(id) | aether365 eas targets remove <id> |
| Verificare un target | POST /tenants/me/attack-surface/targets/{id}/verify | client.attack_surface.verify_target(id) | aether365 eas targets verify <id> |
| Eseguire uno scan EAS | POST /tenants/me/attack-surface/scan | client.attack_surface.scan() | aether365 eas scan |
Postura, remediation e AI Pilot
| Operazione | curl | SDK | CLI |
|---|---|---|---|
| Vista minacce / rischi | GET /tenants/me/threats | client.threats.list() | aether365 threats |
| Postura delle policy | GET /tenants/me/policies | client.policies.list() | aether365 policies |
| Capacità di remediation | GET /tenants/me/remediation/capabilities | client.remediation.capabilities() | aether365 remediation capabilities |
| Elencare i piani di remediation | GET /tenants/me/remediation-plans | client.remediation.plans() | aether365 remediation plans |
| Ottenere un piano di remediation | GET /tenants/me/remediation-plans/{id} | client.remediation.plan(id) | aether365 remediation plan <id> |
| Elementi remediabili AI Pilot | GET /tenants/me/ai-pilot/remediable | client.ai_pilot.remediable() | aether365 ai-pilot remediable |
| Break-glass AI Pilot | GET /tenants/me/ai-pilot/break-glass | client.ai_pilot.break_glass() | aether365 ai-pilot break-glass |
| Policy CA AI Pilot | GET /tenants/me/ai-pilot/conditional-access | client.ai_pilot.conditional_access() | aether365 ai-pilot conditional-access |
La remediation è in sola lettura per le chiavi API. L'applicazione di un piano non è esposta: gli elementi dell'action-registry di un piano possono includere patch Conditional-Access / authorization-policy in grado di escludere un tenant dalla propria directory (AADSTS50097), quindi l'applicazione resta nella dashboard con un operatore coinvolto nel processo. Anche le viste conditional-access e break-glass di AI Pilot sono esposte in sola lettura per lo stesso motivo.
Gestione degli errori e retry
Ogni errore dell'API corrisponde a un'eccezione tipizzata, sottoclasse di 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.")Il client riprova automaticamente 429 RATE_LIMITED e 503 SERVICE_STARTING (il warm-up a freddo del database in dev) con backoff esponenziale, rispettando l'header Retry-After. Gli errori di quota come SCAN_PLAN_LIMIT_REACHED vengono sollevati subito: non vengono ritentati.
Usare la CLI nella CI
aether365 scan results <id> --fail-on-findings termina con codice 2 quando è presente almeno un finding fallito, così una pipeline può vincolare il proprio esito ai risultati dello scan:
bash
aether365 scan run --type compliance --watch
aether365 scan results "$SCAN_ID" --fail-on-findingsLa CLI scrive gli errori su stderr e termina con 1 per qualsiasi errore dell'API, con 2 sul gate dei finding.