mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-08-02 17:28:05 +00:00
Merge branch 'claude/sports-unification-safety-net' into claude/sports-unification-phase1
This commit is contained in:
@@ -106,6 +106,13 @@ def resolve_font_path(font_name: str) -> Optional[str]:
|
|||||||
return None
|
return None
|
||||||
if os.path.isabs(font_name):
|
if os.path.isabs(font_name):
|
||||||
return font_name if os.path.isfile(font_name) else None
|
return font_name if os.path.isfile(font_name) else None
|
||||||
|
# A relative name must be a bare filename. font_name comes from plugin
|
||||||
|
# config, which the web UI writes; a value like "../../config/config.json"
|
||||||
|
# would otherwise escape assets/fonts/ once joined and let a config probe
|
||||||
|
# arbitrary paths for existence. os.path.basename collapses any such value
|
||||||
|
# to its last component, so a name that isn't already bare is rejected.
|
||||||
|
if os.path.basename(font_name) != font_name:
|
||||||
|
return None
|
||||||
candidates = (
|
candidates = (
|
||||||
os.path.join(os.getcwd(), _FONTS_SUBDIR, font_name),
|
os.path.join(os.getcwd(), _FONTS_SUBDIR, font_name),
|
||||||
os.path.join(_CORE_ROOT, _FONTS_SUBDIR, font_name),
|
os.path.join(_CORE_ROOT, _FONTS_SUBDIR, font_name),
|
||||||
|
|||||||
@@ -377,6 +377,22 @@ class TestResolverPlumbing:
|
|||||||
font = load_font("5x7.bdf", 7)
|
font = load_font("5x7.bdf", 7)
|
||||||
assert isinstance(font, freetype.Face)
|
assert isinstance(font, freetype.Face)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("hostile", [
|
||||||
|
"../../config/config.json",
|
||||||
|
"../secrets.txt",
|
||||||
|
"sub/dir/font.ttf",
|
||||||
|
"..",
|
||||||
|
])
|
||||||
|
def test_relative_font_name_with_path_components_is_rejected(self, hostile):
|
||||||
|
# font_name comes from plugin config (web-UI writable); a relative name
|
||||||
|
# carrying path separators would escape assets/fonts/ after os.path.join
|
||||||
|
# and let a config probe arbitrary paths. Only bare filenames resolve.
|
||||||
|
assert resolve_font_path(hostile) is None
|
||||||
|
|
||||||
|
def test_bare_filename_still_resolves(self):
|
||||||
|
# The guard must not reject legitimate bare names.
|
||||||
|
assert resolve_font_path("PressStart2P-Regular.ttf") is not None
|
||||||
|
|
||||||
def test_schema_manager_expands_on_load(self, tmp_path):
|
def test_schema_manager_expands_on_load(self, tmp_path):
|
||||||
# The web-UI form path: SchemaManager.load_schema serves the
|
# The web-UI form path: SchemaManager.load_schema serves the
|
||||||
# expanded schema so the style blocks actually appear in the UI.
|
# expanded schema so the style blocks actually appear in the UI.
|
||||||
|
|||||||
@@ -434,6 +434,17 @@ def build_manager(monkeypatch, tmp_path):
|
|||||||
"src.base_classes.sports.core.get_background_service",
|
"src.base_classes.sports.core.get_background_service",
|
||||||
lambda *args, **kwargs: MagicMock())
|
lambda *args, **kwargs: MagicMock())
|
||||||
|
|
||||||
|
# Rig requests.Session BEFORE any manager is built. Construction creates
|
||||||
|
# both SportsCore.session and the ESPNDataSource.session; replacing only
|
||||||
|
# manager.session after the fact (below) leaves data_source.session real,
|
||||||
|
# so an accidental fetch during or right after construction could reach
|
||||||
|
# the network. Patching the class makes every session created here raise.
|
||||||
|
def _offline_get(*args, **kwargs):
|
||||||
|
raise requests.exceptions.ConnectionError(
|
||||||
|
"characterization tests are offline")
|
||||||
|
|
||||||
|
monkeypatch.setattr(requests.Session, "get", _offline_get)
|
||||||
|
|
||||||
def build(cls, schedule, **mode_cfg):
|
def build(cls, schedule, **mode_cfg):
|
||||||
config = {
|
config = {
|
||||||
"timezone": "UTC",
|
"timezone": "UTC",
|
||||||
|
|||||||
Reference in New Issue
Block a user