Aller au contenu principal

Sandboxing et limitations de sécurité

Sandboxing et limitations de sécurité

Les limites du sandbox ne sont pas des défauts à contourner : ce sont les propriétés qui rendent l’exécution de code généré acceptable. Un environnement sans réseau, sans persistance et sous plafond de ressources ne peut ni exfiltrer vos données, ni laisser traîner un état d’un client à l’autre, ni consommer indéfiniment.

Les connaître à l’avance vous évite surtout de déboguer l’impossible. Les trois questions qui reviennent — pourquoi pip install échoue, pourquoi mon fichier a disparu entre deux appels, pourquoi mon traitement s’arrête au bout de quelques minutes — ont toutes leur réponse ci-dessous, et aucune n’est un bug.

Architecture du sandbox

Chaque exécution de Code Interpreter se fait dans un container isolé avec les propriétés suivantes :

  • Isolation réseau : aucun accès internet, aucune requête sortante
  • Système de fichiers éphémère : tout est effacé après l’appel
  • Timeout d’exécution : le code est interrompu après un délai limite
  • Ressources contrôlées : CPU et mémoire plafonnés
from openai import OpenAI

client = OpenAI()

# Cette requête échouera : pas d'accès réseau
response = client.responses.create(
    model="gpt-5.6-terra",
    input="Telecharge le contenu de https://example.com avec requests",
    tools=[{"type": "code_interpreter"}]
)

# Le modèle expliquera que l'accès réseau n'est pas disponible
print(response.output_text)

Ce qui est disponible

Bibliothèques pré-installées

Le sandbox inclut un ensemble de bibliothèques Python courantes :

# Analyse de données
# pandas, numpy, scipy, statsmodels

# Visualisation
# matplotlib, seaborn, plotly

# Traitement d'images
# Pillow (PIL)

# Mathematiques
# sympy, math, statistics

# Formats de fichiers
# openpyxl (Excel), csv, json, xml

# Utilitaires
# datetime, collections, itertools, re, os

Système de fichiers

Le code peut lire et écrire des fichiers dans le container :

# Le modèle peut créer des fichiers temporaires
response = client.responses.create(
    model="gpt-5.6-terra",
    input="Cree un fichier CSV avec 100 lignes de données de test "
          "(nom, âge, ville, salaire) et retourne-le.",
    tools=[{"type": "code_interpreter"}]
)

Ce qui n’est pas disponible

Accès réseau

L’absence de réseau est la limite la plus structurante, car elle interdit pip install, les appels d’API et le téléchargement de données. Elle n’est pas contournable et c’est voulu : c’est précisément ce qui garantit qu’un code généré ne peut rien envoyer nulle part. Ce dont votre traitement a besoin doit être présent dans le container ou téléversé par vos soins — et si la donnée est en ligne, c’est Web Search qui va la chercher.

# Impossible :
# - requests.get(), urllib.request
# - socket, http.client
# - pip install (pas de téléchargement)
# - Accès a des API externes
# - Envoi d'emails

Persistence entre appels

L’absence de persistance surprend surtout quand on enchaîne les appels : le DataFrame chargé au premier n’existe plus au second. Attention à la nuance, car elle est source de confusion — à l’intérieur d’un même appel, plusieurs exécutions de code partagent bien la même session ; c’est d’un appel à l’autre que tout est perdu.

# Premier appel : cree un fichier
response1 = client.responses.create(
    model="gpt-5.6-terra",
    input="Cree une variable x = 42 et sauvegarde-la dans un fichier",
    tools=[{"type": "code_interpreter"}]
)

# Deuxième appel : le fichier n'existe plus
response2 = client.responses.create(
    model="gpt-5.6-terra",
    input="Lis le fichier cree précédemment",
    tools=[{"type": "code_interpreter"}]
)
# -> Erreur : fichier introuvable

Pour conserver des résultats entre appels, récupérez les fichiers générés et renvoyez-les :

def pipeline_avec_persistence(etapes: list[str]) -> list:
    """Exécute des étapes séquentielles en passant les fichiers."""
    fichiers_context = []
    resultats = []

    for etape in etapes:
        content = [{"type": "text", "text": etape}]
        for fid in fichiers_context:
            content.append({"type": "input_file", "file_id": fid})

        response = client.responses.create(
            model="gpt-5.6-terra",
            input=[{"role": "user", "content": content}],
            tools=[{"type": "code_interpreter"}]
        )

        # Récupérer les fichiers générés pour l'étape suivante
        for item in response.output:
            if item.type == "code_interpreter_call":
                for result in item.results:
                    if hasattr(result, "files"):
                        for f in result.files:
                            fichiers_context.append(f.file_id)

        resultats.append(response.output_text)

    return resultats

