Bump up to version 1.02
This commit is contained in:
parent
2bda98917a
commit
1fe0ac49a5
6 changed files with 129 additions and 38 deletions
|
|
@ -1,4 +1,9 @@
|
|||
# phisherslapper [v1.01]
|
||||
# phisherslapper [v1.02]
|
||||
|
||||
> **Note:** `isthislegit` has been renamed to `phisherslapper`. We do not
|
||||
> condone physical violence against phishers — but you are warmly encouraged
|
||||
> to metaphorically slap them with this tool's output in your reply the next
|
||||
> time they try to scam you.
|
||||
|
||||
Domain Impersonation & OSINT Triage Tool.
|
||||
|
||||
|
|
@ -128,7 +133,7 @@ resort for devices without stored CA certificates.
|
|||
## Example output
|
||||
|
||||
```text
|
||||
phisherslapper [v1.01]
|
||||
phisherslapper [v1.02]
|
||||
Domain Impersonation & OSINT Triage Tool
|
||||
|
||||
[*] Comparing: example.com (Target) <---> example.org (Suspect)
|
||||
|
|
|
|||
84
STYLE.md
Normal file
84
STYLE.md
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
# phisherslapper Code Conventions
|
||||
|
||||
This file is the style contract for this repo. Follow it for every change;
|
||||
reviewers should cite it when requesting revisions.
|
||||
|
||||
## 1. No `else` — map lookups for values
|
||||
|
||||
Boolean or small-domain forks become table lookups, not branches. Fork
|
||||
targets stay visible as data, and diffs add rows instead of nesting.
|
||||
|
||||
```go
|
||||
// Good: the two outcomes sit side by side as data.
|
||||
hostsLine := map[bool]string{
|
||||
true: fmt.Sprintf(" hosts override: STATIC MAPPING %s -> %s\n\n", ip, domain),
|
||||
false: " hosts override: none\n\n",
|
||||
}[hit]
|
||||
|
||||
// Bad: an else branch hiding the same two outcomes.
|
||||
if hit {
|
||||
fmt.Fprintf(&b, " hosts override: STATIC MAPPING %s -> %s\n\n", ip, domain)
|
||||
} else {
|
||||
b.WriteString(" hosts override: none\n\n")
|
||||
}
|
||||
```
|
||||
|
||||
Ordered or priority mappings use `[]struct{…}` slices scanned in order —
|
||||
see `verdictScale` (`internal/score/score.go`), `oidClassPriority`
|
||||
(`internal/cert/cert.go`), and `platformAliases` / `archAliases`
|
||||
(`compile.go`). Flat `map[K]V` lookups cover the rest — see
|
||||
`classLongNames`.
|
||||
|
||||
## 2. Dispatch tables for goroutines
|
||||
|
||||
When a condition selects *behavior* rather than a value — e.g. which
|
||||
probes to launch — dispatch through `map[bool]func()` instead of an
|
||||
if/else launch block:
|
||||
|
||||
```go
|
||||
// Good: one dispatch, same concurrency shape either way.
|
||||
probePaths := map[bool]func(){
|
||||
true: func() { g.Go(probeShared) },
|
||||
false: func() { g.Go(probeSys); g.Go(probePub) },
|
||||
}[sameEndpoint]
|
||||
probePaths()
|
||||
```
|
||||
|
||||
The table lives next to the errgroup it feeds (`cmd/phisherslapper/main.go`).
|
||||
|
||||
## 3. Guard clauses over nesting
|
||||
|
||||
Handle the degenerate case first with an early `return` or `continue`;
|
||||
the happy path stays at the top indent level. Never nest to avoid an
|
||||
else — flatten instead.
|
||||
|
||||
```go
|
||||
// Good: incomplete input exits before the real work.
|
||||
if sys == nil || pub == nil || !sys.HasCert || !pub.HasCert {
|
||||
return false, false, ""
|
||||
}
|
||||
```
|
||||
|
||||
## 4. Pure functions at the core, I/O at the edges
|
||||
|
||||
Parsing, comparison, and scoring take values and return values:
|
||||
`ParseHostsFile`, `ParseCymruTXT`, `cert.ComparePaths`, `detectSplit`,
|
||||
`score.Score`. Network access, the clock, and the filesystem live at the
|
||||
edges (`main.go` orchestration, `LookupASN`, `CheckHostsOverride`).
|
||||
|
||||
## 5. Fail-open probes, fail-closed verdicts
|
||||
|
||||
Lookup helpers return `(T, bool)` and never surface errors for expected
|
||||
absence — an unanswered Team Cymru query or a missing hosts file simply
|
||||
yields `false`. Only `score.VerifyDataQuality` may abort a run, and it
|
||||
must name the underlying cause(s).
|
||||
|
||||
## 6. Tests and hygiene
|
||||
|
||||
- Every pure function gets a table-driven unit test (`cases := map[string]…`,
|
||||
see `internal/dns/dns_test.go`). Live-network behavior is asserted
|
||||
through fail-open semantics, not flaky integration tests.
|
||||
- `gofmt -l .` must print nothing; `go vet ./...` must pass.
|
||||
- Stdlib only, plus `golang.org/x/sync` for the fetch pipeline.
|
||||
- Every exported symbol carries a doc comment.
|
||||
- New scoring signals update `--scale`, the README scale table, and `docs/` together — a signal without docs is unfinished.
|
||||
|
|
@ -28,7 +28,7 @@ import (
|
|||
"phisherslapper/internal/score"
|
||||
)
|
||||
|
||||
const version = "1.01"
|
||||
const version = "1.02"
|
||||
|
||||
const defaultPerCall = 5 * time.Second
|
||||
const globalTimeout = 15 * time.Second
|
||||
|
|
@ -100,25 +100,30 @@ func fetchDomain(ctx context.Context, client *http.Client, domain string, now ti
|
|||
pubIP = dc.PublicAddrs[0]
|
||||
}
|
||||
if sysIP != nil && pubIP != nil {
|
||||
if sysIP.Equal(pubIP) {
|
||||
// Same endpoint both paths: one dial attests for both.
|
||||
g.Go(func() error {
|
||||
c, _ := cert.InspectAddr(ctx, sysIP, domain, perCall)
|
||||
res.sysCert, res.pubCert = c, c
|
||||
return nil
|
||||
})
|
||||
} else {
|
||||
g.Go(func() error {
|
||||
c, _ := cert.InspectAddr(ctx, sysIP, domain, perCall)
|
||||
res.sysCert = c
|
||||
return nil
|
||||
})
|
||||
g.Go(func() error {
|
||||
c, _ := cert.InspectAddr(ctx, pubIP, domain, perCall)
|
||||
res.pubCert = c
|
||||
return nil
|
||||
})
|
||||
}
|
||||
// Shared vs split endpoints dispatch which per-path probes run:
|
||||
// one dial attests for both when the paths converge.
|
||||
probePaths := map[bool]func(){
|
||||
true: func() {
|
||||
g.Go(func() error {
|
||||
c, _ := cert.InspectAddr(ctx, sysIP, domain, perCall)
|
||||
res.sysCert, res.pubCert = c, c
|
||||
return nil
|
||||
})
|
||||
},
|
||||
false: func() {
|
||||
g.Go(func() error {
|
||||
c, _ := cert.InspectAddr(ctx, sysIP, domain, perCall)
|
||||
res.sysCert = c
|
||||
return nil
|
||||
})
|
||||
g.Go(func() error {
|
||||
c, _ := cert.InspectAddr(ctx, pubIP, domain, perCall)
|
||||
res.pubCert = c
|
||||
return nil
|
||||
})
|
||||
},
|
||||
}[sysIP.Equal(pubIP)]
|
||||
probePaths()
|
||||
g.Go(func() error {
|
||||
a, ok := dns.LookupASN(ctx, sysIP, perCall)
|
||||
res.sysASN, res.sysASNOK = a, ok
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ plus <a href="report.html#Verbose">report.Verbose</a> when verbose.</li>
|
|||
</ol>
|
||||
|
||||
<font color="#859900"><h2>Version</h2></font>
|
||||
<p>Current version string is <tt>1.01</tt>, printed in the banner, JSON
|
||||
<p>Current version string is <tt>1.02</tt>, printed in the banner, JSON
|
||||
output, and <tt>--scale</tt> header. Build target:
|
||||
<tt>CGO_ENABLED=0 go build -ldflags="-s -w" -o phisherslapper
|
||||
./cmd/phisherslapper</tt>.</p>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<title>phisherslapper Developer Docs - Overview</title>
|
||||
</head>
|
||||
<body bgcolor="#002b36" text="#a3b8b9" link="#859900" vlink="#b58900">
|
||||
<font color="#b58900"><h1>phisherslapper [v1.01]</h1></font>
|
||||
<font color="#b58900"><h1>phisherslapper [v1.02]</h1></font>
|
||||
<p>Domain Impersonation and OSINT Triage Tool. Compares a known-legitimate
|
||||
domain (target) against a suspect domain using DNS resolution, X.509 TLS
|
||||
certificates, and RDAP registration data, then produces a deterministic risk
|
||||
|
|
|
|||
|
|
@ -287,20 +287,17 @@ func writePaths(b *strings.Builder, label, domain string, d score.DomainData) {
|
|||
case d.PathChecked:
|
||||
b.WriteString(" per-path TLS: inconclusive (one endpoint refused port 443)\n")
|
||||
}
|
||||
if d.ASNChecked {
|
||||
status := "agree"
|
||||
if !d.ASNMatch {
|
||||
status = "SPLIT"
|
||||
}
|
||||
fmt.Fprintf(b, " origin AS: local %s vs public %s [%s]\n", asnOrUnknown(d.SysASN), asnOrUnknown(d.PubASN), status)
|
||||
} else {
|
||||
b.WriteString(" origin AS: unattributed (Team Cymru query unanswered)\n")
|
||||
}
|
||||
if d.HostsHit {
|
||||
fmt.Fprintf(b, " hosts override: STATIC MAPPING %s -> %s\n\n", d.HostsIP, domain)
|
||||
} else {
|
||||
b.WriteString(" hosts override: none\n\n")
|
||||
}
|
||||
asnVerdict := map[bool]string{true: "agree", false: "SPLIT"}[d.ASNMatch]
|
||||
asnLine := map[bool]string{
|
||||
true: fmt.Sprintf(" origin AS: local %s vs public %s [%s]\n", asnOrUnknown(d.SysASN), asnOrUnknown(d.PubASN), asnVerdict),
|
||||
false: " origin AS: unattributed (Team Cymru query unanswered)\n",
|
||||
}[d.ASNChecked]
|
||||
b.WriteString(asnLine)
|
||||
hostsLine := map[bool]string{
|
||||
true: fmt.Sprintf(" hosts override: STATIC MAPPING %s -> %s\n\n", d.HostsIP, domain),
|
||||
false: " hosts override: none\n\n",
|
||||
}[d.HostsHit]
|
||||
b.WriteString(hostsLine)
|
||||
}
|
||||
|
||||
func asnOrUnknown(s string) string {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue