Security: hide the running version from anonymous visitors when a password is set (#2190) (#4306)

This commit is contained in:
Northern Monkey
2026-08-20 11:09:13 +02:00
committed by GitHub
parent 89a41c0dda
commit 4bfce3c4b0
3 changed files with 21 additions and 3 deletions
+6 -2
View File
@@ -628,9 +628,13 @@ def main():
@app.context_processor
def inject_template_globals():
from changedetectionio.llm.evaluator import get_llm_config as _get_llm_config
return dict(right_sticky="v"+__version__,
from flask_login import current_user
has_password = datastore.data['settings']['application']['password'] != False
# Don't reveal the running version to anonymous visitors when password protection is enabled (#2190)
show_version = current_user.is_authenticated or not has_password
return dict(right_sticky="v"+__version__ if show_version else None,
new_version_available=app.config['NEW_VERSION_AVAILABLE'],
has_password=datastore.data['settings']['application']['password'] != False,
has_password=has_password,
socket_io_enabled=datastore.data['settings']['application'].get('ui', {}).get('socket_io_enabled', True),
all_paused=datastore.data['settings']['application'].get('all_paused', False),
all_muted=datastore.data['settings']['application'].get('all_muted', False),
+11 -1
View File
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
import flask_login
import hashlib
import locale
import os
import queue
@@ -194,7 +195,16 @@ def get_darkmode_state():
@app.template_global()
def get_css_version():
return __version__
"""Cache-busting token for static assets.
Changes on every upgrade (so browsers refetch CSS/JS) but is not the raw
version string - the raw version was leaking to anonymous visitors on the
login page via `?v=x.y.z`, which allows exposed instances to be fingerprinted
for known-vulnerable releases (#2190). Salted with the per-installation
app_guid so it can't be reversed to the version.
"""
salt = datastore.data.get('app_guid', '') if datastore else ''
return hashlib.sha256(f"{salt}{__version__}".encode('utf-8')).hexdigest()[:10]
@app.template_global('filtered_action_url')
def _filtered_action_url(endpoint, **overrides):
@@ -1,5 +1,7 @@
from .util import live_server_setup, wait_for_all_checks
from flask import url_for
from changedetectionio import __version__
import re
import time
def test_check_access_control(app, client, live_server, measure_memory_usage, datastore_path):
@@ -43,6 +45,8 @@ def test_check_access_control(app, client, live_server, measure_memory_usage, da
res = c.get(url_for("watchlist.index"), follow_redirects=True)
# Should be logged out
assert b"Login" in res.data
# The login page must not leak the running version via the static asset cache-busters (#2190)
assert not re.search(rb'\?v(?:er)?=' + re.escape(__version__.encode()), res.data)
# The diff page should return something valid when logged out
res = c.get(url_for("ui.ui_diff.diff_history_page", uuid="first"))