tinyweb/app.py
lichenblankie 449174b0ca fixed SSRF bypass, tightened error handling
- SSRF: disable automatic redirects, manually follow up to 5 hops with
  IP re-validation at each step to prevent redirect-to-localhost bypass
- Identity file: enforce 0600 permissions on tinyweb_identity at load
  and creation to prevent other users from reading the private key
- Error messages: replace raw exception strings with generic messages
  to avoid leaking internal paths/hostnames to the UI
- DB connections: wrap all get_db() usage in try/finally to guarantee
  close() even when handlers throw mid-operation
2026-06-05 05:29:35 +00:00

75 lines
2 KiB
Python

import os
import time
import threading
import RNS
from http.server import HTTPServer
from db import init_db, set_setting
from handlers import dispatch_request
from gateway import GatewayState, GatewayHandler, GATEWAY_PORT
APP_NAME = "tinyweb"
ASPECTS = ["server"]
IDENTITY_FILE = "tinyweb_identity"
def load_or_create_identity():
if os.path.isfile(IDENTITY_FILE):
# Ensure identity file is only readable by owner
current = os.stat(IDENTITY_FILE).st_mode & 0o777
if current != 0o600:
os.chmod(IDENTITY_FILE, 0o600)
return RNS.Identity.from_file(IDENTITY_FILE)
identity = RNS.Identity()
identity.to_file(IDENTITY_FILE)
os.chmod(IDENTITY_FILE, 0o600)
return identity
def rns_request_handler(path, data, request_id, link_id, remote_identity, requested_at):
if data is None:
data = {"method": "GET", "path": "/", "query": {}, "body": {}, "gateway_host": ""}
return dispatch_request(data)
def start_gateway(reticulum):
GatewayState.reticulum = reticulum
GatewayState.local_dispatch = dispatch_request
server = HTTPServer(("0.0.0.0", GATEWAY_PORT), GatewayHandler)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
def main():
init_db()
reticulum = RNS.Reticulum()
identity = load_or_create_identity()
destination = RNS.Destination(
identity,
RNS.Destination.IN,
RNS.Destination.SINGLE,
APP_NAME,
*ASPECTS,
)
destination.register_request_handler(
"/tinyweb",
response_generator=rns_request_handler,
allow=RNS.Destination.ALLOW_ALL,
)
destination.announce()
set_setting("dest_hash", destination.hash.hex())
start_gateway(reticulum)
print(f"TinyWeb running!")
print(f"Open http://localhost:{GATEWAY_PORT} in your browser")
print(f"Destination hash: {RNS.prettyhexrep(destination.hash)} (share this so friends can subscribe)")
while True:
time.sleep(1)
if __name__ == "__main__":
main()