aegis_gui.utilities.utilities
1import dash 2import re 3import yaml 4 5from aegis_sim.utilities.container import Container 6from aegis_gui.guisettings.GuiSettings import gui_settings 7 8 9# Strict whitelist for sim names. Filesystem-safe characters only — no slashes, 10# no leading dots (would hide files), no whitespace, capped length. 11_SIM_NAME_PATTERN = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,199}$") 12 13 14class UnsafeSimNameError(ValueError): 15 """Raised when a sim name fails the strict whitelist or escapes sim_dir.""" 16 17 18def safe_sim_name(name) -> str: 19 """Validate `name` as a safe simulation identifier. 20 21 Accepts letters, digits, underscore, dash, and dot (not leading), max 200 22 chars. Rejects anything else — including slashes, traversal sequences, 23 whitespace, null bytes. Returns the validated string. 24 """ 25 if not isinstance(name, str): 26 raise UnsafeSimNameError(f"sim name must be str, got {type(name).__name__}") 27 if not _SIM_NAME_PATTERN.fullmatch(name): 28 raise UnsafeSimNameError(f"rejected sim name: {name!r}") 29 return name 30 31 32def safe_sim_path(name) -> "pathlib.Path": 33 """Return the validated directory path for a sim, guaranteed to be inside 34 `gui_settings.sim_dir`. Raises UnsafeSimNameError if the resolved path 35 escapes the sim directory (defense in depth even after the regex check). 36 """ 37 import pathlib 38 39 name = safe_sim_name(name) 40 base = pathlib.Path(gui_settings.sim_dir).resolve() 41 candidate = (base / name).resolve() 42 try: 43 candidate.relative_to(base) 44 except ValueError: 45 raise UnsafeSimNameError(f"sim path escapes sim_dir: {name!r}") 46 return candidate 47 48 49def read_yml(path): 50 with open(path, "r") as f: 51 return yaml.safe_load(f) 52 53 54def get_container(filename): 55 safe_sim_name(filename) 56 return Container(gui_settings.sim_dir / filename) 57 58 59def get_config_path(filename): 60 safe_sim_name(filename) 61 return gui_settings.sim_dir / f"{filename}.yml" 62 63 64def get_sim_paths(sim_dir=None, sort=True): 65 if sim_dir is None: 66 sim_dir = gui_settings.sim_dir 67 paths = [p for p in sim_dir.iterdir() if p.is_dir()] 68 if sort: 69 paths = sorted(paths, key=lambda path: path.name) 70 return paths 71 72 73def get_sims(): 74 return [p.stem for p in get_sim_paths()] 75 76 77def sim_exists(filename: str) -> bool: 78 paths = get_sim_paths() 79 return any(path.stem == filename for path in paths) 80 81 82def get_icon(icon_name): 83 return dash.html.Img( 84 src=f"/aegis/assets/icons/{icon_name}.svg", 85 width="16", 86 height="16", 87 )
class
UnsafeSimNameError(builtins.ValueError):
15class UnsafeSimNameError(ValueError): 16 """Raised when a sim name fails the strict whitelist or escapes sim_dir."""
Raised when a sim name fails the strict whitelist or escapes sim_dir.
def
safe_sim_name(name) -> str:
19def safe_sim_name(name) -> str: 20 """Validate `name` as a safe simulation identifier. 21 22 Accepts letters, digits, underscore, dash, and dot (not leading), max 200 23 chars. Rejects anything else — including slashes, traversal sequences, 24 whitespace, null bytes. Returns the validated string. 25 """ 26 if not isinstance(name, str): 27 raise UnsafeSimNameError(f"sim name must be str, got {type(name).__name__}") 28 if not _SIM_NAME_PATTERN.fullmatch(name): 29 raise UnsafeSimNameError(f"rejected sim name: {name!r}") 30 return name
Validate name as a safe simulation identifier.
Accepts letters, digits, underscore, dash, and dot (not leading), max 200 chars. Rejects anything else — including slashes, traversal sequences, whitespace, null bytes. Returns the validated string.
def
safe_sim_path(name) -> pathlib.Path:
33def safe_sim_path(name) -> "pathlib.Path": 34 """Return the validated directory path for a sim, guaranteed to be inside 35 `gui_settings.sim_dir`. Raises UnsafeSimNameError if the resolved path 36 escapes the sim directory (defense in depth even after the regex check). 37 """ 38 import pathlib 39 40 name = safe_sim_name(name) 41 base = pathlib.Path(gui_settings.sim_dir).resolve() 42 candidate = (base / name).resolve() 43 try: 44 candidate.relative_to(base) 45 except ValueError: 46 raise UnsafeSimNameError(f"sim path escapes sim_dir: {name!r}") 47 return candidate
Return the validated directory path for a sim, guaranteed to be inside
gui_settings.sim_dir. Raises UnsafeSimNameError if the resolved path
escapes the sim directory (defense in depth even after the regex check).
def
read_yml(path):
def
get_container(filename):
def
get_config_path(filename):
def
get_sim_paths(sim_dir=None, sort=True):
def
get_sims():
def
sim_exists(filename: str) -> bool:
def
get_icon(icon_name):