Aller au contenu

API

Info

Ostorlab a ajouté la prise en charge de l'encodage Universal Binary JSON. UBJSON offre plusieurs avantages en termes de performances et ajoute la prise en charge des types de données binaires. Pour plus d'informations sur le format et les bibliothèques disponibles, consultez le lien suivant UBJSON Spec. La prise en charge de JSON est toujours disponible et l'un ou l'autre peut être spécifié en utilisant l'en-tête content type.

Dans ce guide, nous vous accompagnerons à travers les étapes d'utilisation de l'API d'Ostorlab. Grâce à l'API, vous pouvez effectuer tous types de tâches, de la création de scans à la consultation de leur progression, en passant par le listing des Vulnérabilités. La section suivante décrira comment utiliser l'API, comment l'expérimenter et la tester.

Accès

Il existe plusieurs façons d'accéder à l'API. Vous pouvez soit utiliser l'application web GraphiQL pour expérimenter avec l'API, soit utiliser des scripts, car leur utilisation typique est destinée à l'automatisation dans les pipelines de déploiement (CI/CD) ou à l'automatisation de la création et de la surveillance d'un grand nombre de scans.

Access

1. Bac à sable (Sandbox)

GraphiQL est accessible à l'URL suivante : https://api.ostorlab.co/apis/graphql

Sandbox

L'API est accessible uniquement aux utilisateurs authentifiés. Assurez-vous d'être authentifié sur https://api.ostorlab.co/portal/login

auth_page

Exemples

Pour vous donner une idée de la façon d'utiliser l'API, voici quelques exemples de requêtes courantes :

Lister les Scans

Pour lister tous les scans appartenant à l'organisation de l'utilisateur actuel :

Afin d'exécuter la requête, cliquez sur le bouton d'exécution.

Dans la section de droite, vous pouvez voir le résultat de la requête exécutée.

Lister les Scans par Filtre

Pour lister les scans d'une application mobile particulière, utilisez l'argument target pour spécifier le nom du package de l'application ou l'ID du bundle.

L'API des scans prend en charge d'autres filtres tels que targetAssetTypes, riskRatings, progress et peut être ordonnée et triée en utilisant les arguments orderBy et sort.

Détails du Scan

Vous pouvez également récupérer les détails d'un seul scan en utilisant son ID comme suit :

Détails des Vulnérabilités

De plus, cette requête vous permet de récupérer la liste et les détails des Vulnérabilités d'un scan :

Progression du Scan

Par ailleurs, pour déterminer l'étape actuelle de progression du scan, vous pouvez utiliser la requête suivante :

Créer un Nouveau Scan

Pour créer un scan, les fichiers sont téléchargés sous forme de requête HTTP multipart.

mutation newMobileScan($title: String!, $assetType: String!, $application: Upload!, $scanProfile: String!) {
      createMobileScan(title: $title, assetType:$assetType, application: $application, scanProfile: $scanProfile) {
        scan {
            id
        }
      }
    }

Créer un nouveau scan avec authentification

La création d'un scan avec des identifiants (credentials) est un processus en deux étapes. Tout d'abord, les identifiants sont ajoutés au magasin d'identifiants et un ID d'identifiant est renvoyé, puis une requête de création de scan est envoyée en spécifiant les ID des identifiants à utiliser dans ce scan.

Par exemple, pour créer un simple identifiant de connexion par mot de passe. Vous pouvez utiliser ce qui suit :

mutation newTestCredential {
  createTestCredentials(testCredentials: {loginPassword: {login: "login1", password: "password1"}}) {
    testCredentials {
      __typename
      ... on LoginPasswordTestCredentials {
        id
      }
    }
  }
}

Ce qui renverra l'ID comme suit :

{
  "data": {
    "createTestCredentials": {
      "testCredentials": {
        "__typename": "LoginPasswordTestCredentials",
        "id": "901"
      }
    }
  }
}

Pour passer l'ID d'identifiant, vous devez ajouter l'argument credentialIds avec la liste des ID :

mutation newMobileScan($title: String!, $assetType: String!, $application: Upload!, $scanProfile: String!) {
  createMobileScan(title: $title, assetType: $assetType, application: $application, scanProfile: $scanProfile, credentialIds: [901]) {
    scan {
      id
    }
  }
}

Actifs Découverts

Vous pouvez récupérer la liste des actifs découverts (Domaines, Applications Mobiles, IP ...) en utilisant la requête suivante. Cela permet d'explorer les actifs qui ont été identifiés par la plateforme de découverte de la surface d'attaque. La requête prend en charge des filtres tels que le type d'actif, la propriété et la profondeur des nœuds associés.

query NodesEdges($depth: Int, $limit: Int, $filterExcluded: Boolean, $assets: [NGAssetIdInputType], $ownerIds: [Int], $nodeKeys: [String], $nodeTypes: [String]) {
  nodesEdges(
    depth: $depth
    limit: $limit
    filterExcluded: $filterExcluded
    assets: $assets
    ownerIds: $ownerIds
    nodeKeys: $nodeKeys
    nodeTypes: $nodeTypes
  ) {
    nodes {
      type
      assetType
      assetId
      key
      ownershipType
      customColor
      attributes {
        name
        value
      }
    }
    edges {
      fromType
      fromKey
      toType
      toKey
      attributes {
        name
        value
      }
    }
  }
}

Variables d'Exemple :

{
  "depth": 1,
  "limit": 10,
  "ownerIds": [],
  "nodeKeys": [],
  "nodeTypes": [
    "androidApp"
  ]
}

2. Scripts

Comme mentionné précédemment, GraphiQL n'est pas le seul moyen d'accéder à l'API, car vous pouvez également l'utiliser via des scripts. Son utilisation typique est pour l'automatisation dans les pipelines de déploiement (CI/CD) ou l'automatisation de la création et de la surveillance d'un grand nombre de scans. Pour le processus d'authentification, vous avez deux options : l'authentification par jeton (Token) et l'authentification par clé API.

Authentification par jeton (Token)

Le jeton peut être récupéré à partir de l'URL suivante https://api.ostorlab.co/apis/token en soumettant le nom d'utilisateur et le mot de passe. Un jeton lié à l'utilisateur sera créé. Le jeton est ensuite défini dans un en-tête Authorization avec la valeur Token {value} pour authentifier les requêtes ultérieures vers https://api.ostorlab.co/apis/graphql_token.

Authentification par clé API

La clé API peut être récupérée depuis votre tableau de bord https://report.ostorlab.co. Vous devez cliquer sur Integrations/API :

Ensuite, sur le menu des clés API.

Cliquez sur le bouton "New" pour générer une nouvelle clé

