Skip to content

Commit 26059a6

Browse files
feat(network): sanitize ping output, fix false positive online status, and bump to v2.6.2
1 parent 75e6c30 commit 26059a6

9 files changed

Lines changed: 364 additions & 29 deletions

File tree

‎frontend/css/components/host-list.css‎

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,16 +86,33 @@ body.light-theme .host-item-card {
8686
box-sizing: border-box;
8787
}
8888

89+
.host-name-col,
8990
.host-ip-col {
9091
display: flex;
9192
align-items: center;
9293
gap: 8px;
93-
font-family: 'JetBrains Mono', monospace;
9494
font-size: 13.5px;
95-
font-weight: 600;
95+
font-weight: 700;
9696
color: var(--text-primary);
9797
letter-spacing: -0.01em;
9898
line-height: 1.4;
99+
overflow: hidden;
100+
min-width: 0;
101+
flex: 1;
102+
margin-right: 12px;
103+
}
104+
105+
.host-title-text {
106+
overflow: hidden;
107+
text-overflow: ellipsis;
108+
white-space: nowrap;
109+
display: block;
110+
}
111+
112+
.host-name-col.fallback-ip {
113+
font-family: 'JetBrains Mono', monospace;
114+
font-weight: 600;
115+
color: var(--text-secondary);
99116
}
100117

101118
/* Seta Expansora (Chevron) */
@@ -218,7 +235,14 @@ body.light-theme .host-details-drawer {
218235
font-weight: 600;
219236
}
220237

