T09 · Insecure Skill Coding Practices
- Location
scripts/main.py:42- Finding
Bearer Credentials and User Files Can Be Transmitted to an Unvalidated Endpoint
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This is a coherent remote data-analysis skill, but it needs review because it can send files and credentials to an unvalidated host and mishandles downloaded artifacts.
Install only if you trust the Raccoon service and can set RACCOON_API_HOST to a known HTTPS endpoint. Do not use it for sensitive files without confirming the destination and retention expectations. Prefer running with --no-open, avoid sharing auth-check output, and treat downloaded artifacts as untrusted until the host, download URL, and filename handling are fixed.
scripts/main.py:42Bearer Credentials and User Files Can Be Transmitted to an Unvalidated Endpoint
scripts/main.py:233Server-Controlled Artifact URLs Enable Client-Side SSRF
scripts/main.py:234Server-Controlled Artifact Filenames Permit Path Traversal and File Overwrite
scripts/main.py:466Authentication Check Discloses a Significant Portion of the Bearer Token
scripts/main.py:405Untrusted Remote Artifacts Are Automatically Opened with Local Applications
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
url = f"{host}/api/open/office/v2/sessions"
try:
resp = requests.get(url, headers=headers, params={"paging.limit": 1}, timeout=10)
if resp.status_code == 200:
data = resp.json()
if data.get("code") == 0:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
url = f"{host}{path}"
if method == "POST" and files:
h = {k: v for k, v in headers.items() if k != "Content-Type"}
resp = requests.post(url, headers=h, files=files, stream=True, timeout=120)
elif method == "POST":
resp = requests.post(url, headers=headers, json=data, stream=True, timeout=120)
else:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
h = {k: v for k, v in headers.items() if k != "Content-Type"}
resp = requests.post(url, headers=h, files=files, stream=True, timeout=120)
elif method == "POST":
resp = requests.post(url, headers=headers, json=data, stream=True, timeout=120)
else:
resp = requests.get(url, headers=headers, params=data, timeout=30)
return resp.json().get("data") if resp.status_code == 200 else None
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
elif method == "POST":
resp = requests.post(url, headers=headers, json=data, stream=True, timeout=120)
else:
resp = requests.get(url, headers=headers, params=data, timeout=30)
return resp.json().get("data") if resp.status_code == 200 else None
if resp.status_code != 200:
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
}
print("\n[步骤1] 创建会话...")
resp = requests.post(
f"{host}/api/open/office/v2/sessions",
headers=headers,
json={"name": "Python示例会话"},
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
batch_id = str(uuid.uuid4())
csv_content = "name,age,score\nAlice,25,88\nBob,30,92\nCarol,28,85\nDave,35,95\nEve,22,78"
upload_headers = {k: v for k, v in headers.items() if k != "Content-Type"}
resp = requests.post(
f"{host}/api/open/office/v2/sessions/default_session/{batch_id}/files",
headers=upload_headers,
files={"file": ("sample_data.csv", csv_content.encode(), "text/csv")},
Referenced artifact was not completely inspected
if [ -f "./SKILL.md" ] && [ -f "./scripts/main.py" ] && grep -q "raccoon-dataanalysis" "./SKILL.md"; then
Referenced artifact was not completely inspected
if [ -f "./SKILL.md" ] && [ -f "./scripts/main.py" ] && grep -q "raccoon-dataanalysis" "./SKILL.md"; then
Referenced artifact was not completely inspected
if [ -f "./SKILL.md" ] && [ -f "./scripts/main.py" ] && grep -q "raccoon-dataanalysis" "./SKILL.md"; then
Referenced artifact was not completely inspected
if [ -f "./SKILL.md" ] && [ -f "./scripts/main.py" ] && grep -q "raccoon-dataanalysis" "./SKILL.md"; then
The skill explicitly instructs the agent to use shell, environment variables, local file access, and networked Python scripts to upload user files to a remote host, but it declares no explicit tool scope or allowed-tools boundary. This creates a real security control gap: a host platform or reviewer cannot enforce least privilege, and the skill may gain broader execution or data-access capability than users expect.
The manifest description and core instructions are written as mandatory Chinese-language operating guidance, and the skill name/examples all assume Chinese interaction. There is no statement that users may choose another language or that the Chinese-only behavior is required for a region-specific compliance reason.
This markdown file documents behaviors that can transmit user content or files to external services, including enabling web search and uploading files, but it does not include any warning about privacy, sensitive data handling, or external data disclosure. Under the markdown-specific SQP-2 criteria, descriptions of behaviors affecting user data or privacy should warn users about those impacts.
The module docstring and all CLI-facing text are presented exclusively in Chinese, and there is no indication that users may choose another language. Under the stated policy, forcing a specific language without opt-in can be a natural-language policy violation unless the locale restriction is explicitly documented and justified.
Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.
def _upload_headers(self):
return {"Authorization": f"Bearer {self.token}"}
def _request(self, method, path, json_data=None, params=None, timeout=None):
url = f"{self.host}{path}"
timeout = timeout or self.REQUEST_TIMEOUT
resp = requests.request(
The skill uploads local user-selected files to a remote API but does not present a clear user-facing warning or confirmation about off-device transmission. In the context of data analysis, users may upload sensitive spreadsheets or CSVs, so silent transfer can lead to unintended disclosure of confidential data.
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
url = f"{self.host}/api/open/office/v2/sessions/default_session/{batch_id}/files"
filename = os.path.basename(file_path)
with open(file_path, "rb") as f:
resp = requests.post(
url, headers=self._upload_headers,
files={"file": (filename, f)}, timeout=60,
)
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
def _stream_chat(self, session_id, data):
url = f"{self.host}/api/open/office/v2/sessions/{session_id}/chat/conversations"
resp = requests.post(
url, headers=self._headers, json=data,
stream=True, timeout=self.STREAM_TIMEOUT,
)
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
def _stream_chat(self, session_id, data):
url = f"{self.host}/api/open/office/v2/sessions/{session_id}/chat/conversations"
resp = requests.post(
url, headers=self._headers, json=data,
stream=True, timeout=self.STREAM_TIMEOUT,
)
The skill automatically opens downloaded artifacts with local OS handlers, causing local applications to process remote content without a fresh trust decision from the user. In a data-analysis tool that downloads model-generated files, this increases the attack surface for malicious files or unsafe file-handler behavior and is not strictly necessary for core functionality.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
system = platform.system()
try:
if system == "Darwin":
subprocess.run(["open", path], check=False)
elif system == "Linux":
subprocess.run(["xdg-open", path], check=False)
except Exception:
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
if system == "Darwin":
subprocess.run(["open", path], check=False)
elif system == "Linux":
subprocess.run(["xdg-open", path], check=False)
except Exception:
pass
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
url = f"{host}/api/open/office/v2/sessions"
try:
resp = requests.get(url, headers=headers, params={"paging.limit": 1}, timeout=10)
if resp.status_code == 200:
data = resp.json()
if data.get("code") == 0:
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
url = f"{host}{path}"
if method == "POST" and files:
h = {k: v for k, v in headers.items() if k != "Content-Type"}
resp = requests.post(url, headers=h, files=files, stream=True, timeout=120)
elif method == "POST":
resp = requests.post(url, headers=headers, json=data, stream=True, timeout=120)
else:
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
h = {k: v for k, v in headers.items() if k != "Content-Type"}
resp = requests.post(url, headers=h, files=files, stream=True, timeout=120)
elif method == "POST":
resp = requests.post(url, headers=headers, json=data, stream=True, timeout=120)
else:
resp = requests.get(url, headers=headers, params=data, timeout=30)
return resp.json().get("data") if resp.status_code == 200 else None
No suspicious patterns detected.