Vereenvoudiging van de Check Point API met de Python SDK
De volledige kracht van interactie met API's komt tot uiting wanneer het samen met programmacode wordt gebruikt, wanneer het mogelijk wordt om op dynamische wijze API-verzoeken en tools te genereren voor het analyseren van API-reacties. Het blijft echter nog steeds onmerkbaar Python-softwareontwikkelingskit (hierna Python SDK genoemd) voor Check Point Management-API, maar tevergeefs. Het vereenvoudigt het leven van ontwikkelaars en automatiseringsliefhebbers aanzienlijk. Python is de laatste tijd enorm populair geworden en ik besloot het gat op te vullen en de belangrijkste functies te bekijken. Check Point API Python-ontwikkelingskit. Dit artikel vormt een uitstekende aanvulling op een ander artikel over Habré Controleer punt R80.10 API. Beheer via CLI, scripts en meer. We bekijken hoe u scripts schrijft met behulp van de Python SDK en bekijken de nieuwe Management API-functionaliteit in versie 1.6 (ondersteund vanaf R80.40). Om het artikel te begrijpen, heb je basiskennis nodig van het werken met API's en Python.
Check Point is actief bezig met de ontwikkeling van de API en op dit moment zijn de volgende releases uitgebracht:
De Python SDK ondersteunt momenteel alleen interactie met de Management API en Gaia-API. In deze module bekijken we de belangrijkste klassen, methoden en variabelen.
Module-installatie
Module cpapi installeert snel en eenvoudig vanaf officiële Check Point-repository op github via pit. Gedetailleerde installatie-instructies zijn beschikbaar in README.md. Deze module is aangepast om te werken met Python-versies 2.7 en 3.7. In dit artikel worden voorbeelden gegeven van Python 3.7. De Python SDK kan echter rechtstreeks vanaf de Check Point Management Server (Smart Management) worden uitgevoerd, maar deze ondersteunt alleen Python 2.7, dus de laatste sectie bevat code voor versie 2.7. Direct na het installeren van de module raad ik aan om de voorbeelden in de mappen te bekijken voorbeelden_python2 и voorbeelden_python3.
Aan de slag
Om met de componenten van de cpapi-module te kunnen werken, moeten we vanuit de module importeren cpapi minimaal twee verplichte klassen:
APIClient и APIClientArgs
from cpapi import APIClient, APIClientArgs
Klasse APIClientArgs is verantwoordelijk voor de verbindingsparameters met de API-server en de klasse APIClient is verantwoordelijk voor de interactie met de API.
Verbindingsparameters bepalen
Om verschillende parameters te definiëren voor het verbinden met de API, moet u een exemplaar van de klasse maken APIClientArgs. In principe zijn de parameters ervan vooraf gedefinieerd en wanneer het script op de besturingsserver wordt uitgevoerd, hoeven ze niet te worden gespecificeerd.
client_args = APIClientArgs()
Maar als u op een host van derden draait, moet u minimaal het IP-adres of de hostnaam van de API-server (ook wel de beheerserver genoemd) opgeven. In het onderstaande voorbeeld definiëren we de serververbindingsparameter en wijzen hieraan het IP-adres van de beheerserver toe als een tekenreeks.
Laten we eens kijken naar alle parameters en hun standaardwaarden die kunnen worden gebruikt bij het verbinden met de API-server:
Argumenten van de methode __init__ van de klasse APIClientArgs
class APIClientArgs:
"""
This class provides arguments for APIClient configuration.
All the arguments are configured with their default values.
"""
# port is set to None by default, but it gets replaced with 443 if not specified
# context possible values - web_api (default) or gaia_api
def __init__(self, port=None, fingerprint=None, sid=None, server="127.0.0.1", http_debug_level=0,
api_calls=None, debug_file="", proxy_host=None, proxy_port=8080,
api_version=None, unsafe=False, unsafe_auto_accept=False, context="web_api"):
self.port = port
# management server fingerprint
self.fingerprint = fingerprint
# session-id.
self.sid = sid
# management server name or IP-address
self.server = server
# debug level
self.http_debug_level = http_debug_level
# an array with all the api calls (for debug purposes)
self.api_calls = api_calls if api_calls else []
# name of debug file. If left empty, debug data will not be saved to disk.
self.debug_file = debug_file
# HTTP proxy server address (without "http://")
self.proxy_host = proxy_host
# HTTP proxy port
self.proxy_port = proxy_port
# Management server's API version
self.api_version = api_version
# Indicates that the client should not check the server's certificate
self.unsafe = unsafe
# Indicates that the client should automatically accept and save the server's certificate
self.unsafe_auto_accept = unsafe_auto_accept
# The context of using the client - defaults to web_api
self.context = context
Ik geloof dat de argumenten die kunnen worden gebruikt in instances van de klasse APIClientArgs intuïtief zijn voor Check Point-beheerders en geen extra commentaar vereisen.
Verbinding maken via APIClient en contextmanager
Klasse APIClient De handigste manier om het te gebruiken is via de contextmanager. Het enige dat moet worden doorgegeven aan een exemplaar van de APIClient-klasse zijn de verbindingsparameters die in de vorige stap zijn gedefinieerd.
with APIClient(client_args) as client:
De contextmanager zal niet automatisch een login-oproep doen naar de API-server, maar zal wel een uitlog-oproep doen wanneer deze wordt afgesloten. Als u om de een of andere reden niet hoeft uit te loggen nadat u klaar bent met het werken met API-aanroepen, moet u beginnen te werken zonder de contextmanager te gebruiken:
client = APIClient(clieng_args)
Verbindingstest
De eenvoudigste manier om te controleren of de verbinding aan de opgegeven parameters voldoet, is met behulp van de methode check_vingerafdruk. Als de verificatie van de sha1-hashsom voor de vingerafdruk van het server-API-certificaat mislukt (de methode retourneert Niet waar), dan wordt dit meestal veroorzaakt door verbindingsproblemen en kunnen we de uitvoering van het programma stopzetten (of de gebruiker de mogelijkheid geven om de verbindingsgegevens te corrigeren):
if client.check_fingerprint() is False:
print("Could not get the server's fingerprint - Check connectivity with the server.")
exit(1)
Houd er rekening mee dat in de toekomst de klasse APIClient controleert elke API-aanroep (methods api_call и api_query, we zullen er wat verder over praten) sha1 vingerafdrukcertificaat op de API-server. Maar als er bij het controleren van de sha1-vingerafdruk van het API-servercertificaat een fout wordt ontdekt (het certificaat is onbekend of gewijzigd), kan de methode check_vingerafdruk biedt de mogelijkheid om er automatisch informatie over toe te voegen/wijzigen op de lokale machine. Deze controle kan volledig worden uitgeschakeld (maar dit kan alleen worden aanbevolen als scripts worden uitgevoerd op de API-server zelf, bij verbinding met 127.0.0.1), met behulp van het APIClientArgs-argument - unsafe_auto_accept (zie meer over APIClientArgs eerder in “Verbindingsparameters definiëren”).
У APIClient er zijn maar liefst 3 methoden om in te loggen op de API-server, en elk van hen begrijpt de betekenis sid(session-id), die automatisch wordt gebruikt bij elke volgende API-aanroep in de header (de naam in de header van deze parameter is X-chkp-zijde), dus het is niet nodig om deze parameter verder te verwerken.
login methode
Optie met login en wachtwoord (in het voorbeeld worden de gebruikersnaam admin en het wachtwoord 1q2w3e doorgegeven als positionele argumenten):
login = client.login('admin', '1q2w3e')
Er zijn ook aanvullende optionele parameters beschikbaar in de inlogmethode; hier zijn hun namen en standaardwaarden:
Optie waarbij een API-sleutel wordt gebruikt (ondersteund vanaf beheerversie R80.40/Management API v1.6, "3TsbPJ8ZKjaJGvFyoFqHFA==" dit is de API-sleutelwaarde voor een van de gebruikers op de beheerserver met de API-sleutelautorisatiemethode):
In methode login_met_api_key dezelfde optionele parameters zijn beschikbaar als in de methode Log in.
login_as_root methode
Optie om in te loggen op een lokale machine met een API-server:
login = client.login_as_root()
Er zijn slechts twee optionele parameters beschikbaar voor deze methode:
domain=None, payload=None
En ten slotte roept de API zichzelf aan
We hebben twee opties om API-aanroepen te doen via methoden api_call и api_query. Laten we eens kijken wat het verschil tussen hen is.
api_call
Deze methode is van toepassing op alle oproepen. We moeten indien nodig het laatste deel van de API-aanroep en de payload in de aanvraagtekst doorgeven. Als de payload leeg is, kan deze helemaal niet worden verzonden:
Laat ik meteen een voorbehoud maken dat deze methode alleen van toepassing is op oproepen waarvan de uitvoer offset met zich meebrengt. Een dergelijke gevolgtrekking doet zich voor wanneer het een grote hoeveelheid informatie bevat of kan bevatten. Dit kan bijvoorbeeld een verzoek zijn om een lijst met alle gemaakte hostobjecten op de beheerserver. Voor dergelijke verzoeken retourneert de API standaard een lijst met 50 objecten (u kunt de limiet in het antwoord verhogen tot 500 objecten). En om de informatie niet meerdere keren op te halen en de offsetparameter in het API-verzoek te wijzigen, is er een api_query-methode die dit automatisch doet. Voorbeelden van oproepen waarbij deze methode nodig is: show-sessies, show-hosts, show-netwerken, show-wildcards, show-groepen, show-adresbereiken, show-simple-gateways, show-simple-clusters, show-access-rollen, show-trusted-clients, show-pakketten. In feite zien we meervoudige woorden in de naam van deze API-aanroepen, dus deze oproepen zullen gemakkelijker te verwerken zijn api_query
Hierna kunt u de variabelen en methoden van de klasse gebruiken API-antwoord(zowel binnen de contextmanager als daarbuiten). In de klas API-antwoord Er zijn 4 methoden en 5 variabelen vooraf gedefinieerd; we zullen dieper ingaan op de belangrijkste.
succes
Om te beginnen zou het een goed idee zijn om ervoor te zorgen dat de API-aanroep succesvol was en een resultaat opleverde. Hiervoor bestaat een methode succes:
In [49]: api_versions.success
Out[49]: True
Retourneert True als de API-aanroep succesvol was (reactiecode - 200) en False als deze niet succesvol was (een andere responscode). Het is handig om direct na een API-aanroep verschillende informatie weer te geven, afhankelijk van de responscode.
if api_ver.success:
print(api_versions.data)
else:
print(api_versions.err_message)
status code
Retourneert de responscode nadat een API-aanroep is gedaan.
In dit geval kan het nodig zijn om de waarde van de successtatus te wijzigen. Technisch gezien kun je daar alles plaatsen, zelfs een gewone string. Maar een reëel voorbeeld zou het opnieuw instellen van deze parameter op False zijn onder bepaalde begeleidende omstandigheden. Let hieronder op het voorbeeld waarin er taken worden uitgevoerd op de beheerserver, maar we beschouwen dit verzoek als niet succesvol (we zullen de succesvariabele instellen op Niet waar, ondanks het feit dat de API-aanroep succesvol was en code 200 retourneerde).
for task in task_result.data["tasks"]:
if task["status"] == "failed" or task["status"] == "partially succeeded":
task_result.set_success_status(False)
break
antwoord()
Met de responsmethode kunt u het woordenboek bekijken met de responscode (status_code) en de antwoordtekst (body).
Deze informatie is alleen beschikbaar als er een fout is opgetreden tijdens het verwerken van het API-verzoek (responscode geen 200). Voorbeelduitvoer
In [107]: api_versions.error_message
Out[107]: 'code: generic_err_invalid_parameter_namenmessage: Unrecognized parameter [1]n'
Nuttige voorbeelden
Hieronder volgen voorbeelden waarin de API-aanroepen worden gebruikt die zijn toegevoegd in Management API 1.6.
Laten we eerst eens kijken hoe oproepen werken add-host и adresbereik toevoegen. Laten we zeggen dat we alle IP-adressen van het subnet 192.168.0.0/24, waarvan het laatste octet 5 is, moeten aanmaken als objecten van het hosttype, en alle andere IP-adressen moeten schrijven als objecten van het adresbereiktype. Sluit in dit geval het subnetadres en het broadcastadres uit.
Hieronder staat dus een script dat dit probleem oplost en 50 objecten van het hosttype en 51 objecten van het adresbereiktype maakt. Om het probleem op te lossen zijn 101 API-aanroepen vereist (de laatste publicatieaanroep niet meegerekend). Ook berekenen we met behulp van de timeit-module de tijd die nodig is om het script uit te voeren totdat de wijzigingen worden gepubliceerd.
Script met add-host en add-address-range
import timeit
from cpapi import APIClient, APIClientArgs
start = timeit.default_timer()
first_ip = 1
last_ip = 4
client_args = APIClientArgs(server="192.168.47.240")
with APIClient(client_args) as client:
login = client.login_with_api_key('3TsbPJ8ZKjaJGvFyoFqHFA==')
for ip in range(5,255,5):
add_host = client.api_call("add-host", {"name" : f"h_192.168.0.{ip}", "ip-address": f'192.168.0.{ip}'})
while last_ip < 255:
add_range = client.api_call("add-address-range", {"name": f"r_192.168.0.{first_ip}-{last_ip}", "ip-address-first": f"192.168.0.{first_ip}", "ip-address-last": f"192.168.0.{last_ip}"})
first_ip+=5
last_ip+=5
stop = timeit.default_timer()
publish = client.api_call("publish")
print(f'Time to execute batch request: {stop - start} seconds')
In mijn labomgeving duurt de uitvoering van dit script tussen de 30 en 50 seconden, afhankelijk van de belasting op de beheerserver.
Laten we nu eens kijken hoe we hetzelfde probleem kunnen oplossen met behulp van een API-aanroep add-objecten-batch, waarvoor ondersteuning is toegevoegd in API-versie 1.6. Met deze aanroep kunt u in één API-verzoek veel objecten tegelijk maken. Bovendien kunnen dit objecten van verschillende typen zijn (bijvoorbeeld hosts, subnetten en adresbereiken). Onze taak kan dus worden opgelost binnen het kader van één API-oproep.
Script met add-objects-batch
import timeit
from cpapi import APIClient, APIClientArgs
start = timeit.default_timer()
client_args = APIClientArgs(server="192.168.47.240")
objects_list_ip = []
objects_list_range = []
for ip in range(5,255,5):
data = {"name": f'h_192.168.0.{ip}', "ip-address": f'192.168.0.{ip}'}
objects_list_ip.append(data)
first_ip = 1
last_ip = 4
while last_ip < 255:
data = {"name": f"r_192.168.0.{first_ip}-{last_ip}", "ip-address-first": f"192.168.0.{first_ip}", "ip-address-last": f"192.168.0.{last_ip}"}
objects_list_range.append(data)
first_ip+=5
last_ip+=5
data_for_batch = {
"objects" : [ {
"type" : "host",
"list" : objects_list_ip
}, {
"type" : "address-range",
"list" : objects_list_range
}]
}
with APIClient(client_args) as client:
login = client.login_with_api_key('3TsbPJ8ZKjaJGvFyoFqHFA==')
add_objects_batch = client.api_call("add-objects-batch", data_for_batch)
stop = timeit.default_timer()
publish = client.api_call("publish")
print(f'Time to execute batch request: {stop - start} seconds')
En het uitvoeren van dit script in mijn labomgeving duurt 3 tot 7 seconden, afhankelijk van de belasting op de beheerserver. Dat wil zeggen dat op 101 API-objecten een batchtype-aanroep gemiddeld 10 keer sneller wordt uitgevoerd. Op een groter aantal objecten zal het verschil nog indrukwekkender zijn.
Laten we nu kijken hoe we ermee kunnen werken set-objecten-batch. Met behulp van deze API-aanroep kunnen we elke parameter in bulk wijzigen. Laten we de eerste helft van de adressen uit het vorige voorbeeld (tot .124 hosts, en ook bereiken) instellen op de kleur sienna, en de kleur kaki toewijzen aan de tweede helft van de adressen.
De kleur wijzigen van de objecten die in het vorige voorbeeld zijn gemaakt
from cpapi import APIClient, APIClientArgs
client_args = APIClientArgs(server="192.168.47.240")
objects_list_ip_first = []
objects_list_range_first = []
objects_list_ip_second = []
objects_list_range_second = []
for ip in range(5,125,5):
data = {"name": f'h_192.168.0.{ip}', "color": "sienna"}
objects_list_ip_first.append(data)
for ip in range(125,255,5):
data = {"name": f'h_192.168.0.{ip}', "color": "khaki"}
objects_list_ip_second.append(data)
first_ip = 1
last_ip = 4
while last_ip < 125:
data = {"name": f"r_192.168.0.{first_ip}-{last_ip}", "color": "sienna"}
objects_list_range_first.append(data)
first_ip+=5
last_ip+=5
while last_ip < 255:
data = {"name": f"r_192.168.0.{first_ip}-{last_ip}", "color": "khaki"}
objects_list_range_second.append(data)
first_ip+=5
last_ip+=5
data_for_batch_first = {
"objects" : [ {
"type" : "host",
"list" : objects_list_ip_first
}, {
"type" : "address-range",
"list" : objects_list_range_first
}]
}
data_for_batch_second = {
"objects" : [ {
"type" : "host",
"list" : objects_list_ip_second
}, {
"type" : "address-range",
"list" : objects_list_range_second
}]
}
with APIClient(client_args) as client:
login = client.login_with_api_key('3TsbPJ8ZKjaJGvFyoFqHFA==')
set_objects_batch_first = client.api_call("set-objects-batch", data_for_batch_first)
set_objects_batch_second = client.api_call("set-objects-batch", data_for_batch_second)
publish = client.api_call("publish")
U kunt meerdere objecten in één API-aanroep verwijderen met behulp van verwijder-objecten-batch. Laten we nu eens kijken naar een codevoorbeeld dat alle hosts verwijdert die eerder zijn gemaakt via add-objecten-batch.
Objecten verwijderen met behulp van delete-objects-batch
from cpapi import APIClient, APIClientArgs
client_args = APIClientArgs(server="192.168.47.240")
objects_list_ip = []
objects_list_range = []
for ip in range(5,255,5):
data = {"name": f'h_192.168.0.{ip}'}
objects_list_ip.append(data)
first_ip = 1
last_ip = 4
while last_ip < 255:
data = {"name": f"r_192.168.0.{first_ip}-{last_ip}"}
objects_list_range.append(data)
first_ip+=5
last_ip+=5
data_for_batch = {
"objects" : [ {
"type" : "host",
"list" : objects_list_ip
}, {
"type" : "address-range",
"list" : objects_list_range
}]
}
with APIClient(client_args) as client:
login = client.login_with_api_key('3TsbPJ8ZKjaJGvFyoFqHFA==')
delete_objects_batch = client.api_call("delete-objects-batch", data_for_batch)
publish = client.api_call("publish")
print(delete_objects_batch.data)
Alle functies die in nieuwe releases van Check Point-software verschijnen, krijgen onmiddellijk API-aanroepen. Zo verschenen in R80.40 ‘functies’ als Terug naar revisie en Smart Task, en werden overeenkomstige API-aanroepen onmiddellijk voor hen voorbereid. Bovendien krijgt alle functionaliteit bij het overstappen van Legacy-consoles naar de Unified Policy-modus ook API-ondersteuning. De langverwachte update in softwareversie R80.40 was bijvoorbeeld de verplaatsing van het HTTPS-inspectiebeleid van de Legacy-modus naar de Unified Policy-modus, en deze functionaliteit ontving onmiddellijk API-aanroepen. Hier is een voorbeeld van code die een regel toevoegt aan de toppositie van het HTTPS-inspectiebeleid die 3 categorieën uitsluit van inspectie (gezondheidszorg, financiën, overheidsdiensten), die in een aantal landen volgens de wet verboden zijn voor inspectie.
Python-scripts uitvoeren op de Check Point-beheerserver
Alles is hetzelfde README.md bevat informatie over hoe u Python-scripts rechtstreeks vanaf de controleserver kunt uitvoeren. Dit kan handig zijn als u vanaf een andere machine geen verbinding kunt maken met de API-server. Ik heb een video van zes minuten opgenomen waarin ik kijk naar het installeren van de module cpapi en functies voor het uitvoeren van Python-scripts op de controleserver. Er wordt bijvoorbeeld een script uitgevoerd dat de configuratie van een nieuwe gateway automatiseert voor een taak zoals netwerkaudit Beveiligingscontrole. Een van de features waarmee ik te maken kreeg: de functie is nog niet verschenen in Python 2.7 invoer, dus om de informatie die de gebruiker invoert te verwerken, wordt een functie gebruikt ruwe invoer. Anders is de code hetzelfde als voor het starten vanaf andere machines, alleen is het handiger om de functie te gebruiken login_as_root, zodat u niet opnieuw uw eigen gebruikersnaam, wachtwoord en IP-adres van de beheerserver hoeft op te geven.
Script voor snelle installatie van Security CheckUp
from __future__ import print_function
import getpass
import sys, os
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from cpapi import APIClient, APIClientArgs
def main():
with APIClient() as client:
# if client.check_fingerprint() is False:
# print("Could not get the server's fingerprint - Check connectivity with the server.")
# exit(1)
login_res = client.login_as_root()
if login_res.success is False:
print("Login failed:n{}".format(login_res.error_message))
exit(1)
gw_name = raw_input("Enter the gateway name:")
gw_ip = raw_input("Enter the gateway IP address:")
if sys.stdin.isatty():
sic = getpass.getpass("Enter one-time password for the gateway(SIC): ")
else:
print("Attention! Your password will be shown on the screen!")
sic = raw_input("Enter one-time password for the gateway(SIC): ")
version = raw_input("Enter the gateway version(like RXX.YY):")
add_gw = client.api_call("add-simple-gateway", {'name' : gw_name, 'ipv4-address' : gw_ip, 'one-time-password' : sic, 'version': version.capitalize(), 'application-control' : 'true', 'url-filtering' : 'true', 'ips' : 'true', 'anti-bot' : 'true', 'anti-virus' : 'true', 'threat-emulation' : 'true'})
if add_gw.success and add_gw.data['sic-state'] != "communicating":
print("Secure connection with the gateway hasn't established!")
exit(1)
elif add_gw.success:
print("The gateway was added successfully.")
gw_uid = add_gw.data['uid']
gw_name = add_gw.data['name']
else:
print("Failed to add the gateway - {}".format(add_gw.error_message))
exit(1)
change_policy = client.api_call("set-access-layer", {"name" : "Network", "applications-and-url-filtering": "true", "content-awareness": "true"})
if change_policy.success:
print("The policy has been changed successfully")
else:
print("Failed to change the policy- {}".format(change_policy.error_message))
change_rule = client.api_call("set-access-rule", {"name" : "Cleanup rule", "layer" : "Network", "action": "Accept", "track": {"type": "Detailed Log", "accounting": "true"}})
if change_rule.success:
print("The cleanup rule has been changed successfully")
else:
print("Failed to change the cleanup rule- {}".format(change_rule.error_message))
# publish the result
publish_res = client.api_call("publish", {})
if publish_res.success:
print("The changes were published successfully.")
else:
print("Failed to publish the changes - {}".format(install_tp_policy.error_message))
install_access_policy = client.api_call("install-policy", {"policy-package" : "Standard", "access" : 'true', "threat-prevention" : 'false', "targets" : gw_uid})
if install_access_policy.success:
print("The access policy has been installed")
else:
print("Failed to install access policy - {}".format(install_tp_policy.error_message))
install_tp_policy = client.api_call("install-policy", {"policy-package" : "Standard", "access" : 'false', "threat-prevention" : 'true', "targets" : gw_uid})
if install_tp_policy.success:
print("The threat prevention policy has been installed")
else:
print("Failed to install threat prevention policy - {}".format(install_tp_policy.error_message))
# add passwords and passphrases to dictionary
with open('additional_pass.conf') as f:
line_num = 0
for line in f:
line_num += 1
add_password_dictionary = client.api_call("run-script", {"script-name" : "Add passwords and passphrases", "script" : "printf "{}" >> $FWDIR/conf/additional_pass.conf".format(line), "targets" : gw_name})
if add_password_dictionary.success:
print("The password dictionary line {} was added successfully".format(line_num))
else:
print("Failed to add the dictionary - {}".format(add_password_dictionary.error_message))
main()
Een voorbeeldbestand met een wachtwoordwoordenboek extra_pass.conf {
"passwords" : ["malware","malicious","infected","Infected"],
"phrases" : ["password","Password","Pass","pass","codigo","key","pwd","пароль","Пароль","Ключ","ключ","шифр","Шифр"]
}
Conclusie
Dit artikel onderzoekt alleen de basismogelijkheden van werk Python-SDK en moduul cpapi(zoals je misschien al geraden hebt, zijn dit eigenlijk synoniemen), en door de code in deze module te bestuderen zul je nog meer mogelijkheden ontdekken om ermee te werken. Het is mogelijk dat u het wilt aanvullen met uw eigen klassen, functies, methoden en variabelen. U kunt uw werk altijd delen en andere scripts voor Check Point bekijken in de sectie CodeHub in de gemeenschap CheckMates, dat zowel productontwikkelaars als gebruikers samenbrengt.
Veel codeerplezier en bedankt voor het lezen tot het einde!