checker: enforce prober-as-observation, move all analysis to rules layer

This commit is contained in:
nemunaire 2026-05-15 17:05:53 +08:00
commit 8b86eb6a8b
12 changed files with 155 additions and 152 deletions

View file

@ -93,7 +93,7 @@ type reportEndpoint struct {
Host string
Port uint16
IsIPv6 bool
TCPConnected bool
DialFailed bool
Banner string
SoftwareVer string
Vendor string
@ -239,11 +239,12 @@ func buildReportData(d *SSHData, states []sdk.CheckState) reportView {
matched := false
for _, ep := range d.Endpoints {
for _, k := range ep.HostKeys {
if k.SSHFPAlgo == rr.Algorithm {
if rr.Type == 2 && strings.EqualFold(rr.Fingerprint, k.SHA256) {
if sshfpAlgoForKeyType(k.Type) == rr.Algorithm {
sha1hex, sha256hex := hostKeyFingerprints(k)
if rr.Type == 2 && strings.EqualFold(rr.Fingerprint, sha256hex) {
matched = true
}
if rr.Type == 1 && strings.EqualFold(rr.Fingerprint, k.SHA1) {
if rr.Type == 1 && strings.EqualFold(rr.Fingerprint, sha1hex) {
matched = true
}
}
@ -264,24 +265,24 @@ func buildReportData(d *SSHData, states []sdk.CheckState) reportView {
for _, ep := range d.Endpoints {
re := reportEndpoint{
Address: ep.Address,
Address: ep.Addr(),
Host: ep.Host,
Port: ep.Port,
IsIPv6: ep.IsIPv6,
TCPConnected: ep.TCPConnected,
IsIPv6: ep.IP != nil && ep.IP.To4() == nil,
DialFailed: ep.Stage == "dial",
Banner: ep.Banner,
SoftwareVer: ep.SoftVer,
Vendor: ep.Vendor,
ElapsedMS: ep.ElapsedMS,
Error: ep.Error,
}
if ep.IsIPv6 {
if ep.IP != nil && ep.IP.To4() == nil {
v.AnyIPv6 = true
} else {
v.AnyIPv4 = true
}
perEpIssues := perEp[ep.Address]
perEpIssues := perEp[ep.Addr()]
// Per-endpoint status label.
epWorst := SeverityOK
for _, f := range perEpIssues {
@ -307,15 +308,18 @@ func buildReportData(d *SSHData, states []sdk.CheckState) reportView {
}
for _, k := range ep.HostKeys {
sha1hex, sha256hex := hostKeyFingerprints(k)
sha1Match, sha256Match := keyMatchesSSHFP(k, d.SSHFP)
algo := sshfpAlgoForKeyType(k.Type)
rh := reportHostKey{
Type: k.Type,
Bits: k.Bits,
SHA256: k.SHA256,
SHA1: k.SHA1,
Type: k.Type,
Bits: hostKeyBits(k),
SHA256: sha256hex,
SHA1: sha1hex,
SSHFPMatched: sha256Match || sha1Match,
SSHFPFamily: sshfpAlgoName(algo),
SSHFPSnippet: fmt.Sprintf("%d 2 %s", algo, sha256hex),
}
rh.SSHFPMatched = k.SSHFPMatchSHA256 || k.SSHFPMatchSHA1
rh.SSHFPFamily = sshfpAlgoName(k.SSHFPAlgo)
rh.SSHFPSnippet = fmt.Sprintf("%d 2 %s", k.SSHFPAlgo, k.SHA256)
re.HostKeys = append(re.HostKeys, rh)
}
@ -602,7 +606,7 @@ tr.info td:first-child { border-left: 3px solid #3b82f6; }
<dl class="kv">
<dt>Host</dt><dd>{{.Host}}</dd>
<dt>IP</dt><dd><code>{{.Address}}</code>{{if .IsIPv6}} (IPv6){{end}}</dd>
<dt>TCP</dt><dd>{{if .TCPConnected}}<span class="check-ok">&#10003; connected</span>{{else}}<span class="check-fail">&#10007; failed</span>{{end}}</dd>
<dt>TCP</dt><dd>{{if .DialFailed}}<span class="check-fail">&#10007; failed</span>{{else}}<span class="check-ok">&#10003; connected</span>{{end}}</dd>
{{if .SoftwareVer}}<dt>Version</dt><dd><code>{{.SoftwareVer}}</code>{{if .Vendor}} · <span class="note">{{.Vendor}}</span>{{end}}</dd>{{end}}
<dt>Duration</dt><dd>{{.ElapsedMS}} ms</dd>
{{if .Error}}<dt>Error</dt><dd><span class="check-fail">{{.Error}}</span></dd>{{end}}