From b89caefb11d7a205d28e88f0b84ca83026deb9d7 Mon Sep 17 00:00:00 2001 From: markmental Date: Tue, 8 Sep 2026 19:23:49 -0400 Subject: [PATCH] First iteration of docs site --- docs/cert.html | 69 ++++++++++++++++++++++++++++++++++++ docs/dns.html | 43 ++++++++++++++++++++++ docs/index.html | 24 +++++++++++++ docs/main.html | 52 +++++++++++++++++++++++++++ docs/nav.html | 63 +++++++++++++++++++++++++++++++++ docs/overview.html | 71 +++++++++++++++++++++++++++++++++++++ docs/rdap.html | 69 ++++++++++++++++++++++++++++++++++++ docs/report.html | 64 +++++++++++++++++++++++++++++++++ docs/score.html | 88 ++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 543 insertions(+) create mode 100644 docs/cert.html create mode 100644 docs/dns.html create mode 100644 docs/index.html create mode 100644 docs/main.html create mode 100644 docs/nav.html create mode 100644 docs/overview.html create mode 100644 docs/rdap.html create mode 100644 docs/report.html create mode 100644 docs/score.html diff --git a/docs/cert.html b/docs/cert.html new file mode 100644 index 0000000..96e1fea --- /dev/null +++ b/docs/cert.html @@ -0,0 +1,69 @@ + + + +isthislegit Developer Docs - Package cert + + +

Package cert

+

Leaf X.509 certificate fetching and CA/Browser Forum validation-level +classification from policy OIDs. Source: internal/cert/cert.go.

+

Reserved policy OIDs: 2.23.140.1.1 (EV), +2.23.140.1.2.1 (DV), 2.23.140.1.2.2 (OV), +2.23.140.1.2.3 (IV). Priority order is EV > OV > IV > DV.

+ +
+ +

Details

+
type Details struct {
+    Issuer    string    // Organization, or fallback issuer string
+    Class     string    // EV, OV, IV, DV, or Unknown
+    SANs      []string  // DNS SANs (CN fallback), empty when unavailable
+    OIDs      []string  // raw policy OIDs found
+    Subject   string
+    IssuerStr string
+    NotBefore time.Time
+    NotAfter  time.Time
+    HasCert   bool
+}
+

Extracted leaf-certificate fields. Issuer prefers the +Organization attribute and falls back to Common Name, then the full +issuer string. SANs falls back to the subject Common Name.

+ +
+ +

Classify

+
func Classify(oids []string) string
+

Maps a set of policy OID strings to a validation class using the ordered +priority table (EV first). Returns "Unknown" when none match.

+
cert.Classify([]string{"2.23.140.1.2.1"}) // => "DV"
+ +
+ +

InspectDomain

+
func InspectDomain(ctx context.Context, domain string, perCall time.Duration) (*Details, error)
+

Dials domain:443 with tls.Dialer +(InsecureSkipVerify, since the peer is untrusted by definition) and +returns the parsed leaf via FromLeaf. +Transient failures are retried up to 3 times with a 1 second delay.

+ + + + + +
ParameterDescription
ctxParent context; the global 15 second budget.
domainAlready-normalized domain to dial.
perCallPer-attempt deadline (5 seconds in the CLI).
+ +
+ +

FromLeaf

+
func FromLeaf(leaf *x509.Certificate) *Details
+

Pure constructor: builds Details from an +already-parsed leaf certificate. Preferred entry point for tests.

+ +
+ +

SANsString

+
func (d *Details) SANsString() string
+

Joins SANs with ", " for display, or returns +"Unknown" when empty. Nil-receiver safe.

+ + diff --git a/docs/dns.html b/docs/dns.html new file mode 100644 index 0000000..521baff --- /dev/null +++ b/docs/dns.html @@ -0,0 +1,43 @@ + + + +isthislegit Developer Docs - Package dns + + +

Package dns

+

Domain normalization, syntax validation, and DNS pre-flight resolution +checks. Source: internal/dns/dns.go.

+ +
+ +

NormalizeDomain

+
func NormalizeDomain(d string) string
+

Lowercases the input and strips scheme, path, port, and trailing dot. +Mirrors the shell normalize_domain helper.

