iotsploit-cli 0.0.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,493 @@
1
+ #!/usr/bin/env python
2
+ import os
3
+ import sys
4
+ import time
5
+ import importlib
6
+ import inspect
7
+ from typing import Dict
8
+ import argparse
9
+
10
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "iotsploit_django.settings.dev")
11
+
12
+ import django
13
+ django.setup()
14
+
15
+ def ensure_database_initialized():
16
+ """Automatically initialize database tables if they don't exist"""
17
+ try:
18
+ from iotsploit_django.adapters.django.plugins.models import Plugin
19
+ Plugin.objects.exists()
20
+
21
+ from sqlalchemy.exc import OperationalError
22
+ from iotsploit_django.adapters.django.sqlalchemy_database import get_default_sqlalchemy_db
23
+ from iotsploit_django.adapters.django.device_models import DeviceDriverState
24
+
25
+ session = get_default_sqlalchemy_db().SessionLocal()
26
+ try:
27
+ session.query(DeviceDriverState).first()
28
+ session.close()
29
+ except OperationalError as e:
30
+ session.close()
31
+ if "no such table: device_driver_states" in str(e):
32
+ print("🔧 Setting up SQLAlchemy database tables...")
33
+ db = get_default_sqlalchemy_db()
34
+ db.Base.metadata.create_all(db.engine)
35
+ print("✅ SQLAlchemy tables created successfully!")
36
+ else:
37
+ raise e
38
+
39
+ except Exception as e:
40
+ if "no such table" in str(e) or "no such column" in str(e):
41
+ print("🔧 Database not initialized. Setting up database...")
42
+ try:
43
+ print("📋 Running Django migrations...")
44
+ from django.core.management import execute_from_command_line
45
+ execute_from_command_line(["manage.py", "migrate", "--noinput"])
46
+ print("✅ Django migrations completed!")
47
+
48
+ print("📋 Creating SQLAlchemy tables...")
49
+ from iotsploit_django.adapters.django.sqlalchemy_database import get_default_sqlalchemy_db
50
+ db = get_default_sqlalchemy_db()
51
+ db.Base.metadata.create_all(db.engine)
52
+ print("✅ SQLAlchemy tables created!")
53
+
54
+ print("🎉 Database initialization completed successfully!")
55
+
56
+ except Exception as init_error:
57
+ print(f"❌ Error initializing database: {init_error}")
58
+ print("💡 You may need to run: python manage.py migrate")
59
+ raise init_error
60
+ else:
61
+ raise e
62
+
63
+ # Initialize database before proceeding
64
+ ensure_database_initialized()
65
+
66
+ # Now it's safe to import Django and other modules
67
+ import cmd2
68
+ from cmd2 import ansi
69
+ from iotsploit_django.adapters.django.target_models import TargetManager
70
+ from iotsploit_core.domain.target import Vehicle
71
+ from iotsploit_django.composition_root.wiring import get_device_driver_manager, get_exploit_plugin_manager
72
+ from iotsploit_django.adapters.django.device_models import DeviceManager
73
+ from iotsploit_core.domain.device import DeviceType, SerialDevice, SocketCANDevice, USBDevice
74
+ from iotsploit_django.tools.env_mgr import Env_Mgr
75
+ from iotsploit_django.tools.report_mgr import Report_Mgr
76
+ from iotsploit_django.tools.input_mgr import Input_Mgr
77
+ from iotsploit_django.tools.xlogger import xlog as logger
78
+ from pwnlib import term
79
+ term.term_mode = True
80
+
81
+ def global_exception_handler(exctype, value, traceback):
82
+ logger.error("Unhandled exception", exc_info=(exctype, value, traceback))
83
+
84
+ sys.excepthook = global_exception_handler
85
+
86
+ def discover_command_modules():
87
+ """Auto-discover and load all command modules"""
88
+ from iotsploit_cli.commands.base_commands import BaseCommands
89
+
90
+ command_classes = []
91
+ commands_dir = os.path.join(os.path.dirname(__file__), 'commands')
92
+
93
+ for filename in os.listdir(commands_dir):
94
+ if filename.endswith('.py') and not filename.startswith('__') and filename != 'base_commands.py':
95
+ module_name = filename[:-3]
96
+
97
+ try:
98
+ module = importlib.import_module(f'iotsploit_cli.commands.{module_name}')
99
+
100
+ for name, obj in inspect.getmembers(module):
101
+ if (inspect.isclass(obj) and
102
+ issubclass(obj, BaseCommands) and
103
+ obj != BaseCommands):
104
+ command_classes.append(obj)
105
+ logger.debug(f"Discovered command class: {obj.__name__} from {module_name}")
106
+ except ImportError as e:
107
+ logger.warning(f"Could not import {module_name}: {e}")
108
+ except Exception as e:
109
+ logger.error(f"Error loading {module_name}: {e}")
110
+
111
+ logger.info(f"Auto-discovered {len(command_classes)} command modules")
112
+ return command_classes
113
+
114
+ # Auto-discover command modules
115
+ try:
116
+ CommandMixins = discover_command_modules()
117
+ logger.info(f"Successfully loaded command modules: {[cls.__name__ for cls in CommandMixins]}")
118
+ except Exception as e:
119
+ logger.error(f"Failed to discover command modules: {e}")
120
+ CommandMixins = []
121
+
122
+ # Create dynamic inheritance
123
+ SAT_Shell_Base = type('SAT_Shell_Base', (cmd2.Cmd, *CommandMixins), {})
124
+
125
+ class SAT_Shell(SAT_Shell_Base):
126
+ intro = ansi.style('''
127
+ ██╗ █████╗ ████████╗███████╗██████╗ ██╗ ██████╗ ██╗████████╗
128
+ ██║██╔═══██╗╚══██╔══╝██╔════╝██╔══██╗██║ ██╔═══██╗██║╚══██╔══╝
129
+ ██║██║ ██║ ██║ ███████╗██████╔╝██║ ██║ ██║██║ ██║
130
+ ██║██║ ██║ ██║ ╚════██║██╔═══╝ ██║ ██║ ██║██║ ██║
131
+ ██║╚██████╔╝ ██║ ███████║██║ ███████╗╚██████╔╝██║ ██║
132
+ ╚═╝ ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝
133
+ ''', fg=ansi.Fg.GREEN) + '\n' + ansi.style('Welcome to IoTSploit Shell. Type help or ? to list commands.\n', fg=ansi.Fg.YELLOW)
134
+
135
+ prompt = ansi.style('<IoX_SHELL> ', fg=ansi.Fg.BLUE)
136
+
137
+ def __init__(self):
138
+ # Initialize the command categories dictionary before calling super().__init__()
139
+ self._cmd_to_category = {}
140
+
141
+ # Now call the parent class initialization
142
+ super().__init__()
143
+
144
+ # Rest of your initialization code...
145
+ self.django_server_process = None
146
+ self.daphne_server_process = None
147
+ self.mcp_bridge_process = None
148
+ self.celery_worker_process = None
149
+
150
+ # Initialize device manager and connected devices
151
+ self.device_driver_manager = get_device_driver_manager()
152
+ # 初始化设备相关属性
153
+ self._current_plugin = None
154
+ self._current_device = None
155
+ self._current_driver = None
156
+ self.connected_devices = {}
157
+
158
+
159
+ # Initialize plugin manager
160
+ # Use Django composition root to wire ORM repos (+ optional Celery runner).
161
+ # Default to Celery-enabled behavior for parity with previous implementation.
162
+ use_celery = os.getenv("SAT_SHELL_USE_CELERY", "1").lower() not in ("0", "false", "no")
163
+ self.plugin_manager = get_exploit_plugin_manager(use_celery=use_celery)
164
+ self.plugin_manager.initialize()
165
+
166
+ # Initialize target manager
167
+ self.target_manager = TargetManager.get_instance()
168
+ self.target_manager.register_target("vehicle", Vehicle)
169
+
170
+ # Check if database has any targets, if not, optionally load from JSON as initial setup
171
+ existing_targets = self.target_manager.get_all_targets()
172
+ if not existing_targets:
173
+ logger.info("No targets found in database. You can:")
174
+ logger.info("1. Use Flutter UI to create targets")
175
+ logger.info("2. Use 'target_import' command to import from JSON file")
176
+ logger.info("3. Use target management commands to create targets manually")
177
+ else:
178
+ logger.info(f"Found {len(existing_targets)} targets in database")
179
+
180
+ # Note: Removed automatic JSON loading to prevent overwriting user changes
181
+ # Use 'target_import conf/target.json' command if you need to import from JSON
182
+
183
+ # Initialize device manager
184
+ self.device_manager = DeviceManager.get_instance()
185
+ self.device_manager.register_device(DeviceType.Serial, SerialDevice)
186
+ self.device_manager.register_device(DeviceType.USB, USBDevice)
187
+ self.device_manager.register_device(DeviceType.CAN, SocketCANDevice)
188
+
189
+ # Note: Removed automatic JSON loading to use database as single source of truth
190
+ # Devices are now discovered by drivers via scan() or managed through UI/CLI
191
+ # Use 'device_import <json_file>' command if you need to import from JSON
192
+ existing_devices = self.device_manager.get_all_devices()
193
+ if not existing_devices:
194
+ logger.info("No devices found in database. Devices will be auto-discovered by drivers.")
195
+ logger.info("You can also use 'device_import conf/devices.json' to import from JSON file.")
196
+
197
+
198
+ # Customize help display
199
+ self.help_category_header = ansi.style("\n{:-^80}\n", fg=ansi.Fg.BLUE)
200
+ self.help_category_footer = "\n"
201
+
202
+ # Group all commands under Shell Commands
203
+ self._cmd_to_category.update({
204
+ 'alias': 'Shell Commands',
205
+ 'connect_wifi': 'Shell Commands',
206
+ 'device_info': 'Shell Commands',
207
+ 'edit': 'Shell Commands',
208
+ 'execute_plugin': 'Shell Commands',
209
+ 'exec': 'Shell Commands',
210
+ 'exit': 'Shell Commands',
211
+ 'exploit': 'Shell Commands',
212
+ 'help': 'Shell Commands',
213
+ 'history': 'Shell Commands',
214
+ 'list_device_drivers': 'Shell Commands',
215
+ 'list_devices': 'Shell Commands',
216
+ 'list_plugins': 'Shell Commands',
217
+ 'list_targets': 'Shell Commands',
218
+ 'ls': 'Shell Commands',
219
+ 'lsdev': 'Shell Commands',
220
+ 'lsdrv': 'Shell Commands',
221
+ 'lsp': 'Shell Commands',
222
+ 'lst': 'Shell Commands',
223
+ 'lsusb': 'Shell Commands',
224
+ 'macro': 'Shell Commands',
225
+ 'quit': 'Shell Commands',
226
+ 'run_pyscript': 'Shell Commands',
227
+ 'run_script': 'Shell Commands',
228
+ 'runserver': 'Shell Commands',
229
+ 'set': 'Shell Commands',
230
+ 'set_log_level': 'Shell Commands',
231
+ 'shell': 'Shell Commands',
232
+ 'shortcuts': 'Shell Commands',
233
+ 'stop_server': 'Shell Commands',
234
+ })
235
+
236
+ def emptyline(self):
237
+ self.onecmd("help")
238
+
239
+ def do_help(self, arg):
240
+ 'List available commands with "help" or detailed help with "help cmd".'
241
+ if arg:
242
+ # Show help for specific command
243
+ super().do_help(arg)
244
+ return
245
+
246
+ self.poutput(ansi.style("\nAvailable Commands:", fg=ansi.Fg.GREEN, bold=True))
247
+ self.poutput(ansi.style("Use 'help <command>' for detailed information about a command.\n", fg=ansi.Fg.YELLOW))
248
+
249
+ cmds_by_category = self.get_all_commands_by_category()
250
+
251
+ categories = sorted([cat for cat in cmds_by_category.keys() if cat != 'Shell Commands'])
252
+ if 'Shell Commands' in cmds_by_category:
253
+ categories.append('Shell Commands')
254
+
255
+ for category in categories:
256
+ if category == 'Uncategorized':
257
+ continue
258
+
259
+ self.poutput(self.help_category_header.format(f" {category} "))
260
+ cmd_list = sorted(cmds_by_category[category])
261
+
262
+ max_cmd_length = max(len(cmd) for cmd in cmd_list) + 2
263
+
264
+ for cmd in cmd_list:
265
+ doc = self.get_command_doc(cmd)
266
+ padded_cmd = f" {cmd:<{max_cmd_length}}"
267
+ self.poutput(ansi.style(padded_cmd, fg=ansi.Fg.CYAN) +
268
+ ansi.style(f"- {doc}", fg=ansi.Fg.WHITE))
269
+ self.poutput(self.help_category_footer)
270
+
271
+ total_commands = sum(len(cmds) for cat, cmds in cmds_by_category.items() if cat != 'Uncategorized')
272
+ self.poutput(ansi.style(f"\nTotal commands: {total_commands}", fg=ansi.Fg.GREEN))
273
+
274
+ def get_command_doc(self, cmd_name):
275
+ """Get the first line of the command's docstring."""
276
+ cmd_func = getattr(self, 'do_' + cmd_name, None)
277
+ if cmd_func and cmd_func.__doc__:
278
+ return cmd_func.__doc__.split('\n')[0]
279
+ return ''
280
+
281
+ def get_all_commands_by_category(self):
282
+ """Return a dict mapping category names to lists of command names."""
283
+ categories = {}
284
+
285
+ # Get all command names (methods starting with 'do_')
286
+ command_names = [attr[3:] for attr in dir(self) if attr.startswith('do_')]
287
+
288
+ for cmd_name in command_names:
289
+ # Get the command function
290
+ cmd_func = getattr(self, 'do_' + cmd_name)
291
+
292
+ # Get category from cmd2's category decorator or from our manual mapping
293
+ if hasattr(cmd_func, 'category'):
294
+ category = cmd_func.category
295
+ else:
296
+ # Check our manual mapping or default to 'Uncategorized'
297
+ category = self._cmd_to_category.get(cmd_name, 'Uncategorized')
298
+
299
+ # Add command to appropriate category list
300
+ if category not in categories:
301
+ categories[category] = []
302
+ categories[category].append(cmd_name)
303
+
304
+ return categories
305
+
306
+ def _select_device(self):
307
+ """Helper method to handle device selection process"""
308
+ try:
309
+ available_plugins = [
310
+ driver_name for driver_name, device
311
+ in self.connected_devices.items()
312
+ ]
313
+
314
+ if not available_plugins:
315
+ logger.error(ansi.style("No initialized devices available", fg=ansi.Fg.RED))
316
+ return False
317
+
318
+ selected_plugin = Input_Mgr.Instance().single_choice(
319
+ "Select device plugin",
320
+ available_plugins
321
+ )
322
+
323
+ device = self.connected_devices.get(selected_plugin)
324
+ if not device:
325
+ logger.error(ansi.style(f"Device not found for {selected_plugin}", fg=ansi.Fg.RED))
326
+ return False
327
+
328
+ self._current_device = device
329
+ self._current_driver = self.device_driver_manager.get_driver_instance(selected_plugin)
330
+ self._current_plugin = selected_plugin
331
+
332
+ logger.info(ansi.style(f"Selected device: {device.name}", fg=ansi.Fg.GREEN))
333
+ return True
334
+
335
+ except Exception as e:
336
+ logger.error(ansi.style(f"Error during device selection: {str(e)}", fg=ansi.Fg.RED))
337
+ logger.debug("Detailed error:", exc_info=True)
338
+ return False
339
+
340
+ def _display_devices(self, devices: Dict, sources: Dict):
341
+ """Helper method to display device information"""
342
+ for device_id, device in devices.items():
343
+ source = sources.get(device_id, "unknown")
344
+ source_color = ansi.Fg.GREEN if source == "dynamic" else ansi.Fg.BLUE
345
+
346
+ logger.info(ansi.style(f"\n Device ID: {device_id}", fg=ansi.Fg.CYAN))
347
+ logger.info(ansi.style(f" Source: {source}", fg=source_color))
348
+ logger.info(f" Name: {device.name}")
349
+ logger.info(" " + "-" * 40)
350
+
351
+ def _auto_initialize_devices(self):
352
+ """自动扫描并初始化所有可用设备"""
353
+ logger.info("Automatic device initialization started...")
354
+
355
+ available_drivers = list(self.device_driver_manager.drivers.keys())
356
+ if not available_drivers:
357
+ logger.warning("No device drivers available!")
358
+ return
359
+
360
+ logger.info(f"Found {len(available_drivers)} drivers: {', '.join(available_drivers)}")
361
+
362
+ for driver_name in available_drivers:
363
+ try:
364
+ logger.info(f"Initializing {driver_name}...")
365
+
366
+ scan_result = self.device_driver_manager.scan_devices(driver_name)
367
+ if scan_result['status'] != 'success':
368
+ logger.error(f"Failed to scan {driver_name}: {scan_result.get('message', 'Unknown error')}")
369
+ continue
370
+
371
+ devices = scan_result.get('devices', [])
372
+ if not devices:
373
+ logger.warning(f"No devices found for {driver_name}")
374
+ continue
375
+
376
+ logger.info(f"Found {len(devices)} device(s) for {driver_name}")
377
+
378
+ for device in devices:
379
+ try:
380
+ logger.info(f"Processing device: {device.name} (ID: {device.device_id})")
381
+
382
+ init_result = self.device_driver_manager.initialize_device(driver_name, device)
383
+ if init_result['status'] != 'success':
384
+ logger.error(f"Failed to initialize {device.name}: {init_result['message']}")
385
+ continue
386
+
387
+ connect_result = self.device_driver_manager.connect_device(driver_name, device)
388
+ if connect_result['status'] != 'success':
389
+ logger.error(f"Failed to connect {device.name}: {connect_result['message']}")
390
+ continue
391
+
392
+ self.connected_devices[driver_name] = device
393
+ logger.info(f"Successfully connected {device.name} using {driver_name}")
394
+
395
+ except Exception as e:
396
+ logger.error(f"Error processing device: {str(e)}")
397
+
398
+ except Exception as e:
399
+ logger.error(f"Error initializing {driver_name}: {str(e)}")
400
+
401
+ self._show_initialization_summary()
402
+
403
+ def _show_initialization_summary(self):
404
+ """打印设备初始化摘要"""
405
+ logger.info("Device Initialization Summary:")
406
+
407
+ for driver_name, driver in self.device_driver_manager.drivers.items():
408
+ logger.info("")
409
+ logger.info(f"{driver_name}:")
410
+
411
+ current_device = self.connected_devices.get(driver_name)
412
+
413
+ if current_device:
414
+ state = self.device_driver_manager.get_device_state(
415
+ driver_name,
416
+ device_id=current_device.device_id
417
+ )
418
+ logger.info(f" Device: {current_device.name}")
419
+ logger.info(f" State: {state.value}")
420
+ else:
421
+ logger.info(f" Device: No device connected")
422
+ logger.info(f" State: unknown")
423
+
424
+ commands = self.device_driver_manager.get_supported_commands(driver_name)
425
+ if commands:
426
+ logger.info(f" Commands: {', '.join(commands.keys())}")
427
+
428
+ def _cleanup_devices(self):
429
+ """清理所有设备连接"""
430
+ if not self.connected_devices:
431
+ return
432
+
433
+ logger.info("Cleaning up device connections...")
434
+ for driver_name, device in list(self.connected_devices.items()):
435
+ try:
436
+ result = self.device_driver_manager.close_device(driver_name, device)
437
+ if result['status'] == 'success':
438
+ logger.info(f"Successfully closed {device.name}")
439
+ del self.connected_devices[driver_name]
440
+ else:
441
+ logger.error(f"Failed to close {device.name}: {result['message']}")
442
+ except Exception as e:
443
+ logger.error(f"Error closing {device.name}: {str(e)}")
444
+
445
+ def main():
446
+ import signal
447
+ import atexit
448
+
449
+ parser = argparse.ArgumentParser(description='SAT Shell entrypoint')
450
+ parser.add_argument('--runserver', action='store_true', help='Start servers directly and keep running until Ctrl+C; on Ctrl+C, stop servers')
451
+ args = parser.parse_args()
452
+
453
+ shell = SAT_Shell()
454
+ Report_Mgr.Instance().log_init()
455
+ Env_Mgr.Instance().set("SAT_RUN_IN_SHELL", True)
456
+
457
+ # Register OS signal + atexit handlers in the entrypoint so that unexpected exits
458
+ # (terminal closed / SIGTERM) also trigger the same cleanup path.
459
+ def _shutdown():
460
+ try:
461
+ shell.do_stop_server("")
462
+ finally:
463
+ try:
464
+ shell._cleanup_devices()
465
+ except Exception:
466
+ pass
467
+
468
+ def _signal_handler(signum, frame): # noqa: ARG001
469
+ _shutdown()
470
+ raise SystemExit(0)
471
+
472
+ def _atexit_handler():
473
+ # Do not raise here; just best-effort cleanup.
474
+ _shutdown()
475
+
476
+ signal.signal(signal.SIGTERM, _signal_handler)
477
+ signal.signal(signal.SIGINT, _signal_handler)
478
+ signal.signal(signal.SIGHUP, _signal_handler)
479
+ atexit.register(_atexit_handler)
480
+
481
+ if args.runserver:
482
+ ok = shell.do_runserver("")
483
+ if ok is False:
484
+ sys.exit(1)
485
+ while True:
486
+ time.sleep(1)
487
+ else:
488
+ shell.cmdloop()
489
+
490
+
491
+ if __name__ == '__main__':
492
+ main()
493
+
@@ -0,0 +1,67 @@
1
+ Metadata-Version: 2.3
2
+ Name: iotsploit-cli
3
+ Version: 0.0.6
4
+ Summary: IoTSploit CLI shell (console + command modules) - IoT security testing interactive interface
5
+ License: GPL-3.0-or-later
6
+ Keywords: iot,security,testing,pentest,cli,shell
7
+ Author: IoTSploit Team
8
+ Author-email: support@iotsploit.org
9
+ Requires-Python: >=3.10,<4.0
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Information Technology
14
+ Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Security
21
+ Classifier: Topic :: System :: Hardware
22
+ Requires-Dist: cmd2 (>=2.4,<3.0)
23
+ Requires-Dist: iotsploit-core (>=0.0.6,<0.0.7)
24
+ Requires-Dist: iotsploit-django (>=0.0.6,<0.0.7)
25
+ Requires-Dist: iotsploit-drivers (>=0.0.6,<0.0.7)
26
+ Requires-Dist: iotsploit-exploits (>=0.0.6,<0.0.7)
27
+ Requires-Dist: iotsploit-mcp (>=0.0.6,<0.0.7)
28
+ Requires-Dist: pwntools (>=4.12,<5.0)
29
+ Project-URL: Documentation, https://www.iotsploit.org/
30
+ Project-URL: Homepage, https://www.iotsploit.org/
31
+ Project-URL: Repository, https://github.com/TKXB/iotsploit
32
+ Description-Content-Type: text/markdown
33
+
34
+ # iotsploit-cli
35
+
36
+ IoTSploit interactive CLI shell for IoT security testing.
37
+
38
+ ## Overview
39
+
40
+ This package provides the `iotsploit` command-line shell built on top of `cmd2`.
41
+ It bundles the core console loop (`console.py`) and all command modules
42
+ (`commands/`) that implement device management, plugin execution, target
43
+ management, network operations, and more.
44
+
45
+ ## Installation
46
+
47
+ ```bash
48
+ pip install iotsploit-cli
49
+ ```
50
+
51
+ ## Usage
52
+
53
+ ```bash
54
+ iotsploit
55
+ ```
56
+
57
+ Or with the Django server started immediately:
58
+
59
+ ```bash
60
+ iotsploit --runserver
61
+ ```
62
+
63
+ ## License
64
+
65
+ GPL-3.0-or-later. See [LICENSE](../LICENSE) for details.
66
+ For commercial use, contact wang3919379@gmail.com.
67
+
@@ -0,0 +1,16 @@
1
+ iotsploit_cli/__init__.py,sha256=QpQDB0C0TMW2nV9L602di-QvV29L1HZkbRcSwD7cXOI,29
2
+ iotsploit_cli/commands/__init__.py,sha256=LXBeYtLCrCoVV2REWHEsBXxiDypZPVdLR9vjVg2nE5g,33
3
+ iotsploit_cli/commands/base_commands.py,sha256=I186hrgquBYY0HP_NPWzUsGmWi2LIwjjUckmMJSw3ws,97
4
+ iotsploit_cli/commands/device_commands.py,sha256=JPfAVgQg_BLrsX25vpwu9KN-cbKWDpuXa6C1vh1KJ9g,23466
5
+ iotsploit_cli/commands/django_commands.py,sha256=kwEr1bjnHukT0wgiIEfGyFWBZ-gGudo41Db6UbmcZYw,10828
6
+ iotsploit_cli/commands/firmware_commands.py,sha256=Y4YWxtSnWhS3gXvUbbZYsrjgPo1Q2JzW1lt02RCi-TQ,9142
7
+ iotsploit_cli/commands/linux_commands.py,sha256=XrkjhivUQF3n0k_4TEr7atZhWcqudx5lVCK46VrwukM,1110
8
+ iotsploit_cli/commands/network_commands.py,sha256=xCstdzff-h9kUTHM8sXnZCDSs7RRAOlYglG17f-YFko,1181
9
+ iotsploit_cli/commands/plugin_commands.py,sha256=pQQt85-Z8YoDZaTe46Xr0T4p2emWCiD808gXx_6yEIA,11774
10
+ iotsploit_cli/commands/system_commands.py,sha256=V9E-TQzElbNXYfU2uIsFccrROewHjyIIIjG9my7i9ZY,3187
11
+ iotsploit_cli/commands/target_commands.py,sha256=xG8l9rCPRw4CskTXOjSl2fGEmeRwYxOX1P5FcLYbB-s,10475
12
+ iotsploit_cli/console.py,sha256=S5xBMkstUZd3v6mDj9EXYTeEzuqOOIN2RFKgChLOv7Q,21378
13
+ iotsploit_cli-0.0.6.dist-info/METADATA,sha256=hHe3sgcoGXxq7MPG0DLPHlEqF4i0YdB4IkPB5g1Aev8,2080
14
+ iotsploit_cli-0.0.6.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
15
+ iotsploit_cli-0.0.6.dist-info/entry_points.txt,sha256=c7_NpNNSkaSi0_gYVGX6LeZYekfNx5RhjzReN1cFZ30,56
16
+ iotsploit_cli-0.0.6.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.0.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,3 @@
1
+ [console_scripts]
2
+ iotsploit=iotsploit_cli.console:main
3
+