Back to skill

Security audit

mrmrmr

Security checks for vulnerabilities and agentic risk

Overview

This biomedical analysis skill appears purpose-aligned, but it handles credentials and LLM-derived data in unsafe ways that can expose tokens or execute unintended code.

Install only in an isolated research environment with non-sensitive inputs and disposable API tokens. Avoid the documented curl-to-sh installer, disable synonym expansion unless you provide and protect your own UMLS credential, do not use editable CSVs from untrusted sources, and expect disease/exposure terms and analysis results to be sent to external services.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
Findings (7)

T03 · Remote Payload Retrieval and Execution

Error
Location
mrmrmr/README.md:55
Finding
Remote Installer Downloaded and Executed Without Integrity Verification<![CDATA[ ## Vulnerability Details **File Location**: `mrmrmr/README.md:55-59` **Vulnerability Type**: Remote payload retrieval and immediate shell execution **Risk Level**: Critical ### Vulnerable Code ```shell curl -fsSL https://ollama.com/install.sh | sh pip install ollama ``` ### Technical Analysis The installation instructions pipe the response from a mutable external URL directly into `sh`. The downloaded content is neither pinned to a particular version nor verified using a cryptographic signature or checksum. Although `ollama.com` is the documented upstream domain, the effective payload can change after this project has been reviewed. Compromise of the upstream site, its deployment credentials, DNS resolution, certificate trust path, or content delivery infrastructure would allow arbitrary commands to be delivered to users following these instructions. This behavior is unnecessary for the Skill's core Mendelian-randomization functionality. Ollama is optional, and it can be installed through a separately verified package or artifact. ### Attack Path 1. A user follows the installation instructions. 2. The user runs the documented `curl | sh` command. 3. The upstream endpoint or delivery path returns malicious or compromised shell code. 4. The response is passed directly to `sh` without user inspection or integrity verification. 5. The payload executes with all privileges held by the invoking user, potentially including root privileges if the command is run with elevated permissions. ### Impact Assessment Successful exploitation provides arbitrary command execution on the installation host. The payload could read user-accessible credentials, alter applications, install additional software, modify shell initialization files, or establish persistence. If invoked under an administrator account, the compromise can become system-wide. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Remove the `curl | sh` instruction. - Direct users to a trusted operating-system package manager where available. - Otherwise, download a versioned installer as a separate step. - Publish and verify an official cryptographic signature or a pinned SHA-256 digest before execution. - Display the downloaded file for inspection and execute it only after verification. - Document that the installer should run without unnecessary administrator privileges. - Pin the corresponding Python Ollama client to an audited version with package hashes. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
mrmrmr/mragent/agent_workflow.py:1037
Finding
Remote LLM Output and Editable CSV Data Executed Through Python eval<![CDATA[ ## Vulnerability Details **File Location**: `mrmrmr/mragent/agent_workflow.py:468-481, 500-506, 1037-1043` **Vulnerability Type**: Arbitrary Python code execution through unsafe evaluation **Risk Level**: Critical ### Vulnerable Code LLM output is extracted as text and returned without structural validation: ```python gpt_out = llm_chat(t, self.LLM_model, self.AI_key, self.base_url, self.model_type) print(gpt_out) # 运用正则表达式提取结果中的gwas_id list if '[' in gpt_out and ']' in gpt_out: gpt_out = gpt_out.split('[')[1].split(']')[0] gpt_out = '[' + gpt_out + ']' else: gpt_out = ['null'] print(gpt_out) # python_list = [item.strip() for item in gpt_out.split(",")] # return python_list # 取用时再进行处理 return gpt_out ``` The value is persisted to an intermediate CSV file: ```python df_oe['gwas_id'] = df_oe.apply(lambda x: self.step5_get_gwas_id(x['OE']), axis=1) df = pd.merge(df, df_oe[['OE', 'gwas_id']], on='OE', how='left') out_path = os.path.join(self.path, 'Outcome_SNP.csv') df.to_csv(out_path, index=False, encoding='utf-8') ``` Step 9 then executes the CSV values as Python expressions: ```python Outcome_id = Outcome_id.to_numpy()[0] Exposure_id = Exposure_id.to_numpy()[0] print(Outcome_id, Exposure_id) Outcome_id_list = eval(Outcome_id) Exposure_id_list = eval(Exposure_id) print(Outcome_id_list, Exposure_id_list) ``` ### Technical Analysis `eval` evaluates arbitrary Python expressions, not merely list literals. The evaluated values originate from an external LLM response and are stored in `Outcome_SNP.csv`. The project documentation also describes intermediate CSV files as editable intervention points, creating an additional local input path. The bracket extraction does not make the content safe. An attacker can return an expression inside a list that invokes Python functions, imports modules, reads files, starts processes, or performs network requests. A compromised OpenAI-compatible endpoint, prompt-injected upstream data, malicious mod ...[truncated 955 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace `eval` with `json.loads`. - Require the model to return a strict JSON array of strings. - Verify that the decoded result is a list and that every element is a string. - Validate each GWAS identifier against a restrictive pattern, such as `^[A-Za-z0-9._-]+$`, and enforce reasonable length and item-count limits. - Reject malformed output rather than attempting permissive bracket extraction. - Treat intermediate CSV files as untrusted input and apply the same validation after reading them. - Restrict output-directory permissions if intermediate files can influence later execution. - Where supported, use the LLM provider's structured-output or JSON-schema functionality. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
mrmrmr/mragent/agent_tool.py:261
Finding
Untrusted Identifiers Paths and Credentials Interpolated Into Executable R Source<![CDATA[ ## Vulnerability Details **File Location**: `mrmrmr/mragent/agent_tool.py:261-382` **Vulnerability Type**: R code injection and unsafe generated-code execution **Risk Level**: Critical ### Vulnerable Code ```python def MRtool(Exposure_id, Outcome_id, path, gwas_token): time.sleep(5) r_script = """ Sys.setenv(OPENGWAS_JWT="{gwas_token}") library(TwoSampleMR) library(ieugwasr) num_rows <- 0 tryCatch({{ p_value <- 5e-08 exposure_dat <- extract_instruments(outcomes = '{Exposure_id}', p1=p_value, clump=TRUE, r2=0.001, kb=5000 ) num_rows <- nrow(exposure_dat) print(num_rows) }}, error = function(e) {{ message("First time gwas data error.", e$message) }}) outcomeID="{Outcome_id}" outcome_dat <- extract_outcome_data(snps=exposure_dat$SNP, outcomes=outcomeID) dat <- harmonise_data(exposure_dat, outcome_dat) outTab=dat[dat$mr_keep=="TRUE",] write.csv(outTab, file=".//{path}//table.SNP.csv", row.names=F) """ r_script_run = r_script.format( Exposure_id=Exposure_id, Outcome_id=Outcome_id, path=path, gwas_token=gwas_token ) with open('test.R', 'w', encoding='utf-8') as f: f.write(r_script_run) os.system('R --slave --no-save --no-restore --no-site-file --no-environ -f test.R --args') ``` Equivalent unsafe interpolation is also present in `MRtool_MOE` and `MRtool_MRlap`. ### Technical Analysis The application constructs executable R source by formatting external or indirectly controlled values into quoted R string literals. The affected values include: - LLM-selected or CSV-provided exposure and outcome identifiers. - Output paths derived from user input and LLM-generated exposure/outcome names. - The OpenGWAS token obtained from the envir ...[truncated 1500 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Stop generating R source through string interpolation. - Maintain a fixed, reviewed R script and pass data through a JSON input file, standard input, or positional command-line arguments. - Launch R with `subprocess.run([...], shell=False, check=True)` using an argument array. - Parse all passed values as data in R rather than as code. - Apply strict allowlist validation to GWAS identifiers before invoking R. - Resolve and canonicalize output paths, then verify with `os.path.commonpath` that they remain under a dedicated output root. - Pass credentials only through a minimal child-process environment. - Apply the same redesign to `MRtool`, `MRtool_MOE`, and `MRtool_MRlap`. - Add security tests containing quotes, newlines, backslashes, path traversal sequences, and R metacharacters. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
mrmrmr/mragent/agent_tool.py:376
Finding
OpenGWAS JWT Persisted in a Predictable Plaintext R Script<![CDATA[ ## Vulnerability Details **File Location**: `mrmrmr/mragent/agent_tool.py:276, 376-382, 392, 476-483` **Vulnerability Type**: Plaintext credential exposure and unsafe temporary file handling **Risk Level**: High ### Vulnerable Code ```python r_script = """ Sys.setenv(OPENGWAS_JWT="{gwas_token}") library(TwoSampleMR) library(ieugwasr) ... """ r_script_run = r_script.format( Exposure_id=Exposure_id, Outcome_id=Outcome_id, path=path, gwas_token=gwas_token ) with open('test.R', 'w', encoding='utf-8') as f: f.write(r_script_run) os.system('R --slave --no-save --no-restore --no-site-file --no-environ -f test.R --args') ``` The same predictable filename and token interpolation are used by both standard MR and mixture-of-experts execution paths. ### Technical Analysis The OpenGWAS JWT is read from the environment and rendered directly into a plaintext R file named `test.R` in the current working directory. The file is not deleted after execution. The predictable filename creates several risks: - Other local users or processes may read the credential if directory permissions allow it. - The file may be included in backups or accidentally committed. - Concurrent analyses overwrite and read the same file. - A pre-existing symbolic link can redirect the write to another user-writable target. - Process failure leaves the credential on disk indefinitely. Persisting the JWT is not necessary because R can inherit it from the child-process environment. ### Attack Path 1. A user runs MRAgent with `OPENGWAS_JWT` configured. 2. MRAgent writes the token into `test.R`. 3. A local process, another user with directory access, a backup system, or a later repository operation obtains the file. 4. The exposed bearer token is reused to access OpenGWAS under the victim's identity. A local attacker may also create a `test.R` symbolic link before execution, causing MRAgent to overwrite an unintended file available to the victim ac ...[truncated 326 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Never embed the JWT into generated R source. - Pass `OPENGWAS_JWT` in the environment supplied specifically to the R child process. - Construct a minimal environment dictionary rather than forwarding unrelated secrets. - Replace the predictable working-directory file with a fixed installed script. - If a temporary file remains unavoidable, use `tempfile.TemporaryDirectory` or securely created unique files with restrictive permissions. - Prevent symlink following and ensure cleanup in a `finally` block. - Avoid logging generated scripts or child-process environments. - Revoke and rotate any token that may already have been persisted in accessible `test.R` files. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
mrmrmr/mragent/agent_workflow.py:301
Finding
Hard-Coded UMLS API Credential Exposed and Transmitted in URL Query Strings<![CDATA[ ## Vulnerability Details **File Location**: `mrmrmr/mragent/agent_workflow.py:301-307`; `mrmrmr/mragent/agent_tool.py:675-689` **Vulnerability Type**: Hard-coded secret and credential leakage through URLs **Risk Level**: High ### Vulnerable Code The workflow embeds a reusable API key directly in source: ```python if self.synonyms: for index, row in df2.iterrows(): OE = row['OE'] sID = row['sID'] python_list = get_synonyms( OE, "d6382a8b-5ca8-493a-98ca-2b02fffcaeb5" ) ``` The API client places that credential into request URLs: ```python def get_synonyms(term, api_key): try: url = "https://uts-ws.nlm.nih.gov/rest/search/current?apiKey={apiKey}&string={term}&pageNumber=1&pageSize=1".format( apiKey=api_key, term=term) payload = {} headers = {} response = requests.request("GET", url, headers=headers, data=payload) cui = response.json()["result"]["results"][0]["ui"] url = "https://uts-ws.nlm.nih.gov/rest/content/current/CUI/{cui}/atoms?apiKey={apiKey}&ttys=&language=ENG&pageSize=25".format( apiKey=api_key, cui=cui) ``` ### Technical Analysis The credential is distributed to every recipient of the project and cannot be kept confidential. In addition, placing credentials in query strings increases exposure because full URLs are frequently retained by HTTP clients, reverse proxies, monitoring infrastructure, server access logs, debugging systems, and exception reports. Synonym expansion is enabled by default, so the exposed credential is used during ordinary operation rather than only in an isolated test. ### Attack Path 1. An attacker downloads or reads the project source. 2. The attacker extracts the hard-coded UMLS key. 3. Alternatively, the attacker obtains a logged request URL containing the `apiKey` parameter. 4. The attacker reuses the key directly against the UMLS API. 5. Requests are attributed to the cre ...[truncated 364 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Immediately revoke and rotate the exposed UMLS credential. - Remove the key from all source files and published package history where feasible. - Require users to provide their own UMLS credential through a secret manager or dedicated environment variable. - Do not place credentials in URLs if the service supports an authorization header or another non-URL authentication mechanism. - Ensure HTTP debug logs redact authorization values and query parameters. - Fail safely when no UMLS credential is configured, or disable synonym expansion by default. - Add automated secret scanning to continuous integration and release workflows. ]]>

T08 · Insecure Dependencies

Error
Location
requirements.txt:1
Finding
Unpinned Dependencies and Automatic Runtime Package Installation<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:1-9`; `mrmrmr/mragent/agent_tool.py:264-272, 489-501` **Vulnerability Type**: Unsafe dependency resolution and runtime supply-chain execution **Risk Level**: High ### Vulnerable Code Python dependencies permit any future version above a minimum: ```text pandas>=1.4.2 reportlab>=4.0.9 PyPDF2>=3.0.1 numpy>=1.19.5 biopython>=1.82 requests>=2.27.1 beautifulsoup4>=0.0.1 openai>=1.6.1 ollama>=0.1.8 ``` The generated R code installs packages automatically during analysis: ```r if (!requireNamespace("TwoSampleMR", quietly = TRUE)) { install.packages("TwoSampleMR") } if (!requireNamespace("ieugwasr", quietly = TRUE)) { install.packages("ieugwasr") } ``` The MRlap path does the same for additional packages: ```r if (!requireNamespace("httr", quietly = TRUE)) { install.packages("httr") } if (!requireNamespace("vcfR", quietly = TRUE)) { install.packages("vcfR") } if (!requireNamespace("MRlap", quietly = TRUE)) { install.packages("MRlap") } if (!requireNamespace("jsonlite", quietly = TRUE)) { install.packages("jsonlite") } ``` ### Technical Analysis Minimum-version constraints do not produce a reproducible dependency set and allow future, unaudited releases to be installed. No package hashes are provided. The automatic R installation behavior downloads and executes dependency code during a scientific analysis based on the runtime repository configuration. A compromised upstream account, package repository, transitive dependency, or mirror could therefore introduce code after the Skill itself has been audited. Runtime installation also unnecessarily grants the analysis process network and package-library write access. This finding does not establish that any named dependency is currently malicious; the risk arises from unrestricted future resolution and implicit installation. ### Attack Path 1. An upstream package, maintainer account, transitive dependency, or configured reposit ...[truncated 658 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin Python dependencies to reviewed exact versions. - Generate a lockfile and require cryptographic package hashes. - Review and pin transitive dependencies as well as direct dependencies. - Use trusted, explicitly configured package indexes. - Move all R dependency installation to a separate administrator- or user-approved setup phase. - Pin R package versions using a reproducible environment such as `renv`. - Record repository URLs and integrity metadata. - Run analysis with package-library write access disabled. - Regularly scan locked dependencies for disclosed vulnerabilities and update them through a controlled review process. ]]>

T07 · Tool Hijacking and Spoofing

Warning
Location
mrmrmr/web_demo.py:211
Finding
Web Demo Globally Replaces os.system and Routes Commands Through a Shell<![CDATA[ ## Vulnerability Details **File Location**: `mrmrmr/web_demo.py:211-259` **Vulnerability Type**: Process-wide tool hijacking and unsafe shell execution **Risk Level**: Medium ### Vulnerable Code ```python # 修改os.system函数以捕获R输出 original_os_system = os.system def patched_os_system(command): if 'R ' in command: process = subprocess.Popen( command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) def read_output(stream, output_text): for line in stream: output_text.append(line) r_output = [] stdout_thread = threading.Thread( target=read_output, args=(process.stdout, r_output)) stderr_thread = threading.Thread( target=read_output, args=(process.stderr, r_output)) stdout_thread.start() stderr_thread.start() return_code = process.wait() stdout_thread.join() stderr_thread.join() r_output_text = "".join(r_output) st.session_state.r_output_text += r_output_text r_output_area.markdown( f'<div class="r-output-area">{st.session_state.r_output_text}</div>', unsafe_allow_html=True ) return return_code else: return original_os_system(command) os.system = patched_os_system ``` ### Technical Analysis The web demo modifies the global `os.system` function for the entire Python process. Any imported module expecting normal `os.system` behavior instead invokes attacker-modifiable wrapper logic. This is a process-wide tool replacement rather than a scoped R execution helper. Commands containing the fragile substring `"R "` are sent to `subprocess.Popen` with `shell=True`. Shell metacharacters in such a command are consequently interpreted by the operating-system shell. This compounds the project's existing unsafe generated-R execution paths and makes security be ...[truncated 1095 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove the global assignment to `os.system`. - Implement a dedicated and explicitly invoked R runner. - Use `subprocess.run` or `subprocess.Popen` with an argument array and `shell=False`. - Use an absolute, configured path to the R executable where appropriate. - Keep output-capture behavior local to the dedicated runner. - Apply timeouts, output-size limits, and explicit error handling. - Render process output as escaped text rather than with `unsafe_allow_html=True`. - Add tests confirming that imported modules retain the standard `os.system` implementation. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (94)

Credential Access

High
Category
Privilege Escalation
Content
- `MRlap` - sample overlap correction (optional)
  - `jsonlite` - JSON processing (required for MRlap)
- `OPENAI_API_KEY` environment variable must be set (OpenAI API key)
- `OPENGWAS_JWT` environment variable (optional, for OpenGWAS access token)

## Usage
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
- `MRlap` - sample overlap correction (optional)
  - `jsonlite` - JSON processing (required for MRlap)
- `OPENAI_API_KEY` environment variable must be set (OpenAI API key)
- `OPENGWAS_JWT` environment variable (optional, for OpenGWAS access token)

## Usage
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

External Script Fetching

High
Category
Supply Chain
Content
You need to follow the steps to install ollama and follow the ollama python support package:

```shell
curl -fsSL https://ollama.com/install.sh | sh
pip install ollama
```
Confidence
97% confidence
Finding
The README recommends executing a remote installation script directly via 'curl ... | sh', which bypasses normal review of the downloaded content. If the remote server, CDN, or connection path is compromised, users could execute arbitrary shell commands on their systems with the privileges of the invoking user.

Chaining Abuse

High
Category
Tool Misuse
Content
You need to follow the steps to install ollama and follow the ollama python support package:

```shell
curl -fsSL https://ollama.com/install.sh | sh
pip install ollama
```
Confidence
98% confidence
Finding
The use of a shell pipe into 'sh' is a classic command-chaining risk because untrusted network content is immediately interpreted by the shell. In practice, any compromise of the fetched content results in direct code execution, making the README materially more dangerous than a simple link to installation instructions.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
# 获取OpenGWAS数据库中的GWAS ID
def get_gwas_id(keyword):
    def remove_newlines(lst):
        # Remove /n from each string in a list
        return [s.replace('\n', '') for s in lst]

    data = []
Confidence
90% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Missing User Warnings

High
Confidence
98% confidence
Finding
The OpenGWAS JWT token is embedded directly into the generated R source code and written to disk in test.R before execution. This exposes a sensitive credential to local users, logs, backups, crash reports, or later accidental commits, and combines with the code-injection risk from generated-script execution.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Content
with open('test.R', 'w', encoding='utf-8') as f:
        f.write(r_script_run)

    os.system('R --slave --no-save --no-restore --no-site-file --no-environ -f  test.R --args')


@timer
Confidence
97% confidence
Finding
This function generates an R script by interpolating untrusted inputs such as Exposure_id, Outcome_id, path, and gwas_token directly into code, writes it to disk, and then executes it via os.system. Even though the shell command itself is constant, the executed script content is attacker-controllable, enabling arbitrary R code execution and potentially arbitrary file writes or command execution from within R.

Missing User Warnings

High
Confidence
98% confidence
Finding
The second MR tool repeats the credential-handling issue by embedding the OpenGWAS JWT in a generated R file on disk and then executing it. This creates both credential exposure and an easy path for code injection through interpolated values.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Content
with open('test.R', 'w', encoding='utf-8') as f:
        f.write(r_script_run)

    os.system('R --slave --no-save --no-restore --no-site-file --no-environ -f  test.R --args')


@timer
Confidence
97% confidence
Finding
This second workflow has the same core issue: untrusted values are inserted into an R program and then executed through os.system. An attacker controlling identifiers, path, or token values could break out of string context in the generated R code, run arbitrary R statements, overwrite files, or exfiltrate secrets.

os.system() or os exec-family call

High
Category
Dangerous Code Execution
Content
with open('test.R', 'w', encoding='utf-8') as f:
        f.write(r_script_run)

    os.system('R --slave --no-save --no-restore --no-site-file --no-environ -f  test.R --args')


def get_synonyms(term, api_key):
Confidence
96% confidence
Finding
The MRlap workflow also constructs an executable R script from attacker-influenced inputs, including Exposure_id, Outcome_id, path, N_exposure, and N_outcome, then runs it. Because those values are embedded into code and file paths, this can lead to arbitrary code execution in R, path traversal, and uncontrolled downloading/writing of files.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
A hard-coded UMLS API token is embedded directly in the source and used for live synonym lookups. Anyone with code access can reuse the credential for unrelated authenticated requests, causing credential leakage, abuse of the external account, and loss of control over billed or rate-limited API activity.

Missing User Warnings

High
Confidence
97% confidence
Finding
The skill silently uses a hard-coded third-party credential to perform network requests for synonym expansion without user disclosure. In this context, that is more dangerous because the workflow is an automated agent pipeline that may run in trusted environments, so embedded secrets can be unintentionally redistributed and abused at scale.

eval() call detected

High
Category
Dangerous Code Execution
Content
Outcome_id = Outcome_id.to_numpy()[0]
                Exposure_id = Exposure_id.to_numpy()[0]
                print(Outcome_id, Exposure_id)
                Outcome_id_list = eval(Outcome_id)
                Exposure_id_list = eval(Exposure_id)
                print(Outcome_id_list, Exposure_id_list)
Confidence
99% confidence
Finding
The code calls eval() on Outcome_id values loaded from CSV data that is derived from earlier LLM output and other external sources. An attacker who can influence the CSV contents or upstream model output can turn this into arbitrary Python code execution when step9 runs, making this a direct code execution sink rather than a mere parsing issue.

eval() call detected

High
Category
Dangerous Code Execution
Content
Exposure_id = Exposure_id.to_numpy()[0]
                print(Outcome_id, Exposure_id)
                Outcome_id_list = eval(Outcome_id)
                Exposure_id_list = eval(Exposure_id)
                print(Outcome_id_list, Exposure_id_list)

                # 创建文件夹
Confidence
99% confidence
Finding
This eval() call executes Exposure_id content as Python code after reading it from persisted workflow data. Because that content can be influenced by LLM responses, external services, or edited files, it creates a straightforward arbitrary code execution path in a security-sensitive automation pipeline.

Missing User Warnings

High
Confidence
96% confidence
Finding
The code sends MR analysis inputs and full result tables to an external LLM via llm_chat() without any consent, warning, redaction, or data-classification check. In a biomedical/research workflow, these inputs may contain unpublished, proprietary, or otherwise sensitive analysis data, creating a real confidentiality and compliance risk once transmitted to third-party model providers.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
def patched_os_system(command):
    if 'R ' in command:
        # 使用subprocess代替os.system来捕获输出
        process = subprocess.Popen(
            command,
            shell=True,
            stdout=subprocess.PIPE,
Confidence
94% confidence
Finding
This duplicate match points to the same underlying weakness: command execution through Popen(..., shell=True). Because the application overrides os.system globally, the dangerous pattern affects a broad execution surface and can turn otherwise indirect inputs into shell metacharacter injection opportunities.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
def patched_os_system(command):
    if 'R ' in command:
        # 使用subprocess代替os.system来捕获输出
        process = subprocess.Popen(
            command,
            shell=True,
            stdout=subprocess.PIPE,
Confidence
94% confidence
Finding
This duplicate match points to the same underlying weakness: command execution through Popen(..., shell=True). Because the application overrides os.system globally, the dangerous pattern affects a broad execution surface and can turn otherwise indirect inputs into shell metacharacter injection opportunities.

Credential Access

High
Category
Privilege Escalation
Content
def get_api_keys() -> Dict[str, Optional[str]]:
    """Get API keys from environment variables"""
    return {
        "openai_api_key": os.environ.get("OPENAI_API_KEY"),
        "opengwas_jwt": os.environ.get("OPENGWAS_JWT"),
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def get_api_keys() -> Dict[str, Optional[str]]:
    """Get API keys from environment variables"""
    return {
        "openai_api_key": os.environ.get("OPENAI_API_KEY"),
        "opengwas_jwt": os.environ.get("OPENGWAS_JWT"),
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill advertises capabilities that imply filesystem, environment, shell, and network access, but it does not declare any explicit tool scope or permissions boundary. This creates an authorization ambiguity where a host may grant broader-than-expected execution privileges, increasing the blast radius if the skill or its dependencies behave unsafely.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill sends user-provided biomedical terms to external services including PubMed, OpenGWAS, and an LLM provider, but the description does not clearly warn users that their inputs will leave the local environment. In biomedical contexts, disease, exposure, or study terms may encode sensitive research interests or potentially sensitive health-related information, so lack of disclosure creates privacy and compliance risk.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The README instructs users to provide an LLM API key and a GWAS token and describes literature scanning and online GWAS usage, but it does not clearly warn that user-supplied disease, exposure, and outcome terms may be transmitted to third-party services. In a biomedical context, those inputs can be sensitive research topics or patient-related terms, so the lack of explicit disclosure can lead to unintended external data sharing.

External Transmission

Medium
Category
Data Exfiltration
Content
You need to get the **GWAS token** for the OpenGWAS data.

- [OpenGWAS API](https://api.opengwas.io/)

## Usage
Confidence
81% confidence
Finding
The skill explicitly depends on the OpenGWAS API, which means user queries and analysis parameters may be sent to an external service. External transmission is not inherently malicious, but in this medical-research workflow it creates confidentiality and compliance risk if users assume processing is local or submit sensitive inputs.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This code defines credential variables and passes them into an agent configured with a remote base URL, which implies network transmission to an external service. There is no confirmation prompt, user-facing log, or explanatory comment warning that API keys and request data may be used in outbound requests.

External Transmission

Medium
Category
Data Exfiltration
Content
# openai
    agent = MRAgent(outcome='back pain', AI_key=AI_key, model='MR',
                    num=50, bidirectional=True, introduction=False, LLM_model='gpt-4o',
                    base_url="https://api.gpt.ge/v1/", gwas_token=mr_key,
                    mr_quality_evaluation=True, mr_quality_evaluation_key_item=['4b', '4e', '6e', '10d'], mrlap=True)
    agent.run(step=[1, 2, 3, 4, 5, 6, 7, 8])
    agent.run(step=[9])
Confidence
93% confidence
Finding
The script explicitly configures a third-party API endpoint and then runs an agent workflow, which may transmit prompts, research inputs, or tokens to an external service. In a skill context, this is security-relevant because users may not realize their data is leaving the local environment, and the custom endpoint increases supply-chain and data-handling risk versus a trusted default.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
mrmrmr/mragent/agent_workflow.py:1041