SkillTotal

Is hiyouga/LlamaFactory safe?

No malicious indicators - review capabilities before installing
Notable — review in context (capabilities are not malware):
  • Python dynamic code execution
  • Python shell/command execution
  • Python filesystem write/delete

What to do: Nothing here argues against installing it. Grant the capabilities it lists only if you expect the tool to need them.

hiyouga/LlamaFactory is a PyPI package analyzed by SkillTotal's deterministic static scanner. The scan found no malicious indicators, though 1 risky construct is reported for review. It can: dynamic code execution, filesystem read, filesystem write, network egress and shell execution — capabilities are what the code can do, not a verdict on intent. Risk score 10/100 (low).

llamafactory

python_package · https://github.com/hiyouga/LlamaFactory
LOW
10
/ 100 risk score
Snapshot · scanned Oct 8, 2026 · llamafactory@ce9dc9e · engine 0.56.4 / ruleset 62

Automated static-analysis result. It can contain false positives and false negatives, and is not a claim about the intent of hiyouga/LlamaFactory's authors. Report a false positive.

Capabilities — what this component can do (not a risk score):
dynamic code executionfilesystem readfilesystem writenetwork egressshell execution

Behavioral traits

How this component maps to the CSA agentic threat model. Descriptive — it never affects the risk score.

Execution authority
Tool Access Control / Direct Tool Access
Filesystem reach
Tool Execution Context
Network egress
Interaction & Communication / Direct Communication
Network exposure
Interaction & Communication

Findings (6)

HIGHPython dynamic code executionST-DYN-PY

The code turns strings into live code at runtime (eval / new Function / exec).

Why it matters: If those strings aren't fixed and trusted, they become a way to run arbitrary code.

Fix: Avoid evaluating dynamically constructed code; if unavoidable, ensure the input is a trusted constant and never derived from external data.

HIGHPython shell/command executionST-SHELL-PY

The component can run operating-system commands or spawn processes.