Et copiez la clé API (Vous pouvez ajouter un nom et une date d'expiration à votre clé), et enfin n'oubliez pas de cliquer sur le bouton "Save" pour enregistrer votre clé.

La clé API est utilisée comme valeur de la clé de l'en-tête X-Api-Key pour authentifier les requêtes ultérieures vers https://api.ostorlab.co/apis/graphql_token.

Créer un scan
import json
import requests

query = '''
mutation MobileScan($title: String!, $assetType: String!, $application: Upload!, $sboms: [Upload], $scanProfile: String!, $credentialIds: [Int]) {
  createMobileScan(title: $title, assetType: $assetType, application: $application, sboms: $sboms, scanProfile: $scanProfile, credentialIds: $credentialIds) {
    scan {
      id
    }
  }
}
'''

# Configurer la clé API dans l'en-tête d'autorisation.
api_key = "XXXXXXXXXXX"
headers = {"X-Api-Key": f"{api_key}"}
# Variables
title = "scan_title"
asset_type = "android"
scan_profile = "Fast Scan"
data = {"operations": json.dumps({"query":query,"variables": {"title":title,"assetType":asset_type,"application": None,"scanProfile": scan_profile,},}), "map": json.dumps({"0": ["variables.application"]}),}
path_to_file = "my_full_path"
# Envoyer la requête post.
with open(path_to_file, "rb") as f:
    request = requests.post(url='https://api.ostorlab.co/apis/graphql_token/',
                          data=data,
                          files={"0": f.read()},  
                          headers=headers)
    print(request.json())
curl -v -X POST https://api.ostorlab.co/apis/graphql \
--header "X-Api-Key: $API_KEY" \
-F 'operations={"query": "mutation newMobileScan($title: String!, $assetType: String!, \
$application: Upload!, $scanProfile: String!) { createMobileScan(title: $title, assetType:$assetType, \
application: $application, scanProfile: $scanProfile) {scan {id }  }}", "variables": \
{"title": "test_title", "assetType": "android", "application": null, "scanProfile": "$SCAN_PROFILE"}}' \
-F 'map={"0": ["variables.application"]}' -F "0=@$PATH_APPLICATION"
Extraire la progression du scan
import requests

query = '''
query {
  scans {
    scans {   
      id
      title
      progress
    }
  }
}
'''

# Configurer la clé API dans l'en-tête d'autorisation.
api_key = "XXXXXXXXXXX"
headers = {"X-Api-Key": f"{api_key}"}
# Envoyer la requête post.
request = requests.post(url='https://api.ostorlab.co/apis/graphql_token/',
                      json={"query": query},
                      headers=headers)
print(request.json())
curl -v -X POST https://api.ostorlab.co/apis/graphql \
--header 'Content-Type: application/json' \
--header "X-Api-Key: $API_KEY" \
--data '{"query": "query {scans { scans { id title  progress }}}"}'  
Extraire les tickets du scan
import requests

query = '''
query tickets($scanId: Int) {
    tickets(scanId: $scanId) {   
    tickets{
      id  
      withinSlo
      status
      priority  
      }
    }
  }
'''

# Variables
scan_id = XXXXX
# Configurer la clé API dans l'en-tête d'autorisation.
api_key = "XXXXXXXXXXX"
headers = {"X-Api-Key": f"{api_key}"}
# Envoyer la requête post.
request = requests.post(url='https://api.ostorlab.co/apis/graphql_token/',
                      json={"query": query, "variables": {"scanId": scan_id}},
                      headers=headers)
print(request.json())
curl -v -X POST https://api.ostorlab.co/apis/graphql \
--header 'Content-Type: application/json' \
--header "X-Api-Key: $API_KEY" \
--data '{"query": "query tickets($scanId: Int) { tickets(scanId: $scanId) { tickets { id withinSlo status priority } } }", "variables": {"scanId": $SCAN_ID}}'  
Créer un scan with SBOM
import json
import requests
import pathlib

query = '''
mutation MobileScan($title: String!, $assetType: String!, $application: Upload!, $sboms: [Upload], $scanProfile: String!) {
  createMobileScan(title: $title, assetType: $assetType, application: $application, sboms: $sboms, scanProfile: $scanProfile) {
    scan {
      id
    }
  }
}
'''

# Configurer la clé API dans l'en-tête d'autorisation.
api_key = "XXXXXXXXXXX"
headers = {"X-Api-Key": f"{api_key}"}

# Variables
title = "scan_with_sbom"
asset_type = "android"
scan_profile = "Fast Scan"
path_to_apk = "your_apk.apk"
path_to_sboms = ["gradle.lockfile"]

# Données
data = {
    "operations": json.dumps({
        "query": query,
        "variables": {
            "title": title,
            "assetType": asset_type,
            "application": None,
            "sboms": [None],  # Un seul élément
            "scanProfile": scan_profile
        }
    }),
    "map": json.dumps({
        "0": ["variables.application"],
        "1": ["variables.sboms.0"]
    })
}

# Envoyer la requête post
apk_content = pathlib.Path(path_to_apk).read_bytes()
sbom_content = pathlib.Path(path_to_sboms[0]).read_bytes()
response = requests.post(
    url='https://api.ostorlab.co/apis/graphql_token/',
    data=data,
    files={
        "0": apk_content,
        "1": sbom_content
    },
    headers=headers
)
print(response.json())
curl -v -X POST https://api.ostorlab.co/apis/graphql \
--header "X-Api-Key: $API_KEY" \
-F 'operations={"query": "mutation MobileScan($title: String!, $assetType: String!, $application: Upload!, $sboms: [Upload], $scanProfile: String!) { createMobileScan(title: $title, assetType: $assetType, application: $application, sboms: $sboms, scanProfile: $scanProfile) { scan { id } } }", "variables": {"title": "scan_with_sbom", "assetType": "android", "application": null, "sboms": [null], "scanProfile": "Fast Scan"}}' \
-F 'map={"0": ["variables.application"], "1": ["variables.sboms.0"]}' \
-F "0=@$PATH_APK" \
-F "1=@$PATH_SBOM"
Créer un scan with test credentials

Créer les identifiants de test

Exemple de création d'un simple identifiant de connexion par mot de passe :

import json
import requests

query = '''
    mutation CreateTestCredentials($credentials: TestCredentialsInput!) {
          createTestCredentials(testCredentials: $credentials) {
            testCredentials {
              ... on LoginPasswordTestCredentials {
                id
                credentialName
                login
                password
                type
                url
              }
            }
          }
        }
'''


# Définir la clé d'API dans l'en-tête Authorization
api_key = "XXXXXXXXXX"
headers = {"X-Api-Key": f"{api_key}"}

# Variables
variables = {
    "credentials": {
        "credentialName": "login_creds",
        "loginPassword": {
            "login": "fake_login",
            "password": "fake_password",
            "url": "fake_url"
        },
    }
}

# Données
data = {
    "query": query,
    "variables": variables
}

# Envoyer la requête post
response = requests.post(
    url='https://api.ostorlab.co/apis/graphql_token/',
    json=data,
    headers=headers
)
print(response.json())
curl -X POST https://api.ostorlab.co/apis/graphql_token/ -H "X-Api-Key: $API_KEY" -H "Content-Type: application/json" -d '{
  "query": "mutation CreateTestCredentials($credentials: TestCredentialsInput!) { createTestCredentials(testCredentials: $credentials) { testCredentials { ... on LoginPasswordTestCredentials { id credentialName login password type url } } } }",
  "variables": {
    "credentials": {
      "credentialName": "login_creds",
      "loginPassword": {
        "login": "fake_login",
        "password": "fake_password",
        "url": "fake_url"
      }
    }
  }
}'

