From 778d78cebe712f40e86790adfcdafc1221396434 Mon Sep 17 00:00:00 2001 From: shunfeng8421 Date: Mon, 29 Jun 2026 07:33:33 +0800 Subject: [PATCH] security: replace eval() with ast.literal_eval + document pickle risk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - config.py:418 — Replace eval(value) with ast.literal_eval() for environment variable config overrides. ast.literal_eval only parses Python literals and cannot execute arbitrary code, preventing environment-variable-based code injection. - config.py:310-328 — Add security notes on pickle.load/dump usage. Pickle is safe here (local appdata file, same-process write/read), but notes suggest JSON migration or HMAC signing for future hardening. Fixes: potential RCE via controlled environment variables --- config.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/config.py b/config.py index 03bf9abe..d7bf650b 100644 --- a/config.py +++ b/config.py @@ -307,6 +307,12 @@ class Config(dict): self.user_datas[user] = {} return self.user_datas[user] + # SECURITY NOTE: pickle.load() can execute arbitrary code during + # deserialization. This is safe as long as user_datas.pkl is trusted + # (local app data directory, written only by this process). For a future + # hardening pass, consider migrating to JSON (json.load/json.dump) if the + # data structures are JSON-serializable, or adding an HMAC signature to + # detect tampering of the pickle file. def load_user_datas(self): try: with open(os.path.join(get_appdata_dir(), "user_datas.pkl"), "rb") as f: @@ -320,6 +326,8 @@ class Config(dict): def save_user_datas(self): try: + # SECURITY: pickle.dump output should only be loaded by this same + # process. See note on load_user_datas() above. with open(os.path.join(get_appdata_dir(), "user_datas.pkl"), "wb") as f: pickle.dump(self.user_datas, f) logger.info("[Config] User datas saved.") @@ -415,11 +423,16 @@ def load_config(): if name in available_setting: logger.info("[INIT] override config by environ args: {}={}".format(name, value)) try: - config[name] = eval(value) - except Exception: - if value == "false": + # SECURITY: Use ast.literal_eval instead of eval(). + # ast.literal_eval only parses Python literals (strings, numbers, + # tuples, lists, dicts, booleans, None) and CANNOT execute + # arbitrary code, preventing environment-variable injection. + import ast + config[name] = ast.literal_eval(value) + except (ValueError, SyntaxError): + if value.lower() == "false": config[name] = False - elif value == "true": + elif value.lower() == "true": config[name] = True else: config[name] = value