MCPcopy Index your code
hub / github.com/AutoForgeAI/autoforge / validate_custom_command_strict

Function validate_custom_command_strict

server/routers/devserver.py:91–184  ·  view source on GitHub ↗

Strict allowlist validation for dev server commands. Prevents arbitrary command execution (no sh -c, no cmd /c, no python -c, etc.)

(cmd: str)

Source from the content-addressed store, hash-verified

89
90
91def validate_custom_command_strict(cmd: str) -> None:
92 """
93 Strict allowlist validation for dev server commands.
94 Prevents arbitrary command execution (no sh -c, no cmd /c, no python -c, etc.)
95 """
96 if not isinstance(cmd, str) or not cmd.strip():
97 raise ValueError("custom_command cannot be empty")
98
99 argv = shlex.split(cmd, posix=(sys.platform != "win32"))
100 if not argv:
101 raise ValueError("custom_command could not be parsed")
102
103 base = Path(argv[0]).name.lower()
104
105 # Block direct shells / interpreters commonly used for command injection
106 if base in BLOCKED_SHELLS:
107 raise ValueError(f"custom_command runner not allowed: {base}")
108
109 if base not in ALLOWED_RUNNERS:
110 raise ValueError(
111 f"custom_command runner not allowed: {base}. "
112 f"Allowed: {', '.join(sorted(ALLOWED_RUNNERS))}"
113 )
114
115 # Block one-liner execution for python
116 lowered = [a.lower() for a in argv]
117 if base in {"python", "python3"}:
118 if "-c" in lowered:
119 raise ValueError("python -c is not allowed")
120 if len(argv) >= 3 and argv[1] == "-m":
121 # Allow: python -m <allowed_module> ...
122 if argv[2] not in ALLOWED_PYTHON_MODULES:
123 raise ValueError(
124 f"python -m {argv[2]} is not allowed. "
125 f"Allowed modules: {', '.join(sorted(ALLOWED_PYTHON_MODULES))}"
126 )
127 elif len(argv) >= 2 and argv[1].endswith(".py"):
128 # Allow: python manage.py runserver, python app.py, etc.
129 pass
130 else:
131 raise ValueError(
132 "Python commands must use 'python -m <module> ...' or 'python <script>.py ...'"
133 )
134
135 if base == "flask":
136 # Allow: flask run [--host ...] [--port ...]
137 if len(argv) < 2 or argv[1] != "run":
138 raise ValueError("flask custom_command must be 'flask run [options]'")
139
140 if base == "poetry":
141 # Allow: poetry run <subcmd> ...
142 if len(argv) < 3 or argv[1] != "run":
143 raise ValueError("poetry custom_command must be 'poetry run <command> ...'")
144
145 if base == "uvicorn":
146 if len(argv) < 2 or ":" not in argv[1]:
147 raise ValueError("uvicorn must specify an app like module:app")
148

Callers 15

test_npm_run_devMethod · 0.90
test_npm_run_startMethod · 0.90
test_npm_run_serveMethod · 0.90
test_npm_run_previewMethod · 0.90
test_pnpm_devMethod · 0.90
test_pnpm_run_devMethod · 0.90
test_yarn_startMethod · 0.90
test_yarn_run_serveMethod · 0.90
test_uvicorn_basicMethod · 0.90
test_python_m_uvicornMethod · 0.90

Calls

no outgoing calls

Tested by 15

test_npm_run_devMethod · 0.72
test_npm_run_startMethod · 0.72
test_npm_run_serveMethod · 0.72
test_npm_run_previewMethod · 0.72
test_pnpm_devMethod · 0.72
test_pnpm_run_devMethod · 0.72
test_yarn_startMethod · 0.72
test_yarn_run_serveMethod · 0.72
test_uvicorn_basicMethod · 0.72
test_python_m_uvicornMethod · 0.72