Ce guide explique comment écrire un plugin New User Risk Exchange et tirer le maximum de valeur de votre écosystème du risque en tirant parti de la fonctionnalité fournie par le module User 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.
Module d'échange de risques pour l'utilisateur
La plateforme Cloud Exchange, et son module User Risk Exchange, est dotée d'un riche ensemble 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 de partager les données de Netskope avec des outils tiers et vice versa.
Concepts de Netskope & Terminologie
- Le moteur principal : Le moteur central de Cloud Exchange gère les plugins tiers et leurs méthodes de cycle de vie. Il dispose de points d'extrémité API permettant d'interagir avec la plateforme pour effectuer diverses tâches.
- Module : Les zones de code fonctionnel qui invoquent des plugins spécifiques aux modules pour accomplir différents flux de travail. User Risk Exchange est l'un des modules de Cloud Exchange.
- Plugin : Les plugins sont des paquets Python dont la logique permet de récupérer des utilisateurs et des scores de risque auprès de systèmes Threat Intel tiers, qui seront ensuite stockés dans User Risk Exchange. Il peut également effectuer des actions.
- 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.
- Utilisateurs (alertes UBA) : Les enregistrements (d'utilisateurs) sont des objets ayant un email et leurs scores de risque sont collectés à partir de différentes plateformes et stockés 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 été vérifiées pour détecter les 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 de flake8 Lint passe avec la vérification de docstring activée. La longueur maximale d'une ligne doit être 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.
- Pour les scripts/intégrations écrits en Python, veillez à créer des tests unitaires. Reportez-vous à la section Tests unitaires.
- 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 de Cloud Exchange 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.
- 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.
- Veillez à mettre en place un mécanisme de journalisation approprié avec l'objet logger transmis par la plateforme Cloud Exchange. 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.
- Veillez à fournir un type de configuration approprié (texte, nombre, mot de passe, choix, multi-choix) aux paramètres.
- Assurez-vous d'utiliser la configuration du proxy et le drapeau de validation du certificat SSL qui est transmis par la plateforme Cloud Exchange 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 .get() méthode.
- Les champs Tokens API et Password ne doivent pas utiliser strip().
- The log messages should start with “<module> <app name> Plugin [configuration_name]: “. Example: “URE Crowdstrike Plugin [CrowdStrike Configuration Name]: <log_message>“. [This is a suggestion, we can avoid configuration name]. (logger.info(“<module> <plugin_name> Plugin: <message>”))
- Lors de l'enregistrement d'un journal d'erreurs, nous devrions, si possible, ajouter une trace de l'exception. USE : self.logger.error(error, details=traceback.format_exc())
- The Toast message should not contain the <app_name> <module> Plugin: 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.
- 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.
- Suivez les directives relatives à la structure du répertoire des plugins.
- Si la description contient un lien, il doit s'agir d'un lien hypertexte et non d'un texte en clair.
- Veillez à mettre en correspondance les différents champs reçus lors des appels API avec le modèle de données de l'utilisateur et du score afin de tirer le meilleur parti du système. 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 à cartographier le champ de score qui donne plus de contexte à l'analyste SOC. Le champ score 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.
- Utilisez un objet notificateur pour émettre une notification en cas d'échec ou de situation critique (comme la limitation du débit, le dépassement de la taille de la charge utile) afin d'informer l'utilisateur de l'état du plugin.
- Veillez à mettre en place un mécanisme de journalisation approprié avec l'objet logger transmis par la plateforme Cloud Exchange. 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.
- Assurez-vous que le nom du répertoire du plugin (sample_plugin) correspond à celui du fichier manifest.json. Champ ID.
- User Agent should be added to the headers while making any API call. Format for the User Agent: netskope-ce-<ce_version>-<module>–<plugin_name>–<plugin_version>.
- La déclaration de l'enregistreur doit suivre ce format :
- logger.info(“<module> <plugin_name> Plugin: <message>”)
É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
Notre système utilise Python3 (v3.7 et plus). Veillez à installer python3 dans votre environnement de développement. Pytest est utilisé pour exécuter des tests unitaires.
Bibliothèques Python incluses
Les bibliothèques Python suivantes sont incluses dans la plateforme Netskope Cloud Exchange.
| Nom de la bibliothèque | Version |
|---|---|
| aiofiles | 22.1.0 |
| amqp | 5.1.1 |
| 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 |
| boto3 | 1.26.51 |
| botocore | 1.29.51 |
| billiard | 3.6.4.0 |
| celery | 5.2.7 |
| cabby | 0.1.23 |
| cachetools | 5.2.1 |
| celerybeat-mongo | 0.2.0 |
| certifi | 2022.12.7 |
| 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 | 39.0.0 |
| cybox | 2.1.0.21 |
| defusedxml | 0.7.1 |
| dnspython | 2.3.0 |
| docker | 6.0.1 |
| fastapi | 0.89.1 |
| 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.51.1 |
| grpcio-status | 1.51.1 |
| gunicorn | 20.1.0 |
| h11 | 0.14.0 |
| idna | 3.4 |
| importlib-metadata | 6.0.0 |
| isodate | 0.6.1 |
| jmespath | 1.0.1 |
| jsonpath | 0.82 |
| jsonschema | 4.17.3 |
| kombu | 5.2.4 |
| libcst | 0.3.21 |
| libtaxii | 1.1.119 |
| lxml | 4.9.2 |
| mongoengine | 0.25.0 |
| more-itertools | 9.0.0 |
| MarkupSafe | 2.1.2 |
| memory-profiler | 0.61.0 |
| mixbox | 1.0.5 |
| mongoquery | 1.4.2 |
| msrest | 0.7.1 |
| multidict | 6.0.4 |
| mypy-extensions | 0.4.3 |
| netskopesdk | 0.0.25 |
| numpy | 1.23.5 |
| oauthlib | 3.2.2 |
| onelogin | 3.1.0 |
| ordered-set | 4.1.0 |
| orderedmultidict | 1.0.1 |
| overrides | 6.5.0 |
| pandas | 1.5.0 |
| packaging | 23.0 |
| passlib | 1.7.4 |
| pycparser | 2.21 |
| prompt-toolkit | 3.0.36 |
| proto-plus | 1.22.2 |
| protobuf | 4.21.12 |
| psutil | 5.9.4 |
| pydantic | 1.10.4 |
| pyasn1 | 0.4.8 |
| pyasn1-modules | 0.2.8 |
| PyJWT | 2.6.0 |
| pymongo | 4.3.3 |
| pyparsing | 3.0.9 |
| python-dateutil | 2.8.2 |
| pyrsistent | 0.19.3 |
| python-multipart | 0.0.5 |
| python3-saml | 1.15.0 |
| pytz | 2022.7.1 |
| PyYAML | 6.0 |
| requests | 2.28.2 |
| requests-oauthlib | 1.3.1 |
| rsa | 4.9 |
| six | 1.16.0 |
| starlette | 0.22.0 |
| sniffio | 1.3.0 |
| s3transfer | 0.6.0 |
| stix | 1.2.0.11 |
| taxii2-client | 2.3.0 |
| typing-inspect | 0.8.0 |
| typing-utils | 0.1.0 |
| typing_extensions | 4.4.0 |
| urllib3 | 1.26.14 |
| uvicorn | 0.20.0 |
| vine | 5.0.0 |
| wcwidth | 0.2.6 |
| weakrefmethod | 1.0.3 |
| websocket-client | 1.4.2 |
| Werkzeug | 2.2.2 |
| xmlsec | 1.3.11 |
| zipp | 3.11.0 |
| requests-mock | 1.7.0 |
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, la commande ci-dessous 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.
Lorsque vous importez des modules à partir du dossier lib ci-dessus, vous devez utiliser une importation relative plutôt qu'une importation absolue.
IDE
Les IDE recommandés sont PyCharm ou Visual Studio Code.
Structure du répertoire des plugins
Cette section présente la structure typique du répertoire d'un plugin User Risk Exchange.
/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 User Risk Exchange. Assurez-vous que chaque paquet de plugins contient le fichier vide "__init__.py" fichier.
- 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 et une taille recommandée de 300*50 pixels ou un rapport d'aspect similaire.
- main.py : Ce fichier python contient la classe Plugin contenant l'implémentation concrète de la méthode de récupération des utilisateurs et des scores, 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.
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.
Exemple de Changelog.md
# 1.0.1 ## Fixed - Fixed pagination while fetching records. # 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 URE pour rendre le plugin dans l'interface utilisateur et permettre au module URE 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 User 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 : (string) Description du plugin. Fournissez une description détaillée qui mentionne les capacités et les instructions d'utilisation du plugin, (ex. Recherche des utilisateurs et des scores d'utilisateurs et effectue des actions pour les membres du groupe/des groupes) Cette description apparaîtrait sur la carte de configuration du plugin. (Obligatoire)
- 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)
- Type : ([Chaîne] ) : Le type d'utilisateurs que les données recherchent. Il peut s'agir d'un hôte, d'un utilisateur ou des deux.
- 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)
- 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 : Type de valeur du paramètre. Les valeurs autorisées sont "texte", "mot de passe", "nombre", "choix" et "multichoix". (Obligatoire) Pour plus de détails, reportez-vous aux types de paramètres de la configuration du plugin ci-dessous.
- default : La valeur par défaut de ce paramètre. Cette valeur apparaîtra sur la page de configuration du plugin dans l'interface utilisateur de l'URE. Les types de données pris en charge sont "texte", "nombre" et "liste" (pour les types à choix multiples). (Obligatoire)
- obligatoire : Booléen qui indique si ce paramètre est obligatoire ou non. Si un paramètre est obligatoire, l'interface utilisateur de User Risk Exchange ne vous laissera pas passer une valeur vide pour le paramètre. 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 le "type" : "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"
},
]
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"
},
]
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"
},
]
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, le toggle activé est True et toggle désactivé est False.
Utilisez le Proxy Système (« proxy ») : Use system proxy configured in Settings.(Default: False)
Vue de configuration des plugins
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 de Netskope.integrations.cre.plugin_base import PluginBase, ValidationResult, PushResult
from Netskope.integrations.cre.models.business_rule import Action, ActionSansParam, ActionSansParam, Action.
Variables de PluginBase
PluginBase donne accès à des variables qui peuvent être utilisées pendant le cycle de vie du plugin Méthodes. 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.storage | Cloud Exchange fournit au plugin un mécanisme pour maintenir l'état. Utilisez cet objet pour conserver tout état qui serait nécessaire lors d'appels ultérieurs. Le type de données de cet objet est python dict. | |
| self.notifier | self.notifier.info(“message”) self.notifier.warn(“message”) self.notifier.error(“message”) | Cet objet fournit une poignée pour le notificateur du noyau d'échange de risques de l'utilisateur. 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.cre.plugin_base.
- Assurez-vous que la classe Plugin fournit une implémentation pour les fonctions fetch_records, fetch_scores, validate, get_actions, get_action_fields, validate_actions et execute_actions.
- La classe du 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.cre.plugin_base import PluginBase, ValidationResult, PushResult from netskope.integrations.cre.models import Indicator, IndicatorType from typing import List from datetime import datetime import requests PLUGIN_NAME = "<module> <plugin_name> Plugin" 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 URE core engine. """
def fetch_records()
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 utilisateurs (indicateurs) à partir des points d'extrémité de l'API tierce. Cette méthode est invoquée périodiquement.
- Assurez-vous qu'il est testable à l'unité.
- Utilisez la configuration du proxy transmise par la plateforme Cloud Exchange 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.
- Utilisez la fonction self.ssl_validation booléen pour activer/désactiver la validation du certificat du serveur SSL.
- Renvoyer la liste des enregistrements contenant les données reçues du point de terminaison de l'API. 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.
- Si le point de terminaison de l'API ne prend pas en charge les requêtes multiples (vous pouvez le pousser en un seul appel API), le plugin peut ignorer les indicateurs restants et envoyer une notification à l'utilisateur pour qu'il ajuste les filtres de partage ou il peut échouer avec l'erreur de dépassement de la taille de la charge utile.
def fetch_records(self):
"""Fetches users from a 3rd party API.
Implement the logic of fetching users from 3rd party apis and
return the list of objects netskope.integrations.cre.models.Records on
successful fetch otherwise raises an exception.
Returns:
List[netskope.integrations.cre.models.Records]: List of
Record objects received from the 3rd 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.
config = self.configuration
# get proxy settings dict, just the way requests module requires.
proxy_dict = self.proxy
# get the ssl_validation bool for enabling/disabling validation of SSL server certificates.
ssl_validation = self.ssl_validation
# How to use proxy dict and ssl_validation flag.
resp = requests.get("www.example.com", proxies=proxy_dict, verify=ssl_validation)
# Get the logger object for logging purpose. This logger object logs all the logs to mongodb
# under the cre database logs collection. Log timestamp is automatically recorded by the logger library.
# Supported logging levels are info, warn and error.
logger = self.logger
logger.info(f"{PLUGIN_NAME}: Finished fetching records")
return records
def fetch_scores()
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 scores (indicateurs) à partir des points d'extrémité de l'API tierce. Cette méthode est invoquée périodiquement.
- Assurez-vous qu'il est testable à l'unité.
- 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.
- Utilisez la fonction self.ssl_validation booléen pour activer/désactiver la validation du certificat du serveur SSL.
- 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.
- Si le point de terminaison de l'API ne prend pas en charge les demandes multiples (c'est-à-dire nous pouvons le pousser en un seul appel API), soit le plugin peut ignorer les indicateurs restants et envoyer une notification à l'utilisateur pour qu'il ajuste les filtres de partage, soit il peut échouer avec l'erreur de dépassement de la taille de la charge utile.
- Veillez à ce que les scores correspondent à un nombre entier compris entre 0 et 1000.
def fetch_scores(self):
"""Fetches users from a 3rd party API.
Implement the logic of fetching risk scores from 3rd party apis and return the list of objects netskope.integrations.cre.models. Records a successful fetch otherwise raises an exception.
Returns:
List[netskope.integrations.cre.models.Records]: List of Record objects received from the 3rd 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.
config = self.configuration
# get proxy settings dict, just the way requests module requires.
proxy_dict = self.proxy
# get the ssl_validation bool for enabling/disabling validation of SSL server certificates.
ssl_validation = self.ssl_validation
# How to use proxy dict and ssl_validation flag.
resp = requests.get("www.example.com", proxies=proxy_dict, verify=ssl_validation)
# Get the logger object for logging purpose. This logger object logs all the logs to mongodb
# under the cre database logs collection. Log timestamp is automatically recorded by the logger library.
# Supported logging levels are info, warn and error.
logger = self.logger
logger.info(f"{PLUGIN_NAME}: Finished fetching records")
return scores
def validate()
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.
- Validez 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, data):
"""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.cre.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:
data (dict): Dict object having all the Plugin configuration parameters.
Returns:
netskope.integrations.cre.plugin_base.ValidateResult: ValidateResult object with success flag and message.
"""
self.logger.info(f"{PLUGIN_NAME}: Executing validate method for Sample plugin")
if (
"secret_field_id1" not in data
or not data["secret_field_id1"]
or type(data["secret_field_id1"]) != str
):
self.logger.error(
"f{PLUGIN_NAME}: Validation error occurred Error: Secret Field1 is required with type string."
)
return ValidationResult(
success=False, message="Invalid Secret Field 1 provided.",
def get_actions()
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.
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 actions”, value=”generate”)
]
def get_action_fields()
Il s'agit d'une méthode abstraite de la classe PluginBase.
- 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.
- Allez dans Manifest.json pour voir comment les champs sont définis.
def get_action_fields(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 == “add”:
return [
{
“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()
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.
- 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é.
- 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.
- Lors de la validation, utilisez strip() pour les paramètres de configuration tels que l'URL de base, l'email, le nom d'utilisateur, etc. à l'exception des champs API Tokens et Password.
- 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.cre.plugin_base.ValidateResult: ValidateResult object with success flag and message.
"""
if action.value not in [“add”, “remove”]:
return ValidationResult(
success=False, message=”Unsupported action provided.”
)
if action.value == “add”:
if action.parameters.get(“group_name”) is None:
return ValidationResult(
success=False, message=”Group Name should not be empty.”
)
return ValidationResult(
success=True, message=”Validation successful.”
)
def execute_action()
Il s'agit d'une méthode abstraite de la classe PluginBase.
- Cette méthode valide les actions exécutées.
- 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 à l'API tierce.
def execute_action(self, record: Record, action: Action):
"""Execute action on the user.
Calls _add_to_group or _remove_to_group helper methods in the case
of add or remove action
Passes when action is generate
if action.value == "generate":
return
if action.value == "add":
group_id = action.parameters.get("group")
self._add_to_group(user, group_id)
self.logger.info(f"Added {user} to group to group ")
)
Modèles de données
Cette section énumère les modèles de données et leurs propriétés.
Modèle RecordType
- Le modèle RecordType contient le type d'enregistrement, c'est-à-dire l'utilisateur ou l'hôte.
- Le modèle d'enregistrement comporte 2 champs : USER, et HOST qui est un type d'enregistrement.
- Vous interagirez avec le modèle dans les méthodes fetch_records et fetch_scores en renvoyant une liste d'enregistrements.
Propriétés du modèle de données
| Nom | Type | Description |
|---|---|---|
| USER | string | Il peut s'agir d'un utilisateur. |
| HOST | string | Peut être un hôte. |
from netskope.integrations.cre.models import RecordType, Record
for user in users:
res.append(
Record(uid=user["email"], type=RecordType.USER, score=None)
)
Modèle d'enregistrement
- Le modèle d'enregistrement contient des enregistrements d'utilisateurs (indicateurs).
- Le modèle d'enregistrement comporte trois champs : uid, type et score, qui correspondent respectivement à l'adresse électronique, au type d'utilisateur et à la valeur entière du score.
- Vous interagirez avec le modèle dans les méthodes fetch_users et fetch_scores en renvoyant une liste d'enregistrements.
Propriétés du modèle de données
| Nom | Type | Description |
|---|---|---|
| uid | string | Courriel de l'utilisateur. |
| type | list[string] | Il peut s'agir d'un utilisateur, d'un hôte ou des deux. |
| score | int | Score de l'utilisateur. Il doit être associé à un nombre entier compris entre 0 et 1000. |
from netskope.integrations.cre.models import Record
Record(uid=each_user.get("userPrincipalName"),
type=RecordType.USER,
score=None
)
Classe d'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 les fonctions de recherche de scores, de recherche d'utilisateurs, de validation d'actions, d'exécution d'actions et d'obtention de champs d'action.
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é pour y accéder. |
| parameters | Dict | Il contiendra tous les champs d'action requis pour les plugins respectifs. |
| generateAlerts | Bool | Génère des alertes au CTO lorsque c'est le cas. |
| syncAction | Bool | Synchroniser les actions si c'est vrai. |
from netskope.integrations.cre.models import Action
if action.value == "generate":
return
Classe 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é pour y accéder. |
from netskope.integrations.cre.models import ActionWithoutParams
return [
ActionWithoutParams(label="Add to group", value="add"),
ActionWithoutParams(label="Remove from group", value="remove"),
ActionWithoutParams(label="No actions", value="generate"),
]
Classe 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.cre.plugin_base import ValidationResult
ValidationResult(
success=True,
message="Validation Successful for Sample plugin"
)
Enregistrement
L'URE 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, 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.
self.logger.error(
f"{PLUGIN_NAME}: Error log-message goes here."
)
self.logger.warn(
f"{PLUGIN_NAME}: Warning log-message goes here."
)
self.logger.info(
f"{PLUGIN_NAME}: Info log-message goes here."
)
Notifications
URE fournit un ensemble d'objets de notification, qui peuvent être utilisés pour générer des notifications sur l'interface utilisateur d'URE.
- 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"{PLUGIN_NAME}: Info notification-message goes here."
)
self.notifier.error(
f"{PLUGIN_NAME}: Error notification-message goes here."
)
self.notifier.warn(
f"{PLUGIN_NAME}: Warning notification-message goes here."
)
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. Nous utilisons également 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 pour lequel vous désactivez la fonctionnalité. Ainsi, si d'autres erreurs se produisent 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
La vérification des chaînes de documentation au format PEP8 est également activée avec le linter flake8. Assurez-vous donc que chaque fonction/module possède une documentation appropriée.
Tests unitaires
Assurer des tests unitaires pour tester de petites unités de code de manière isolée et déterministe. Veillez à ce que les tests unitaires évitent de communiquer avec des API externes et à ce qu'ils utilisent la technique du "mocking". Assurez-vous que les tests unitaires garantissent une couverture du code supérieure à 70%.
Configuration de l'environnement
Pour pouvoir utiliser les tests unitaires, le script d'intégration ou d'automatisation doit être développé dans la structure du répertoire des plugins. Utilisez PIP pour installer toutes les dépendances du module Python nécessaires à l'exécution de l'installation. Avant d'exécuter les tests, assurez-vous d'installer toutes les dépendances requises mentionnées dans le fichier requirements.txt du dépôt l core.
Écrire vos tests unitaires
Assurez-vous que les tests unitaires sont écrits dans un fichier Python séparé nommé : <your_plugin_name>_test.py. Dans le fichier de test unitaire, chaque fonction de test unitaire doit être nommée : test_.<your test case> Plus d’informations sur la rédaction de tests unitaires et leur format sont disponibles sur PyTest Docs.
Mocking
Utilisez pytest-mock pour la simulation. pytest-mock est activé par défaut et installé dans l'environnement de base mentionné ci-dessus. Pour utiliser un objet mocker, il suffit de le passer en paramètre à votre fonction de test. Le simulateur peut alors être utilisé pour simuler à la fois l'objet de classe du plugin et les API externes.
Exemple:
def test_netskope_fetch_users(mocker):
mocker.patch("cre.plugins.sample.main.Sample.fetch_users")
fetch_users_return_result = [
Record(uid="a@def.com", type=Record.user, score=400),
Record(uid="b@pqr.com", type=Record.user, score=300),
Record(uid="c@xyz.com", type=Record.user, score=750),
]
samplePlugin.fetch_users.return_value = fetch_users_return_result
sp = samplePlugin(None, None, None, logger)
actual_fetch = sp.fetch_users()
assert fetch_return_result == actual_fetch
Pour simuler la réponse du module de demande pour les appels API (le plugin requests_mock Pytest est installé avec toutes les dépendances).
def test_fetch_users(requests_mock):
endpoint_url = "https://example-api.com"
mock_response_json = {
{
"@odata.context": "https://graph.microsoft.com/v1.0/$metadata#directoryObjects",
"value": [
{
"@odata.type": "#microsoft.graph.user",
"id": "48a97c4b-65af-4cb7-90f9-c7835d5ecd8b",
"userPrincipalName": "adaml@xyz.com"
}
]
}
requests_mock.get(endpoint_url, json=mock_response_json)
config_dict = {
"tenant_name": "ecre-ccec-jpvasq",
"client_id": "ue4y-3id8-mnx87q",
"client_secret": "d9oe-ple7-nfi34w",
"api_token": "token",
}
sp = SamplePlugin(config_dict, None, None, logger)
actual_users_lists = sp.fetch_users(
endpoint_url,
config_dict['api_token']
)
users_mock = [
Record(uid="a@def.com", type=Record.user, score=400),
Record(uid="b@pqr.com", type=Record.user, score=300),
Record(uid="c@xyz.com", type=Record.user, score=750),
]
assert len(actual_users_lists) == len(users_mock)
for i in range(len(actual_users_lists)):
assert actual_ind_lists[i].value == users_mock[i].value
Exécuter vos tests unitaires
$ PYTHONPATH=. pytest
Déploiement de plugins sur CE
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
Télécharger le plugin
Pour déployer ce zip ou tar.gz sur la plateforme Cloud Exchange :
- Connectez-vous à la plateforme Cloud Exchange.
- Aller à Settings > Plugin.

- Cliquez Add New Plugin.

- Cliquez sur Browse.
- Select votre fichier zip ou tar.gz.

- Cliquez sur Upload.
Add a Repo
Pour déployer votre plugin dans la plateforme Cloud Exchange, vous pouvez ajouter votre répertoire dans la plateforme Cloud Exchange en suivant ces étapes :
- Connectez-vous à la plateforme Cloud Exchange.
- Allez à Settings > Plugin Repository.

- Cliquez Configure New Repository.

- Fournissez 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 un nom de référentiel dans la liste déroulante Référentiel.

Deliverables
Plugin Guide
- Veillez à mettre à jour le guide du plugin à chaque version.
- Le guide du plugin doit contenir ce contenu :
- Compatibility
- Notes de mise à jour
- Description
- Conditions préalables
- Permissions
- Autorisation du produit
- Configuration du plugin Netskope User Risk Exchange
- Configuration du plugin User Risk Exchange que nous avons développé.
- Configuration d'une règle de gestion
- Configuration d'une action
- Validation.
Vidéo de démonstration
Après le développement réussi du plugin User Risk Exchange, créez une vidéo de démonstration et montrez le plugin de bout en bout workflow.