+
dns.NormalizeDomain("HTTPS://Example.COM:8443/path")
+// => "example.com"
+ +
+ +

ValidSyntax

+
func ValidSyntax(d string) bool
+

Reports whether d looks like a dotted domain name: labels of +alphanumerics and hyphens with at least one dot. Used to reject malformed +input before any network call.

+
dns.ValidSyntax("jdsoft.com")  // => true
+dns.ValidSyntax("no-dot")      // => false
+ +
+ +

CheckDNS

+
func CheckDNS(ctx context.Context, domain string) error
+

Aborts early on NXDOMAIN or unresolvable names via +net.DefaultResolver.LookupIP with a 5 second deadline. Returns +nil when at least one IPv4 address resolves, an error otherwise.

+ + + + +
ParameterDescription
ctxParent context; the global 15 second budget.
domainAlready-normalized domain to resolve.
+ + diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..8bc8645 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,24 @@ + + + +isthislegit Developer Docs + + + + + +<body bgcolor="#002b36" text="#a3b8b9" link="#859900" vlink="#b58900"> +<p>Your browser does not support frames. Use these links instead:</p> +<ul> +<li><a href="overview.html">Overview</a></li> +<li><a href="dns.html">Package dns</a></li> +<li><a href="cert.html">Package cert</a></li> +<li><a href="rdap.html">Package rdap</a></li> +<li><a href="score.html">Package score</a></li> +<li><a href="report.html">Package report</a></li> +<li><a href="main.html">Command main</a></li> +</ul> +</body> + + + diff --git a/docs/main.html b/docs/main.html new file mode 100644 index 0000000..40b4271 --- /dev/null +++ b/docs/main.html @@ -0,0 +1,52 @@ + + + +isthislegit Developer Docs - Command main + + +

Command main

+

CLI entry point and fetch orchestration. Source: +cmd/isthislegit/main.go. Package main exposes no exported +symbols; this page documents behavior.

+ +

Arguments

+

Syntax: isthislegit [FLAGS] <legit_domain> +<suspect_domain>. Flags -v/--verbose, +-j/--json, --scale, -h/--help, plus a +-- separator. Unknown flags and anything other than exactly two +positional domains exit 1 with a usage message. Domains are normalized with +dns.NormalizeDomain and validated +with dns.ValidSyntax.

+ +

Budgets

+ + + + + +
BudgetValue
Global deadline (both domains)15 seconds
Per-call deadline (DNS, TLS, RDAP)5 seconds
Attempts per call3, with 1 second between retries
+ +

Flow

+
    +
  1. DNS pre-flight for both domains in parallel; any failure aborts.
  2. +
  3. Per-domain fetch in parallel: RDAP +(rdap.Query) plus TLS +(cert.InspectDomain) concurrently. +Field-level failures stay open (Unknown) here.
  4. +
  5. Fail-closed gate: +score.VerifyDataQuality; aborts +on missing dates or TLS data for either side.
  6. +
  7. score.Score produces score, findings, +verdict, and age flag.
  8. +
  9. Render: report.JSON for +-j/--json, otherwise report.Human +plus report.Verbose when verbose.
  10. +
+ +

Version

+

Current version string is 1.01, printed in the banner, JSON +output, and --scale header. Build target: +CGO_ENABLED=0 go build -ldflags="-s -w" -o isthislegit +./cmd/isthislegit.

+ + diff --git a/docs/nav.html b/docs/nav.html new file mode 100644 index 0000000..a7e398f --- /dev/null +++ b/docs/nav.html @@ -0,0 +1,63 @@ + + + +isthislegit Docs - Navigation + + +

isthislegit?

+

Overview

+
+

Packages

+ +
+

dns

+ +

cert

+ +

rdap

+ +

score

+ +

report

+ + + diff --git a/docs/overview.html b/docs/overview.html new file mode 100644 index 0000000..1b65618 --- /dev/null +++ b/docs/overview.html @@ -0,0 +1,71 @@ + + + +isthislegit Developer Docs - Overview + + +

isthislegit? [v1.01]

+

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 +score and verdict. Single static binary; standard library plus +golang.org/x/sync only.

+ +

Build and Run

