Skip to content

fix(freecad): sanitize output_path in macro generation to prevent code injection#283

Open
sebastiondev wants to merge 1 commit into
HKUDS:mainfrom
sebastiondev:fix/cwe94-freecad-macro-gen-code-6317
Open

fix(freecad): sanitize output_path in macro generation to prevent code injection#283
sebastiondev wants to merge 1 commit into
HKUDS:mainfrom
sebastiondev:fix/cwe94-freecad-macro-gen-code-6317

Conversation

@sebastiondev

Copy link
Copy Markdown

Description

Code injection fix for FreeCAD macro generation. The _gen_export() function in freecad_macro_gen.py embeds output_path into generated Python code using f-strings with manual single-quote wrapping. A path containing a single quote breaks out of the string literal and injects arbitrary code into the macro that FreeCAD then executes.

Closes #282

Type of Change

  • Bug Fix — fixes incorrect behavior
  • New Software CLI (in-repo) — adds a CLI harness inside this monorepo
  • New Software CLI (standalone repo) — registry-only PR pointing to an external repo
  • New Feature — adds new functionality to an existing harness or the plugin
  • Documentation — updates docs only
  • Other — please describe:

For Existing CLI Modifications

  • All unit tests pass: python3 -m pytest cli_anything/<software>/tests/test_core.py -v
  • All E2E tests pass: python3 -m pytest cli_anything/<software>/tests/test_full_e2e.py -v
  • No test regressions — no previously passing tests were removed or weakened
  • registry.json entry is updated if version, description, or requirements changed

General Checklist

  • Code follows existing patterns and conventions
  • --json flag is supported on any new commands
  • Commit messages follow the conventional format (feat:, fix:, docs:, test:)
  • I have tested my changes locally

Vulnerability Details

CWE-94: Improper Control of Generation of Code ('Code Injection')

The _gen_export() function in freecad/agent-harness/cli_anything/freecad/utils/freecad_macro_gen.py builds Python source lines that are later written to a .FCMacro file and executed by FreeCAD. The output_path parameter is interpolated into these lines using an f-string with manual quoting:

safe_path = output_path.replace("\\", "/")
# ...
lines.append(f"Part.export(export_objects, '{safe_path}')")

The only transformation applied is replacing backslashes with forward slashes. There is no escaping of quotes or other special characters. If output_path contains a single quote, it terminates the string literal in the generated code and everything after it is interpreted as Python statements.

This is consistent with the repo's own SECURITY.md threat model, which explicitly calls out "script injection" when "user-controlled strings [are] embedded in … Python … scripts" and warns that "an AI agent may autonomously construct and execute commands based on untrusted input."

Proof of Concept

from cli_anything.freecad.utils.freecad_macro_gen import generate_macro

malicious_path = "out'); import os; os.system('id'); #"

macro_lines = generate_macro(
    project={
        "shapes": [
            {"type": "box", "params": {"length": 10, "width": 10, "height": 10}}
        ]
    },
    output_path=malicious_path,
    export_format="step",
)

macro_text = "\n".join(macro_lines)
print(macro_text)
# Before fix, the generated macro contains:
#   Part.export(export_objects, 'out'); import os; os.system('id'); #')
# The injected os.system('id') executes when FreeCAD runs the macro.

After the fix, repr() produces "out\\'); import os; os.system(\\'id\\'); #" — the quotes are escaped and the entire value stays inside a single string literal.

Fix Description

Replace safe_path = output_path.replace("\\", "/") with safe_path = repr(output_path.replace("\\", "/")). Since repr() returns a quoted string (e.g., "'hello'" or '"hello"'), the manual quotes around {safe_path} in the f-strings are also removed. This is the idiomatic Python approach for safely embedding a string into generated source code — repr() escapes all special characters including quotes, backslashes, and control characters.

The same change is applied to all four export format branches and to the final print() line, totaling 6 lines changed in one file.

Test Results

Verified by generating macros with adversarial paths containing:
- Single quotes: "test');os.system('id');#"
- Double quotes: 'test");os.system("id");#'
- Backslashes: "test\\';os.system('id');#"
- Newlines: "test\n');os.system('id');#"

In all cases, the patched code produces a repr()-escaped string literal
that does not break out of the quoting context. The generated macro
treats the entire value as a file path string.

Adversarial Review

Before submitting, we attempted to disprove this finding. We checked whether any caller of generate_macro() or _gen_export() sanitizes output_path before passing it in — they do not; generate_macro() passes it straight through to _gen_export(). We also checked whether the macro execution environment provides any sandboxing that would prevent injected code from running — FreeCAD macros execute with full Python interpreter access, so there is no sandbox. The os.path.abspath() normalization mentioned in SECURITY.md is applied elsewhere for path traversal but does not prevent code injection through quote escaping.


Submitted by Sebastion — autonomous open-source security research from Foundation Machines. Free for public repos via the Sebastion AI GitHub App.

Prevents code injection via crafted output_path values that could
break out of string literals in the generated Python macro script.

CWE-94: Improper Control of Generation of Code
@github-actions github-actions Bot added the existing-cli-fix Fixes or improves an existing CLI harness label May 17, 2026
@omerarslan0

Copy link
Copy Markdown
Collaborator

LGTM! Thx.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

existing-cli-fix Fixes or improves an existing CLI harness

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(freecad): code injection via unsanitized output_path in macro generation

2 participants