nuke/web/index.templ
Xe Iaso 122e4bc072
feat: first implementation of honeypot logic (#1342)
* feat: first implementation of honeypot logic

This is a bit of an experiment, stick with me.

The core idea here is that badly written crawlers are that: badly
written. They look for anything that contains `<a href="whatever" />`
tags and will blindly use those values to recurse. This takes advantage
of that by hiding a link in a `<script>` tag like this:

```html
<script type="ignore"><a href="/bots-only">Don't click</a></script>
```

Browsers will ignore it because they have no handler for the "ignore"
script type.

This current draft is very unoptimized (it takes like 7 seconds to
generate a page on my tower), however switching spintax libraries will
make this much faster.

The hope is to make this pluggable with WebAssembly such that we force
administrators to choose a storage method. First we crawl before we
walk.

The AI involvement in this commit is limited to the spintax in
affirmations.txt, spintext.txt, and titles.txt. This generates a bunch
of "pseudoprofound bullshit" like the following:

> This Restoration to Balance & Alignment
>
> There's a moment when creators are being called to realize that the work
> can't be reduced to results, but about energy. We don't innovate products
> by pushing harder, we do it by holding the vision. Because momentum can't
> be forced, it unfolds over time when culture are moving in the same
> direction. We're being invited into a paradigm shift in how we think
> about innovation. [...]

This is intended to "look" like normal article text. As this is a first
draft, this sucks and will be improved upon.

Assisted-by: GLM 4.6, ChatGPT, GPT-OSS 120b
Signed-off-by: Xe Iaso <me@xeiaso.net>

* fix(honeypot/naive): optimize hilariously

Signed-off-by: Xe Iaso <me@xeiaso.net>

* feat(honeypot/naive): attempt to automatically filter out based on crawling

Signed-off-by: Xe Iaso <me@xeiaso.net>

* fix(lib): use mazeGen instead of bsGen

Signed-off-by: Xe Iaso <me@xeiaso.net>

* docs: add honeypot docs

Signed-off-by: Xe Iaso <me@xeiaso.net>

* chore(test): go mod tidy

Signed-off-by: Xe Iaso <me@xeiaso.net>

* chore: fix spelling metadata

Signed-off-by: Xe Iaso <me@xeiaso.net>

* chore: spelling

Signed-off-by: Xe Iaso <me@xeiaso.net>

---------

Signed-off-by: Xe Iaso <me@xeiaso.net>
2025-12-16 04:14:29 -05:00

173 lines
6 KiB
Text

package web
import (
"fmt"
"github.com/TecharoHQ/anubis"
"github.com/TecharoHQ/anubis/lib/config"
"github.com/TecharoHQ/anubis/lib/localization"
"github.com/TecharoHQ/anubis/xess"
"github.com/google/uuid"
)
templ base(title string, body templ.Component, impressum *config.Impressum, challenge any, ogTags map[string]string, localizer *localization.SimpleLocalizer) {
<!DOCTYPE html>
<html lang={ localizer.GetLang() }>
<head>
<title>{ title }</title>
<link rel="stylesheet" href={ anubis.BasePrefix + xess.URL }/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="robots" content="noindex,nofollow"/>
for key, value := range ogTags {
<meta property={ key } content={ value }/>
}
<style>
body,
html {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
margin-left: auto;
margin-right: auto;
}
.centered-div {
text-align: center;
}
#status {
font-variant-numeric: tabular-nums;
}
#progress {
display: none;
width: 90%;
width: min(20rem, 90%);
height: 2rem;
border-radius: 1rem;
overflow: hidden;
margin: 1rem 0 2rem;
outline-offset: 2px;
outline: #b16286 solid 4px;
}
.bar-inner {
background-color: #b16286;
height: 100%;
width: 0;
transition: width 0.25s ease-in;
}
</style>
@templ.JSONScript("anubis_version", anubis.Version)
@templ.JSONScript("anubis_challenge", challenge)
@templ.JSONScript("anubis_base_prefix", anubis.BasePrefix)
@templ.JSONScript("anubis_public_url", anubis.PublicUrl)
</head>
<body id="top">
@honeypotLink(fmt.Sprintf("%shoneypot/%s/init", anubis.APIPrefix, uuid.NewString()))
<main>
<h1 id="title" class="centered-div">{ title }</h1>
@body
<footer>
<div class="centered-div">
<p>
{ localizer.T("protected_by") } <a href="https://github.com/TecharoHQ/anubis">Anubis</a> { localizer.T("protected_from") } <a
href="https://techaro.lol"
>Techaro</a>. { localizer.T("made_with") }.
</p>
<p>{ localizer.T("mascot_design") } <a href="https://bsky.app/profile/celphase.bsky.social">{ localizer.T("celphase") }</a>.</p>
if impressum != nil {
<p>
@templ.Raw(impressum.Footer)
-- <a href={ templ.SafeURL(fmt.Sprintf("%simprint", anubis.APIPrefix)) }>Imprint</a>
</p>
}
<p>{ localizer.T("version_info") } <code>{ anubis.Version }</code>.</p>
</div>
</footer>
</main>
</body>
</html>
}
templ errorPage(message, mail, code string, localizer *localization.SimpleLocalizer) {
<div class="centered-div">
<img id="image" alt="Sad Anubis" style="width:100%;max-width:256px;" src={ anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/img/reject.webp?cacheBuster=" + anubis.Version }/>
<p>{ message }.</p>
if code != "" {
<code><pre>{ code }</pre></code>
}
if mail != "" {
<p>
<a href="/">{ localizer.T("go_home") }</a> { localizer.T("contact_webmaster") }
<a href={ "mailto:" + templ.SafeURL(mail) }>
{ mail }
</a>
</p>
} else {
<p><a href="/">{ localizer.T("go_home") }</a></p>
}
</div>
}
templ StaticHappy(localizer *localization.SimpleLocalizer) {
<div class="centered-div">
<img
style="display:none;"
style="width:100%;max-width:256px;"
src={ "/.within.website/x/cmd/anubis/static/img/happy.webp?cacheBuster=" +
anubis.Version }
/>
<p>{ localizer.T("static_check_endpoint") }</p>
</div>
}
templ bench(localizer *localization.SimpleLocalizer) {
<div style="height:20rem;display:flex">
<table style="margin-top:1rem;display:grid;grid-template:auto 1fr/auto auto;gap:0 0.5rem">
<thead
style="border-bottom:1px solid black;padding:0.25rem 0;display:grid;grid-template:1fr/subgrid;grid-column:1/-1"
>
<tr id="table-header" style="display:contents">
<th style="width:4.5rem">{ localizer.T("time") }</th>
<th style="width:4rem">{ localizer.T("iters") }</th>
</tr>
<tr id="table-header-compare" style="display:none">
<th style="width:4.5rem">{ localizer.T("time_a") }</th>
<th style="width:4rem">{ localizer.T("iters_a") }</th>
<th style="width:4.5rem">{ localizer.T("time_b") }</th>
<th style="width:4rem">{ localizer.T("iters_b") }</th>
</tr>
</thead>
<tbody
id="results"
style="padding-top:0.25rem;display:grid;grid-template-columns:subgrid;grid-auto-rows:min-content;grid-column:1/-1;row-gap:0.25rem;overflow-y:auto;font-variant-numeric:tabular-nums"
></tbody>
</table>
<div class="centered-div">
<img id="image" style="width:100%;max-width:256px;" src={ anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/img/pensive.webp?cacheBuster=" + anubis.Version }/>
<p id="status" style="max-width:256px">{ localizer.T("loading") }</p>
<script async type="module" src={ anubis.BasePrefix + "/.within.website/x/cmd/anubis/static/js/bench.mjs?cacheBuster=" + anubis.Version }></script>
<div id="sparkline"></div>
<noscript>
<p>{ localizer.T("benchmark_requires_js") }</p>
</noscript>
</div>
</div>
<form id="controls" style="position:fixed;top:0.5rem;right:0.5rem">
<div style="display:flex;justify-content:end">
<label for="difficulty-input" style="margin-right:0.5rem">{ localizer.T("difficulty") }</label>
<input id="difficulty-input" type="number" name="difficulty" style="width:3rem"/>
</div>
<div style="margin-top:0.25rem;display:flex;justify-content:end">
<label for="algorithm-select" style="margin-right:0.5rem">{ localizer.T("algorithm") }</label>
<select id="algorithm-select" name="algorithm"></select>
</div>
<div style="margin-top:0.25rem;display:flex;justify-content:end">
<label for="compare-select" style="margin-right:0.5rem">{ localizer.T("compare") }</label>
<select id="compare-select" name="compare">
<option value="NONE">-</option>
</select>
</div>
</form>
}