Exemple de création d'un identifiant de test personnalisé :

import json
import requests

query = '''
    mutation CreateTestCredentials($credentials: TestCredentialsInput!) {
      createTestCredentials(testCredentials: $credentials) {
        testCredentials {
          ... on CustomTestCredentials {
            id
            credentialName
            credentials {
              name
              value
            }
          }
        }
      }
    }
'''


# Définir la clé d'API dans l'en-tête Authorization
api_key = "XXXXXXXXXXXXX"
headers = {"X-Api-Key": f"{api_key}"}

# Variables
variables = {
        "credentials": {
            "credentialName": "testCustomCredentials",
            "custom": {
                "credentials": [
                    {"name": "a", "value": "b"},
                    {"name": "a2", "value": "b2"},
                ],
            },
        }
    }

# Données
data = {
    "query": query,
    "variables": variables
}

# Envoyer la requête post
response = requests.post(
    url='https://api.ostorlab.co/apis/graphql_token/',
    json=data,
    headers=headers
)
print(response.json())
curl -X POST https://api.ostorlab.co/apis/graphql_token/ \
-H "X-Api-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
  "query": "mutation CreateTestCredentials($credentials: TestCredentialsInput!) { createTestCredentials(testCredentials: $credentials) { testCredentials { ... on CustomTestCredentials { id credentialName credentials { name value } } } } }",
  "variables": {
    "credentials": {
      "credentialName": "testCustomCredentials",
      "custom": {
        "credentials": [
          { "name": "a", "value": "b" },
          { "name": "a2", "value": "b2" }
        ]
      }
    }
  }
}'

Interroger les identifiants de test

import requests

query = '''
    query {
      testsCredentials(page: 1, numberElements: 10) {
        pageInfo {
          count
          numPages
        }
        testsCredentials {
          __typename
          ... on LoginPasswordTestCredentials {
            credentialName
            id
            login
            password
            role
            url
          }
          ... on CreditCardTestCredentials {
            id
            credentialName
            creditCardNumber
            expirationDate
            name
            cvv
          }
          ... on AddressTestCredentials {
            id
            credentialName
            addressLine
            city
            zipCode
            country
          }
          ... on EmailTestCredentials {
            id
            credentialName
            email
          }
          ... on PhoneNumberTestCredentials {
            id
            credentialName
            phoneNumber
          }
          ... on ScriptTestCredentials {
            id
            credentialName
            script
          }
          ... on TlsCertificateTestCredentials {
            id
            credentialName
            tlsCertificate
          }
          ... on CustomTestCredentials {
            id
            credentialName
            credentials {
              name
              value
            }
          }
          ... on TestHeaders {
            id
            credentialName
            testHeaders {
              name
              value
            }
          }
          ... on BasicTestCredentials {
            id
            credentialName
            login
            password
          }
        }
      }
    }
'''

