First iteration of docs site

This commit is contained in:
markmental 2026-09-08 19:23:49 -04:00
commit b89caefb11
9 changed files with 543 additions and 0 deletions

69
docs/cert.html Normal file
View file

@ -0,0 +1,69 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>isthislegit Developer Docs - Package cert</title>
</head>
<body bgcolor="#002b36" text="#a3b8b9" link="#859900" vlink="#b58900">
<font color="#b58900"><h1>Package cert</h1></font>
<p>Leaf X.509 certificate fetching and CA/Browser Forum validation-level
classification from policy OIDs. Source: <tt>internal/cert/cert.go</tt>.</p>
<p>Reserved policy OIDs: <tt>2.23.140.1.1</tt> (EV),
<tt>2.23.140.1.2.1</tt> (DV), <tt>2.23.140.1.2.2</tt> (OV),
<tt>2.23.140.1.2.3</tt> (IV). Priority order is EV &gt; OV &gt; IV &gt; DV.</p>
<hr>
<a name="Details"></a>
<font color="#859900"><h2>Details</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>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
}</pre></td></tr></table>
<p>Extracted leaf-certificate fields. <tt>Issuer</tt> prefers the
<tt>Organization</tt> attribute and falls back to Common Name, then the full
issuer string. <tt>SANs</tt> falls back to the subject Common Name.</p>
<hr>
<a name="Classify"></a>
<font color="#859900"><h2>Classify</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>func Classify(oids []string) string</pre></td></tr></table>
<p>Maps a set of policy OID strings to a validation class using the ordered
priority table (EV first). Returns <tt>"Unknown"</tt> when none match.</p>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>cert.Classify([]string{"2.23.140.1.2.1"}) // =&gt; "DV"</pre></td></tr></table>
<hr>
<a name="InspectDomain"></a>
<font color="#859900"><h2>InspectDomain</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>func InspectDomain(ctx context.Context, domain string, perCall time.Duration) (*Details, error)</pre></td></tr></table>
<p>Dials <tt>domain:443</tt> with <tt>tls.Dialer</tt>
(<tt>InsecureSkipVerify</tt>, since the peer is untrusted by definition) and
returns the parsed leaf via <a href="cert.html#FromLeaf">FromLeaf</a>.
Transient failures are retried up to 3 times with a 1 second delay.</p>
<table border="1" cellpadding="4">
<tr><th>Parameter</th><th>Description</th></tr>
<tr><td><tt>ctx</tt></td><td>Parent context; the global 15 second budget.</td></tr>
<tr><td><tt>domain</tt></td><td>Already-normalized domain to dial.</td></tr>
<tr><td><tt>perCall</tt></td><td>Per-attempt deadline (5 seconds in the CLI).</td></tr>
</table>
<hr>
<a name="FromLeaf"></a>
<font color="#859900"><h2>FromLeaf</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>func FromLeaf(leaf *x509.Certificate) *Details</pre></td></tr></table>
<p>Pure constructor: builds <a href="cert.html#Details">Details</a> from an
already-parsed leaf certificate. Preferred entry point for tests.</p>
<hr>
<a name="SANsString"></a>
<font color="#859900"><h2>SANsString</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>func (d *Details) SANsString() string</pre></td></tr></table>
<p>Joins SANs with <tt>", "</tt> for display, or returns
<tt>"Unknown"</tt> when empty. Nil-receiver safe.</p>
</body>
</html>

43
docs/dns.html Normal file
View file

@ -0,0 +1,43 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>isthislegit Developer Docs - Package dns</title>
</head>
<body bgcolor="#002b36" text="#a3b8b9" link="#859900" vlink="#b58900">
<font color="#b58900"><h1>Package dns</h1></font>
<p>Domain normalization, syntax validation, and DNS pre-flight resolution
checks. Source: <tt>internal/dns/dns.go</tt>.</p>
<hr>
<a name="NormalizeDomain"></a>
<font color="#859900"><h2>NormalizeDomain</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>func NormalizeDomain(d string) string</pre></td></tr></table>
<p>Lowercases the input and strips scheme, path, port, and trailing dot.
Mirrors the shell <tt>normalize_domain</tt> helper.</p>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>dns.NormalizeDomain("HTTPS://Example.COM:8443/path")
// =&gt; "example.com"</pre></td></tr></table>
<hr>
<a name="ValidSyntax"></a>
<font color="#859900"><h2>ValidSyntax</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>func ValidSyntax(d string) bool</pre></td></tr></table>
<p>Reports whether <tt>d</tt> 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.</p>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>dns.ValidSyntax("jdsoft.com") // =&gt; true
dns.ValidSyntax("no-dot") // =&gt; false</pre></td></tr></table>
<hr>
<a name="CheckDNS"></a>
<font color="#859900"><h2>CheckDNS</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>func CheckDNS(ctx context.Context, domain string) error</pre></td></tr></table>
<p>Aborts early on NXDOMAIN or unresolvable names via
<tt>net.DefaultResolver.LookupIP</tt> with a 5 second deadline. Returns
<tt>nil</tt> when at least one IPv4 address resolves, an error otherwise.</p>
<table border="1" cellpadding="4">
<tr><th>Parameter</th><th>Description</th></tr>
<tr><td><tt>ctx</tt></td><td>Parent context; the global 15 second budget.</td></tr>
<tr><td><tt>domain</tt></td><td>Already-normalized domain to resolve.</td></tr>
</table>
</body>
</html>