+
git clone https://github.com/example/isthislegit.git
+cd isthislegit
+CGO_ENABLED=0 go build -ldflags="-s -w" -o isthislegit ./cmd/isthislegit
+./isthislegit jdsoft.com jdsoftcareers.com
+go test ./...
+ +

Flags

+ + + + + + +
FlagDescription
-v, --verboseAppend raw cert policy OIDs, TLS details, and RDAP JSON.
-j, --jsonOutput the result summary as JSON for scripting.
--scalePrint the scoring scale table and exit.
-h, --helpDisplay usage and exit.
+ +

Fetch Pipeline

+

Both domains are fetched concurrently with +golang.org/x/sync/errgroup under a 15 second global deadline; each +DNS, TLS, and RDAP call carries a 5 second deadline and is retried up to 3 +times. Total run time equals the slower of the two domains.

+
parse args --> DNS pre-flight (target + suspect, parallel)
+           --> fetch target   = RDAP + TLS in parallel
+           --> fetch suspect  = RDAP + TLS in parallel
+           --> fail-closed verify --> score --> render
+

Exit codes: 0 on success (including --scale and +--help), 1 on bad flags, invalid domains, DNS failure, or +failed data-quality verification.

+ +

Scoring Scale

+ + + + + + + +
SignalPoints
Suspect domain age < 30 days (CRITICAL)+80
Suspect domain age 30-89 days (WARNING)+80
Cert downgrade: target EV/OV vs suspect DV+40
Registrar mismatch+30
Same-class issuer difference (display only)+0
+
+ + + + + + +
ScoreVerdict
0-14LIKELY LEGITIMATE
15-39LIKELY UNRELATED / MISCONFIGURED
40-50SUSPICIOUS - MANUAL REVIEW RECOMMENDED
51+LIKELY PHISHING / IMPERSONATION ATTEMPT
+ +

Packages

+ + + diff --git a/docs/rdap.html b/docs/rdap.html new file mode 100644 index 0000000..1da45ce --- /dev/null +++ b/docs/rdap.html @@ -0,0 +1,69 @@ + + + +isthislegit Developer Docs - Package rdap + + +

Package rdap

+

RDAP registration-data client over https://rdap.org/domain/. +Source: internal/rdap/rdap.go.

+ +
+ +

Event

+
type Event struct {
+    Action string `json:"eventAction"`
+    Date   string `json:"eventDate"`
+}
+

Single RDAP event object; only registration actions are used.

+ +
+ +

Response

+
type Response struct {
+    Events   []Event  `json:"events"`
+    Entities []entity `json:"entities"`
+}
+

Minimal parsed RDAP payload. Entities carry registrar roles and vCard +arrays used for registrar extraction.

+ +
+ +

Result

+
type Result struct {
+    Raw   []byte    // raw JSON, kept for --verbose output
+    Resp  *Response // parsed payload
+    Found bool      // false when the lookup failed
+}
+ +
+ +

Query

+
func Query(ctx context.Context, client *http.Client, domain string, perCall time.Duration) (*Result, error)
+

Fetches RDAP JSON for domain with an Accept: +application/json request, retrying transient failures up to 3 times. +Non-2xx status and invalid JSON are errors.

+ + + + + + +
ParameterDescription
ctxParent context; the global 15 second budget.
clientShared *http.Client; per-call deadline via context.
domainAlready-normalized domain to look up.
perCallPer-attempt deadline (5 seconds in the CLI).
+ +
+ +

RegistrationDate

+
func RegistrationDate(r *Result) string
+

Returns the YYYY-MM-DD date of the registration event, or +"Unknown / Lookup Failed" when unavailable. Nil-safe.

+ +
+ +

Registrar

+
func Registrar(r *Result) string
+

Best-effort registrar name, or "Unknown". Prefers entities with +the registrar role, falling back to the first vCard +fn property. Nil-safe.

+ + diff --git a/docs/report.html b/docs/report.html new file mode 100644 index 0000000..c8d1d89 --- /dev/null +++ b/docs/report.html @@ -0,0 +1,64 @@ + + + +isthislegit Developer Docs - Package report + + +

Package report

+

Human, JSON, scale, and verbose renderers. Source: +internal/report/report.go.

+ +
+ +

Data

