lanscape 1.4.3__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.
- lanscape/__init__.py +4 -0
- lanscape/libraries/app_scope.py +21 -3
- lanscape/libraries/decorators.py +3 -1
- lanscape/libraries/net_tools.py +135 -42
- lanscape/libraries/scan_config.py +101 -3
- lanscape/libraries/service_scan.py +172 -18
- lanscape/libraries/subnet_scan.py +13 -6
- lanscape/resources/services/definitions.jsonc +576 -400
- lanscape/ui/blueprints/web/routes.py +28 -1
- lanscape/ui/static/css/style.css +145 -2
- lanscape/ui/static/js/main.js +30 -2
- lanscape/ui/static/js/scan-config.js +39 -0
- lanscape/ui/templates/scan/config.html +43 -0
- lanscape/ui/templates/scan/device-detail.html +111 -0
- lanscape/ui/templates/scan/ip-table-row.html +12 -78
- lanscape/ui/templates/scan/ip-table.html +1 -1
- {lanscape-1.4.3.dist-info → lanscape-1.5.0.dist-info}/METADATA +1 -1
- {lanscape-1.4.3.dist-info → lanscape-1.5.0.dist-info}/RECORD +21 -20
- {lanscape-1.4.3.dist-info → lanscape-1.5.0.dist-info}/WHEEL +0 -0
- {lanscape-1.4.3.dist-info → lanscape-1.5.0.dist-info}/licenses/LICENSE +0 -0
- {lanscape-1.4.3.dist-info → lanscape-1.5.0.dist-info}/top_level.txt +0 -0
|
@@ -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
|
"""
|
lanscape/ui/static/css/style.css
CHANGED
|
@@ -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(--
|
|
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
|
+
}
|
lanscape/ui/static/js/main.js
CHANGED
|
@@ -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">
|
|
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>
|