24
docs/index.html Normal file
View file

@ -0,0 +1,24 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>isthislegit Developer Docs</title>
</head>
<frameset cols="220,*">
<frame src="nav.html" name="nav">
<frame src="overview.html" name="content">
<noframes>
<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>
</noframes>
</frameset>
</html>

52
docs/main.html Normal file
View file

@ -0,0 +1,52 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>isthislegit Developer Docs - Command main</title>
</head>
<body bgcolor="#002b36" text="#a3b8b9" link="#859900" vlink="#b58900">
<font color="#b58900"><h1>Command main</h1></font>
<p>CLI entry point and fetch orchestration. Source:
<tt>cmd/isthislegit/main.go</tt>. Package <tt>main</tt> exposes no exported
symbols; this page documents behavior.</p>
<font color="#859900"><h2>Arguments</h2></font>
<p>Syntax: <tt>isthislegit [FLAGS] &lt;legit_domain&gt;
&lt;suspect_domain&gt;</tt>. Flags <tt>-v/--verbose</tt>,
<tt>-j/--json</tt>, <tt>--scale</tt>, <tt>-h/--help</tt>, plus a
<tt>--</tt> separator. Unknown flags and anything other than exactly two
positional domains exit 1 with a usage message. Domains are normalized with
<a href="dns.html#NormalizeDomain">dns.NormalizeDomain</a> and validated
with <a href="dns.html#ValidSyntax">dns.ValidSyntax</a>.</p>
<font color="#859900"><h2>Budgets</h2></font>
<table border="1" cellpadding="4">
<tr><th>Budget</th><th>Value</th></tr>
<tr><td>Global deadline (both domains)</td><td>15 seconds</td></tr>
<tr><td>Per-call deadline (DNS, TLS, RDAP)</td><td>5 seconds</td></tr>
<tr><td>Attempts per call</td><td>3, with 1 second between retries</td></tr>
</table>
<font color="#859900"><h2>Flow</h2></font>
<ol>
<li>DNS pre-flight for both domains in parallel; any failure aborts.</li>
<li>Per-domain fetch in parallel: RDAP
(<a href="rdap.html#Query">rdap.Query</a>) plus TLS
(<a href="cert.html#InspectDomain">cert.InspectDomain</a>) concurrently.
Field-level failures stay open (Unknown) here.</li>
<li>Fail-closed gate:
<a href="score.html#VerifyDataQuality">score.VerifyDataQuality</a>; aborts
on missing dates or TLS data for either side.</li>
<li><a href="score.html#Score">score.Score</a> produces score, findings,
verdict, and age flag.</li>
<li>Render: <a href="report.html#JSON">report.JSON</a> for
<tt>-j/--json</tt>, otherwise <a href="report.html#Human">report.Human</a>
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
output, and <tt>--scale</tt> header. Build target:
<tt>CGO_ENABLED=0 go build -ldflags="-s -w" -o isthislegit
./cmd/isthislegit</tt>.</p>
</body>
</html>

63
docs/nav.html Normal file
View file