# Configurer la clé API dans l'en-tête d'autorisation.
api_key = "XXXXXXXXXXX"
headers = {"X-Api-Key": f"{api_key}"}

# Envoyer la requête post
response = requests.post(
    url='https://api.ostorlab.co/apis/graphql_token/',
    json={"query": query},
    headers=headers
)
print(response.json())
curl -v -X POST https://api.ostorlab.co/apis/graphql \
--header 'Content-Type: application/json' \
--header "X-Api-Key: $API_KEY" \
--data '{"query": "query { testsCredentials(page: 1, numberElements: 10) { pageInfo { count numPages } testsCredentials { __typename ... on LoginPasswordTestCredentials { credentialName id login password role url } ... on CreditCardTestCredentials { id credentialName creditCardNumber expirationDate name cvv } ... on AddressTestCredentials { id credentialName addressLine city zipCode country } ... on EmailTestCredentials { id credentialName email } ... on PhoneNumberTestCredentials { id credentialName phoneNumber } ... on ScriptTestCredentials { id credentialName script } ... on TlsCertificateTestCredentials { id credentialName tlsCertificate } ... on CustomTestCredentials { id credentialName credentials { name value } } ... on TestHeaders { id credentialName testHeaders { name value } } ... on BasicTestCredentials { id credentialName login password } } } }"}'

Create a scan with test credentials

import json
import requests

query = '''
mutation MobileScan($title: String!, $assetType: String!, $application: Upload!, $scanProfile: String!, $credentialIds: [Int]) {
  createMobileScan(title: $title, assetType: $assetType, application: $application, scanProfile: $scanProfile, credentialIds: $credentialIds) {
    scan {
      id
    }
  }
}
'''

# Configurer la clé API dans l'en-tête d'autorisation.
api_key = "XXXXXXXXXXX"
headers = {"X-Api-Key": f"{api_key}"}

# Variables
title = "scan_with_test_credentials"
asset_type = "android"
scan_profile = "Full Scan"
path_to_apk = "your_apk.apk"

# ID des identifiants à utiliser dans le scan, vous pouvez obtenir l'ID d'identifiant à partir de l'étape précédente de création des identifiants de test.
credential_ids = [123]

# Données
data = {
    "operations": json.dumps({
        "query": query,
        "variables": {
            "title": title,
            "assetType": asset_type,
            "application": None,
            "scanProfile": scan_profile,
            "credentialIds": credential_ids
        }
    }),
    "map": json.dumps({
        "0": ["variables.application"]
    })
}

# Envoyer la requête post
apk_content = pathlib.Path(path_to_apk).read_bytes()
response = requests.post(
    url='https://api.ostorlab.co/apis/graphql_token/',
    data=data,
    files={
        "0": apk_content
    },
    headers=headers
)
print(response.json())
curl -v -X POST https://api.ostorlab.co/apis/graphql \
--header "X-Api-Key: $API_KEY" \
-F 'operations={"query": "mutation MobileScan($title: String!, $assetType: String!, $application: Upload!, $scanProfile: String!, $credentialIds: [Int]) { createMobileScan(title: $title, assetType: $assetType, application: $application, scanProfile: $scanProfile, credentialIds: $credentialIds) { scan { id } } }", "variables": {"title": "scan_with_test_credentials", "assetType": "android", "application": null, "scanProfile": "Full Scan", "credentialIds": [123]}}' \
-F 'map={"0": ["variables.application"]}' \
-F "0=@$PATH_APK"
Créer un scan with on prem scanner
  • Étape 1 : Récupérer les Scanners Disponibles

Avant de créer un scan, vous devez d'abord identifier l'ID du scanner. Vous pouvez récupérer la liste des scanners disponibles pour votre organisation en utilisant la requête suivante :

import json
import requests

query_scanners = '''
query GetScanners {
  scanners {
    scanners {
      id
      uuid
      name
      description
    }
  }
}
'''

