Dodock intègre plusieurs mécanismes pour sécuriser l'exécution de code personnalisé et protéger les données sensibles. Cette page décrit les bonnes pratiques et les outils disponibles pour les développeurs.
Dans un environnement ERP comme Dokos, il est fréquent d'exécuter du code personnalisé (scripts, formules, hooks) pour répondre à des besoins métiers spécifiques. Cependant, cette flexibilité peut introduire des risques de sécurité si le code n'est pas correctement contrôlé.
Dodock fournit plusieurs couches de protection pour limiter ces risques :
Le module safe_exec de Dodock permet d'exécuter du code Python dans un environnement restreint, où seules certaines fonctions et méthodes sont accessibles.
from frappe.utils.safe_exec import safe_exec
code = """
result = frappe.get_doc('Client', 'Maison Verte SARL').nom
"""
safe_exec(code, _globals={}, _locals={})
Dans cet exemple, le code exécuté ne peut accéder qu'aux méthodes explicitement autorisées par safe_exec.
La classe SafeDoc est une extension sécurisée de frappe._dict qui expose un sous-ensemble contrôlé des méthodes disponibles dans les objets Document. Elle est particulièrement utile dans les contextes où l'exécution de code doit être restreinte, comme dans les formules de champs personnalisés ou les scripts de validation.
La classe SafeDoc fournit les méthodes suivantes, conçues pour être utilisées en toute sécurité dans un contexte d'exécution restreint :
in_format_data(fieldname)Permet de vérifier si une valeur de champ doit être formatée selon les règles définies pour ce champ.
Exemple d'utilisation :
client = SafeDoc({
"nom": "Maison Verte SARL",
"montant_ttc": 1200.50,
"date_commande": "2026-07-15"
})
# Vérifier si le champ 'montant_ttc' doit être formaté
if client.in_format_data("montant_ttc"):
montant_formatte = f"{client.montant_ttc:.2f} €"
is_print_hide(fieldname, print_format=None)Détermine si un champ doit être masqué lors de l'impression d'un document, en fonction du format d'impression spécifié.
Exemple d'utilisation :
client = SafeDoc({
"nom": "Maison Verte SARL",
"marge_beneficiaire": 0.35,
"commentaire_interne": "Client prioritaire pour 2026"
})
# Vérifier si le champ 'marge_beneficiaire' doit être masqué à l'impression
if client.is_print_hide("marge_beneficiaire", "Facture standard"):
print("Ce champ ne sera pas visible sur la facture imprimée")
get_label_from_fieldname(fieldname)Récupère l'étiquette (label) associée à un champ, telle que définie dans le doctype.
Exemple d'utilisation :
client = SafeDoc({
"nom": "Maison Verte SARL",
"date_commande": "2026-07-15"
})
# Récupérer l'étiquette du champ 'date_commande'
label = client.get_label_from_fieldname("date_commande")
# label contiendra "Date de commande" si c'est l'étiquette définie dans le doctype
metaLa propriété meta donne accès aux métadonnées du document, comme le nom du doctype ou les propriétés des champs.
Exemple d'utilisation :
client = SafeDoc({
"doctype": "Client",
"nom": "Maison Verte SARL"
})
# Accéder au nom du doctype
doctype_name = client.meta.name
# doctype_name contiendra "Client"
# Accéder aux propriétés d'un champ spécifique
nom_field = client.meta.get_field("nom")
if nom_field:
print(f"Le champ 'nom' est de type {nom_field.fieldtype}")
Dans les formules de champs personnalisés, SafeDoc permet d'accéder aux données du document en cours de manière sécurisée :
# Formule pour calculer un montant avec remise
montant_ht = doc.montant_ht
remise = doc.remise or 0
# Utilisation de SafeDoc pour accéder aux métadonnées
doc_safe = SafeDoc(doc.as_dict())
if doc_safe.in_format_data("montant_ht"):
montant_ht = float(montant_ht)
montant_ttc = montant_ht * (1 - remise/100) * 1.2
Dans les scripts de validation, SafeDoc peut être utilisé pour vérifier des conditions spécifiques sans exposer l'intégralité des méthodes du document :
# Validation que le champ 'date_livraison' n'est pas antérieur à la date du jour
doc_safe = SafeDoc(doc.as_dict())
if doc.date_livraison and doc_safe.get_label_from_fieldname("date_livraison"):
from datetime import datetime
if doc.date_livraison < datetime.today().date():
frappe.throw(f"La {doc_safe.get_label_from_fieldname('date_livraison')} ne peut pas être antérieure à la date du jour")
Pour contrôler quels champs apparaissent dans un format d'impression :
# Déterminer quels champs masquer dans un format d'impression spécifique
doc_safe = SafeDoc(doc.as_dict())
champs_a_masquer = []
for fieldname in ["marge_beneficiaire", "commentaire_interne"]:
if doc_safe.is_print_hide(fieldname, "Facture standard"):
champs_a_masquer.append(fieldname)
SafeDoc dans les contextes d'exécution restreinte : Préférez SafeDoc à l'objet Document complet lorsque vous travaillez avec safe_exec ou dans des scripts où la sécurité est critique.SafeDoc expose des méthodes sûres, certaines peuvent ne pas être pertinentes pour tous les types de documents.SafeDoc, soyez prudent lorsque vous exposez des données sensibles dans des contextes comme les formules de champs ou les scripts de validation.