Back to skill

Security audit

Bohrium Dataset Management

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly aligned with Bohrium dataset management, but it asks users to run an unverifiable remote shell installer and its helper script sends the Bohrium access key to a different API host than the one documented.

Review before installing. Prefer a verified Bohrium CLI installation method with signatures or checksums, confirm whether openapi.dp.tech is an official endpoint allowed to receive your Bohrium ACCESS_KEY, and use a least-privileged key. For deletion or version creation, require explicit confirmation of the exact dataset ID and project before running commands.

Vulnerability Patterns
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • 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
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T03 · Remote Payload Retrieval and Execution

Error
Location
SKILL.md:25
Finding
Unverified Remote Installer Downloaded and Executed by Bash<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 25–28 **Vulnerability Type**: Remote payload retrieval and execution **Risk Level**: High ### Vulnerable Code ```bash # macOS /bin/bash -c "$(curl -fsSL https://dp-public.oss-cn-beijing.aliyuncs.com/bohrctl/1.0.0/install_bohr_mac_curl.sh)" # Linux /bin/bash -c "$(curl -fsSL https://dp-public.oss-cn-beijing.aliyuncs.com/bohrctl/1.0.0/install_bohr_linux_curl.sh)" ``` ### Technical Analysis The documented installation procedure retrieves a shell script from an external object-storage URL and immediately passes the response to Bash. No cryptographic signature, pinned digest, checksum verification, or manual inspection step is required before execution. Although installing the Bohrium CLI supports the declared dataset-management functionality, executing mutable remote content directly is not the minimum safe mechanism for installing it. The code reviewed during the Skill audit is not necessarily the code later returned by these URLs. Compromise of the storage account, installer publishing pipeline, DNS resolution, or applicable TLS trust infrastructure could therefore change the effective payload without modifying this Skill package. The use of HTTPS provides transport encryption and server authentication, but it does not establish the integrity or provenance of the installer as a specific reviewed artifact. ### Attack Path 1. A user or agent follows the prerequisite installation instructions. 2. An attacker compromises the external storage account, installer publishing process, or another component capable of controlling the response. 3. `curl` retrieves the attacker-controlled shell script. 4. Command substitution supplies the downloaded response directly to `/bin/bash`. 5. Bash executes the payload with the permissions of the user running the installation. 6. The payload can access files and credentials available to that user, modify the local environment, install persistence, or retri ...[truncated 420 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not pipe or substitute a network response directly into a shell. 2. Distribute a versioned installer artifact and publish a SHA-256 digest or cryptographic signature through an independently protected channel. 3. Download the artifact to a local file, verify its digest or signature, and execute it only after successful verification. 4. Prefer installation through a trusted package manager that verifies signed repository metadata. 5. Pin the installer to an immutable, content-addressed release rather than relying only on a version-like URL path. 6. Document the expected signer, digest-verification process, and minimum permissions needed during installation. 7. Avoid elevated execution unless the CLI installation strictly requires it. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
dataset_manager.py:18
Finding
Access Key Transmitted to an API Host Inconsistent with the Declared Bohrium Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `dataset_manager.py`, lines 18–29; additional request sites at lines 40, 53, 73–77, and 89 **Vulnerability Type**: Sensitive credential exposure through inconsistent endpoint configuration **Risk Level**: High ### Vulnerable Code ```python AK = os.environ.get("ACCESS_KEY", "") BASE = "https://openapi.dp.tech/openapi/v1/ds" HEADERS = {"accessKey": AK} HEADERS_JSON = {**HEADERS, "Content-Type": "application/json"} def check_quota(project_id: int): """Check dataset quota for a project.""" r = requests.get( f"{BASE}/quota/check", headers=HEADERS, params={"projectId": project_id}, ) ``` The same credential-bearing headers are used at the other request sites: ```python r = requests.get(f"{BASE}/{dataset_id}", headers=HEADERS) r = requests.get(f"{BASE}/{dataset_id}/version", headers=HEADERS) r = requests.post( f"{BASE}/{dataset_id}/version", headers=HEADERS_JSON, json={"versionDesc": desc}, ) r = requests.get(f"{BASE}/{dataset_id}/permission", headers=HEADERS) ``` The endpoint conflicts with the host declared in `SKILL.md`: ```bash export OPENAPI_HOST=https://open.bohrium.com ``` ```python BASE = "https://open.bohrium.com/openapi/v1/ds" HEADERS = {"accessKey": AK} ``` ### Technical Analysis Authenticated API access is necessary for the declared dataset-management operations. However, the executable implementation sends the reusable `ACCESS_KEY` value in an `accessKey` HTTP header to `https://openapi.dp.tech`, while the Skill documentation identifies `https://open.bohrium.com` as the Bohrium API host. The reviewed project does not explain or validate the relationship between these hosts. Consequently, a user who provides a credential based on the documented trust boundary may have that credential disclosed to a different hostname. Even if the alternate endpoint is operationally legitimate, the mismatch prevents users from making an informed authorization d ...[truncated 1698 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use the documented `https://open.bohrium.com` API endpoint consistently unless the alternate hostname is verified as an official endpoint. 2. If `openapi.dp.tech` is required, explicitly document why it is trusted, which organization controls it, and why credentials issued for Bohrium may safely be sent to it. 3. Maintain a strict allowlist of approved HTTPS API origins and reject all unrecognized schemes, hosts, ports, and user-information components. 4. Disable automatic redirects for authenticated requests or validate every redirect destination before sending credentials. 5. Prefer short-lived, narrowly scoped tokens rather than reusable account-wide access keys. 6. Add explicit connection and response timeouts. 7. Call `raise_for_status()` before parsing or acting on response bodies. 8. Avoid retaining credentials in broadly accessible global structures longer than necessary. 9. Add automated tests that assert credentials are sent only to approved origins. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (17)