# Configurer la clé API dans l'en-tête d'autorisation.
api_key = "XXXXXXXXXXX"
headers = {"X-Api-Key": f"{api_key}"}
path_to_file = "my_full_path"
data = {"operations": json.dumps({"query": query_scanners}) , "map": json.dumps({}),}
# Envoyer la requête post.
with open(path_to_file, "rb") as f:
    request = requests.post(url='https://api.ostorlab.co/apis/graphql_token/',
                          data=data,
                          files={"0": f.read()},
                          headers=headers)
    print(request.json())
curl -v -X POST "https://api.ostorlab.co/apis/graphql_token/" \
  -H "X-Api-Key: $API_KEY" \
  -F 'operations={
    "query": "query GetScanners { scanners { scanners { id uuid name description } } }"
  }' \
  -F 'map={}' \
  -F "0=@$PATH_TO_FILE"

Cette requête renverra une liste de scanners associés à votre compte. Notez l'ID du scanner que vous souhaitez utiliser lors de la création d'un scan.

  • Étape 2 : Créer un Web Scan

Une fois que vous avez identifié le bon ID de scanner, vous pouvez créer un nouveau Web Scan en fournissant le titre du scan, les URL cibles, le profil de scan et l'ID du scanner.

import json
import requests

query = '''
mutation NewWebScan($title: String, $urls: [String]!, $scanProfile: String!,  $scannerId: Int,) {
  createWebScan(title: $title, urls: $urls, scanProfile: $scanProfile,  scannerId: $scannerId,) {
    scan {
      id
    }
  }
}
'''

# Configurer la clé API dans l'en-tête d'autorisation.
api_key = "XXXXXXXXXXX"
headers = {"X-Api-Key": f"{api_key}"}
# Variables
title = "scan_title"
urls = ["example.com"] # List of urls to scan.
scan_profile = "Full Web Scan"
scanner_id = 000000 # Replace with your scanner id.
path_to_file = "my_full_path"

data = {"operations": json.dumps({"query":query, "variables": {"title": title, "urls": urls,
                           "scanProfile": scan_profile, "scannerId": scanner_id
                          }}) , "map": json.dumps({}),}
# Envoyer la requête post.
with open(path_to_file, "rb") as f:
    request = requests.post(url='https://api.ostorlab.co/apis/graphql_token/',
                          data=data,
                          files={"0": f.read()},
                          headers=headers)
    print(request.json())
curl -v -X POST "https://api.ostorlab.co/apis/graphql_token/" \
  -H "X-Api-Key: $API_KEY" \
  -F 'operations={
    "query": "mutation NewWebScan($title: String, $urls: [String]!, $scanProfile: String!, $scannerId: Int) { createWebScan(title: $title, urls: $urls, scanProfile: $scanProfile, scannerId: $scannerId) { scan { id } } }",
    "variables": {
      "title": "scan_title",
      "urls": ["example.com"],
      "scanProfile": "Full Web Scan",
      "scannerId": 000000
    }
  }' \
  -F 'map={}' \
  -F "0=@$PATH_TO_FILE"

Télécharger le PDF

Pour télécharger le rapport PDF, vous devez suivre 3 étapes :

1- Déclencher la création de la tâche PDF. Cette étape nécessite un ID de scan et renvoie l'ID de la tâche (Job Id).

2- Vérifier le statut de la tâche et attendre qu'elle ait le statut "SUCCESS".

3- Télécharger le PDF en utilisant l'ID de la tâche.

import json
import requests
import time
import sys

API_KEY = "XXXXXXXXXXX" # Remplacez par une clé API valide.
API_URL = 'https://api.ostorlab.co/apis/graphql_token/'
HEADERS = {"X-Api-Key": API_KEY}
PDF_OUTPUT_PATH = '/tmp/f123.pdf'
Time_SLEEP = 10

