3GOD was "three.js god" — a quick way to eyeball a GLB and drop it into a game. wardrobegod's stage, grid and publish flow cover that far better now, so the bench is retired. What survives is the half that quietly became infrastructure. Absorbed into depot/: server.py (271 lines, verbatim), its hardening tests, and viewer.html kept as superseded reference only. Retiring the REPO is safe; retiring the SERVICE is not. The live instance on the dealgod VPS holds 773 assets and two shipping games fetch from it at runtime — thriftgod web/index.html:1176 and procity loaders.js:7 both hardcode https://digalot.fyi/3god. So the /3god URL stays even though the name is gone: changing it means editing two shipping codebases for no benefit, and the name now survives only as a URL path nobody looks at. The VPS service is deployed, not sourced live from the Gitea checkout, so archiving that repo changes nothing at runtime. Checked before discarding the UI: the depot's /api/meta tag store had 0 tags and 0 notes across all 773 assets, so there was nothing to migrate. Tagging lives in library/index.json now. depot/README.md records the two things that are easy to get wrong and expensive to rediscover: publishing must go direct over the tailnet (the Cloudflare front 403s because auth trusts the raw socket peer — thriftgod's own prop_campaign.py --publish is broken for exactly this), and clean_name DELETES illegal characters so 'shop!-cat.glb' silently resolves to an existing different mesh. Verified after the change: both the public and tailnet endpoints still answer 200. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
31 lines
1.6 KiB
Python
31 lines
1.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Self-check for the SSRF guard added to server.py. No framework, no network:
|
|
literal-IP URLs resolve via getaddrinfo without a DNS lookup, so this is hermetic.
|
|
|
|
GOD3_OPEN=1 python3 test_hardening.py # OPEN so import doesn't sys.exit on missing GOD3_PW
|
|
"""
|
|
import os
|
|
os.environ.setdefault('GOD3_OPEN', '1') # let server.py import without a real GOD3_PW
|
|
import server
|
|
|
|
# --- blocked: non-http schemes (file://, ftp://) — the /etc/passwd + FileHandler vector ---
|
|
assert server.public_host('file:///etc/passwd') is False
|
|
assert server.public_host('ftp://ftp.example.com/x') is False
|
|
assert server.public_host('gopher://x/') is False
|
|
assert server.public_host('') is False
|
|
|
|
# --- blocked: internal / metadata / loopback targets (literal IPs, no DNS) ---
|
|
assert server.public_host('http://127.0.0.1/') is False # loopback
|
|
assert server.public_host('http://[::1]/') is False # loopback v6
|
|
assert server.public_host('http://169.254.169.254/latest/meta-data/') is False # cloud metadata
|
|
assert server.public_host('http://10.0.0.5/') is False # private
|
|
assert server.public_host('http://192.168.1.1/') is False # private
|
|
assert server.public_host('http://172.16.0.9/') is False # private
|
|
assert server.public_host('http://0.0.0.0/') is False # unspecified
|
|
|
|
# --- allowed: public literal IPs (hermetic — no DNS) ---
|
|
assert server.public_host('http://8.8.8.8/model.glb') is True
|
|
assert server.public_host('https://1.1.1.1/x.glb') is True
|
|
|
|
print('ok — SSRF guard blocks file/ftp + loopback/private/metadata, allows public http(s)')
|