From db4a6e746c7ef1f5a9ee788f28c1cd7817dd2c2a Mon Sep 17 00:00:00 2001 From: tobi Date: Wed, 23 Apr 2025 12:17:47 +0200 Subject: [PATCH] penis --- cmd/gotosocial/action/server/server.go | 3 +- internal/middleware/nollamas.go | 53 ++++++++--- internal/web/web.go | 6 +- web/source/index.js | 18 ++++ web/source/nollamas/index.js | 52 ++++++++++ web/source/nollamasworker/index.js | 52 ++++++++++ web/template/nollamas.tmpl | 127 ++++++------------------- 7 files changed, 200 insertions(+), 111 deletions(-) create mode 100644 web/source/nollamas/index.js create mode 100644 web/source/nollamasworker/index.js diff --git a/cmd/gotosocial/action/server/server.go b/cmd/gotosocial/action/server/server.go index 0845e5ed1..6bc27a7c4 100644 --- a/cmd/gotosocial/action/server/server.go +++ b/cmd/gotosocial/action/server/server.go @@ -499,7 +499,6 @@ var Start action.GTSAction = func(ctx context.Context) error { s2sLimit := middleware.RateLimit(rlLimit, exceptions) // server-to-server (AP) fsMainLimit := middleware.RateLimit(rlLimit, exceptions) // fileserver / web templates fsEmojiLimit := middleware.RateLimit(rlLimit*2, exceptions) // fileserver (emojis only, use high limit) - nollamas := middleware.NoLLaMas(instanceAccount) // throttling cpuMultiplier := config.GetAdvancedThrottlingMultiplier() @@ -545,7 +544,7 @@ var Start action.GTSAction = func(ctx context.Context) error { nodeInfoModule.Route(route, s2sLimit, s2sThrottle, gzip) activityPubModule.Route(route, s2sLimit, s2sThrottle, robotsDisallowAll, gzip) activityPubModule.RoutePublicKey(route, s2sLimit, pkThrottle, robotsDisallowAll, gzip) - webModule.Route(route, fsMainLimit, fsThrottle, robotsDisallowAIOnly, nollamas, gzip) + webModule.Route(route, fsMainLimit, fsThrottle, robotsDisallowAIOnly, gzip) // Finally start the main http server! if err := route.Start(); err != nil { diff --git a/internal/middleware/nollamas.go b/internal/middleware/nollamas.go index fc7a24716..0ebdf8c15 100644 --- a/internal/middleware/nollamas.go +++ b/internal/middleware/nollamas.go @@ -18,6 +18,9 @@ package middleware import ( + "context" + "crypto/rand" + "crypto/rsa" "crypto/sha256" "crypto/sha512" "crypto/subtle" @@ -29,15 +32,23 @@ import ( "codeberg.org/gruf/go-byteutil" "github.com/gin-gonic/gin" - "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" + apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" + apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util" + "github.com/superseriousbusiness/gotosocial/internal/gtserror" "github.com/superseriousbusiness/gotosocial/internal/oauth" ) -func NoLLaMas(instanceAcc *gtsmodel.Account) gin.HandlerFunc { - // Generate seed hash from - // this instance private key. - priv := instanceAcc.PrivateKey - bpriv := x509.MarshalPKCS1PrivateKey(priv) +func NoLLaMas( + getInstance func(ctx context.Context) (*apimodel.InstanceV1, gtserror.WithCode), +) gin.HandlerFunc { + privKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + panic(err) + } + + // Generate seed hash + // from this private key. + bpriv := x509.MarshalPKCS1PrivateKey(privKey) seed := sha512.Sum512(bpriv) // Configure nollamas. @@ -45,6 +56,7 @@ func NoLLaMas(instanceAcc *gtsmodel.Account) gin.HandlerFunc { nollamas.seed = seed[:] nollamas.ttl = time.Hour nollamas.diff = 4 + nollamas.getInstance = getInstance return nollamas.Serve } @@ -57,9 +69,10 @@ const encodedHashLen = 2 * hashLen func newHash() hash.Hash { return sha256.New() } type nollamas struct { - seed []byte // securely hashed instance private key - ttl time.Duration - diff uint8 + seed []byte // securely hashed private key + ttl time.Duration + diff uint8 + getInstance func(ctx context.Context) (*apimodel.InstanceV1, gtserror.WithCode) } func (m *nollamas) Serve(c *gin.Context) { @@ -169,10 +182,26 @@ func (m *nollamas) renderChallenge(c *gin.Context, challenge string) { // our challenge page. c.Abort() + instance, errWithCode := m.getInstance(c.Request.Context()) + if errWithCode != nil { + apiutil.ErrorHandler(c, errWithCode, m.getInstance) + return + } + // Write the templated challenge HTML response to client. - c.HTML(http.StatusOK, "nollamas.tmpl", map[string]any{ - "challenge": challenge, - "difficulty": m.diff, + apiutil.TemplateWebPage(c, apiutil.WebPage{ + Template: "nollamas.tmpl", + Instance: instance, + Extra: map[string]any{ + "challenge": challenge, + "difficulty": m.diff, + }, + Javascript: []apiutil.JavascriptEntry{ + { + Src: "/assets/dist/nollamas.js", + Defer: true, + }, + }, }) } diff --git a/internal/web/web.go b/internal/web/web.go index 5bccca06d..402a45ae7 100644 --- a/internal/web/web.go +++ b/internal/web/web.go @@ -99,12 +99,16 @@ func (m *Module) Route(r *router.Router, mi ...gin.HandlerFunc) { // Handlers that serve profiles and statuses should use // the SignatureCheck middleware, so that requests with - // content-type application/activity+json can be served + // content-type application/activity+json can be served, + // and (if enabled) the nollamas middleware, to protect + // against scraping by shitty LLM bullshit. profileGroup := r.AttachGroup(profileGroupPath) profileGroup.Use(mi...) profileGroup.Use(middleware.SignatureCheck(m.isURIBlocked), middleware.CacheControl(middleware.CacheControlConfig{ Directives: []string{"no-store"}, })) + nollamas := middleware.NoLLaMas(m.processor.InstanceGetV1) + profileGroup.Use(nollamas) profileGroup.Handle(http.MethodGet, "", m.profileGETHandler) // use empty path here since it's the base of the group profileGroup.Handle(http.MethodGet, statusPath, m.threadGETHandler) diff --git a/web/source/index.js b/web/source/index.js index d66afe757..e99c27fcd 100644 --- a/web/source/index.js +++ b/web/source/index.js @@ -73,6 +73,24 @@ skulk({ ["babelify", { global: true }] ], }, + nollamas: { + entryFile: "nollamas", + outputFile: "nollamas.js", + preset: ["js"], + prodCfg: prodCfg, + transform: [ + ["babelify", { global: true }] + ], + }, + nollamasworker: { + entryFile: "nollamasworker", + outputFile: "nollamasworker.js", + preset: ["js"], + prodCfg: prodCfg, + transform: [ + ["babelify", { global: true }] + ], + }, settings: { entryFile: "settings", outputFile: "settings.js", diff --git a/web/source/nollamas/index.js b/web/source/nollamas/index.js new file mode 100644 index 000000000..c330dcdc1 --- /dev/null +++ b/web/source/nollamas/index.js @@ -0,0 +1,52 @@ +/* + GoToSocial + Copyright (C) GoToSocial Authors admin@gotosocial.org + SPDX-License-Identifier: AGPL-3.0-or-later + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +document.addEventListener('DOMContentLoaded', function() { + // Read the challenge and difficulty from + // data attributes on the nollamas section. + const nollamas = document.querySelector(".nollamas"); + const challenge = nollamas.dataset.nollamasChallenge; + const difficulty = nollamas.dataset.nollamasDifficulty; + + console.log('challenge:', challenge); + console.log('difficulty:', difficulty); + + // Not sure what this is for. Kim help?? + const jsOnlyElements = document.querySelectorAll('.hidden'); + jsOnlyElements.forEach(el => { + el.classList.remove('hidden'); + }); + + // Prepare the worker with task function. + const worker = new Worker("/assets/dist/nollamasworker.js"); + worker.postMessage({ + challenge: challenge, + difficulty: difficulty, + }); + + // Set the main worker function. + worker.onmessage = function (e) { + if (e.data.done) { + console.log('solution found for:', e.data.nonce); + let url = new URL(window.location.href); + url.searchParams.append('nollamas_solution', e.data.nonce); + window.location.href = url.toString(); + } + }; +}); diff --git a/web/source/nollamasworker/index.js b/web/source/nollamasworker/index.js new file mode 100644 index 000000000..518345a45 --- /dev/null +++ b/web/source/nollamasworker/index.js @@ -0,0 +1,52 @@ +/* + GoToSocial + Copyright (C) GoToSocial Authors admin@gotosocial.org + SPDX-License-Identifier: AGPL-3.0-or-later + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +onmessage = async function(e) { + console.log('worker started'); + + const challenge = e.data.challenge; + const textEncoder = new TextEncoder(); + + // Get difficulty and generate the expected + // zero ASCII prefix to check for in hashes. + const difficultyStr = e.data.difficulty; + const difficulty = parseInt(difficultyStr, 10); + const zeroPrefix = '0'.repeat(difficulty); + + let nonce = 0; + while (true) { + // Create possible solution string from challenge + nonce. + const solution = textEncoder.encode(challenge + nonce.toString()); + + // Generate SHA256 hashsum of solution string and hex encode the result. + const hashBuffer = await crypto.subtle.digest('SHA-256', solution); + const hashArray = Array.from(new Uint8Array(hashBuffer)); + const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); + + // Check if the hex encoded hash has + // difficulty defined zeroes prefix. + if (hashHex.startsWith(zeroPrefix)) { + postMessage({ nonce: nonce, done: true }); + break; + } + + // Iter. + nonce++; + } +}; diff --git a/web/template/nollamas.tmpl b/web/template/nollamas.tmpl index faaf70054..667cbf346 100644 --- a/web/template/nollamas.tmpl +++ b/web/template/nollamas.tmpl @@ -1,97 +1,32 @@ - - +{{- /* +// GoToSocial +// Copyright (C) GoToSocial Authors admin@gotosocial.org +// SPDX-License-Identifier: AGPL-3.0-or-later +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +*/ -}} - - Verifying... - - - - - - -
-
- - - -
-
- - - +{{- with . }} +
+
+

One moment while we verify your connection...

+ +
+
+{{- end }} \ No newline at end of file