@ -0,0 +1,63 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>isthislegit Docs - Navigation</title>
</head>
<body bgcolor="#002b36" text="#a3b8b9" link="#859900" vlink="#b58900">
<font color="#b58900"><h3>isthislegit?</h3></font>
<p><a href="overview.html" target="content"><b>Overview</b></a></p>
<hr>
<p><b>Packages</b></p>
<ul>
<li><a href="dns.html" target="content">dns</a></li>
<li><a href="cert.html" target="content">cert</a></li>
<li><a href="rdap.html" target="content">rdap</a></li>
<li><a href="score.html" target="content">score</a></li>
<li><a href="report.html" target="content">report</a></li>
<li><a href="main.html" target="content">main</a></li>
</ul>
<hr>
<p><b>dns</b></p>
<ul>
<li><a href="dns.html#NormalizeDomain" target="content">NormalizeDomain</a></li>
<li><a href="dns.html#ValidSyntax" target="content">ValidSyntax</a></li>
<li><a href="dns.html#CheckDNS" target="content">CheckDNS</a></li>
</ul>
<p><b>cert</b></p>
<ul>
<li><a href="cert.html#Details" target="content">Details</a></li>
<li><a href="cert.html#Classify" target="content">Classify</a></li>
<li><a href="cert.html#InspectDomain" target="content">InspectDomain</a></li>
<li><a href="cert.html#FromLeaf" target="content">FromLeaf</a></li>
<li><a href="cert.html#SANsString" target="content">SANsString</a></li>
</ul>
<p><b>rdap</b></p>
<ul>
<li><a href="rdap.html#Event" target="content">Event</a></li>
<li><a href="rdap.html#Response" target="content">Response</a></li>
<li><a href="rdap.html#Result" target="content">Result</a></li>
<li><a href="rdap.html#Query" target="content">Query</a></li>
<li><a href="rdap.html#RegistrationDate" target="content">RegistrationDate</a></li>
<li><a href="rdap.html#Registrar" target="content">Registrar</a></li>
</ul>
<p><b>score</b></p>
<ul>
<li><a href="score.html#DomainData" target="content">DomainData</a></li>
<li><a href="score.html#Result" target="content">Result</a></li>
<li><a href="score.html#ClassLong" target="content">ClassLong</a></li>
<li><a href="score.html#VerifyDataQuality" target="content">VerifyDataQuality</a></li>
<li><a href="score.html#Score" target="content">Score</a></li>
<li><a href="score.html#Verdict" target="content">Verdict</a></li>
<li><a href="score.html#ValidISODate" target="content">ValidISODate</a></li>
<li><a href="score.html#AgeDays" target="content">AgeDays</a></li>
</ul>
<p><b>report</b></p>
<ul>
<li><a href="report.html#Data" target="content">Data</a></li>
<li><a href="report.html#Scale" target="content">Scale</a></li>
<li><a href="report.html#Human" target="content">Human</a></li>
<li><a href="report.html#JSON" target="content">JSON</a></li>
<li><a href="report.html#Verbose" target="content">Verbose</a></li>
</ul>
</body>
</html>

71
docs/overview.html Normal file
View file

@ -0,0 +1,71 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>isthislegit Developer Docs - Overview</title>
</head>
<body bgcolor="#002b36" text="#a3b8b9" link="#859900" vlink="#b58900">
<font color="#b58900"><h1>isthislegit? [v1.01]</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
score and verdict. Single static binary; standard library plus
<tt>golang.org/x/sync</tt> only.</p>
<font color="#859900"><h2>Build and Run</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>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 ./...</pre></td></tr></table>
<font color="#859900"><h2>Flags</h2></font>
<table border="1" cellpadding="4">
<tr><th>Flag</th><th>Description</th></tr>
<tr><td><tt>-v, --verbose</tt></td><td>Append raw cert policy OIDs, TLS details, and RDAP JSON.</td></tr>
<tr><td><tt>-j, --json</tt></td><td>Output the result summary as JSON for scripting.</td></tr>
<tr><td><tt>--scale</tt></td><td>Print the scoring scale table and exit.</td></tr>
<tr><td><tt>-h, --help</tt></td><td>Display usage and exit.</td></tr>
</table>
<font color="#859900"><h2>Fetch Pipeline</h2></font>
<p>Both domains are fetched concurrently with
<tt>golang.org/x/sync/errgroup</tt> 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.</p>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>parse args --&gt; DNS pre-flight (target + suspect, parallel)
--&gt; fetch target = RDAP + TLS in parallel
--&gt; fetch suspect = RDAP + TLS in parallel
--&gt; fail-closed verify --&gt; score --&gt; render</pre></td></tr></table>
<p>Exit codes: <tt>0</tt> on success (including <tt>--scale</tt> and
<tt>--help</tt>), <tt>1</tt> on bad flags, invalid domains, DNS failure, or
failed data-quality verification.</p>
<font color="#859900"><h2>Scoring Scale</h2></font>
<table border="1" cellpadding="4">
<tr><th>Signal</th><th>Points</th></tr>
<tr><td>Suspect domain age &lt; 30 days (CRITICAL)</td><td>+80</td></tr>
<tr><td>Suspect domain age 30-89 days (WARNING)</td><td>+80</td></tr>
<tr><td>Cert downgrade: target EV/OV vs suspect DV</td><td>+40</td></tr>
<tr><td>Registrar mismatch</td><td>+30</td></tr>
<tr><td>Same-class issuer difference (display only)</td><td>+0</td></tr>
</table>
<br>
<table border="1" cellpadding="4">
<tr><th>Score</th><th>Verdict</th></tr>
<tr><td>0-14</td><td>LIKELY LEGITIMATE</td></tr>
<tr><td>15-39</td><td>LIKELY UNRELATED / MISCONFIGURED</td></tr>
<tr><td>40-50</td><td>SUSPICIOUS - MANUAL REVIEW RECOMMENDED</td></tr>
<tr><td>51+</td><td>LIKELY PHISHING / IMPERSONATION ATTEMPT</td></tr>
</table>
<font color="#859900"><h2>Packages</h2></font>
<ul>
<li><a href="dns.html"><b>dns</b></a> - normalization, syntax checks, DNS pre-flight.</li>
<li><a href="cert.html"><b>cert</b></a> - TLS leaf fetch and validation-level classification.</li>
<li><a href="rdap.html"><b>rdap</b></a> - RDAP client, creation date and registrar extraction.</li>
<li><a href="score.html"><b>score</b></a> - risk scoring and verdict mapping.</li>
<li><a href="report.html"><b>report</b></a> - human, JSON, scale, and verbose renderers.</li>
<li><a href="main.html"><b>main</b></a> - CLI entry point and fetch orchestration.</li>
</ul>
</body>
</html>