Exécution longue

Le plafond de temps disqualifie les traitements lourds : entraînement de modèle, boucle sur des centaines de milliers de lignes, calcul massif. Ce n’est pas une limite à contourner par des astuces mais un signal — au-delà, c’est votre propre infrastructure qu’il faut utiliser, le sandbox restant l’outil de l’analyse interactive.

# Pattern pour les traitements lourds : découper en étapes
response = client.responses.create(
    model="gpt-5.6-terra",
    input="Traite ce fichier CSV de 500 000 lignes en lots de 50 000. "
          "Pour chaque lot, calcule les statistiques et accumule les résultats.",
    tools=[{"type": "code_interpreter"}],
    instructions="Si le traitement risque d'etre long, decoupe-le en etapes "
                "et affiche la progression."
)

Sécurité des données

Données sensibles

Les fichiers que vous envoyez sont traités dans le sandbox puis supprimés. Néanmoins, le raisonnement de conformité ne s’arrête pas là : ils ont bien quitté votre infrastructure pour être traités par un tiers. Anonymiser ou pseudonymiser avant l’envoi coûte quelques lignes et vous dispense de la question — car un identifiant client qui ne sort jamais ne peut poser aucun problème.

En production, retenez ces trois précautions :

def analyser_donnees_sensibles(fichier_path: str, colonnes_a_masquer: list[str]):
    """Anonymise les données avant envoi au Code Interpreter."""
    import pandas as pd

    # Charger et anonymiser localement
    df = pd.read_csv(fichier_path)
    for col in colonnes_a_masquer:
        if col in df.columns:
            df[col] = df[col].apply(lambda x: hash(str(x)) % 1000000)

    # Sauvegarder la version anonymisee
    fichier_anonyme = "/tmp/donnees_anonymes.csv"
    df.to_csv(fichier_anonyme, index=False)

    # Envoyer la version anonymisee
    with open(fichier_anonyme, "rb") as f:
        fichier = client.files.create(file=f, purpose="assistants")

    response = client.responses.create(
        model="gpt-5.6-terra",
        input=[{
            "role": "user",
            "content": [
                {"type": "text", "text": "Analyse les tendances dans ces données"},
                {"type": "input_file", "file_id": fichier.id}
            ]
        }],
        tools=[{"type": "code_interpreter"}]
    )
    return response.output_text

Code malveillant

Le sandbox protège contre les tentatives d’évasion, mais ne validez jamais du code généré côté client sans vérification :

# Le sandbox empêche :
# - os.system("rm -rf /")  -> pas de permissions
# - subprocess.call(...)    -> pas de sous-processus dangereux
# - Accès aux variables d'environnement sensibles
# - Escalade de privileges

Gérer les erreurs d’exécution

Le code peut échouer dans le sandbox. Le modèle tente généralement de corriger :

response = client.responses.create(
    model="gpt-5.6-terra",
    input="Calcule la matrice inverse d'une matrice singuliere [[1,2],[2,4]]",
    tools=[{"type": "code_interpreter"}],
    instructions="Si le code produit une erreur, explique pourquoi "
                "et propose une alternative."
)

# Le modele execute le code, detecte l'erreur LinAlgError,
# puis explique que la matrice est singuliere et propose
# la pseudo-inverse de Moore-Penrose

Bonnes pratiques

# 1. Soyez spécifique dans vos instructions
# Mauvais :
input_vague = "Analyse ces données"

# Bon :
input_precis = ("Charge ce CSV avec pandas. "
               "Calcule la moyenne, médiane et écart-type de la colonne 'montant'. "
               "Identifie les valeurs supérieures a 3 écarts-types. "
               "Génère un boxplot.")

# 2. Demandez du code commente
instructions = "Commente chaque étape du code. Affiche les résultats intermédiaires."

# 3. Gérez les fichiers volumineux
instructions_lot = ("Si le fichier depasse 100 000 lignes, "
                   "travaille sur un échantillon représentatif de 10 000 lignes "
                   "et indique-le dans ta reponse.")

Points clés à retenir

  • Le sandbox est isolé : pas de réseau, pas de persistence, timeout limite
  • Les bibliothèques d’analyse et de visualisation sont pré-installées
  • Anonymisez les données sensibles avant envoi
  • Passez les fichiers entre appels via l’API files pour simuler la persistence
  • Le modèle gère les erreurs d’exécution et tente de s’auto-corriger