commit_info = subprocess.run(["git", "rev-parse", "HEAD"], capture_output=True, text=True, check=True)
process = subprocess.run(
                (
                    "torchrun --nnodes {rdzv_nnodes} --nproc-per-node {nproc_per_node} "
                    "--rdzv-id {rdzv_id} --rdzv-backend c10d --rdzv-endpoint {master_addr}:{master_port} " …
process = subprocess.run(
                (
                    "torchrun --nnodes {nnodes} --node_rank {node_rank} --nproc_per_node {nproc_per_node} "
                    "--master_addr {master_addr} --master_port {master_port} {file_name} …
process = subprocess.run(
            torchrun_args + script_args,
            env=env,
            check=True,
        )
self.trainer = Popen(
                ["llamafactory-cli", "train", save_cmd(args)],
                env=env,
                stdout=webui_log,
                stderr=webui_log,
                text=True,
            )

Why it matters: Powerful and often legitimate — confirm the commands aren't built from untrusted input.

Fix: Confirm the command and its arguments are fully controlled and not derived from untrusted input; avoid shell=True.

MEDIUMServer bound to all network interfacesST-EXPOSE-BIND

A server is bound to all network interfaces (0.0.0.0), not just your own machine.

api_host = os.getenv("API_HOST", "0.0.0.0")
api_host = os.getenv("API_HOST", "0.0.0.0")
server_name = os.getenv("GRADIO_SERVER_NAME", "[::]" if gradio_ipv6 else "0.0.0.0")
server_name = os.getenv("GRADIO_SERVER_NAME", "[::]" if gradio_ipv6 else "0.0.0.0")
server_name = os.getenv("GRADIO_SERVER_NAME", "[::]" if gradio_ipv6 else "0.0.0.0")

Why it matters: Without authentication, other hosts on the network can reach it.

Fix: Bind to 127.0.0.1 for local-only use, or require authentication and restrict access if remote exposure is intended.

MEDIUMPython filesystem readST-FS-PY-READ

The component reads files from disk.

with open(os.path.join(input_dir, CONFIG_NAME), encoding="utf-8") as f:
with open(os.path.join(input_dir, CONFIG_NAME), encoding="utf-8") as f:
with open(os.path.join("saves", "test_mfu", "all_results.json"), encoding="utf-8") as f:
image_stream = open(image_url, "rb")
with open(mapping, encoding="utf-8") as f:
with open(os.path.join(save_dictionary, TRAINER_STATE_NAME), encoding="utf-8") as f:
with open(self.extra_config, encoding="utf-8") as f:
mca_config = json.load(open(os.path.join(model_args.model_name_or_path, "mca_config.json")))
with open(cfg_path, encoding="utf-8") as f:
with open(os.path.join(ckpt_dir, "metadata.json")) as f:
with open(_get_config_path(), encoding="utf-8") as f:
with open(os.path.join(dataset_dir, DATA_CONFIG), encoding="utf-8") as f:
with open(config_path, encoding="utf-8") as f:
with open(path, encoding="utf-8") as f:
with open(os.path.join(dataset_dir, DATA_CONFIG), encoding="utf-8") as f:
with open(file_path, encoding="utf-8") as f:
with open(os.path.join(dataset_dir, DATA_CONFIG), encoding="utf-8") as f:
with open(running_log_path, encoding="utf-8") as f:
with open(trainer_log_path, encoding="utf-8") as f:
with open(swanlab_config_path, encoding="utf-8") as f:
with open(webui_log_path, "rb") as f:

Why it matters: Usually legitimate, but worth confirming it can't be steered into reading sensitive files.

Fix: Confirm which files are read and that paths cannot be influenced by untrusted input to reach sensitive locations.

MEDIUMPython filesystem write/deleteST-FS-PY-WRITE

The component writes or deletes files on disk.

with open(os.path.join(output_dir, index_name), "w", encoding="utf-8") as f:
with open(os.path.join(output_dir, CONFIG_NAME), "w", encoding="utf-8") as f:
with open(os.path.join(output_dir, index_name), "w", encoding="utf-8") as f:
with open(os.path.join(output_dir, CONFIG_NAME), "w", encoding="utf-8") as f:
with open("predictions_score.json", "w", encoding="utf-8") as f:
with open(os.path.join(output_dir, index_name), "w", encoding="utf-8") as f:
shutil.copy(source_file, os.path.join(save_path, extra_file))
shutil.copy(source_file, os.path.join(save_path, extra_file))
with open(save_name, "w", encoding="utf-8") as f:
with open(save_name, "w", encoding="utf-8") as f:
with open(matrix_save_name, "w", encoding="utf-8") as f:
with open(os.path.join(self.eval_args.save_dir, "results.json"), "w", encoding="utf-8", newline="\n") as f:
with open(os.path.join(self.eval_args.save_dir, "results.log"), "w", encoding="utf-8", newline="\n") as f:
with open(self.running_log, "a", encoding="utf-8") as f:
with open(os.path.join(output_dir, TRAINER_LOG), "a", encoding="utf-8") as f:
os.remove(os.path.join(args.output_dir, TRAINER_LOG))
with open(done_file, "w", encoding="utf-8") as f:
with open(output_prediction_file, "w", encoding="utf-8") as writer:
with open(output_prediction_file, "w", encoding="utf-8") as f:
with open(os.path.join(args.output_dir, SWANLAB_CONFIG), "w") as f:

Why it matters: Usually legitimate, but worth confirming the paths can't be controlled by untrusted input.

Fix: Confirm which files are written/deleted and that paths cannot be influenced by untrusted input.

MEDIUMPython network egressST-NET-PY

The component makes outbound network requests.

image_stream = requests.get(image_url, stream=True).raw
video_stream = requests.get(video_url, stream=True).raw
audio_stream = requests.get(audio_url, stream=True).raw
response = requests.get(f"{self.base_url}/get_model_info", timeout=5)
response = requests.post(f"{self.base_url}/generate", json=json_data, stream=True)
response = requests.post(server_url, json=payload, headers=headers)

Why it matters: Usually legitimate, but confirm the destinations are expected and no sensitive data leaves.

Fix: Confirm the destination hosts are expected and that no sensitive data is sent off-host.

How attackers abuse these capabilities

Interactive labs on the attack class behind the rules above. They show the technique, not anything found in hiyouga/LlamaFactory.

Check your own component

Run the same evidence-backed scan on any MCP server, agent skill, or package.

Scan your own component

How we determine this: deterministic static analysis (regex + AST), evidence-anchored, no code execution. Methodology →