69
docs/rdap.html Normal file
View file

@ -0,0 +1,69 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>isthislegit Developer Docs - Package rdap</title>
</head>
<body bgcolor="#002b36" text="#a3b8b9" link="#859900" vlink="#b58900">
<font color="#b58900"><h1>Package rdap</h1></font>
<p>RDAP registration-data client over <tt>https://rdap.org/domain/</tt>.
Source: <tt>internal/rdap/rdap.go</tt>.</p>
<hr>
<a name="Event"></a>
<font color="#859900"><h2>Event</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>type Event struct {
Action string `json:"eventAction"`
Date string `json:"eventDate"`
}</pre></td></tr></table>
<p>Single RDAP event object; only <tt>registration</tt> actions are used.</p>
<hr>
<a name="Response"></a>
<font color="#859900"><h2>Response</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>type Response struct {
Events []Event `json:"events"`
Entities []entity `json:"entities"`
}</pre></td></tr></table>
<p>Minimal parsed RDAP payload. Entities carry registrar roles and vCard
arrays used for registrar extraction.</p>
<hr>
<a name="Result"></a>
<font color="#859900"><h2>Result</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>type Result struct {
Raw []byte // raw JSON, kept for --verbose output
Resp *Response // parsed payload
Found bool // false when the lookup failed
}</pre></td></tr></table>
<hr>
<a name="Query"></a>
<font color="#859900"><h2>Query</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>func Query(ctx context.Context, client *http.Client, domain string, perCall time.Duration) (*Result, error)</pre></td></tr></table>
<p>Fetches RDAP JSON for <tt>domain</tt> with an <tt>Accept:
application/json</tt> request, retrying transient failures up to 3 times.
Non-2xx status and invalid JSON are errors.</p>
<table border="1" cellpadding="4">
<tr><th>Parameter</th><th>Description</th></tr>
<tr><td><tt>ctx</tt></td><td>Parent context; the global 15 second budget.</td></tr>
<tr><td><tt>client</tt></td><td>Shared <tt>*http.Client</tt>; per-call deadline via context.</td></tr>
<tr><td><tt>domain</tt></td><td>Already-normalized domain to look up.</td></tr>
<tr><td><tt>perCall</tt></td><td>Per-attempt deadline (5 seconds in the CLI).</td></tr>
</table>
<hr>
<a name="RegistrationDate"></a>
<font color="#859900"><h2>RegistrationDate</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>func RegistrationDate(r *Result) string</pre></td></tr></table>
<p>Returns the <tt>YYYY-MM-DD</tt> date of the <tt>registration</tt> event, or
<tt>"Unknown / Lookup Failed"</tt> when unavailable. Nil-safe.</p>
<hr>
<a name="Registrar"></a>
<font color="#859900"><h2>Registrar</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>func Registrar(r *Result) string</pre></td></tr></table>
<p>Best-effort registrar name, or <tt>"Unknown"</tt>. Prefers entities with
the <tt>registrar</tt> role, falling back to the first vCard
<tt>fn</tt> property. Nil-safe.</p>
</body>
</html>

64
docs/report.html Normal file
View file

