Ce guide explique comment écrire un plugin New Risk Exchange et tirer le maximum de valeur de votre écosystème du risque en tirant parti de la fonctionnalité fournie par le module Risk Exchange. En suivant ce guide, le développeur devrait être en mesure d'écrire un plugin New de manière autonome sans aucun problème technique.
Conditions préalables
- Expérience en programmation Python 3.x (niveau intermédiaire).
- Accès à la plateforme Netskope Cloud Exchange.
- Accès à l'API ou au SDK Python du produit ou de la solution pour lequel vous devez écrire le plugin.
- Un compte disposant des autorisations minimales pour le produit.
Compatibility
Ce guide de développement des plugins est spécifique au développement des plugins Risk Exchange pris en charge par la version 5.1.0 du noyau.
Module Risk Exchange
La plateforme Cloud Exchange, et son module Risk Exchange, est dotée d'un ensemble riche de caractéristiques et de fonctionnalités qui permettent un haut degré de personnalisation. Nous vous recommandons donc de vous familiariser avec les différents aspects de la plateforme énumérés ci-dessous.
Ce module permet d'extraire des enregistrements de plateformes tierces et d'effectuer des actions telles que l'ajout d'un utilisateur à un groupe sur la plateforme.
Concepts de Netskope & Terminologie
- Core: The Cloud Exchange core engine manages the 3rd-party plugins and their life cycle methods. It has API endpoints for interacting with the platform to perform various tasks.
- Module : Les zones de code fonctionnel qui invoquent des plugins spécifiques aux modules pour accomplir différents flux de travail. Risk Exchange est l'un des modules de Cloud Exchange.
- Plugin: Plugins are Python packages that have logic to fetch users, devices or applications details and risk/threat information from 3rd-party Threat Intel systems, which will then be stored in Risk Exchange. Risk Exchange plugins also perform actions like add user to group, remove user from group, or other actions on device records or applications records if supported on a third-party platform.
- Configurations de plugin : Les configurations de plugin sont les objets de la classe de plugin qui sont configurés avec les paramètres requis et sont programmés par le moteur central de Cloud Exchange pour récupérer les utilisateurs et les scores.
- Editeur de schéma : Les utilisateurs disposant d'un accès en écriture peuvent gérer les schémas et les champs d'entité de Risk Exchange à partir de l'éditeur de schémas.
- Entité : L'entité est une collection de champs d'un schéma spécifique et les enregistrements sont stockés dans cette entité. Les entités peuvent être gérées à partir de la page Schema Editor du module Risk Exchange.
- Enregistrements : Les enregistrements (d'utilisateurs, de périphériques ou d'applications) sont des objets dont les champs sont mappés dans l'étape Entity Sources de la configuration du plugin, recueillis à partir de plateformes tierces et stockés dans l'entité mappée dans la base de données Cloud Exchange.
Lignes directrices pour le développement
- Utilisez la structure du répertoire des plugins pour tout le code Python.
- Assurez-vous que toutes les bibliothèques tierces fournies avec le plugin ont fait l'objet d'une vérification des vulnérabilités connues.
- Veillez à respecter les conventions de code Python standard. (https://peps.python.org/pep-0008/)
- Exécutez et vérifiez que la vérification Lint de flake8 passe avec la vérification docstring activée. La longueur maximale d'une ligne est de 80.
- Convertissez les valeurs de l'horodatage au format lisible par l'homme (de l'époque à l'objet DateTime). Assurez-vous que l'heure affichée sur l'interface utilisateur correspond au fuseau horaire local.
- Si possible, ajoutez une valeur par défaut lors de l'ajout d'un paramètre de configuration dans le plugin.
- L'architecture du plugin permet de stocker des états, mais il faut éviter de stocker des objets volumineux pour la gestion des états.
- Vérifiez que votre code python ne présente pas de vulnérabilités.
- L'icône du plugin doit être inférieure à 10kb. Veillez à utiliser le logo de l'entreprise (et non celui du produit) sur un fond transparent. La taille recommandée pour le logo est de 300×50 ou un rapport d'aspect similaire.
- Utilisez le point de contrôle fourni par le noyau CE plutôt que d'en mettre un en place par vous-même.
- Les messages du logger et les messages Toast ne doivent pas contenir les valeurs des champs de type API Token et Password.
- La pagination doit toujours être prise en compte lors du développement d'une fonctionnalité dans un plugin.
- Veillez à ajouter un mécanisme de relance pour le code de statut 429.
- Veillez à mettre en œuvre un mécanisme de journalisation approprié avec l'objet logger transmis par la plate-forme CE. Veillez à ce que la journalisation soit suffisante pour aider l'équipe d'exploitation à résoudre les problèmes. Assurez-vous que les données sensibles ne sont pas enregistrées ou divulguées dans la notification.
- Fournissez le texte d'aide approprié (infobulle) pour tous les paramètres. Si possible, veillez à expliquer la signification du paramètre dans l'infobulle.
- Veillez à donner un nom et une description significatifs aux paramètres de configuration du plugin.
- Si la description contient un lien, il doit s'agir d'un lien hypertexte et non d'un texte en clair.
- Veillez à fournir un type de configuration approprié (texte, nombre, mot de passe, choix, multi-choix) aux paramètres.
- Veillez à utiliser la configuration du proxy et l'indicateur de validation du certificat SSL transmis par la plateforme CE lors de toute requête sortante (API/SDK).
- Veillez à collecter la valeur d'un paramètre non obligatoire à l'aide de la fonction .get() et fournir une valeur par défaut lors de l'utilisation de la méthode .get() méthode.
- Les champs Tokens API et Password ne doivent pas utiliser strip().
- Les messages du journal doivent commencer par "<module> <plugin_name> [nom_de_la_configuration]:". Exemple : "CRE CrowdStrike [CrowdStrike Nom de la configuration] : <log_message>". (Il s'agit d'une suggestion, nous pouvons éviter les noms de configuration). [logger.info(":")]<module> <plugin_name><message>
- Lors de l'enregistrement d'un journal d'erreurs, nous devrions, si possible, ajouter une trace de l'exception. USE : logger.error(error, details=traceback.format_exc())
- The Toast message should not contain the <app_name> <module>: in the message.
- Assurez-vous d'attraper les exceptions et les codes d'état appropriés pendant et après les appels à l'API. Si possible, les développeurs peuvent créer une méthode d'aide à partir de laquelle les demandes seront faites et cette méthode peut être appelée avec les paramètres appropriés lorsque cela est nécessaire.
- Assurez-vous d’utiliser l’API helper pour exécuter les appels API tiers. Voir la section extrait de code de fetch_records pour l’exemple de l’API Helper. Vérifiez la référence pour le fichier d’aide (https://github.com/netskopeoss/ta_cloud_exchange_plugins/blob/main/crowdstrike_ztre/utils/helper.py)
- Pour l'authentification par jeton porteur, rechargez le jeton d'authentification sur le code de statut 401 Unauthorized pour chaque demande d'API, à l'exception des demandes dans les méthodes de validation. Maintenir le drapeau pour indiquer quand recharger le jeton d'authentification dans l'API Helper.
- Traitez l'erreur exceptions.ReadTimeout dans l'aide API.
- Le fichier CHANGELOG.md doit être mis à jour avec les balises appropriées telles que Added, Changed et Fixed ainsi qu'un message convivial approprié. Assurez-vous que le nom du fichier correspond exactement à CHANGELOG.md
- For Action parameters validation:
- Si le paramètre d'action est sélectionné dans le champ Source, validez la valeur de ce champ dans le champ execute_action
- Si le paramètre d'action est de type `choice`, le message de validation devrait être : "{Paramètre} contient le champ source. Veuillez sélectionner {field} dans la liste déroulante Static Field only."
- Si les plugins prennent en charge un champ de type datetime, convertissez ce champ en objet datetime dans la méthode fetch_records lors du retour des enregistrements.
- Veillez à renvoyer des enregistrements contenant la valeur de tous les champs pris en charge par le plugin et reçus lors d'appels à l'API avec. Les champs tels que le score dans les enregistrements ont plus de sens pour l'utilisateur du SOC lorsqu'il analyse les données. Veillez à mapper le champ Score normalisé de Netskope qui donne plus de contexte à l'analyste SOC. Le champ Score normalisé de Netskope doit être un nombre entier.
- Utilisez une validation appropriée pour les paramètres transmis à la méthode validate et fournissez le texte d'aide approprié pour tous les paramètres.
- Assurez-vous que le nom du répertoire du plugin (par exemple sample_plugin) correspond à celui du fichier manifest.json. id.
- L'agent utilisateur doit être ajouté aux en-têtes lors de tout appel API. Format de l'agent utilisateur : Netskope-ce-<ce_version>-<module>-<plugin_name>-<plugin_version>.
Écrire un plugin
Cette section illustre le processus d'écriture d'un plugin à partir de zéro.
Téléchargez le plugin d’exemple depuis le dépôt public NetskopeOSS sur Github ou depuis la base de connaissances Cloud Exchange disponible ici : https://support.netskope.com/hc/en-us/articles/360052128734-Cloud-Threat-Exchange.
Développement de l'installation
Python
Our system utilizes Python3 (v3.11 and above). Make sure to set up python3 in your development environment. Pytest is used to run unit tests.
Bibliothèques Python incluses
Ces bibliothèques Python sont incluses dans la plateforme Netskope Cloud Exchange.
| Nom de la bibliothèque | Version |
|---|---|
| aiofiles | 22.1.0 |
| amqp | 5.1.1 |
| annotated-types | 0.5.0 |
| anyio | 3.6.2 |
| asgiref | 3.6.0 |
| attrs | 22.2.0 |
| azure-core | 1.26.2 |
| azure-storage-blob | 12.14.1 |
| bcrypt | 4.0.1 |
| billiard | 4.2.0 |
| boto3 | 1.26.51 |
| botocore | 1.29.51 |
| cabby | 0.1.23 |
| cachetools | 5.2.1 |
| celery | 5.3.6 |
| certifi | 2024.7.4 |
| cffi | 1.15.1 |
| chardet | 5.1.0 |
| charset-normalizer | 3.0.1 |
| click | 8.1.3 |
| click-didyoumean | 0.3.0 |
| click-plugins | 1.1.1 |
| click-repl | 0.2.0 |
| colorama | 0.4.6 |
| colorlog | 6.7.0 |
| cryptography | 42.0.5 |
| cybox | 2.1.0.21 |
| Cython | 0.29.37 |
| defusedxml | 0.7.1 |
| dnspython | 2.6.1 |
| docker | 6.0.1 |
| email_validator | 2.2.0 |
| fastapi | 0.111.0 |
| fastapi-cli | 0.0.5 |
| furl | 2.1.3 |
| google-api-core | 2.11.0 |
| google-auth | 2.16.0 |
| google-cloud-core | 2.3.2 |
| google-cloud-pubsub | 2.13.12 |
| google-cloud-pubsublite | 1.6.0 |
| google-cloud-storage | 2.7.0 |
| google-crc32c | 1.5.0 |
| google-resumable-media | 2.4.0 |
| googleapis-common-protos | 1.58.0 |
| grpc-google-iam-v1 | 0.12.6 |
| grpcio | 1.62.2 |
| grpcio-status | 1.51.1 |
| gunicorn | 22.0.0 |
| h11 | 0.14.0 |
| httpcore | 1.0.5 |
| httptools | 0.6.1 |
| httpx | 0.27.0 |
| hvac | 1.1.0 |
| idna | 3.7 |
| importlib-metadata | 6.0.0 |
| isodate | 0.6.1 |
| Jinja2 | 3.1.4 |
| jmespath | 1.0.1 |
| jsonpath | 0.82.2 |
| jsonschema | 4.17.3 |
| kombu | 5.3.7 |
| libtaxii | 1.1.119 |
| lxml | 5.3.0 |
| markdown-it-py | 3.0.0 |
| MarkupSafe | 2.1.2 |
| mdurl | 0.1.2 |
| memory-profiler | 0.61.0 |
| mixbox | 1.0.5 |
| mongoengine | 0.25.0 |
| mongoquery | 1.4.2 |
| more-itertools | 9.0.0 |
| msrest | 0.7.1 |
| multidict | 6.0.4 |
| mypy-extensions | 0.4.3 |
| netskopesdk | 0.0.38 |
| numpy | 1.26.2 |
| oauthlib | 3.2.2 |
| onelogin | 3.1.0 |
| ordered-set | 4.1.0 |
| orderedmultidict | 1.0.1 |
| orjson | 3.10.7 |
| overrides | 6.5.0 |
| packaging | 23.0 |
| pandas | 2.1.3 |
| passlib | 1.7.4 |
| prompt-toolkit | 3.0.36 |
| proto-plus | 1.22.2 |
| protobuf | 4.21.12 |
| psutil | 5.9.4 |
| py-expression-eval | 0.3.14 |
| pyasn1 | 0.4.8 |
| pyasn1-modules | 0.2.8 |
| pycparser | 2.21 |
| pydantic | 2.4.0 |
| pydantic_core | 2.10.0 |
| Pygments | 2.18.0 |
| pyhcl | 0.4.4 |
| PyJWT | 2.6.0 |
| pymongo | 4.6.3 |
| Pympler | 1.0.1 |
| pyparsing | 3.0.9 |
| pyrsistent | 0.19.3 |
| python-dateutil | 2.8.2 |
| python-dotenv | 1.0.1 |
| python-multipart | 0.0.7 |
| python3-saml | 1.15.0 |
| pytz | 2022.7.1 |
| PyYAML | 6.0.1 |
| requests | 2.32.0 |
| requests-oauthlib | 1.3.1 |
| rich | 13.7.1 |
| rsa | 4.9 |
| s3transfer | 0.6.0 |
| shellingham | 1.5.4 |
| six | 1.16.0 |
| sniffio | 1.3.0 |
| starlette | 0.37.2 |
| stix | 1.2.0.11 |
| taxii2-client | 2.3.0 |
| typer | 0.12.3 |
| typing-inspect | 0.8.0 |
| typing-utils | 0.1.0 |
| typing_extensions | 4.12.2 |
| tzdata | 2023.3 |
| ujson | 5.10.0 |
| urllib3 | 1.26.19 |
| uvicorn | 0.23.1 |
| uvloop | 0.19.0 |
| vine | 5.1.0 |
| watchfiles | 0.23.0 |
| wcwidth | 0.2.6 |
| weakrefmethod | 1.0.3 |
| websocket-client | 1.4.2 |
| websockets | 12.0 |
| Werkzeug | 3.0.3 |
| wheel | 0.44.0 |
| xmlsec | 1.3.14 |
| zipp | 3.19.1 |
Inclure des bibliothèques de plugins personnalisés
Netskope conseille de regrouper toutes les bibliothèques python tierces dont votre plugin aura besoin dans le paquetage du plugin lui-même. Utilisez le programme d'installation pip pour réaliser ce regroupement ; il fournit un commutateur qui prend un répertoire en entrée. Si le répertoire est fourni, pip installera les paquets dans ce répertoire.
Par exemple, cette commande installera le paquet "cowsay" dans le répertoire "lib".
> pip install cowsay --target ./lib
Pour la documentation officielle à ce sujet, consultez https://pip.pypa.io/en/stable/reference/pip_install/#cmdoption-t.
Pour importer des modules à partir du dossier lib ci-dessus, nous devons définir le chemin du système python pour le module personnalisé dans le fichier __init__.py du paquetage du plugin.
__init__.py
import sys from pathlib import Path src_path = Path(__file__).resolve() src_dir = src_path.parent sys.path.insert(0, str(src_dir / "lib"))
Après avoir défini le chemin d'accès à la bibliothèque personnalisée, importez la bibliothèque comme indiqué ci-dessous :
import cowsay
IDE
Les environnements de développement intégrés (IDE) recommandés sont PyCharm ou Visual Studio Code. Installez l'outil de linting flake8 et le formateur black pour une meilleure cohérence et lisibilité du code des plugins. Se référer à Flake8 Noir.
Structure du répertoire des plugins
Cette section présente la structure typique du répertoire d'un plugin d'échange de risques.
/sample_plugin/ ├── __init__.py ├── Changelog.md ├── icon.png ├── main.py └── manifest.json
- __init__.py : Chaque paquet de plugins est considéré comme un module python par le code CRE. Assurez-vous que chaque paquet de plugins contient le fichier vide "__init__.py" fichier. Ajout d'un code permettant de définir le chemin d'accès à la bibliothèque personnalisée du plugin lorsque le module personnalisé est requis pour le plugin. Reportez-vous à la section __init__.py ci-dessus pour plus d'informations.
- CHANGELOG.md : Ce fichier contient les détails des mises à jour du plugin et doit être mis à jour avec les balises appropriées telles que Added, Changed, et Fixed ainsi qu'un message convivial approprié.
- icon.png : Logo de l'icône du plugin, qui sera visible dans le chiclet du plugin et dans les cartes de configuration de l'interface utilisateur. Le logo doit avoir un fond transparent, une taille recommandée de 300*50 pixels ou un rapport d'aspect similaire et une taille inférieure à 10 kb.
- main.py : Ce fichier python contient la classe Plugin qui contient l'implémentation concrète des méthodes d'extraction et de mise à jour des enregistrements, d'exécution des actions, de validation et de validation des actions.
- manifest.json : Fichier manifeste pour le paquet de plugins contenant des informations sur tous les paramètres configurables et leurs types de données. Ce fichier contient également plus d'informations sur l'intégration du plugin.
Les fichiers énumérés ici sont obligatoires pour toute intégration de plugin, mais les développeurs peuvent ajouter d'autres fichiers en fonction des exigences d'intégration spécifiques.
Les fichiers suivants ne sont pas obligatoires, mais il est recommandé de les ajouter dans le dossier du plugin.
- /utils/constants.py : Ce fichier contient toutes les constantes utilisées dans l'implémentation du plugin. Importez les constantes du fichier requis à partir du fichier py.
- utils/helper.py : Ce fichier contient la classe d'aide du plugin contenant l'implémentation des méthodes api_helper, add user agent et autres méthodes d'aide requises dans le plugin.
Vérifiez la référence pour le fichier d’aide : https://github.com/netskopeoss/ta_cloud_exchange_plugins/blob/main/crowdstrike_ztre/utils/helper.py
Note
Assurez-vous que le nom du répertoire du plugin (sample_plugin) correspond à celui du fichier manifest.json. Champ ID.
Changelog.md
Il s'agit d'un fichier qui contient des détails sur les mises à jour du plugin et qui doit être mis à jour avec les balises appropriées telles que Added, Changed, et Fixed ainsi qu'un message convivial approprié.
- Ajouté : utilisez-le lorsque les fonctionnalités de New sont ajoutées.
- Corrigé : Utilisez-le lorsqu'un bogue/une erreur est corrigé(e).
- Changed : Utilisez-le lorsqu'il y a un changement dans l'implémentation existante du plugin.
- Removed : utilisez-le lorsqu'un paramètre ou une fonctionnalité est supprimé du plugin.
Exemple de Changelog.md
# 1.1.0 ## Removed - Removed priority from the syslog message for the logs that are not transformed in CEF. # 1.0.1 ## Changed - Changed error logs to warning if a single field is skipped. ## Fixed - Fixed JSON format of raw data. # 1.0.0 ## Added - Initial release.
Manifest.json
Il s'agit d'un fichier JSON qui stocke les méta-informations relatives au plugin, qui sont ensuite lues par le module Risk Exchange pour rendre le plugin dans l'interface utilisateur, ainsi que pour permettre au module Risk Exchange d'en savoir plus sur le plugin, y compris les paramètres de configuration requis, l'identifiant du plugin, le nom du plugin, etc.
- Chaque plugin doit contenir ce fichier avec les informations nécessaires pour que Risk Exchange puisse instancier l'objet Plugin correctement.
- Les paramètres courants du fichier manifest.json sont les suivants
- name : (string) Nom du plugin. (Obligatoire)
- Netskope:(Booléen) il est utilisé pour vérifier si le plugin est un plugin Netskope ou non (exemple : True si c'est un plugin Netskope sinon False)
- description : (chaîne de caractères) Description du plugin. Fournissez une description détaillée mentionnant les fonctionnalités et les instructions d'utilisation du plugin, (ex. Récupère les détails des utilisateurs/périphériques ainsi que les scores et effectue des actions. Ajoutez également la formule de score normalisé Netskope si les plugins prennent en charge la récupération des scores à partir de plateformes tierces. Cette description apparaîtrait sur la carte de configuration du plugin. (Obligatoire) Veuillez consulter ce fichier(https://github.com/netskopeoss/ta_cloud_exchange_plugins/blob/main/crowdstrike_ztre/manifest.json) Référence pour la description.
- ID : (chaîne) ID du paquet de plugins. Assurez-vous qu'il est unique pour tous les plugins installés dans le Cloud Exchange. L'ID doit correspondre au nom du répertoire du paquet de plugins. (Obligatoire)
- version : (string) Version du plugin. Utilisation d'un MAJOR.MINOR.PATCH (ex. 1.0.1) est encouragé, bien qu'il n'y ait pas de restrictions. (Obligatoire)
- minimum_version : (string) Version minimale de Cloud Exchange pour que ce plugin soit exécuté. 5.1.0 pour les plugins d'échange de risques. (Obligatoire)
- module: (string) CE Module name for the plugin, like CRE, CTO, CTE, CLS. (Required)
- configuration : (array) Array of JSON objects that contains information about all the parameters required by the plugin - their name, type, id, etc. Les paramètres communs des objets JSON imbriqués sont expliqués ci-dessous.
- label : Nom du paramètre. Ceci sera affiché sur la page de configuration du plugin. (Obligatoire)
- key : Clé unique du paramètre, qui sera utilisée comme clé dans l'objet dict python où la configuration du plugin est utilisée. (Obligatoire)
- type: Value type of the parameter. Allowed values are text, password, number, choice, and multichoice. (Required) Refer to the Plugin Configuration Parameter Types below for more details.
- default: The default value for this parameter. This value will appear on the plugin configuration page on the Risk Exchange UI. Supported data-types are text, number, and list (for multichoice type). (Required)
- obligatoire : Booléen qui indique si ce paramètre est obligatoire ou non. Si un paramètre est obligatoire, l'interface utilisateur de Risk Exchange ne vous permettra pas de lui attribuer une valeur vide. Les valeurs autorisées sont true et false. (Obligatoire)
- description : Description du paramètre au niveau du texte d'aide qui peut donner plus de détails sur le paramètre et la valeur attendue. Cette chaîne apparaîtra sur la page de configuration du plugin en tant que texte d'aide. (Obligatoire)
- choix : Une liste d'objets JSON contenant la clé et la valeur comme clés JSON. Ce paramètre n'est pris en charge que par les types : choix et multichoix.
Plugin Configuration Parameter types
Assurez-vous que tous les paramètres de configuration du plugin sont listés dans la section de configuration de manifest.json pour le plugin.
Password Parameter
Utilisez ce paramètre pour stocker les secrets/mots de passe pour l'authentification avec les points d'extrémité de l'API. Les paramètres dont le mot de passe est un type auront une zone de texte de mot de passe dans la page de configuration du plugin et seront obscurcis et cryptés par la plateforme.
Exemple de JSON
"configuration": [
{
"label": "API Token",
"key": "api_token",
"type": "password",
"mandatory": true,
"default": "",
"description": "API Token for the platform."
},
]
Vue de la configuration du plugin

Text Parameter
Utilisez ce paramètre pour stocker des informations sous forme de chaîne, telles que base-url, nom d'utilisateur, etc. Ce paramètre aura une entrée de texte normale sur la page de configuration du plugin.
Exemple de JSON
"configuration": [
{
"label": "Tenant Name",
"key": "tenant_name",
"type": "text",
"mandatory": true,
"default": "",
"description": "Tenant Name of the platform."
},
]
Vue de la configuration du plugin

Number Parameter
Utilisez ce paramètre pour stocker des valeurs numériques ou flottantes. Ce paramètre aura un champ de saisie numérique sur la page de configuration du plugin. (La liste des hachages se trouve dans le module Threat Exchange).
"configuration": [
{
"label": "Maximum File hash list size in MB.",
"key": "max_size",
"type": "number"
"mandatory": true,
"default": 12,
"description": "File hash list size in MB."
},
]
Vue de la configuration du plugin

Choice Parameter
Utilisez ce paramètre pour stocker les valeurs des paramètres d'énumération. Ce paramètre aura une liste déroulante sur la page de configuration du plugin.
Exemple de JSON
"configuration": [
{
"label": "Base URL",
"key": "base_url",
"type": "choice",
"choices": [
{
"key": "Commercial cloud (api.crowdstrike.com)",
"value": "https://api.crowdstrike.com"
},
{
"key": "US 2 (api.us-2.crowdstrike.com)",
"value": "https://api.us-2.crowdstrike.com"
},
{
"key": "Falcon on GovCloud (api.laggar.gcw.crowdstrike.com)",
"value": "https://api.laggar.gcw.crowdstrike.com"
},
{
"key": "EU cloud (api.eu-1.crowdstrike.com)",
"value": "https://api.eu-1.crowdstrike.com"
}
],
"default": "https://api.crowdstrike.com",
"mandatory": true,
"description": "API Base URL."
},
]
Vue de configuration du plugin

Après avoir sélectionné l'entrée :

Multichoice Parameter
Utilisez ce paramètre pour stocker des valeurs à choix multiples. Ce paramètre aura une liste déroulante sur la page de configuration du plugin avec la possibilité de sélectionner plusieurs valeurs. (La gravité est indiquée dans le module Threat Exchange).
Exemple de JSON
"configuration": [
{
"label": "Severity",
"key": "severity",
"type": "multichoice",
"choices": [
{
"key": "Unknown",
"value": "unknown"
},
{
"key": "Low",
"value": "low"
},
{
"key": "Medium",
"value": "medium"
},
{
"key": "High",
"value": "high"
},
{
"key": "Critical",
"value": "critical"
}
],
"default": [
"critical",
"high",
"medium",
"low",
"unknown"
],
"mandatory": false,
"description": "Only indicators with matching severity will be saved."
}
]
Vue de la configuration du plugin

Toggle Parameter
Ce paramètre stocke une valeur booléenne : « activé » renvoie « Vrai », « désactivé » renvoie « Faux ».
Utiliser le proxy système (« proxy ») : Use system proxy configured in Settings. (Default: False)
Vue de configuration du plugin

Note
Ce paramètre est fourni par Core, et il n'est pas autorisé à être ajouté à partir du fichier manifest.json du plugin.
Main.py
Ce fichier python contient l'implémentation de base du plugin.
Importations standard
from netskope.integrations.crev2.plugin_base import ( PluginBase, ValidationResult, Entity, EntityField, EntityFieldType ) from netskope.integrations.crev2.models import ( Action, ActionWithoutParams )
Variables de PluginBase
PluginBase donne accès à des variables qui peuvent être utilisées pendant le cycle de vie du plugin. Voici la liste des variables.
| Nom de la variable | Usage | Description |
|---|---|---|
| self.logger | self.logger.error("Message") self.logger.warn("Message") self.logger.info("Message") | Poignée du logger fournie par le noyau. Utilisez cet objet pour enregistrer les événements importants. Les journaux seront visibles dans les journaux d'audit de Cloud Exchange. Reportez-vous à la documentation de l'objet Logger. |
| self.configuration | self.configuration.get(<nom-clé-attribut>)<attribute-key-name> | Représentation JSON de l'objet de configuration de l'instance de plugin. Utilisez-le pour accéder aux attributs de configuration tels que les informations d'authentification, les détails du serveur, etc. Utilisez le nom de la clé de l'attribut mentionné dans manifest.json. |
| self.last_run_at | Si self.last_run_at : self.last_run_at.timestamp() Utilisez ce format pour convertir la dernière durée d'exécution en format d'époque. | Fournit l'horodatage de la dernière exécution réussie de la méthode d'extraction du plugin. Le noyau de Cloud Exchange maintient l'heure du point de contrôle après chaque exécution réussie de pull(). Pour la première exécution, la valeur sera None. Le type de données de l'objet est datetime. |
| self.notifier (optional) | self.notifier.info(“message”) self.notifier.warn(“message”) self.notifier.error(“message”) | Cet objet fournit une poignée pour le notificateur du noyau de Risk Exchange. Utilisez cet objet pour envoyer une notification à la plateforme. Les notifications seront visibles dans l'interface utilisateur de l'échange de risques. Veillez à ce que le message contienne des informations résumées pour que l'utilisateur puisse les lire et prendre les mesures nécessaires. Notification utilisée dans le plugin Netskope si la méthode push() dépasse la limite de 8MB du produit. |
| self.proxy | requests.get(url=url, proxies=self.proxy) | Gestion des paramètres proxy du système s'ils sont configurés, sinon {}. |
| self.ssl_validation | requests.get(url=url, verify=self.ssl_validation) | Valeur booléenne indiquant si la validation ssl est appliquée pour les appels à l'API REST. |
Classe de plugin
- La classe Plugin doit être héritée de la classe PluginBase. La classe PluginBase est définie dans Netskope.integrations.crev2.plugin_base.
- Make sure the Plugin class provides implementation for the get_entities, fetch_records, update_records, validate, get_actions, get_action_params, validate_action, and execute_action.
- La classe de plugin contiendra tous les paramètres nécessaires pour établir la connexion et l'authentification avec l'API tierce.
- La pagination doit toujours être prise en compte lors du développement d'une fonctionnalité dans un plugin.
"""Sample plugin implementation.
This is a sample implementation of the base PluginBase class. Which explains the concrete implementation of the base class.
"""
from netskope.integrations.crev2.models import (
Action,
ActionWithoutParams
)
from netskope.integrations.crev2.plugin_base import (
PluginBase,
ValidationResult,
Entity,
EntityField,
EntityFieldType
)
from .utils.helper import SamplePluginException, SamplePluginHelper
class SamplePlugin(PluginBase):
"""SamplePlugin class having concrete implementation for fetching information and performing actions.
This class is responsible for implementing pull, perform actions and validate methods with proper return types, so that it's lifecycle execution can be scheduled by the CRE core engine.
"""
def __init__(
self,
name,
*args,
**kwargs,
):
"""Init method.
Args:
name (str): Configuration name.
"""
super().__init__(
name,
*args,
**kwargs,
)
self.plugin_name, self.plugin_version = self._get_plugin_info()
self.log_prefix = f"{MODULE_NAME} {self.plugin_name} [{name}]"
self.sample_plugin_helper = SamplePluginHelper(
logger=self.logger,
log_prefix=self.log_prefix,
plugin_name=self.plugin_name,
plugin_version=self.plugin_version,
configuration=self.configuration,
)
def _get_plugin_info(self) -> tuple:
"""Get plugin name and version from metadata.
Returns:
tuple: Tuple of plugin's name and version fetched from metadata.
"""
try:
metadata_json = SamplePlugin.metadata
plugin_name = metadata_json.get("name", PLATFORM_NAME)
plugin_version = metadata_json.get("version", PLUGIN_VERSION)
return (plugin_name, plugin_version)
except Exception as exp:
self.logger.error(
message=(
"{} {}: Error occurred while"
" getting plugin details. Error: {}".format(
MODULE_NAME, PLATFORM_NAME, exp
)
),
details=traceback.format_exc(),
)
return (PLATFORM_NAME, PLUGIN_VERSION)
def validate()
Arguments :
configuration (dict): Objet dict contenant tous les paramètres de configuration du plugin.
Retourne :
ValidationResult: Objet ValidationResult avec le drapeau de réussite et le message.
Il s'agit d'une méthode abstraite de la classe PluginBase.
- Cette méthode valide la configuration du plugin et les paramètres d'authentification transmis lors de la création d'une configuration de plugin.
- Cette méthode n'est appelée que lorsqu'une configuration New est créée ou mise à jour.
- Valide que tous les paramètres obligatoires sont transmis avec le type de données approprié.
- Validez les paramètres d'authentification et le point de terminaison de l'API pour garantir le bon déroulement du cycle de vie du plugin.
- Retourne l'objet ValidationResult (voir le modèle de données ValidationResult ) avec un drapeau de réussite indiquant le succès ou l'échec de la validation et le message de validation contenant la raison de l'échec de la validation.
def validate(self, configuration):
"""Validate the Plugin configuration parameters.
Validation for all the parameters mentioned in the manifest.json
for the existence and data type. Method returns the
netskope.integrations.crev2.plugin_base.ValidationResult object
with success = True in the case of successful validation and
success = False and an error message in the case of failure.
Args:
configuration (dict): Dict object having all the Plugin configuration parameters.
Returns:
netskope.integrations.crev2.plugin_base.ValidationResult:
ValidationResult object with success flag and message.
"""
# Validate Base URL
instance_url = configuration.get("base_url", "").strip().strip("/")
if not instance_url:
err_msg = "Base URL is required configuration parameter."
self.logger.error(
f"{self.log_prefix}: Validation error occurred. {err_msg}"
)
return ValidationResult(success=False, message=err_msg)
elif not (isinstance(instance_url, str) and self._validate_url(instance_url)):
err_msg = "Invalid Base URL provided in configuration parameters."
self.logger.error(
f"{self.log_prefix}: Validation error occurred. {err_msg}"
)
return ValidationResult(success=False, message=err_msg)
# Validate client_id
client_id = configuration.get("client_id", "").strip()
if not client_id:
err_msg = "Client ID is a required field."
self.logger.error(f"{{self.log_prefix}}: Validation error occurred. Error: {err_msg}")
return ValidationResult(success=False, message=err_msg,)
elif not isinstance(client_id, str):
err_msg = "Invalid Client ID value provided."
self.logger.error(f"{{self.log_prefix}}: {err_msg}")
return ValidationResult(success=False, message=err_msg)
return ValidationResult(
success=True,
message="Validation successful for Sample Plugin."
)
def _validate_url(self, url: str) -> bool:
"""Validate the given URL.
Args:
url (str): URL to validate.
Returns:
bool: True if URL is valid else False.
"""
parsed_url = urlparse(url.strip())
return parsed_url.scheme and parsed_url.netloc
def get_entities()
Arguments :
Aucun
Retourne :
List[Entity]: Liste des classes d'entités avec les entités, les champs et les types pris en charge par les plugins.
Il s'agit d'une méthode abstraite de la classe PluginBase.
- Cette méthode doit renvoyer une liste de classes d'entités avec les entités, les champs et les types pris en charge par les plugins.
- Si le plugin ne prend en charge aucune entité, il renvoie une liste vide.
def get_entities(self) -> list[Entity]:
"""Get available entities."""
return [
Entity(
name="Users",
fields=[
EntityField(name="User Email", type=EntityFieldType.STRING, required=True),
EntityField(name="Netskope Normalized Score", type=EntityFieldType.NUMBER),
],
),
Entity(
name="Applications",
fields=[
EntityField(name="Application ID", type=EntityFieldType.STRING, required=True),
EntityField(name="Tags", type=EntityFieldType.LIST),
],
)
]
def fetch_records()
Arguments :
entité (str): Entité à rechercher.
Retours :
Liste: Liste des enregistrements à stocker sur la plate-forme.
Il s'agit d'une méthode abstraite de la classe PluginBase.
- Cette méthode met en œuvre la logique de récupération des enregistrements () à partir des points d'extrémité de l'API tierce. Cette méthode est invoquée périodiquement.
- Utilisez la configuration du proxy transmise par la plate-forme CE en invoquant self.proxy. Il renvoie l'objet dict python qui peut être utilisé directement avec le module de requêtes.
- Tous les paramètres de configuration pour l'authentification de l'API sont transmis en tant que python dict les reçoit en invoquant self.configuration.
- Tous les journaux peuvent être enregistrés par l'objet self.logger avec le niveau de journal approprié (info, warn, error). Cet objet enregistre les journaux dans MongoDB et est accessible via des appels API.
- Use self.ssl_validation bool to enable/disable validation of the SSL server certificate.
- Traiter le tirage en fonction de l'entité si le plugin prend en charge plusieurs entités.
- Cette méthode permet d'obtenir des informations sur les risques si des détails sur les risques sont disponibles dans la même réponse de l'API.
- Retourne la liste des dictionnaires qui contiennent les données reçues du point de terminaison de l'API, mappées avec les champs de l'entité du plugin.
- En cas d'échec, levez une erreur ou une exception du type approprié avec le message adéquat.
- Veillez à utiliser l'API helper pour toutes les demandes d'API. Référer
- Veillez à gérer le cas où la taille maximale de la charge utile prise en charge par le point de terminaison de l'API est dépassée. Il peut y avoir plusieurs façons de traiter ce cas.
- Si le point de terminaison de l'API prend en charge plusieurs demandes avec une taille de charge utile fixe, envoyez les données par morceaux. Ce concept est appelé pagination.
def fetch_records(self, entity: str):
"""Fetches entity records from a third party API.
Implement the logic of fetching data based on entity from third party apis
and return the list of dictionary objects containing plugin supported entity
fields and their values on a successful fetch otherwise raises an exception.
Returns:
List[{}]: List of dictionary objects of entity data
fetched from the third party platform.
"""
# Load all the configured plugin parameters as python dict objects.
# Use the key name provided in the manifest.json file for the
# configuration parameters to get the value of that particular parameter.
# See api_helper for API requests method to check
# how to use proxy dict and ssl_validation flag.
# check entity type and fetch records accordingly
# if plugin supports multiple entity types
headers = {
"Authorization": "<>",
}
if entity == "Users":
resp_json = self.sample_plugin_helper.api_helper(
url="www.example.com/users",
method="GET",
headers=headers,
proxies=self.proxy,
verify=self.ssl_validation,
logger_msg=f"fetching {entity_name} from the platform",
)
records = resp_json.get("value", [])
elif entity == "Applications":
resp_json = self.sample_plugin_helper.api_helper(
url="www.example.com/applications",
method="GET",
headers=headers,
proxies=self.proxy,
verify=self.ssl_validation,
logger_msg=f"fetching {entity_name} from the platform",
)
records = resp_json.get("value", [])
# Get the logger object for logging purposes. This logger object logs
# all the logs to mongodb under the core database logs collection.
# Log timestamp is automatically recorded by the logger library.
# Supported logging levels are info, warn and error.
self.logger.info(f"{self.log_prefix}: Successfully fetched {
len(records)}{entity} from the platform.")
return records
def update_records()
Arguments:
entity (str): Entity to be updated.
records (list): Records to be updated.
Returns:
List: List of updated records.
This is an abstract method of PluginBase Class.
- Cette méthode met en œuvre la logique de mise à jour des enregistrements stockés à partir des points d'extrémité de l'API tierce. Cette méthode est invoquée périodiquement.
- Utilisé pour mettre à jour les valeurs des champs de l'API tierce des enregistrements récupérés. Tous les champs doivent être mis à jour dans cette méthode, à l'exception du champ unique et obligatoire pour l'entité.
- Calculer le score normalisé de Netskope dans la méthode update_records et ajouter un champ dans le dictionnaire des enregistrements si les informations sur les risques sont récupérées dans la méthode fetch_record. Veillez à ce que les scores correspondent à un nombre entier compris entre 0 et 1000.
- Veillez à ce qu'aucun champ ne renvoie de valeur nulle.
- Utilisez la configuration du proxy transmise par la plate-forme CE en invoquant self.proxy. Il renvoie l'objet dict python qui peut être utilisé directement avec le module de requêtes.
- Tous les paramètres de configuration pour l'authentification de l'API sont transmis en tant que python dict les reçoit en invoquant self.configuration.
- Tous les journaux peuvent être enregistrés par l'objet self.logger avec le niveau de journal approprié (info, warn, error). Cet objet enregistre les journaux dans MongoDB et est accessible via des appels API.
- Use self.ssl_validation bool to enable/disable validation of the SSL server certificate.
- Renvoyez la liste des scores contenant les données reçues du point de terminaison de l'API, qui seront stockées dans l'objet Record. Note : La liste contiendra des objets de type Record.
- En cas d'échec, levez une erreur ou une exception du type approprié avec le message adéquat.
- Veillez à gérer le cas où la taille maximale de la charge utile prise en charge par le point de terminaison de l'API est dépassée. Il peut y avoir plusieurs façons de traiter ce cas.
- Si le point de terminaison de l'API prend en charge plusieurs demandes avec une taille de charge utile fixe, envoyez les données par morceaux. Ce concept est également appelé pagination.
- Avant de mettre à jour les enregistrements, vérifiez d'abord que le champ unique/obligatoire de l'entité plugin est disponible dans les enregistrements récupérés et créez une liste. Exemple :
if entity == "Users":
user_email = []
for record in records:
if record.get("User Email"):
user_email.append(record.get("User Email"))
Note:
Assurez-vous de calculer le score normalisé dans la méthode update_record et non dans fetch_records lorsque les détails du risque sont disponibles.
def update_records(self, entity: str, records: list[dict]):
"""Update entity records from a third party API.
Implement the logic of fetching updated data based on entity
from third party apis and return the list of updated values
for all fetched records dictionary containing plugin supported entity
fields and Netskope Normalize Score if risk information available on
a successful fetch otherwise raises an exception.
Returns:
List[{}]: List of records with updated values of entity data
fetched from the third party platform.
"""
# Load all the configured plugin parameters as python dict objects.
# Use the key name provided in the manifest.json file for the
# configuration parameters to get the value of that particular parameter.
# See api_helper for API requests method to check
# how to use proxy dict and ssl_validation flag.
# check entity type and fetch records accordingly
# if plugin supports multiple entity types
headers = {
"Authorization": "<>",
}
updated_records = {}
if entity == "Users":
user_email = []
for record in records:
if record.get("User Email"):
user_email.append(record.get("User Email"))
resp_json = self.sample_plugin_helper.api_helper(
url="www.example.com/users",
method="GET",
headers=headers,
proxies=self.proxy,
verify=self.ssl_validation,
logger_msg=f"updating {entity_name} from the platform",
)
updated_records = resp_json.get("value", [])
for record in records:
user_email = record.get("User Email")
if user_email and user_email in updated_records:
record.update(updated_records[user_email])
count += 1
elif entity == "Applications":
app_id = []
for record in records:
if app_id.get("Application ID"):
app_id.append(record.get("Application ID"))
resp_json = self.sample_plugin_helper.api_helper(
url="www.example.com/applications",
method="GET",
headers=headers,
proxies=self.proxy,
verify=self.ssl_validation,
logger_msg=f"updating {entity_name} from the platform",
)
updated_records = resp_json.get("value", [])
for record in records:
app_id = record.get("Application ID")
if app_id and app_id in updated_records:
record.update(updated_records[app_id])
count += 1
self.logger.info(
f"{self.log_prefix}: Successfully updated {count} "
f"{entity} from the platform."
)
return records
def get_actions()
Arguments :
Aucun
Retourne :
Liste[ActionWithoutParams]: Liste d'ActionWithoutParams dont l'étiquette et la valeur sont définies.
Il s'agit d'une méthode abstraite de la classe PluginBase.
- Cette méthode doit renvoyer une liste de toutes les actions prises en charge.
- Ajoutez toutes les actions prises en charge dans la classe ActionWithoutParams et renvoyez une liste d'objets de la classe ActionWithoutParams.
- If the plugin does not support any action, No action should be implemented.
def get_actions(self):
"""Get available actions.
Returns:
List[ActionWithoutParams]: List of ActionWithoutParams objects
that are supported by the plugin.
"""
return [
ActionWithoutParams(label="Add to group", value="add"),
ActionWithoutParams(label="Remove from group", value="remove"),
ActionWithoutParams(label="No action", value="generate"),
]
def get_action_params() :
Arguments:
action (Action): The type of action
Returns:
List: Returns a list of selected action parameters.
This is an abstract method of PluginBase Class.
- Cette méthode doit renvoyer la liste des champs à afficher dans l'interface utilisateur lorsqu'une cible est sélectionnée dans la liste déroulante.
- Cette méthode doit être appelée après que l'utilisateur a sélectionné l'une des actions.
- Si l'action sélectionnée nécessite des paramètres, elle renvoie une liste de dictionnaires (où chaque dictionnaire est une entrée configurable), sinon elle renvoie une liste vide. Vérifiez les types de paramètres de la configuration du plugin pour voir comment les champs sont définis.
- Si le plugin ne supporte aucune action, aucune action ne doit être implémentée et renvoyer une liste vide dans la méthode get_action_fields.
def get_action_params(self, action: Action):
"""Get fields required for an action.
Args:
action (Action): Action object which is selected as Target.
Return:
List[Dict]: List of configurable fields based on selected action.
"""
if action.value in ["generate"]:
return []
if action.value == "add":
return [
{
"label": "Email",
"key": "email",
"type": "text",
"default": "",
"mandatory": True,
"description": "Email ID of the user to perform the action on.",
},
{
"label": "Group Name",
"key": "group_name",
"type": "choice",
"choice": [{key: "name", value: "id"}, ...],
"default": "",
"mandatory": True,
"description": "Name of group."
}
]
else:
return []
def validate_action() :
Arguments :
action (Action) : Le type d'action avec les paramètres d'action
Retourne :
ValidationResult: Objet ValidationResult avec le drapeau de réussite et le message.
Il s'agit d'une méthode abstraite de la classe PluginBase.
- Cette méthode valide les paramètres d'action transmis lors de la création d'une configuration d'action.
- Cette méthode ne sera appelée que lorsqu'une configuration d'action New est créée ou mise à jour.
- Des validations séparées doivent être effectuées pour la validation des champs vides et la validation des contrôles de type dans la méthode de validation de la configuration du plugin.
- Valide que tous les paramètres obligatoires sont transmis avec le type de données approprié.
- Lors de la validation, utilisez strip() pour les paramètres d'action tels que l'URL, le nom d'utilisateur ou le type de texte, etc. à l'exception des champs Jetons API et Mot de passe.
- Retourne l'objet ValidationResult (voir le modèle de données ValidationResult ) avec un drapeau de réussite indiquant le succès ou l'échec de la validation et le message de validation contenant la raison de l'échec de la validation.
- Si le plugin n'a pas d'actions, il renvoie l'objet ValidationResult avec un drapeau de réussite, sinon il vérifie les validations.
def validate_action(self, action: Action):
"""Validate Action Parameters.
Args:
action (Action): Action object having all the configurable parameters.
Return:
netskope.integrations.crev2.plugin_base.ValidationResult:
ValidationResult object with success flag and message.
"""
action_params = action.parameters
if action.value not in ["generate", "add", "remove"]:
err_msg = "Unsupported action provided."
self.logger.error(f"{self.log_prefix}: {err_msg}")
return ValidationResult(
success=False, message=err_msg
)
if action.value in ["generate"]:
return ValidationResult(
success=True, message="Validation successful."
)
email = action_params.get("email", "")
if not email:
err_msg = "Email is a required action parameter."
self.logger.error(f"{self.log_prefix}: {err_msg}")
return ValidationResult(success=False, message=err_msg)
elif not isinstance(email, str):
err_msg = "Invalid Email provided in action parameters."
self.logger.error(f"{self.log_prefix}: {err_msg}")
return ValidationResult(success=False, message=err_msg)
if "$" in email:
log_msg = (
"Email contains the Source Field"
" hence validation for this field will be performed"
" while executing the action."
)
self.logger.info(f"{self.log_prefix}: {log_msg}")
if action.value == "add":
if action_params.get("group_name") is None:
err_msg = "Group Name can not be an empty field."
self.logger.error(f"{self.log_prefix}: {err_msg}")
return ValidationResult(success=False, message=err_msg)
self.logger.debug(f"{self.log_prefix}: Validation successful.")
return ValidationResult(
success=True, message="Validation successful."
)
def execute_action() :
Arguments :
action (Action) : L'action qui doit être exécutée avec les paramètres de l'action.
Retourne :
Aucun
Il s'agit d'une méthode abstraite de la classe PluginBase.
- Cette méthode met en œuvre la logique d'exécution de l'action.
- Assurez-vous que la méthode complète une action telle que l'ajout au groupe ou la suppression du groupe.
- Il est préférable d'utiliser une méthode d'aide pour exécuter les appels d'API de tiers.
Note:
Pour les actions « ajouter un utilisateur au groupe » et « supprimer un utilisateur du groupe », vérifiez d'abord si l'utilisateur existe sur la plateforme, si celle-ci prend en charge une API pour la validation des utilisateurs dans la méthode execute_action.
def execute_action(self, action: Action):
"""Execute action on the record.
Calls _add_to_group helper methods in the case of add action.
Return when action is generate.
Args:
action (Action): Action object having all the configurable parameters.
"""
action_label = action.label
action_params = action.parameters
if action.value == "generate":
self.logger.debug(
f'{self.log_prefix}: Successfully executed "{action_label}" action.'
" Note: No processing will be done from plugin for "
f'the "{action_label}" action.'
)
return
email = action_params.get("email", "").strip()
if not email:
err_msg = (
"Email not found in the action parameters. "
f"Hence, skipping execution of '{action_label}' action."
)
self.logger.error(f"{self.log_prefix}: {err_msg}")
return
elif not isinstance(email, str):
err_msg = (
"Invalid Email found in the action parameters. "
f"Hence, skipping execution of '{action_label}' action."
)
self.logger.error(f"{self.log_prefix}: {err_msg}")
return
if action.value == "add":
group_id = action_params.get("group")
email = action_params.get("email", "").strip()
self.logger.info(f"{log_prefix}: Adding user with email '{email}' to group.")
self._add_to_group(email, group_id)
self.logger.info(f"{log_prefix}: Successfully added user with email '{email}' to group.")
Modèles de données
Cette section énumère les modèles de données et leurs propriétés.
Entity
- Cette classe représente l'entité plugin prise en charge.
- En général, il y a des utilisateurs, des périphériques ou des applications, mais ils peuvent être différents en fonction des besoins.
- Il sera utilisé pour remplir les entités et les champs pris en charge par le plugin dans la page de configuration du plugin Risk Exchange.
Propriétés du modèle de données
| Nom | Type | Description |
|---|---|---|
| name | string | Étiquette de l'entité affichée dans l'interface utilisateur |
| champs | liste[EntityField] | Contient la liste des champs d'entité pris en charge |
from netskope.integrations.crev2.plugin_base import (
Entity,
EntityField,
EntityFieldType
)
def get_entities(self) -> list[Entity]:
"""Get available entities."""
return [
Entity(
name="Users",
fields=[
EntityField(name="User Email", type=EntityFieldType.STRING, required=True),
EntityField(name="Netskope Normalized Score", type=EntityFieldType.NUMBER),
],
)
]
EntityField
- Cette classe représente le champ de l'entité et le type de champ (chaîne de caractères, nombre, etc.).
- Il sera utilisé dans la méthode get_entities.
- Il est utilisé pour afficher les champs pris en charge sur la page de configuration du plugin.
Propriétés du modèle de données
| Nom | Type | Description |
|---|---|---|
| name | string | Libellé du champ affiché dans l'interface utilisateur |
| type | EntityFieldType | Classe Eum pour définir le type de champ |
| required | bool | Le champ doit être mappé dans la configuration du plugin s'il est vrai. |
EntityField(name="User Email", type=EntityFieldType.STRING, required=True)
EntityFieldType
- Cette classe représente le type de champ de l'entité, comme une chaîne de caractères, un nombre, etc.
- Il sera utilisé dans la méthode get_entities pour définir le type de champ.
Propriétés du modèle de données (Enum)
| Nom |
|---|
| STRINGNUMBER LIST DATETIME CALCULATED IPV4 IPV6 REFERENCE VALUE_MAP RANGE_MAP |
EntityField(name="Netskope Normalized Score", type=EntityFieldType.NUMBER)
Action
- Cette classe représente l'action que l'on souhaite effectuer.
- Généralement, il y a des actions de génération, d'ajout, de suppression, ou plus, à mettre en œuvre.
- Il sera utilisé dans validate_action, execute_actions et get_action_params.
Propriétés du modèle de données
| Nom | Type | Description |
|---|---|---|
| label | string | Libellé de l'action affiché dans l'interface utilisateur |
| value | string | Clé d'accès |
| parameters | Dict | Il contiendra tous les champs d'action requis pour les plugins respectifs. |
| generateAlerts | Bool | Génère des alertes au CTO lorsqu'elles sont vraies |
| performLater | Bool | Synchroniser les actions lorsque c'est vrai |
| requireApproval | Bool | Les actions doivent être approuvées par la page Action lorsqu'elles sont vraies |
ActionWithoutParams
- Cette classe représente l'action que l'on souhaite effectuer sans utiliser de paramètres.
- Il sera utilisé dans la méthode get_actions.
- Il est utilisé pour montrer à l'utilisateur les actions prises en charge.
Propriétés du modèle de données
| Nom | Type | Description |
|---|---|---|
| label | string | Libellé de l'action affiché dans l'interface utilisateur |
| value | string | Clé de l'action pour y accéder dans le code du plugin |
from netskope.integrations.crev2.models import ActionWithoutParams
return [
ActionWithoutParams(label="Add to group", value="add"),
ActionWithoutParams(label="Remove from group", value="remove"),
ActionWithoutParams(label="No actions", value="generate"),
]
ValidationResult
- Cette classe contient le résultat du processus de validation des paramètres de configuration du plugin transmis à l'objet Plugin lors de la création d'une configuration New pour le plugin.
- Assurez-vous que tous les paramètres transmis à la méthode validate sont validés par rapport au type de données et à la valeur.
- La méthode Validate renvoie l'objet de cette classe avec un drapeau de réussite indiquant le résultat de l'opération de validation et un champ de message contenant le message d'erreur approprié en cas d'échec de la validation. (En cas de succès, un simple message de succès avec l'indicateur de succès True doit être renvoyé).
- Notez que l'action Validate renvoie également l'objet de cette classe.
Propriétés du modèle de données
| Nom | Type | Description |
|---|---|---|
| success | bool | indique le résultat de l'opération de validation, qu'elle ait réussi ou échoué. |
| message | string | Le champ message indique l'erreur en cas d'échec. En cas de succès, il peut s'agir d'un simple message de réussite. |
from netskope.integrations.crev2.plugin_base import ValidationResult
ValidationResult(
success=True,
message="Validation Successful for Sample plugin"
)
Enregistrement
Risk Exchange fournit un handle de l'objet logger pour la journalisation.
- Évitez les instructions d'impression dans le code.
- Cet objet s'enregistre dans la base de données centrale de Cloud Exchange avec le champ horodatage. Les niveaux de journalisation pris en charge sont info, debug, warn et error.
- Assurez-vous que le secret d'authentification de l'API ou toute autre information sensible n'est pas exposé dans les messages du journal.
- Veillez à mettre en place un mécanisme de journalisation approprié avec l'objet logger transmis par la plateforme Cloud Exchange.
- Assurez-vous que la journalisation est suffisante pour aider l'équipe d'exploitation à résoudre les problèmes.
- Assurez-vous que les données sensibles ne sont pas enregistrées ou divulguées dans la notification ou les journaux.
- Référez-vous au format de préfixe du journal pour tous les enregistreurs.
- Les messages du journal doivent commencer par "<module> <plugin_name> [nom_de_la_configuration]:". Exemple : "CRE CrowdStrike [CrowdStrike Nom de la configuration] : <log_message>". (Il s'agit d'une suggestion, nous pouvons éviter les noms de configuration). [logger.info(":")]<module> <plugin_name><message>
- Veillez à ajouter des détails dans les journaux d'erreurs dans la mesure du possible.
- Il est recommandé d'ajouter la trace de l'erreur dans les détails des journaux d'erreurs.
import traceback
self.logger.error(
message=f"{self.log_prefix}: Error log-message goes here.",
details=str(traceback.format_exc())
)
self.logger.warn(
f"{self.log_prefix}: Warning log-message goes here."
)
self.logger.info(
f"{self.log_prefix}: Info log-message goes here."
)
self.logger.debug(
f"{self.log_prefix}: Debug log-message goes here."
)
Notifications (facultatif)
Risk Exchange fournit un ensemble d'objets de notification qui peuvent être utilisés pour générer des notifications dans l'interface utilisateur de Risk Exchange.
- Cet objet est transmis de la plateforme Cloud Exchange à l'objet Plugin. Chaque intégration de plugin peut utiliser cet objet chaque fois qu'il y a un cas d'échec qui doit être notifié à l'utilisateur immédiatement. L'horodatage de la notification est géré par la plateforme Cloud Exchange.
- Cet objet déclenche une notification dans l'interface utilisateur avec un code couleur correspondant à la gravité de l'échec. Les niveaux de notification pris en charge sont info, avertissement et erreur.
- Assurez-vous que le secret d'authentification de l'API ou toute autre information sensible n'est pas exposé dans les messages de notification.
- Utilisez un objet notificateur pour déclencher une notification en cas d'échec ou de situation critique (comme la limitation du débit ou le dépassement de la taille de la charge utile) afin d'informer l'utilisateur de l'état du plugin.
self.notifier.info(
f"{log_prefix}: Info notification-message goes here."
)
self.notifier.error(
f"{log_prefix}: Error notification-message goes here."
)
self.notifier.warn(
f"{log_prefix}: Warning notification-message goes here."
)
Consultez https://github.com/netskopeoss/ta_cloud_exchange_plugins/tree/main/microsoft_entra_id_ztre plugin pour Risk Exchange : développement de plugins, méthodes de développement, meilleures pratiques et gestion de plusieurs entités.
Testing
Linting
Dans le cadre du processus de construction, nous exécutons quelques linters pour détecter les erreurs de programmation courantes, les erreurs stylistiques et les éventuels problèmes de sécurité.
Flake8
Il s'agit d'un linter de base. Il peut être exécuté sans que toutes les dépendances soient disponibles et il détectera les erreurs les plus courantes. Vous pouvez également utiliser ce linter pour appliquer le style de formatage standard python pep8. En de rares occasions, vous pouvez être amené à désactiver une erreur/un avertissement renvoyé(e) par ce lecteur. Pour ce faire, ajoutez un commentaire en ligne de ce type sur la ligne où vous souhaitez désactiver l'erreur :
# noqa: <error-id>
Par exemple :
example = lambda: 'example' # noqa: E731
Lorsque vous ajoutez un commentaire en ligne, incluez toujours le code d’erreur que vous désactivez. Ainsi, s’il y a d’autres erreurs sur la même ligne, elles seront signalées.
Pour plus d’informations : https://flake8.pycqa.org/en/latest/user/violations.html#in-line-ignoring-errors
Le contrôle docstring de type PEP8 est également activé avec le linter flake8. Assurez-vous que chaque fonction/module a un docstring approprié ajouté.
Déploiement de plugins sur Cloud Exchange
Paqueter le plugin
Cloud Exchange attend le plugin développé au format zip ou tar.gz.
Exécutez cette commande pour compresser le paquet :
zip -r sample_plugin.zip sample_plugin
Exécutez cette commande pour générer le paquet tar.gz :
tar -zcvf sample_plugin.tar.gz sample_plugin
Add a Repo
Pour déployer votre plugin dans Cloud Exchange, ajoutez d'abord un dépôt.
- Connectez-vous à Cloud Exchange.
- Va sur Settings > Plugin Repository.

- Cliquez Configure New Repository.

- Saisissez un nom de référentiel, une URL de référentiel, un nom d'utilisateur et un jeton d'accès personnel.
- Cliquez sur Save.

- Allez sur Settings > Plugins.
- Select le nom de votre référentiel dans la liste déroulante Référentiel, et passez à la section suivante.

Téléchargez le fichier zip ou tar.gz du plugin
- Si vous continuez depuis la dernière section, passez à l’étape 2. Sinon, allez à Plugin Repositories.

- Cliquez sur l’icône Upload Plugin de votre dépôt.

- Cliquez Browse.

- Select le fichier zip ou tar.gz que vous avez créé.

- Cliquez sur Upload.
Deliverables
Plugin Guide
- Veillez à mettre à jour le guide du plugin à chaque version.
- Le guide du plugin doit contenir ce contenu :
- Notes de mise à jour
- Description
- Conditions préalables
- Champ d'application du plugin
- Type de données prises en charge
- Mapping
- Pull Mapping for <Entity>
- Permissions
- Détails de l'API
- Liste des API utilisées
- Matrice de performance
- Agent utilisateur
- Workflow
- Configuration sur une plateforme de plugins tiers
- Configuration sur Netskope CE
- Configuration des plugins tiers
- Business Rule Configuration
- Action Configuration
- Validation
- Dépannage
- Limites
Reportez-vous à tout guide publié sur les plugins d'échange de risques pour obtenir des exemples.
Vidéo de démonstration
Après le développement réussi du plugin Risk Exchange, créez une vidéo de démonstration et montrez le plugin de bout en bout workflow.
Note: Reportez-vous au guide du plugin Microsoft Entra ID.

