lanscape 1.4.4__py3-none-any.whl → 1.5.0__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.

Potentially problematic release.


This version of lanscape might be problematic. Click here for more details.

@@ -2,7 +2,7 @@
2
2
  Web blueprint routes for the LANscape application.
3
3
  Handles UI views including the main dashboard, scan results, error display, and exports.
4
4
  """
5
- from flask import render_template, request, redirect
5
+ from flask import render_template, request, redirect, url_for
6
6
  from lanscape.ui.blueprints.web import web_bp
7
7
  from lanscape.libraries.net_tools import (
8
8
  get_all_network_subnets,
@@ -81,6 +81,33 @@ def view_errors(scan_id):
81
81
  return redirect('/')
82
82
 
83
83
 
84
+ @web_bp.route('/device/<scan_id>/<device_ip>')
85
+ def view_device(scan_id, device_ip):
86
+ """
87
+ Display detailed information about a specific device from a scan.
88
+
89
+ Args:
90
+ scan_id: Unique identifier for the scan
91
+ device_ip: IP address of the device to view
92
+
93
+ Returns:
94
+ Rendered device detail template or redirect to home if scan not found
95
+ """
96
+ if scanner := scan_manager.get_scan(scan_id):
97
+ devices = scanner.results.devices
98
+ device_info = next(
99
+ (device for device in devices if getattr(
100
+ device, 'ip', None) == device_ip), None)
101
+
102
+ if device_info:
103
+ return render_template('scan/device-detail.html', device=device_info, scan_id=scan_id)
104
+
105
+ log.debug(f'Device {device_ip} not found in scan {scan_id}')
106
+ return redirect(url_for('render_scan', scan_id=scan_id))
107
+ log.debug(f'Redirecting, scan {scan_id} doesnt exist in memory')
108
+ return redirect('/')
109
+
110
+
84
111
  @web_bp.route('/export/<scan_id>')
85
112
  def export_scan(scan_id):
86
113
  """
@@ -555,6 +555,41 @@ input[type="range"] {
555
555
  color: var(--text-color);
556
556
  }
557
557
 
558
+ /* Service Strategy Select Styles */
559
+ .service-strategy-wrapper {
560
+ position: relative;
561
+ display: inline-block;
562
+ width: 60%; /* Narrower than port list */
563
+ }
564
+
565
+ .service-strategy {
566
+ position: relative;
567
+ background-color: var(--secondary-bg);
568
+ border: 1px solid var(--border-color);
569
+ color: var(--text-color);
570
+ padding: 10px;
571
+ cursor: pointer;
572
+ width: 100%;
573
+ height: 42px;
574
+ user-select: none;
575
+ appearance: none; /* Hide default arrow */
576
+ transition: all .2s ease-in-out;
577
+ }
578
+
579
+ .service-strategy:focus {
580
+ border-color: var(--primary-accent-hover);
581
+ outline: none;
582
+ }
583
+
584
+ .service-strategy-wrapper::after {
585
+ content: '▼';
586
+ position: absolute;
587
+ top: 10px;
588
+ right: 10px;
589
+ pointer-events: none;
590
+ color: var(--text-color);
591
+ }
592
+
558
593
 
559
594
 