@ -0,0 +1,64 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>isthislegit Developer Docs - Package report</title>
</head>
<body bgcolor="#002b36" text="#a3b8b9" link="#859900" vlink="#b58900">
<font color="#b58900"><h1>Package report</h1></font>
<p>Human, JSON, scale, and verbose renderers. Source:
<tt>internal/report/report.go</tt>.</p>
<hr>
<a name="Data"></a>
<font color="#859900"><h2>Data</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>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
}</pre></td></tr></table>
<p>Everything a renderer needs: scored domain data plus the raw cert and
RDAP results backing verbose output.</p>
<hr>
<a name="Scale"></a>
<font color="#859900"><h2>Scale</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>func Scale(version string) string</pre></td></tr></table>
<p>Static scoring-scale reference table (no lookups). Backs the
<tt>--scale</tt> flag.</p>
<hr>
<a name="Human"></a>
<font color="#859900"><h2>Human</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>func Human(d Data) string</pre></td></tr></table>
<p>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.</p>
<hr>
<a name="JSON"></a>
<font color="#859900"><h2>JSON</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>func JSON(d Data) string</pre></td></tr></table>
<p>Machine-readable summary with <tt>tool</tt>, <tt>version</tt>,
<tt>target</tt>, <tt>suspect</tt> (including suspect <tt>age_days</tt>),
<tt>risk_score</tt>, <tt>findings</tt>, and <tt>verdict</tt>. Ends with a
newline for pipeline use.</p>
<hr>
<a name="Verbose"></a>
<font color="#859900"><h2>Verbose</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>func Verbose(d Data) string</pre></td></tr></table>
<p>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.</p>
</body>
</html>

88
docs/score.html Normal file
View file

@ -0,0 +1,88 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
<title>isthislegit Developer Docs - Package score</title>
</head>
<body bgcolor="#002b36" text="#a3b8b9" link="#859900" vlink="#b58900">
<font color="#b58900"><h1>Package score</h1></font>
<p>Deterministic risk scoring: domain-age check (+80), certificate
downgrade check (+40), and registrar mismatch (+30), mapped onto a
four-tier verdict. Sources: <tt>internal/score/score.go</tt>,
<tt>internal/score/date.go</tt>.</p>
<hr>
<a name="DomainData"></a>
<font color="#859900"><h2>DomainData</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>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
}</pre></td></tr></table>
<p>Collected triage fields for one domain; the scoring input.</p>
<hr>
<a name="Result"></a>
<font color="#859900"><h2>Result</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>type Result struct {
Score int
Findings []string
Verdict string
AgeFlag string // display suffix for young suspect domains
}</pre></td></tr></table>
<hr>
<a name="ClassLong"></a>
<font color="#859900"><h2>ClassLong</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>func ClassLong(c string) string</pre></td></tr></table>
<p>Human-readable validation name via lookup table: EV to
<tt>"Extended Validation"</tt>, OV to <tt>"Organization Validated"</tt>, DV
to <tt>"Domain Validated"</tt>, IV to <tt>"Individual Validated"</tt>,
anything else to <tt>"Unknown"</tt>.</p>
<hr>
<a name="VerifyDataQuality"></a>
<font color="#859900"><h2>VerifyDataQuality</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>func VerifyDataQuality(target, suspect DomainData) error</pre></td></tr></table>
<p>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.</p>
<hr>
<a name="Score"></a>
<font color="#859900"><h2>Score</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>func Score(target, suspect DomainData) Result</pre></td></tr></table>
<p>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.</p>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>r := score.Score(target, suspect)
// r.Score, r.Findings, r.Verdict, r.AgeFlag</pre></td></tr></table>
<hr>
<a name="Verdict"></a>
<font color="#859900"><h2>Verdict</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>func Verdict(score int) string</pre></td></tr></table>
<p>Maps the total via threshold table: 51+ phishing, 40+ suspicious, 15+
unrelated/misconfigured, otherwise legitimate.</p>
<hr>
<a name="ValidISODate"></a>
<font color="#859900"><h2>ValidISODate</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>func ValidISODate(s string) bool</pre></td></tr></table>
<p>True only for clean, calendar-valid <tt>YYYY-MM-DD</tt> strings. Any
sentinel or garbage value fails by design.</p>
<hr>
<a name="AgeDays"></a>
<font color="#859900"><h2>AgeDays</h2></font>
<table border="0" cellpadding="8" width="100%"><tr><td bgcolor="#073642"><pre>func AgeDays(ymd string, now time.Time) (days int, ok bool)</pre></td></tr></table>
<p>Whole days between <tt>ymd</tt> and <tt>now</tt>; <tt>ok</tt> is false
when <tt>ymd</tt> is unparseable.</p>
</body>
</html>