Tainted flow: 'HEADERS' from os.environ.get (line 20, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
def check_quota(project_id: int):
    """Check dataset quota for a project."""
    r = requests.get(
        f"{BASE}/quota/check",
        headers=HEADERS,
        params={"projectId": project_id},
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'HEADERS' from os.environ.get (line 20, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
def get_detail(dataset_id: int):
    """Get dataset details."""
    r = requests.get(f"{BASE}/{dataset_id}", headers=HEADERS)
    data = r.json().get("data", {})
    print(f"Dataset: {data.get('title', '?')}")
    print(f"  ID:      {data.get('id')}")
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'HEADERS' from os.environ.get (line 20, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
def list_versions(dataset_id: int):
    """List all versions of a dataset."""
    r = requests.get(f"{BASE}/{dataset_id}/version", headers=HEADERS)
    data = r.json().get("data", {})
    items = data if isinstance(data, list) else data.get("items", [])
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'HEADERS_JSON' from os.environ.get (line 21, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
def create_version(dataset_id: int, desc: str):
    """Create a new version of an existing dataset."""
    r = requests.post(
        f"{BASE}/{dataset_id}/version",
        headers=HEADERS_JSON,
        json={"versionDesc": desc},
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'HEADERS' from os.environ.get (line 20, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
def check_permission(dataset_id: int):
    """Check dataset permissions."""
    r = requests.get(f"{BASE}/{dataset_id}/permission", headers=HEADERS)
    data = r.json().get("data", {})
    print(f"Permissions for dataset {dataset_id}:")
    print(json.dumps(data, indent=2, ensure_ascii=False))
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The code is clearly Bohrium dataset-related, so it aligns at a high level with dataset management and version management. However, the declared description emphasizes creating/listing/deleting datasets and uploading data, while the supplied code instead implements quota inspection, dataset detail lookup, version listing/creation, and permission inspection. Two concrete capabilities—quota checking and permission retrieval—are undeclared. Also, several prominently declared capabilities (create dataset, delete dataset, upload data) are not present in this chunk. Because the actual behavior includes materially different dataset-management operations and omits several central declared functions, this is a description-behavior mismatch.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill documents use of environment variables and outbound network/API access, but it does not declare any explicit tool scope or permission boundaries. In an agent setting, this weakens least-privilege controls and can allow broader-than-expected access to secrets or network operations if the runtime interprets the skill permissively.

Unbounded Output

Medium
Category
Output Handling
Content
Manage datasets on the Bohrium platform. **Prefer `bohr` CLI**; fall back to the API for version management, quota checks, etc.

`bohr dataset create` advantages over web upload: **no size limit** and **resumable upload**.

Datasets solve common pain points:
- Repeated file upload on every job submission -> mount datasets to avoid re-upload
Confidence
75% confidence
Finding
Output size or generation rate is not bounded. Unbounded output enables denial-of-service through resource exhaustion, log flooding, or context-window stuffing.

External Transmission

Medium
Category
Data Exfiltration
Content
Via API:
```python
requests.post(f"{BASE}/{dataset_id}/version", headers=HEADERS_JSON,
    json={"versionDesc": "v2 update"})
```
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
Via API:
```python
requests.post(f"{BASE}/{dataset_id}/version", headers=HEADERS_JSON,
    json={"versionDesc": "v2 update"})
```
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The skill provides irreversible deletion commands without placing a clear warning or confirmation requirement before the examples. In an agent-assisted workflow, this increases the chance of accidental destructive actions, especially if a user request is ambiguous or the agent auto-generates commands directly from the skill.

External Transmission

Medium
Category
Data Exfiltration
Content
r = requests.get(f"{BASE}/{dataset_id}/version/{version_id}", headers=HEADERS)

# Create via API
r = requests.post(f"{BASE}/", headers=HEADERS_JSON, json={
    "title": "my-dataset", "projectId": 154,
    "identifier": "my-dataset",  # Required, unique ID
})
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
# Then upload files via tiefblue, then call commit

# Commit
requests.put(f"{BASE}/commit", headers=HEADERS_JSON,
    json={"datasetId": dataset_id})

# New version
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
json={"versionDesc": "v2 update"})

# Update info
requests.put(f"{BASE}/{dataset_id}", headers=HEADERS_JSON,
    json={"title": "new-title"})

# Delete version
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The code performs a state-changing operation that creates a new dataset version immediately from command input without any confirmation, dry-run, or user warning. In an agent setting, this increases the chance of unintended mutations from prompt misunderstanding or prompt injection, and the skill context makes it more sensitive because dataset versioning can consume quota and alter operational records.

External Transmission

Medium
Category
Data Exfiltration
Content
def create_version(dataset_id: int, desc: str):
    """Create a new version of an existing dataset."""
    r = requests.post(
        f"{BASE}/{dataset_id}/version",
        headers=HEADERS_JSON,
        json={"versionDesc": desc},
Confidence
80% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Description-Behavior Mismatch

Medium
Confidence
92% confidence
Finding
The skill manifest says it is for creating/listing/deleting datasets, uploading data, or managing versions, but the code also exposes dataset permission inspection. This scope expansion can disclose sensitive authorization metadata to users or agents who invoke the skill under a narrower trust assumption, making the skill context more dangerous because permission structures can aid reconnaissance and access-mapping.

Static analysis

No suspicious patterns detected.