Files
PrimAITE/sandbox.ipynb
Marek Wolan f79ed99bd2 end of day
2023-09-25 19:17:57 +01:00

346 lines
21 KiB
Plaintext

{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"%load_ext autoreload\n",
"%autoreload 2"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"import yaml"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from primaite.simulator.network.networks import arcd_uc2_network\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"net = arcd_uc2_network()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"random_node = list(net.nodes.keys())[0]\n",
"f = net.nodes[random_node].file_system.create_file(file_name=\"testfile\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"f.describe_state()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def test_file_observation():\n",
" from primaite.simulator.sim_container import Simulation\n",
" from primaite.simulator.network.hardware.nodes.computer import Computer\n",
" from primaite.game.actor.observations import FileObservation\n",
"\n",
" sim = Simulation()\n",
" pc = Computer(hostname=\"beep\", ip_address=\"123.123.123.123\", subnet_mask=\"255.255.255.0\")\n",
" sim.network.add_node(pc)\n",
" f = pc.file_system.create_file(file_name=\"dog.png\")\n",
"\n",
" dog_file_obs = FileObservation(where=['network','nodes',pc.uuid,'file_system'])\n",
" print(sim.describe_state())\n",
"test_file_observation()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from primaite.simulator.sim_container import Simulation\n",
"from primaite.simulator.network.hardware.nodes.computer import Computer\n",
"from primaite.game.actor.observations import FileObservation, FolderObservation\n",
"\n",
"sim = Simulation()\n",
"pc = Computer(hostname=\"beep\", ip_address=\"123.123.123.123\", subnet_mask=\"255.255.255.0\")\n",
"sim.network.add_node(pc)\n",
"f = pc.file_system.create_file(file_name=\"dog.png\")\n",
"\n",
"state = sim.describe_state()\n",
"\n",
"dog_file_obs = FileObservation(where=['network','nodes',pc.uuid,'file_system', 'folders','root','files','dog.png'])\n",
"root_folder_obs = FolderObservation(where=['network','nodes',pc.uuid,'file_system', 'folders','root'],files=[dog_file_obs])\n",
"print(dog_file_obs(state))\n",
"print(root_folder_obs(state))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"dog_file_obs.space"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"root_folder_obs.space"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"state"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import yaml"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with open('example_config.yaml', 'r') as file:\n",
" conf = yaml.safe_load(file)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"conf['simulation']"
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {},
"outputs": [],
"source": [
"import yaml\n",
"from primaite.simulator.sim_container import Simulation\n",
"from primaite.simulator.network.hardware.nodes.computer import Computer\n",
"from primaite.simulator.network.hardware.nodes.server import Server\n",
"from primaite.simulator.network.hardware.nodes.switch import Switch\n",
"from primaite.simulator.network.hardware.nodes.router import Router\n",
"\n",
"from primaite.simulator.system.applications.database_client import DatabaseClient\n",
"from primaite.simulator.system.services.database_service import DatabaseService\n",
"from primaite.simulator.system.services.dns_client import DNSClient\n",
"from primaite.simulator.system.services.dns_server import DNSServer\n",
"from primaite.simulator.system.services.red_services.data_manipulation_bot import DataManipulationBot\n"
]
},
{
"cell_type": "code",
"execution_count": 42,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2023-09-25 19:10:46,253: Added node 3d356af7-15f8-41d4-bb2a-423d5a2e5978 to Network 44950e57-81b0-4964-9b12-223592c785aa\n",
"2023-09-25 19:10:46,254::WARNING::primaite.simulator.network.container::181::Can't add node 3d356af7-15f8-41d4-bb2a-423d5a2e5978. It is already in the network.\n",
"2023-09-25 19:10:46,258: Added node 8a94447a-ccb3-47b2-b7ba-488e631f8246 to Network 44950e57-81b0-4964-9b12-223592c785aa\n",
"2023-09-25 19:10:46,262: Added node 4e3de72a-60ad-416f-abb7-da352a59d13b to Network 44950e57-81b0-4964-9b12-223592c785aa\n",
"2023-09-25 19:10:46,264: Added node 21ae4eed-489f-43a7-af0a-f70000714c79 to Network 44950e57-81b0-4964-9b12-223592c785aa\n",
"2023-09-25 19:10:46,267: Added node c4b7d388-c13e-4f4e-be61-5cf05989da71 to Network 44950e57-81b0-4964-9b12-223592c785aa\n",
"2023-09-25 19:10:46,270: Added node 12dc9e72-fb7c-4a8f-ab79-d7574863fa16 to Network 44950e57-81b0-4964-9b12-223592c785aa\n",
"2023-09-25 19:10:46,273: Added node 37ff728f-64b5-4203-8177-cf27f51dc7c9 to Network 44950e57-81b0-4964-9b12-223592c785aa\n",
"2023-09-25 19:10:46,277: Added node fe9ef0ad-e0d6-48b9-a884-f7c0b95de32a to Network 44950e57-81b0-4964-9b12-223592c785aa\n",
"2023-09-25 19:10:46,281: Added node dfd186b9-7dc7-4d6f-a736-0cce3d22bfb6 to Network 44950e57-81b0-4964-9b12-223592c785aa\n"
]
}
],
"source": [
"# import yaml\n",
"\n",
"from primaite.simulator.network.hardware.nodes.router import ACLAction\n",
"from primaite.simulator.network.transmission.network_layer import IPProtocol\n",
"from primaite.simulator.network.transmission.transport_layer import Port\n",
"from primaite.simulator.system.services.dns_server import DNSServer\n",
"\n",
"\n",
"class PrimaiteSession:\n",
"\n",
" def __init__(self):\n",
" self.simulation: Simulation\n",
" self.agents = []\n",
"\n",
" @classmethod\n",
" def from_config(cls, cfg_path):\n",
" ref_to_uuid = {}\n",
"\n",
" game = cls()\n",
" with open(cfg_path, 'r') as file:\n",
" conf = yaml.safe_load(file)\n",
" \n",
" #1. create nodes \n",
" sim = Simulation()\n",
" net = sim.network\n",
" nodes_cfg = conf['simulation']['network']['nodes']\n",
" links_cfg = conf['simulation']['network']['links']\n",
" for node_cfg in nodes_cfg:\n",
" node_ref = node_cfg['ref']\n",
" n_type = node_cfg['type']\n",
" if n_type == 'computer':\n",
" new_node = Computer(hostname = node_cfg['hostname'], \n",
" ip_address = node_cfg['ip_address'], \n",
" subnet_mask = node_cfg['subnet_mask'], \n",
" default_gateway = node_cfg['default_gateway'],\n",
" dns_server = node_cfg['dns_server'])\n",
" elif n_type == 'server':\n",
" new_node = Server(hostname = node_cfg['hostname'], \n",
" ip_address = node_cfg['ip_address'], \n",
" subnet_mask = node_cfg['subnet_mask'], \n",
" default_gateway = node_cfg['default_gateway'],\n",
" dns_server = node_cfg.get('dns_server'))\n",
" elif n_type == 'switch':\n",
" new_node = Switch(hostname = node_cfg['hostname'],\n",
" num_ports = node_cfg.get('num_ports'))\n",
" elif n_type == 'router':\n",
" new_node = Router(hostname=node_cfg['hostname'],\n",
" num_ports = node_cfg.get('num_ports'))\n",
" if 'ports' in node_cfg:\n",
" for port_num, port_cfg in node_cfg['ports'].items():\n",
" new_node.configure_port(port=port_num, \n",
" ip_address=port_cfg['ip_address'],\n",
" subnet_mask=port_cfg['subnet_mask'])\n",
" if 'acl' in node_cfg:\n",
" for r_num, r_cfg in node_cfg['acl'].items():\n",
" # excuse the uncommon walrus operator ` := `. It's just here as a shorthand, so that we can do\n",
" # both of these things once: check if a key is defined, access and convert it to a \n",
" # Port/IPProtocol. TODO Refactor\n",
" new_node.acl.add_rule(\n",
" action = ACLAction[r_cfg['action']],\n",
" src_port = None if not (p:=r_cfg.get('src_port')) else Port[p],\n",
" dst_port = None if not (p:=r_cfg.get('dst_port')) else Port[p],\n",
" protocol = None if not (p:=r_cfg.get('protocol')) else IPProtocol[p],\n",
" src_ip_address = r_cfg.get('ip_address'),\n",
" dst_ip_address = r_cfg.get('ip_address'),\n",
" position = r_num\n",
" )\n",
" if 'services' in node_cfg:\n",
" for service_cfg in node_cfg['services']:\n",
" service_ref = service_cfg['ref']\n",
" service_type = service_cfg['type']\n",
" service_types_mapping = {\n",
" 'dns_server' : DNSServer,\n",
" 'database_client': DatabaseClient,\n",
" 'database_service': DatabaseService,\n",
" # 'database_backup': ,\n",
" 'data_manipulation_bot': DataManipulationBot,\n",
" 'dns_client': DNSClient,\n",
" # 'web_browser'\n",
" }\n",
" if service_type\n",
"\n",
"\n",
" net.add_node(new_node)\n",
" ref_to_uuid[node_ref] = new_node.uuid\n",
"\n",
"\n",
" #2. start/setup simulation objects\n",
" #3. create agents\n",
" #4. set up agents' actions and observation spaces.\n",
" game.simulation = sim\n",
" return game\n",
"\n",
"s = PrimaiteSession.from_config('example_config.yaml')\n",
"# print(s.simulation.describe_state())"
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"{'uuid': '532b534a-78b7-4630-92f9-626d775ee4e2', 'network': {'uuid': '44950e57-81b0-4964-9b12-223592c785aa', 'nodes': {'3d356af7-15f8-41d4-bb2a-423d5a2e5978': {'uuid': '3d356af7-15f8-41d4-bb2a-423d5a2e5978', 'hostname': 'router_1', 'operating_state': 2, 'NICs': {'c52a1516-3488-4011-95ae-44e77935720d': {'uuid': 'c52a1516-3488-4011-95ae-44e77935720d', 'ip_adress': '192.168.1.1', 'subnet_mask': '255.255.255.0', 'mac_address': '12:75:33:08:ae:04', 'speed': 100, 'mtu': 1500, 'wake_on_lan': False, 'enabled': False}, '075a3387-b3c5-4e30-b7d5-f39b82b1c9d8': {'uuid': '075a3387-b3c5-4e30-b7d5-f39b82b1c9d8', 'ip_adress': '192.168.1.1', 'subnet_mask': '255.255.255.0', 'mac_address': 'ce:67:79:4a:48:90', 'speed': 100, 'mtu': 1500, 'wake_on_lan': False, 'enabled': False}, '3fe76cb7-84fe-4592-b527-be69c06c8064': {'uuid': '3fe76cb7-84fe-4592-b527-be69c06c8064', 'ip_adress': '127.0.0.1', 'subnet_mask': '255.0.0.0', 'mac_address': '83:13:43:3a:e6:de', 'speed': 100, 'mtu': 1500, 'wake_on_lan': False, 'enabled': False}, 'b4ad54f8-8508-4d34-9117-8f194a1076b1': {'uuid': 'b4ad54f8-8508-4d34-9117-8f194a1076b1', 'ip_adress': '127.0.0.1', 'subnet_mask': '255.0.0.0', 'mac_address': '4f:1a:3f:76:c7:2c', 'speed': 100, 'mtu': 1500, 'wake_on_lan': False, 'enabled': False}, '21efd6b1-8b49-4b56-aafe-a3cbdf61e0fb': {'uuid': '21efd6b1-8b49-4b56-aafe-a3cbdf61e0fb', 'ip_adress': '127.0.0.1', 'subnet_mask': '255.0.0.0', 'mac_address': '76:4d:65:e4:0d:11', 'speed': 100, 'mtu': 1500, 'wake_on_lan': False, 'enabled': False}}, 'file_system': {'uuid': 'df3b627a-76c3-4e60-83ef-81442fbf4643', 'folders': {'root': {'uuid': 'b21a0c20-e852-4d05-a0fe-9158f76f3a98', 'name': 'root', 'health_status': 1, 'files': {}, 'is_quarantined': False}}}, 'applications': {}, 'services': {}, 'process': {}, 'num_ports': (5,), 'acl': ({'uuid': '2fc154d6-9ccf-4c60-b6d3-ac3fcce68fc8', 'implicit_action': 0, 'implicit_rule': {'uuid': '6a5553d0-37a6-4ac5-b5da-38f7926ffe7e', 'action': 0, 'protocol': None, 'src_ip_address': None, 'src_port': None, 'dst_ip_address': None, 'dst_port': None}, 'max_acl_rules': 25, 'acl': {0: {'uuid': '8204a738-6010-44a8-8df3-317d4a03f600', 'action': 1, 'protocol': None, 'src_ip_address': None, 'src_port': 5432, 'dst_ip_address': None, 'dst_port': 5432}, 1: {'uuid': 'af7b39a5-e0ed-4047-b684-0cfbdc14f70b', 'action': 1, 'protocol': None, 'src_ip_address': None, 'src_port': 53, 'dst_ip_address': None, 'dst_port': 53}, 2: None, 3: None, 4: None, 5: None, 6: None, 7: None, 8: None, 9: None, 10: None, 11: None, 12: None, 13: None, 14: None, 15: None, 16: None, 17: None, 18: None, 19: None, 20: None, 21: None, 22: {'uuid': '4c6320f5-d9f4-4958-9572-d2ebc06ee120', 'action': 1, 'protocol': None, 'src_ip_address': None, 'src_port': 219, 'dst_ip_address': None, 'dst_port': 219}, 23: {'uuid': 'f39796b9-7511-4f23-b8a6-cbf0e87ec51a', 'action': 1, 'protocol': 'icmp', 'src_ip_address': None, 'src_port': None, 'dst_ip_address': None, 'dst_port': None}}},)}, '8a94447a-ccb3-47b2-b7ba-488e631f8246': {'uuid': '8a94447a-ccb3-47b2-b7ba-488e631f8246', 'num_ports': 8, 'ports': {1: {'uuid': '32eb0312-124d-42df-93bc-9cc25eb4b81f', 'mac_address': '1a:4e:83:7a:d7:4b', 'speed': 100, 'mtu': 1500, 'enabled': False}, 2: {'uuid': 'afe7a1b8-bfa6-42a2-b4aa-28187c67f7d3', 'mac_address': '9e:09:af:37:1d:e3', 'speed': 100, 'mtu': 1500, 'enabled': False}, 3: {'uuid': '98f46066-7e05-45cd-920e-54bbb3513fec', 'mac_address': '00:30:02:18:11:52', 'speed': 100, 'mtu': 1500, 'enabled': False}, 4: {'uuid': '65fb27f3-22ca-4bd3-898e-a2fdf49ec3b0', 'mac_address': 'f2:3f:0b:09:50:e1', 'speed': 100, 'mtu': 1500, 'enabled': False}, 5: {'uuid': 'f69c7045-40f7-490f-9c7c-280d0eb4fcd0', 'mac_address': '38:56:f5:dd:0a:a4', 'speed': 100, 'mtu': 1500, 'enabled': False}, 6: {'uuid': '6dbbac83-cd01-45ec-9ad6-352e1afee231', 'mac_address': 'aa:bb:c3:92:05:88', 'speed': 100, 'mtu': 1500, 'enabled': False}, 7: {'uuid': 'a39591ea-d50d-4649-ba90-f5fddd55b61d', 'mac_address': '60:67:46:45:21:cc', 'speed': 100, 'mtu': 1500, 'enabled': False}, 8: {'uuid': '488cdddb-9e5e-4fb1-8e03-cfcfd47b0cb3', 'mac_address': '0f:a0:4f:52:42:5c', 'speed': 100, 'mtu': 1500, 'enabled': False}}, 'mac_address_table': {}}, '4e3de72a-60ad-416f-abb7-da352a59d13b': {'uuid': '4e3de72a-60ad-416f-abb7-da352a59d13b', 'hostname': 'domain_controller', 'operating_state': 2, 'NICs': {'34962c55-cea2-479b-ac1f-61a683c82eb1': {'uuid': '34962c55-cea2-479b-ac1f-61a683c82eb1', 'ip_adress': '192.168.1.10', 'subnet_mask': '255.255.255.0', 'mac_address': '41:a0:d4:22:b9:81', 'speed': 100, 'mtu': 1500, 'wake_on_lan': False, 'enabled': False}}, 'file_system': {'uuid': 'e3f270c9-2941-4592-892c-5662bbc49e39', 'folders': {'root': {'uuid': '486c9c58-7715-4bfc-a125-ec6ceaef5951', 'name': 'root', 'health_status': 1, 'files': {}, 'is_quarantined': False}}}, 'applications': {}, 'services': {}, 'process': {}}, '21ae4eed-489f-43a7-af0a-f70000714c79': {'uuid': '21ae4eed-489f-43a7-af0a-f70000714c79', 'hostname': 'web_server', 'operating_state': 2, 'NICs': {'060e96d7-399c-49d1-afcb-d448b3eec2d5': {'uuid': '060e96d7-399c-49d1-afcb-d448b3eec2d5', 'ip_adress': '192.168.1.12', 'subnet_mask': '255.255.255.0', 'mac_address': '7f:e0:b3:05:2c:d4', 'speed': 100, 'mtu': 1500, 'wake_on_lan': False, 'enabled': False}}, 'file_system': {'uuid': 'e8b1c5c5-f5ce-4d2f-840a-8b56e3fbd06b', 'folders': {'root': {'uuid': '86857c08-f5ed-4c60-98c9-a5c7f0985549', 'name': 'root', 'health_status': 1, 'files': {}, 'is_quarantined': False}}}, 'applications': {}, 'services': {}, 'process': {}}, 'c4b7d388-c13e-4f4e-be61-5cf05989da71': {'uuid': 'c4b7d388-c13e-4f4e-be61-5cf05989da71', 'hostname': 'database_server', 'operating_state': 2, 'NICs': {'bed479cf-480b-463b-9fff-66e4dd3d23cc': {'uuid': 'bed479cf-480b-463b-9fff-66e4dd3d23cc', 'ip_adress': '192.168.1.14', 'subnet_mask': '255.255.255.0', 'mac_address': 'b4:d7:b6:04:7b:07', 'speed': 100, 'mtu': 1500, 'wake_on_lan': False, 'enabled': False}}, 'file_system': {'uuid': '9c6f86b8-c14f-4593-8397-e3fe2e88ef66', 'folders': {'root': {'uuid': '1fd374f4-398a-4b98-9295-c2969cb3025c', 'name': 'root', 'health_status': 1, 'files': {}, 'is_quarantined': False}}}, 'applications': {}, 'services': {}, 'process': {}}, '12dc9e72-fb7c-4a8f-ab79-d7574863fa16': {'uuid': '12dc9e72-fb7c-4a8f-ab79-d7574863fa16', 'hostname': 'backup_server', 'operating_state': 2, 'NICs': {'b299a15c-cf03-4c50-92ed-ac2932472927': {'uuid': 'b299a15c-cf03-4c50-92ed-ac2932472927', 'ip_adress': '192.168.1.16', 'subnet_mask': '255.255.255.0', 'mac_address': '7b:0e:7c:b3:d1:9c', 'speed': 100, 'mtu': 1500, 'wake_on_lan': False, 'enabled': False}}, 'file_system': {'uuid': '455a7e64-bee9-41e7-80c5-a38a26d3e7db', 'folders': {'root': {'uuid': 'd5f22eda-5f15-4df6-b9c5-5f107a0d7ef0', 'name': 'root', 'health_status': 1, 'files': {}, 'is_quarantined': False}}}, 'applications': {}, 'services': {}, 'process': {}}, '37ff728f-64b5-4203-8177-cf27f51dc7c9': {'uuid': '37ff728f-64b5-4203-8177-cf27f51dc7c9', 'hostname': 'security_suite', 'operating_state': 2, 'NICs': {'9011d432-dd95-4b0b-b793-2f9d173009da': {'uuid': '9011d432-dd95-4b0b-b793-2f9d173009da', 'ip_adress': '192.168.1.110', 'subnet_mask': '255.255.255.0', 'mac_address': '58:17:29:b0:d2:e4', 'speed': 100, 'mtu': 1500, 'wake_on_lan': False, 'enabled': False}}, 'file_system': {'uuid': '9bb869b9-7253-4890-92bc-b3f7e5119b6d', 'folders': {'root': {'uuid': '5415d598-e8ad-445d-b573-f4b56e6268e3', 'name': 'root', 'health_status': 1, 'files': {}, 'is_quarantined': False}}}, 'applications': {}, 'services': {}, 'process': {}}, 'fe9ef0ad-e0d6-48b9-a884-f7c0b95de32a': {'uuid': 'fe9ef0ad-e0d6-48b9-a884-f7c0b95de32a', 'hostname': 'client_1', 'operating_state': 2, 'NICs': {'80e94189-3bd7-45f5-ac04-50974e6db2e1': {'uuid': '80e94189-3bd7-45f5-ac04-50974e6db2e1', 'ip_adress': '192.168.10.21', 'subnet_mask': '255.255.255.0', 'mac_address': 'e1:ce:28:ef:74:76', 'speed': 100, 'mtu': 1500, 'wake_on_lan': False, 'enabled': False}}, 'file_system': {'uuid': 'e4caeb03-81c1-4c0f-b619-f305ffd35c57', 'folders': {'root': {'uuid': 'b7f5c0c0-e41e-4d79-9ef8-d06e3e601929', 'name': 'root', 'health_status': 1, 'files': {}, 'is_quarantined': False}}}, 'applications': {}, 'services': {}, 'process': {}}, 'dfd186b9-7dc7-4d6f-a736-0cce3d22bfb6': {'uuid': 'dfd186b9-7dc7-4d6f-a736-0cce3d22bfb6', 'hostname': 'client_2', 'operating_state': 2, 'NICs': {'631bf440-da8f-41f0-947b-fdef122410ec': {'uuid': '631bf440-da8f-41f0-947b-fdef122410ec', 'ip_adress': '192.168.10.22', 'subnet_mask': '255.255.255.0', 'mac_address': '67:da:5c:11:c7:e4', 'speed': 100, 'mtu': 1500, 'wake_on_lan': False, 'enabled': False}}, 'file_system': {'uuid': 'd4723cc6-32b9-4b21-8eab-3517ceb47130', 'folders': {'root': {'uuid': '2bbf4393-b5b3-4c2d-8b8e-6e6b883cdace', 'name': 'root', 'health_status': 1, 'files': {}, 'is_quarantined': False}}}, 'applications': {}, 'services': {}, 'process': {}}}, 'links': {}}, 'domain': {'uuid': '3216790f-d143-47e8-aa8d-8a2f819b34c7', 'accounts': {}}}\n"
]
}
],
"source": [
"print(s.simulation.describe_state())"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.12"
},
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
}