221-
body.light-theme .detail-value.mono {
238+
.detail-value.host-ip-value {
239+
font-size: 13px;
240+
font-weight: 700;
241+
color: #38bdf8;
242+
}
243+
244+
body.light-theme .detail-value.mono,
245+
body.light-theme .detail-value.host-ip-value {
222246
color: #0284c7;
223247
}
224248

‎frontend/index.html‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@
162162
</div>
163163

164164
<div class="hosts-table-head">
165-
<span class="head-ip">ENDEREÇO IP / HOST</span>
165+
<span class="head-ip">DISPOSITIVO / IDENTIFICAÇÃO</span>
166166
<span class="head-status">STATUS</span>
167167
</div>
168168

@@ -280,7 +280,7 @@
280280
</div>
281281
<div class="about-row">
282282
<span class="about-label">Versão:</span>
283-
<span class="about-value" id="aboutVersionText">v2.6.1</span>
283+
<span class="about-value" id="aboutVersionText">v2.6.2</span>
284284
</div>
285285
<div class="about-row">
286286
<span class="about-label">Stack:</span>

‎frontend/js/components/hostList.js‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,20 +110,21 @@ export function renderHosts(data) {
110110
card.dataset.id = device.id;
111111

112112
const badgeClass = isOnline ? 'online' : (isOffline ? 'offline' : 'unknown');
113-
const hostName = device.name ? device.name : 'Não especificado (Host manual)';
113+
const displayName = device.name && device.name.trim() !== '' ? device.name : device.ip;
114+
const isFallback = !device.name || device.name.trim() === '';
114115
const hostMethod = device.method ? device.method : 'PING';
115116
const thresholdMs = device.thresholdMs ? `${device.thresholdMs} ms` : '2000 ms';
116117
const hostUUID = device.uuid ? device.uuid : '—';
117118

118119
card.innerHTML = `
119120
<div class="host-main-row">
120-
<div class="host-ip-col">
121+
<div class="host-name-col ${isFallback ? 'fallback-ip' : ''}">
121122
<button class="host-expand-btn" title="Expandir/Recolher Detalhes" type="button">
122123
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
123124
<polyline points="9 18 15 12 9 6"></polyline>
124125
</svg>
125126
</button>
126-
<span>${device.ip}</span>
127+
<span class="host-title-text" title="${displayName}">${displayName}</span>
127128
</div>
128129
<div>
129130
<span class="status-badge ${badgeClass}">
@@ -135,8 +136,8 @@ export function renderHosts(data) {
135136
<div class="host-details-drawer">
136137
<div class="details-grid">
137138
<div class="detail-item full-width">
138-
<span class="detail-label">NOME / IDENTIFICAÇÃO</span>
139-
<span class="detail-value host-name-value" style="${!device.name ? 'color: var(--text-dim); font-style: italic;' : ''}">${hostName}</span>
139+
<span class="detail-label">ENDEREÇO IP / HOST</span>
140+
<span class="detail-value mono host-ip-value">${device.ip}</span>
140141
</div>
141142
<div class="detail-item">
142143
<span class="detail-label">MÉTODO DE TESTE</span>

‎internal/service/monitor_service.go‎

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -497,23 +497,13 @@ func checkIPWithTimeout(target string, timeout time.Duration) string {
497497
}
498498

499499
// 2. Fallback para ping nativo do sistema operacional (macOS / Linux / Windows)
500-
// Essencial no macOS onde o utilitário `/sbin/ping` possui setuid-root e sempre tem permissão ICMP
500+
// Essencial no macOS onde o utilitário `/sbin/ping` possui setuid-root e sempre tem permissão ICMP,
501+
// e no Windows quando o socket raw ICMP falha ou não tem privilégios.
501502
if pingSystemCommand(target) {
502503
return "Online"
503504
}
504505

505-
// 3. Fallback para portas TCP comuns de serviços (80, 443, 8080, 22)
506-
tcpTimeout := timeout
507-
if tcpTimeout > 1000*time.Millisecond {
508-
tcpTimeout = 1000 * time.Millisecond
509-
}
510-
for _, port := range []string{"443", "80", "8080", "22"} {
511-
conn, dialErr := net.DialTimeout("tcp", net.JoinHostPort(target, port), tcpTimeout)
512-
if dialErr == nil {
513-
_ = conn.Close()
514-
return "Online"
515-
}
516-
}
517-
506+
// Não realizamos fallback silencioso para TCP quando o método é PING (ICMP),
507+
// pois portas abertas/firewalls/proxies mascaram o status real do host ICMP.
518508
return "Offline"
519509
}

‎internal/service/ping_other.go‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package service
55
import (
66
"os/exec"
77
"runtime"
8+
"strings"
89
)
910

1011
// pingSystemCommand executa o ping nativo em sistemas Unix-like (macOS / Linux)
@@ -21,6 +22,10 @@ func pingSystemCommand(target string) bool {
2122
cmd = exec.Command("ping", "-c", "1", target)
2223
}
2324

24-
err := cmd.Run()
25-
return err == nil
25+
out, err := cmd.CombinedOutput()
26+
if err != nil {
27+
return false
28+
}
29+
return isUnixPingOutputSuccess(string(out))
2630
}
31+

‎internal/service/ping_parser.go‎

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package service
2+
3+
import "strings"
4+
5+
// isWindowsPingOutputSuccess verifica se a resposta do ping do Windows indica sucesso real de eco do host destino
6+
func isWindowsPingOutputSuccess(output, target string) bool {
7+
lower := strings.ToLower(output)
8+
9+
// Indicadores explícitos de falha/erro ICMP intermediário ou esgotamento
10+
// Mesmo com pacotes recebidos (de roteadores intermediários), estes são falhas:
11+
errorKeywords := []string{
12+
"ttl expirou",
13+
"ttl expired",
14+
"time to live exceeded",
15+
"inacessível",
16+
"inacessivel",
17+
"unreachable",
18+
"esgotado o tempo",
19+
"timed out",
20+
"falha geral",
21+
"general failure",
22+
"não foi possível encontrar",
23+
"could not find host",
24+
"100% de perda",
25+
"100% loss",
26+
}
27+
28+
for _, kw := range errorKeywords {
29+
if strings.Contains(lower, kw) {
30+
return false
31+
}
32+
}
33+
34+
// Para ser considerado sucesso real do host alvo:
35+
// 1. Deve conter tempo de resposta (tempo= / tempo< / time= / time<)
36+
hasTime := strings.Contains(lower, "tempo=") ||
37+
strings.Contains(lower, "tempo<") ||
38+
strings.Contains(lower, "time=") ||
39+
strings.Contains(lower, "time<")
40+
41+
// 2. Deve conter contagem de bytes de payload (bytes=)
42+
hasBytes := strings.Contains(lower, "bytes=")
43+
44+
return hasTime && hasBytes
45+
}
46+
47+
// isUnixPingOutputSuccess valida se o utilitário nativo Unix/Linux/macOS realmente obteve Echo Reply
48+
func isUnixPingOutputSuccess(output string) bool {
49+
lower := strings.ToLower(output)
50+
51+
// Falhas explícitas em sistemas Unix
52+
errorKeywords := []string{
53+
"100% packet loss",
54+
"0 packets received",
55+
"0 packets transmitted",
56+
"destination host unreachable",
57+
"time to live exceeded",
58+
"request timeout",
59+
"unknown host",
60+
}
61+
62+
for _, kw := range errorKeywords {
63+
if strings.Contains(lower, kw) {
64+
return false
65+
}
66+
}
67+
68+
// Deve conter indicação de pacote recebido (ex: "1 packets received" ou "1 received")
69+
hasReceived := strings.Contains(lower, "1 packets received") ||
70+
strings.Contains(lower, "1 received") ||
71+
strings.Contains(lower, "bytes from")
72+
73+
return hasReceived
74+
}

0 commit comments

Comments
 (0)