+
type Data struct {
+    Version       string
+    Target        score.DomainData
+    Suspect       score.DomainData
+    Score         int
+    Findings      []string
+    Verdict       string
+    AgeFlag       string
+    TargetCert    *cert.Details
+    SuspectCert   *cert.Details
+    TargetRDAP    *rdap.Result
+    SuspectRDAP   *rdap.Result
+    TargetDomain  string
+    SuspectDomain string
+}
+

Everything a renderer needs: scored domain data plus the raw cert and +RDAP results backing verbose output.

+ +
+ +

Scale

+
func Scale(version string) string
+

Static scoring-scale reference table (no lookups). Backs the +--scale flag.

+ +
+ +

Human

+
func Human(d Data) string
+

Triage report with per-domain blocks (creation date, registrar, issuer, +validation, SANs), the scored risk analysis, and the verdict. Empty +findings render a clean bill of health.

+ +
+ +

JSON

+
func JSON(d Data) string
+

Machine-readable summary with tool, version, +target, suspect (including suspect age_days), +risk_score, findings, and verdict. Ends with a +newline for pipeline use.

+ +
+ +

Verbose

+
func Verbose(d Data) string
+

Raw policy OIDs with classes, per-domain TLS details (issuer, subject, +validity, SANs), and raw RDAP JSON for both domains. Missing data renders +an explicit unavailable notice per section.

+ + diff --git a/docs/score.html b/docs/score.html new file mode 100644 index 0000000..87e41c0 --- /dev/null +++ b/docs/score.html @@ -0,0 +1,88 @@ + + + +isthislegit Developer Docs - Package score + + +

Package score

+

Deterministic risk scoring: domain-age check (+80), certificate +downgrade check (+40), and registrar mismatch (+30), mapped onto a +four-tier verdict. Sources: internal/score/score.go, +internal/score/date.go.

+ +
+ +

DomainData

+
type DomainData struct {
+    Domain    string // queried domain
+    Date      string // YYYY-MM-DD, or sentinel on failure
+    Registrar string // "Unknown" when unavailable
+    Issuer    string // "Unknown" when unavailable
+    Class     string // EV, OV, IV, DV, or Unknown
+    SANs      string // comma-joined, or "Unknown"
+    AgeDays   int
+    AgeKnown  bool
+}
+

Collected triage fields for one domain; the scoring input.

+ +
+ +

Result

+
type Result struct {
+    Score    int
+    Findings []string
+    Verdict  string
+    AgeFlag  string // display suffix for young suspect domains
+}
+ +
+ +

ClassLong

+
func ClassLong(c string) string
+

Human-readable validation name via lookup table: EV to +"Extended Validation", OV to "Organization Validated", DV +to "Domain Validated", IV to "Individual Validated", +anything else to "Unknown".

+ +
+ +

VerifyDataQuality

+
func VerifyDataQuality(target, suspect DomainData) error
+

Fails closed: returns an error when either domain lacks a valid RDAP +date, a computable age, or any TLS signal (issuer and class both unknown). +The CLI aborts instead of printing a verdict built on missing data.

+ +
+ +

Score

+
func Score(target, suspect DomainData) Result
+

Applies the three signals. Suspect age under 30 days adds 80 with a +CRITICAL flag; 30-89 days adds 80 with a WARNING flag. Target EV/OV versus +suspect DV adds 40 as DOWNGRADE; a same-class issuer difference is a +display-only NOTE (+0). Case-insensitive registrar mismatch adds 30, +skipped when either side is unknown.

+
r := score.Score(target, suspect)
+// r.Score, r.Findings, r.Verdict, r.AgeFlag
+ +
+ +

Verdict

+
func Verdict(score int) string
+

Maps the total via threshold table: 51+ phishing, 40+ suspicious, 15+ +unrelated/misconfigured, otherwise legitimate.

+ +
+ +

ValidISODate

+
func ValidISODate(s string) bool
+

True only for clean, calendar-valid YYYY-MM-DD strings. Any +sentinel or garbage value fails by design.

+ +
+ +

AgeDays

+
func AgeDays(ymd string, now time.Time) (days int, ok bool)
+

Whole days between ymd and now; ok is false +when ymd is unparseable.

+ +