aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkj_sh6042026-04-04 02:39:18 -0400
committerkj_sh6042026-04-04 02:39:18 -0400
commit3b2cd29315147f0808d46e6cd5822dda24bddcb5 (patch)
tree8ed848e9711884f79816f3c6085b0543e9766fd0
parent03ce508b468090b3e1e83d1c683482b4df2299a2 (diff)
refactor: don't use env variables if not necessary
-rw-r--r--README23
-rw-r--r--server.py2
-rw-r--r--shim_app.py96
3 files changed, 47 insertions, 74 deletions
diff --git a/README b/README
index 680ec4d..59b7a99 100644
--- a/README
+++ b/README
@@ -14,21 +14,18 @@ quick start (assumes POSIX)
- python3 server.py
- open http://127.0.0.1:8585/app
-production configuration (gunicorn)
+production service (gunicorn)
- gunicorn server:app --bind 0.0.0.0:8585 --workers 4 --threads 8 --timeout 60 --graceful-timeout 30 --keep-alive 5 --access-logfile - --error-logfile -
config
- - SHIM_APP_NAME: ui/app name (default: shim)
- - SHIM_BIND: bind address (default: 0.0.0.0)
- - SHIM_PORT: port (default: 8585)
- - SHIM_MOJICRYPT_BIN: mojicrypt path (default: ./vendor/mojicrypt)
- - SHIM_COOKIE_SECURE: auto|true|false (default: auto)
- - SHIM_ENFORCE_APP_REQUEST_GUARDS: false by default (set true only when proxy/host headers are correct)
- - SHIM_SQLITE_TIMEOUT_SECONDS (default: 30.0)
- - SHIM_SQLITE_BUSY_TIMEOUT_MS (default: 30000)
- - SHIM_SQLITE_CACHE_SIZE_KIB (default: 32768)
- - SHIM_SQLITE_MMAP_SIZE_BYTES (default: 268435456)
- - SHIM_SQLITE_WAL_AUTOCHECKPOINT_PAGES (default: 1000)
+ - edit values directly in shim_app.py constants
+ - common constants: APP_NAME, BIND_HOST, PORT
+ - upload/session limits: SESSION_TTL_SECONDS, MAX_UPLOAD_BYTES, MAX_EXTRACTED_BYTES, MAX_EXTRACTED_FILES, MAX_FORM_MEMORY_SIZE
+ - sqlite tuning: SQLITE_TIMEOUT_SECONDS, SQLITE_BUSY_TIMEOUT_MS, SQLITE_CACHE_SIZE_KIB, SQLITE_MMAP_SIZE_BYTES, SQLITE_WAL_AUTOCHECKPOINT_PAGES
+
+env vars
+ - SECRET_KEY: external secret injection for production
+ - ENFORCE_APP_REQUEST_GUARDS: optional same-origin/csrf enforcement toggle for authenticated mutating /app/ requests
security hardening
- archive handling is restricted to common archive suffixes and secure extraction checks (no traversal, no symlinks/devices, file count cap, extracted size cap)
@@ -42,7 +39,7 @@ security hardening
- app shell responses include nonce-based CSP and additional security headers (frame/referrer/permissions/content-type protections)
- sqlite hardening includes foreign key enforcement, extension loading disabled, trusted_schema off, and WAL-based runtime tuning
- cookie handling uses httponly + samesite for session and active-site cookies, with secure flag applied automatically on https/proxied-https
- - SHIM_ENFORCE_APP_REQUEST_GUARDS behavior
+ - ENFORCE_APP_REQUEST_GUARDS behavior
- this is the only security toggle
- when true: authenticated mutating requests under /app/ must pass same-origin verification and csrf token verification
- when false: those same-origin/csrf request guards are skipped (useful behind some proxy/kubernetes setups)
diff --git a/server.py b/server.py
index e735a36..a3f59a1 100644
--- a/server.py
+++ b/server.py
@@ -7,4 +7,4 @@ app = create_app()
if __name__ == "__main__":
- app.run(host=app.config["SHIM_BIND"], port=app.config["SHIM_PORT"]) \ No newline at end of file
+ app.run(host=app.config["BIND"], port=app.config["PORT"]) \ No newline at end of file
diff --git a/shim_app.py b/shim_app.py
index 0e8297b..7723233 100644
--- a/shim_app.py
+++ b/shim_app.py
@@ -35,12 +35,23 @@ from flask import (
from auth_backend import AuthBackend, LocalMojicryptAuthBackend
-SESSION_TTL_SECONDS = int(os.getenv("SHIM_SESSION_TTL_SECONDS", "86400"))
-MAX_UPLOAD_BYTES = int(os.getenv("SHIM_MAX_UPLOAD_BYTES", str(1024 * 1024 * 1024)))
-MAX_EXTRACTED_BYTES = int(
- os.getenv("SHIM_MAX_EXTRACTED_BYTES", str(2 * 1024 * 1024 * 1024))
-)
-MAX_EXTRACTED_FILES = int(os.getenv("SHIM_MAX_EXTRACTED_FILES", "20000"))
+# config
+APP_NAME = "shim"
+BIND_HOST = "0.0.0.0"
+PORT = 8585
+
+SESSION_TTL_SECONDS = 86400
+MAX_UPLOAD_BYTES = 1024 * 1024 * 1024
+MAX_EXTRACTED_BYTES = 2 * 1024 * 1024 * 1024
+MAX_EXTRACTED_FILES = 20000
+MAX_FORM_MEMORY_SIZE = 2 * 1024 * 1024
+
+SQLITE_TIMEOUT_SECONDS = 30.0
+SQLITE_BUSY_TIMEOUT_MS = 30000
+SQLITE_CACHE_SIZE_KIB = 32768
+SQLITE_MMAP_SIZE_BYTES = 256 * 1024 * 1024
+SQLITE_WAL_AUTOCHECKPOINT_PAGES = 1000
+
SESSION_COOKIE = "shim_session"
ACTIVE_SITE_COOKIE = "shim_active_site"
MUTATING_METHODS = {"POST", "PUT", "PATCH", "DELETE"}
@@ -62,6 +73,7 @@ ROOT_ATTR_RE = re.compile(r"(?i)\b(href|src|action|poster)=([\"'])/([^\"']*)\2")
CSS_URL_RE = re.compile(r"(?i)url\(\s*([\"']?)/([^\)'\"\s]+)\1\s*\)")
+# template configs
SHELL_TEMPLATE = """<!doctype html>
<html lang="en">
<head>
@@ -389,7 +401,7 @@ DASHBOARD_BODY_TEMPLATE = """
{% endif %}
"""
-
+# code and server logic
@dataclass(frozen=True)
class AppConfig:
base_dir: Path
@@ -681,24 +693,6 @@ def find_site_root(extracted_dir: Path) -> Path:
return candidates[0].parent
-def env_int(name: str, default: int, minimum: int) -> int:
- raw = os.getenv(name, str(default)).strip()
- try:
- value = int(raw)
- except ValueError:
- value = default
- return max(value, minimum)
-
-
-def env_float(name: str, default: float, minimum: float) -> float:
- raw = os.getenv(name, str(default)).strip()
- try:
- value = float(raw)
- except ValueError:
- value = default
- return max(value, minimum)
-
-
def env_bool(name: str, default: bool) -> bool:
raw = os.getenv(name, "true" if default else "false").strip().lower()
if raw in {"1", "true", "yes", "on"}:
@@ -710,14 +704,10 @@ def env_bool(name: str, default: bool) -> bool:
def create_app(base_dir: Optional[Path] = None) -> Flask:
project_dir = Path(base_dir or Path(__file__).parent).resolve()
- app_name = os.getenv("SHIM_APP_NAME", "shim").strip() or "shim"
+ app_name = APP_NAME
db_path = project_dir / "data" / "shim.db"
sites_dir = project_dir / "data" / "sites"
- default_mojicrypt = project_dir / "vendor" / "mojicrypt"
- mojicrypt_env = os.getenv("SHIM_MOJICRYPT_BIN", str(default_mojicrypt))
- mojicrypt_bin = Path(mojicrypt_env).expanduser()
- if not mojicrypt_bin.is_absolute():
- mojicrypt_bin = (project_dir / mojicrypt_bin).resolve()
+ mojicrypt_bin = (project_dir / "vendor" / "mojicrypt").resolve()
cfg = AppConfig(
base_dir=project_dir,
@@ -725,8 +715,8 @@ def create_app(base_dir: Optional[Path] = None) -> Flask:
db_path=db_path,
sites_dir=sites_dir,
mojicrypt_bin=mojicrypt_bin,
- bind=os.getenv("SHIM_BIND", "0.0.0.0"),
- port=int(os.getenv("SHIM_PORT", "8585")),
+ bind=BIND_HOST,
+ port=PORT,
)
cfg.db_path.parent.mkdir(parents=True, exist_ok=True)
@@ -734,29 +724,19 @@ def create_app(base_dir: Optional[Path] = None) -> Flask:
app = Flask(__name__, static_folder=None)
app.config["MAX_CONTENT_LENGTH"] = MAX_UPLOAD_BYTES
- app.config["MAX_FORM_MEMORY_SIZE"] = int(
- os.getenv("SHIM_MAX_FORM_MEMORY_SIZE", str(2 * 1024 * 1024))
- )
- app.config["SECRET_KEY"] = os.getenv("SHIM_SECRET_KEY", secrets.token_hex(32))
- app.config["SHIM_PORT"] = cfg.port
- app.config["SHIM_BIND"] = cfg.bind
- app.config["SHIM_APP_NAME"] = cfg.app_name
- app.config["SHIM_MOJICRYPT_BIN"] = str(cfg.mojicrypt_bin)
-
- sqlite_timeout_seconds = env_float("SHIM_SQLITE_TIMEOUT_SECONDS", 30.0, 1.0)
- sqlite_busy_timeout_ms = env_int("SHIM_SQLITE_BUSY_TIMEOUT_MS", 30000, 1000)
- sqlite_cache_size_kib = env_int("SHIM_SQLITE_CACHE_SIZE_KIB", 32768, 4096)
- sqlite_mmap_size_bytes = env_int(
- "SHIM_SQLITE_MMAP_SIZE_BYTES", 256 * 1024 * 1024, 0
- )
- sqlite_wal_autocheckpoint_pages = env_int(
- "SHIM_SQLITE_WAL_AUTOCHECKPOINT_PAGES", 1000, 100
- )
- enforce_app_request_guards = env_bool("SHIM_ENFORCE_APP_REQUEST_GUARDS", False)
-
- cookie_secure_mode = os.getenv("SHIM_COOKIE_SECURE", "auto").strip().lower()
- if cookie_secure_mode not in {"auto", "true", "false"}:
- cookie_secure_mode = "auto"
+ app.config["MAX_FORM_MEMORY_SIZE"] = MAX_FORM_MEMORY_SIZE
+ app.config["SECRET_KEY"] = os.getenv("SECRET_KEY", secrets.token_hex(32))
+ app.config["PORT"] = cfg.port
+ app.config["BIND"] = cfg.bind
+ app.config["APP_NAME"] = cfg.app_name
+ app.config["MOJICRYPT_BIN"] = str(cfg.mojicrypt_bin)
+
+ sqlite_timeout_seconds = SQLITE_TIMEOUT_SECONDS
+ sqlite_busy_timeout_ms = SQLITE_BUSY_TIMEOUT_MS
+ sqlite_cache_size_kib = SQLITE_CACHE_SIZE_KIB
+ sqlite_mmap_size_bytes = SQLITE_MMAP_SIZE_BYTES
+ sqlite_wal_autocheckpoint_pages = SQLITE_WAL_AUTOCHECKPOINT_PAGES
+ enforce_app_request_guards = env_bool("ENFORCE_APP_REQUEST_GUARDS", False)
def connect_db() -> sqlite3.Connection:
conn = sqlite3.connect(str(cfg.db_path), timeout=sqlite_timeout_seconds)
@@ -848,10 +828,6 @@ def create_app(base_dir: Optional[Path] = None) -> Flask:
)
def cookie_secure_enabled() -> bool:
- if cookie_secure_mode == "true":
- return True
- if cookie_secure_mode == "false":
- return False
if request.is_secure:
return True
xfp = request.headers.get("X-Forwarded-Proto", "")