def generate_pdf_job(scan_id):
    """Initier la génération du PDF et renvoyer l'ID de la tâche"""
    query = '''
    mutation generatePdf($scanId: Int!, $riskRatings: [RiskRating]!, $statuses: [TicketStatusEnum]!, $pdfReportTemplate: PDFReportTemplate!, $standards: [ScanCategoryGroupEnum]) {
        generatePdf(scanId: $scanId, riskRatings: $riskRatings, statuses: $statuses, pdfReportTemplate: $pdfReportTemplate, standards: $standards) {
          job {
            id
            status
          }
        }
    }
    '''

    variables = {
        "scanId": scan_id,
        "riskRatings": ["CRITICAL", "HIGH", "MEDIUM", "LOW", "POTENTIALLY", "HARDENING"],
        "statuses": ["OPEN", "REOPEN"],
        "standards": [],
        "pdfReportTemplate": "full_report"
    }

    response = requests.post(API_URL, json={"query": query, "variables": variables}, headers=HEADERS)
    response_data = response.json()

    if 'errors' in response_data:
        print("Erreur lors de la génération du PDF :", response_data, file=sys.stderr)
        sys.exit(1)

    return response_data['data']['generatePdf']['job']['id']

def wait_for_job_completion(job_id):
    """Attendre la fin de la tâche"""
    query = """
    query job($jobId: String!) {
        job(jobId: $jobId) {
            id
            status
        }
    }
    """

    while True:
        response = requests.post(API_URL, json={"query": query, "variables": {"jobId": job_id}}, headers=HEADERS)
        status = response.json()['data']['job']['status']

        if status == 'SUCCESS':
            break
        time.sleep(Time_SLEEP)

def download_pdf(job_id, output_path=PDF_OUTPUT_PATH):
    """Télécharger le PDF généré"""
    response = requests.get(f'https://api.ostorlab.co/apis/job/result/{job_id}/', headers=HEADERS)
    with open(output_path, 'wb') as f:
        f.write(response.content)

def main(scan_id, output_path=PDF_OUTPUT_PATH):
    """Fonction principale pour générer et télécharger un PDF"""
    try:
        print("Démarrage de la génération du PDF...")
        job_id = generate_pdf_job(scan_id)
        print(f"Tâche créée avec l'ID : {job_id}")

        print("Attente de la fin de la tâche...")
        wait_for_job_completion(job_id)

        print("Téléchargement du PDF...")
        download_pdf(job_id, output_path)
        print(f"PDF téléchargé avec succès vers : {output_path}")
    except Exception as e:
        print(f"Une erreur s'est produite : {str(e)}", file=sys.stderr)
        sys.exit(1)

scan_id = XXXXXXXXXXX # Remplacez par l'ID du scan. 
main(scan_id)
curl -X POST https://api.ostorlab.co/apis/graphql_token/ -H "X-Api-Key: $API_KEY" -H "Content-Type: application/json" -d '{
  "query": "mutation generatePdf($scanId: Int!, $riskRatings: [RiskRating]!, $statuses: [TicketStatusEnum]!, $pdfReportTemplate: PDFReportTemplate!, $standards: [ScanCategoryGroupEnum]) { generatePdf(scanId: $scanId, riskRatings: $riskRatings, statuses: $statuses, pdfReportTemplate: $pdfReportTemplate, standards: $standards) { job { id status } } }",
  "variables": {
    "scanId": $SCAN_ID,
    "riskRatings": ["CRITICAL", "HIGH", "MEDIUM", "LOW", "POTENTIALLY", "HARDENING"],
    "statuses": ["OPEN", "REOPEN"],
    "standards": [],
    "pdfReportTemplate": "full_report"
  }
}'

Extrayez le statut de l'ID de la tâche et utilisez la requête ci-dessous pour extraire le statut de la tâche (où XXXXXXX est l'ID de la tâche) :

``` shell
curl -v -X POST https://api.ostorlab.co/apis/graphql \
--header 'Content-Type: application/json' \
--header "X-Api-Key: $API_KEY" \
--data '{"query": "query { job(jobId: XXXXXXX) { id status } }"}'
```

Une fois que le statut devient SUCCESS, vous pouvez exécuter la requête ci-dessous pour télécharger le PDF :

``` shell
curl --header "X-Api-Key: $API_KEY" "https://api.ostorlab.co/apis/job/result/<job_id>/" -o <output_path>
```

Pour résumer, ce guide a présenté l'API d'Ostorlab. Nous avons exploré diverses méthodes d'utilisation de l'API, y compris l'application web GraphiQL et les scripts. De plus, nous avons examiné un ensemble de requêtes courantes pour servir de référence.