560
595
  .text-color {
@@ -612,6 +647,35 @@ input[type="range"] {
612
647
  background-color: var(--primary-bg-accent);
613
648
  }
614
649
 
650
+ .table tbody tr td:has(.info-icon-container),
651
+ .table thead tr th.detail-col
652
+ {
653
+ width: 30px;
654
+ /*
655
+ background-color: var(--body-bg);
656
+ border: 1px solid var(--text-almost-hidden);
657
+ */
658
+ }
659
+ .table td:has(.info-icon-container) {
660
+ width: 30px;
661
+ text-align: center; /* horizontal center */
662
+ vertical-align: middle; /* vertical center inside the cell */
663
+ padding: 0; /* optional: remove extra padding */
664
+ }
665
+ .table td .info-icon-container {
666
+ display: flex;
667
+ justify-content: center; /* horizontal center */
668
+ align-items: center; /* vertical center */
669
+ height: 100%; /* ensure it takes full cell height */
670
+ }
671
+ .table td .info-icon-container .info-icon {
672
+ font-size: 1.2em;
673
+ color: var(--text-placeholder);
674
+ cursor: pointer;
675
+ }
676
+ .table td .info-icon:hover {
677
+ color: var(--text-color);
678
+ }
615
679
 
616
680
  /* Badge Styles */
617
681
  .badge-warning {
@@ -627,7 +691,7 @@ input[type="range"] {
627
691
  }
628
692
 
629
693
  .badge-info {
630
- background-color: var(--info-accent);
694
+ background-color: var(--primary-accent);
631
695
  }
632
696
 
633
697
  .badge-secondary {
@@ -835,4 +899,83 @@ html {
835
899
 
836
900
  }
837
901
 
838
- /* END overview container */
902
+ /* END overview container */
903
+
904
+ /* Device Modal Styles */
905
+ #device-modal {
906
+ --bs-modal-width: 750px;
907
+ }
908
+ #device-modal .modal-content {
909
+ background-color: var(--primary-bg);
910
+ }
911
+ #device-modal h6 {
912
+ color: var(--primary-accent);
913
+ }
914
+
915
+ /* Key/Value grid for overview */
916
+ #device-modal .kv-grid {
917
+ display: grid;
918
+ grid-template-columns: 180px 1fr;
919
+ column-gap: 12px;
920
+ row-gap: 8px;
921
+ align-items: center;
922
+ }
923
+ #device-modal .kv-label {
924
+ color: var(--text-placeholder);
925
+ text-align: right;
926
+ }
927
+ #device-modal .kv-value {
928
+ color: var(--text-color);
929
+ }
930
+
931
+ /* Port/service chips */
932
+ #device-modal .chip-group { margin-top: 4px; }
933
+ #device-modal .chip {
934
+ display: inline-flex;
935
+ align-items: center;
936
+ gap: 4px;
937
+ padding: 2px 8px;
938
+ margin: 2px;
939
+ font-size: .85rem;
940
+ background-color: var(--primary-bg-accent);
941
+ border: 1px solid var(--border-color);
942
+ border-radius: 999px;
943
+ color: var(--text-color);
944
+ }
945
+ #device-modal .chip .material-symbols-outlined {
946
+ font-size: 16px;
947
+ }
948
+
949
+ /* Services layout */
950
+ #device-modal .service-list { width: 100%; }
951
+ #device-modal .service-row {
952
+ display: flex;
953
+ align-items: flex-start;
954
+ gap: 8px;
955
+ padding: 6px 0;
956
+ border-bottom: 1px dashed var(--border-color);
957
+ }
958
+ #device-modal .service-row:last-child {
959
+ border-bottom: none;
960
+ }
961
+ #device-modal .service-name {
962
+ min-width: 140px;
963
+ color: var(--text-accent-color);
964
+ font-weight: 600;
965
+ }
966
+ #device-modal .service-ports { flex: 1; }
967
+
968
+ /* Errors */
969
+ #device-modal .error-list li {
970
+ margin-bottom: 4px;
971
+ }
972
+
973
+ /* Responsive tweaks */
974
+ @media screen and (max-width: 576px) {
975
+ #device-modal .kv-grid {
976
+ grid-template-columns: 130px 1fr;
977
+ }
978
+ #device-modal .service-name {
979
+ min-width: 110px;
980
+ }
981
+ }
@@ -1,5 +1,3 @@
1
-
2
-
3
1
  $(document).ready(function() {
4
2
  // Load port lists into the dropdown
5
3
  const scanId = getActiveScanId();
@@ -217,6 +215,36 @@ $(window).on('resize', function() {
217
215
  resizeIframe($('#ip-table-frame')[0]);
218
216
  });
219
217
 
218
+ function openDeviceDetail(deviceIp) {
219
+ try {
220
+ const scanId = getActiveScanId();
221
+ if (!scanId || !deviceIp) return;
222
+
223
+ const safeIp = encodeURIComponent(deviceIp.trim());
224
+
225
+ // Remove any existing modal instance to avoid duplicates
226
+ $('#device-modal').remove();
227
+
228
+ $.get(`/device/${scanId}/${safeIp}`, function(html) {
229
+ // Append modal HTML to the document
230
+ $('body').append(html);
231
+
232
+ // Show the modal
233
+ const $modal = $('#device-modal');
234
+ $modal.modal('show');
235
+
236
+ // Clean up after closing
237
+ $modal.on('hidden.bs.modal', function() {
238
+ $(this).remove();
239
+ });
240
+ }).fail(function() {
241
+ console.error('Failed to load device details');
242
+ });
243
+ } catch (e) {
244
+ console.error('Error opening device detail modal:', e);
245
+ }
246
+ }
247
+
220
248
 
221
249
 
222
250
 
@@ -13,6 +13,7 @@ $(document).ready(function() {
13
13
 
14
14
  $('#t_cnt_port_scan, #t_cnt_port_test').on('input', updatePortTotals);
15
15
  $('#ping_attempts, #ping_ping_count').on('input', updatePingTotals);
16
+ $('#task_scan_port_services').on('change', updateVisibility);
16
17
 
17
18
  // Lookup type toggles
18
19
  $('.lookup-type-input').on('change', onLookupTypeChanged);
@@ -43,6 +44,30 @@ function setScanConfig(configName) {
43
44
  $('#task_scan_ports').prop('checked', config.task_scan_ports);
44
45
  $('#task_scan_port_services').prop('checked', config.task_scan_port_services);
45
46
 
47
+ // port scan config
48
+ if (config.port_scan_config) {
49
+ $('#port_scan_timeout').val(config.port_scan_config.timeout);
50
+ $('#port_scan_retries').val(config.port_scan_config.retries);
51
+ $('#port_scan_retry_delay').val(config.port_scan_config.retry_delay);
52
+ } else {
53
+ // defaults if missing
54
+ $('#port_scan_timeout').val(1.0);
55
+ $('#port_scan_retries').val(0);
56
+ $('#port_scan_retry_delay').val(0.1);
57
+ }
58
+
59
+ // service config
60
+ if (config.service_scan_config) {
61
+ $('#service_lookup_type').val(config.service_scan_config.lookup_type || 'BASIC');
62
+ $('#service_timeout').val(config.service_scan_config.timeout);
63
+ $('#service_max_concurrent_probes').val(config.service_scan_config.max_concurrent_probes);
64
+ } else {
65
+ // defaults if missing
66
+ $('#service_lookup_type').val('BASIC');
67
+ $('#service_timeout').val(5.0);
68
+ $('#service_max_concurrent_probes').val(10);
69
+ }
70
+
46
71
  // lookup type (array of enum values as strings)
47
72
  setLookupTypeUI(config.lookup_type || []);
48
73
 
@@ -99,6 +124,16 @@ function getScanConfig() {
99
124
  poke_config: {
100
125
  attempts: parseInt($('#poke_attempts').val()),
101
126
  timeout: parseFloat($('#poke_timeout').val())
127
+ },
128
+ port_scan_config: {
129
+ timeout: parseFloat($('#port_scan_timeout').val()),
130
+ retries: parseInt($('#port_scan_retries').val()),
131
+ retry_delay: parseFloat($('#port_scan_retry_delay').val())
132
+ },
133
+ service_scan_config: {
134
+ timeout: parseFloat($('#service_timeout').val()),
135
+ lookup_type: $('#service_lookup_type').val(),
136
+ max_concurrent_probes: parseInt($('#service_max_concurrent_probes').val())
102
137
  }
103
138
  };
104
139
  }
@@ -168,6 +203,10 @@ function updateVisibility() {
168
203
  // Poke section only when POKE_THEN_ARP is selected
169
204
  const showPoke = types.has('POKE_THEN_ARP');
170
205
  toggleSection('#section-poke', showPoke);
206
+
207
+ // Service scan section visible only if stage enabled
208
+ const showService = $('#task_scan_port_services').is(':checked');
209
+ toggleSection('#section-service-scan', showService);
171
210
  }
172
211
 
173
212
  function toggleSection(selector, show) {
@@ -181,6 +181,49 @@
181
181
  </div>
182
182
  </div>
183
183
  </div>
184
+ <div class="row mt-2">
185
+ <div class="col">
186
+ <label for="port_scan_timeout" class="form-label">Port Timeout (sec)</label>
187
+ <input type="number" step="0.1" id="port_scan_timeout" class="form-control">
188
+ </div>
189
+ <div class="col">
190
+ <label for="port_scan_retries" class="form-label">Retries</label>
191
+ <input type="number" id="port_scan_retries" class="form-control">
192
+ </div>
193
+ <div class="col">
194
+ <label for="port_scan_retry_delay" class="form-label">Retry Delay (sec)</label>
195
+ <input type="number" step="0.1" id="port_scan_retry_delay" class="form-control">
196
+ </div>
197
+ </div>
198
+ </div>
199
+
200
+ <div id="section-service-scan" class="form-group mt-2 config-section div-hide">
201
+ <h6>Service Scanning</h6>
202
+ <div class="row">
203
+ <div class="col">
204
+ <label for="service_lookup_type" class="form-label">Strategy</label>
205
+ <div class="service-strategy-wrapper">
206
+ <select id="service_lookup_type" class="service-strategy">
207
+ <option value="LAZY">Lazy (few probes)</option>
208
+ <option value="BASIC">Basic (common probes)</option>
209
+ <option value="AGGRESSIVE">Aggressive (all probes)</option>
210
+ </select>
211
+ </div>
212
+ </div>
213
+ <div class="col">
214
+ <label for="service_timeout" class="form-label">Timeout (sec)</label>
215
+ <input type="number" step="0.1" id="service_timeout" class="form-control">
216
+ </div>
217
+ <div class="col">
218
+ <label for="service_max_concurrent_probes" class="form-label">Max Concurrent Probes</label>
219
+ <input type="number" id="service_max_concurrent_probes" class="form-control">
220
+ </div>
221
+ </div>
222
+ <div class="row mt-1">
223
+ <div class="col">
224
+ <small class="text-secondary">Attempts service identification on open ports using selected strategy.</small>
225
+ </div>
226
+ </div>
184
227
  </div>
185
228
 
186
229
  <div class="form-group mt-2 config-section">
@@ -0,0 +1,111 @@
1
+ <div class="modal fade" id="device-modal" tabindex="-1" aria-hidden="true">
2
+ <div class="modal-dialog">
3
+ <div class="modal-content">
4
+ <div class="modal-header border-secondary">
5
+ {% set ip = device.ip|default('Unknown IP') %}
6
+ {% set hostname = device.hostname|default('') %}
7
+ {% set manufacturer = device.manufacturer|default('') %}
8
+ {% set mac = device.mac_addr|default('') %}
9
+ {% set stage = device.stage|default('unknown') %}
10
+ {% set ports = device.ports|default([]) %}
11
+ {% set services = device.services|default({}) %}
12
+ {% set errors = device.caught_errors|default([]) %}
13
+ {% set stage_lower = (stage|string|lower) %}
14
+ {% set stage_badge_class = 'badge-success' if stage_lower == 'complete' else 'badge-warning' if stage_lower in ['running', 'in_progress'] else 'badge-secondary' %}
15
+ <h5 class="modal-title" id="device-modalLabel">
16
+ Device Details
17
+ <span class="text-secondary small ms-2">IP: {{ ip }}</span>
18
+ </h5>
19
+ <button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
20
+ </div>
21
+
22
+ <div class="modal-body">
23
+ <div class="config-sections">
24
+ <div class="form-group mt-2 config-section">
25
+ <h6>Overview</h6>
26
+ <div class="kv-grid mt-1">
27
+ <div class="kv-label">IP Address</div>
28
+ <div class="kv-value">{{ ip }}</div>
29
+
30
+ <div class="kv-label">Hostname</div>
31
+ <div class="kv-value">{{ hostname|trim if hostname|default('')|trim else 'Unknown Hostname' }}</div>
32
+
33
+ <div class="kv-label">MAC Address</div>
34
+ <div class="kv-value">{{ mac|trim if mac|default('')|trim else 'Unknown MAC' }}</div>
35
+
36
+ <div class="kv-label">Manufacturer</div>
37
+ <div class="kv-value">{{ manufacturer|trim if manufacturer|default('')|trim else 'Unknown Manufacturer' }}</div>
38
+
39
+ <div class="kv-label">Stage</div>
40
+ <div class="kv-value">
41
+ <span class="badge {{ stage_badge_class }}">{{ stage|default('unknown')|capitalize }}</span>
42
+ </div>
43
+ </div>
44
+ </div>
45
+
46
+ <div class="form-group mt-2 config-section">
47
+ <h6>Open Ports</h6>
48
+ {% if ports and ports|length > 0 %}
49
+ <div class="chip-group mt-1">
50
+ {% for p in ports %}
51
+ <span class="chip" title="Port {{ p }}">
52
+ <span class="material-symbols-outlined">lan</span>{{ p }}
53
+ </span>
54
+ {% endfor %}
55
+ </div>
56
+ {% else %}
57
+ <div class="text-secondary">No open ports detected.</div>
58
+ {% endif %}
59
+ </div>
60
+
61
+ <div class="form-group mt-2 config-section">
62
+ <h6>Services</h6>
63
+ {% if services and services|length > 0 %}
64
+ <div class="service-list mt-1">
65
+ {% for svc, svc_ports in services|dictsort %}
66
+ <div class="service-row">
67
+ <div class="service-name">
68
+ <span class="material-symbols-outlined align-middle me-1">dns</span>{{ svc|default('Unknown') }}
69
+ </div>
70
+ <div class="service-ports">
71
+ {% set s_ports = svc_ports|default([]) %}
72
+ {% if s_ports and s_ports|length > 0 %}
73
+ {% for sp in s_ports %}
74
+ <span class="chip" title="{{ svc }} on {{ ip }}:{{ sp }}">{{ sp }}</span>
75
+ {% endfor %}
76
+ {% else %}
77
+ <span class="text-secondary">No ports</span>
78
+ {% endif %}
79
+ </div>
80
+ </div>
81
+ {% endfor %}
82
+ </div>
83
+ {% else %}
84
+ <div class="text-secondary">No service information available.</div>
85
+ {% endif %}
86
+ </div>
87
+
88
+ <div class="form-group mt-2 config-section">
89
+ <h6>Errors</h6>
90
+ {% if errors and errors|length > 0 %}
91
+ <ul class="list-unstyled error-list mt-1">
92
+ {% for err in errors %}
93
+ <li class="text-danger">
94
+ <span class="material-symbols-outlined align-middle me-1">error</span>
95
+ {{ err }}
96
+ </li>
97
+ {% endfor %}
98
+ </ul>
99
+ {% else %}
100
+ <div class="text-secondary">No errors captured.</div>
101
+ {% endif %}
102
+ </div>
103
+ </div>
104
+ </div>
105
+
106
+ <div class="modal-footer border-secondary">
107
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
108
+ </div>
109
+ </div>
110
+ </div>
111
+ </div>
@@ -1,4 +1,12 @@
1
1
  <tr>
2
+ <td>
3
+ <div class="info-icon-container">
4
+ <span
5
+ class="material-symbols-outlined info-icon"
6
+ onclick="parent.openDeviceDetail('{{ device.ip }}')"
7
+ >info</span>
8
+ </div>
9
+ </td>
2
10
  <td>
3
11
  <div>{{ device.ip }}</div>
4
12
  {% if device.hostname %}
@@ -18,86 +26,12 @@
18
26
  {% elif device.stage == 'found' %}
19
27
  <span class="badge badge-secondary">found</span>
20
28
  {% elif device.stage == 'scanning' %}
21
- <span class="badge badge-info">scanning</span>
29
+ <span class="badge badge-info">
30
+ scanning
31
+ {{ (device.ports_scanned/data.port_list_length*100)|round|int if data.port_list_length > 0 else 0 }}%
32
+ </span>
22
33
  {% else %}
23
34
  <span class="badge badge-warning">{{device.stage}}</span>
24
35
  {% endif %}
25
36
  </td>
26
- <td class="colorful-buttons">
27
- {% if 80 in device.ports %}
28
- <a href="http://{{ device.ip }}" class="btn btn-sm" target="_blank">HTTP</a>
29
- {% endif %}
30
- {% if 443 in device.ports %}
31
- <a href="https://{{ device.ip }}" class="btn btn-sm" target="_blank">HTTPS</a>
32
- {% endif %}
33
- {% if 22 in device.ports %}
34
- <a href="ssh://{{ device.ip }}" class="btn btn-sm " target="_blank">SSH</a>
35
- {% endif %}
36
- {% if 21 in device.ports %}
37
- <a href="ftp://{{ device.ip }}" class="btn btn-sm " target="_blank">FTP</a>
38
- {% endif %}
39
- {% if 23 in device.ports %}
40
- <a href="telnet://{{ device.ip }}" class="btn btn-sm " target="_blank">Telnet</a>
41
- {% endif %}
42
- {% if 445 in device.ports %}
43
- <a href="smb://{{ device.ip }}" class="btn btn-sm " target="_blank">SMB</a>
44
- {% endif %}
45
- {% if 3306 in device.ports %}
46
- <a href="mysql://{{ device.ip }}" class="btn btn-sm " target="_blank">MySQL</a>
47
- {% endif %}
48
- {% if 5432 in device.ports %}
49
- <a href="postgresql://{{ device.ip }}" class="btn btn-sm " target="_blank">PostgreSQL</a>
50
- {% endif %}
51
- {% if 3389 in device.ports %}
52
- <a href="rdp://{{ device.ip }}" class="btn btn-sm " target="_blank">RDP</a>
53
- {% endif %}
54
- {% if 5900 in device.ports %}
55
- <a href="vnc://{{ device.ip }}" class="btn btn-sm " target="_blank">VNC</a>
56
- {% endif %}
57
- {% if 27017 in device.ports %}
58
- <a href="mongodb://{{ device.ip }}" class="btn btn-sm " target="_blank">MongoDB</a>
59
- {% endif %}
60
- {% if 6379 in device.ports %}
61
- <a href="redis://{{ device.ip }}" class="btn btn-sm " target="_blank">Redis</a>
62
- {% endif %}
63
- {% if 11211 in device.ports %}
64
- <a href="memcached://{{ device.ip }}" class="btn btn-sm " target="_blank">Memcached</a>
65
- {% endif %}
66
- {% if 1433 in device.ports %}
67
- <a href="mssql://{{ device.ip }}" class="btn btn-sm " target="_blank">MSSQL</a>
68
- {% endif %}
69
- {% if 1521 in device.ports %}
70
- <a href="oracle://{{ device.ip }}" class="btn btn-sm " target="_blank">Oracle</a>
71
- {% endif %}
72
- {% if 27015 in device.ports %}
73
- <a href="source://{{ device.ip }}" class="btn btn-sm " target="_blank">Source</a>
74
- {% endif %}
75
- {% if 27015 in device.ports %}
76
- <a href="minecraft://{{ device.ip }}" class="btn btn-sm " target="_blank">Minecraft</a>
77
- {% endif %}
78
- {% if 27015 in device.ports %}
79
- <a href="teamspeak://{{ device.ip }}" class="btn btn-sm " target="_blank">Teamspeak</a>
80
- {% endif %}
81
- {% if 27015 in device.ports %}
82
- <a href="gmod://{{ device.ip }}" class="btn btn-sm " target="_blank">Garry's Mod</a>
83
- {% endif %}
84
- {% if 27015 in device.ports %}
85
- <a href="rust://{{ device.ip }}" class="btn btn-sm " target="_blank">Rust</a>
86
- {% endif %}
87
- {% if 27015 in device.ports %}
88
- <a href="ark://{{ device.ip }}" class="btn btn-sm " target="_blank">ARK</a>
89
- {% endif %}
90
- {% if 27015 in device.ports %}
91
- <a href="terraria://{{ device.ip }}" class="btn btn-sm " target="_blank">Terraria</a>
92
- {% endif %}
93
- {% if 27015 in device.ports %}
94
- <a href="factorio://{{ device.ip }}" class="btn btn-sm " target="_blank">Factorio</a>
95
- {% endif %}
96
- {% if 27015 in device.ports %}
97
- <a href="starmade://{{ device.ip }}" class="btn btn-sm " target="_blank">StarMade</a>
98
- {% endif %}
99
- {% if 27015 in device.ports %}
100
- <a href="7dtd://{{ device.ip }}" class="btn btn-sm " target="_blank">7 Days to Die</a>
101
- {% endif %}
102
- </td>
103
37
  </tr>
@@ -8,11 +8,11 @@
8
8
  <table class="table table-bordered table-striped">
9
9
  <thead>
10
10
  <tr>
11
+ <th class="detail-col" scope="col"></th>
11
12
  <th scope="col">IP / <span class="alt">Host</span></th>
12
13
  <th scope="col">MAC / <span class="alt">Manufacturer</span></th>
13
14
  <th scope="col">Open Ports</th>
14
15
  <th scope="col">Stage</th>
15
- <th scope="col">Actions</th>
16
16
  </tr>
17
17
  </thead>
18
18
  <tbody>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lanscape
3
- Version: 1.4.4
3
+ Version: 1.5.0
4
4
  Summary: A python based local network scanner
5
5
  Author-email: Michael Dennis <michael@dipduo.com>
6
6
  License-Expression: MIT