T09 · Insecure Skill Coding Practices
- Location
scripts/review_skills.py:44- Finding
GitHub Token Sent Unnecessarily to a Public Raw-Content Endpoint
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
Scout is mainly a disclosed update advisor, but it needs review because it can run configured local commands, inspect installed skills, and unnecessarily attach a GitHub token to a remote reference fetch.
Review this before installing. Use only trusted watchlist entries, because command detectors run local programs. Avoid running the skill health review over private or sensitive skill directories unless you intend to expose their structure and SKILL.md contents to the report. Prefer an unset or fine-grained GitHub token, and treat fetched best-practices content as untrusted reference text rather than instructions.
scripts/review_skills.py:44GitHub Token Sent Unnecessarily to a Public Raw-Content Endpoint
scripts/review_skills.py:33Mutable Remote Instructions Are Placed in Agent-Consumable Output Without Integrity Controls
This mismatch is more severe because the detected behavior appears to inspect local skill directories and fetch unrelated best-practices content instead of performing the promised software-release monitoring workflow. A skill presented as an update advisor but capable of scanning local content and reaching out to external URLs can cause users to authorize unexpected data access under false pretenses.
This mismatch is more severe because the detected behavior appears to inspect local skill directories and fetch unrelated best-practices content instead of performing the promised software-release monitoring workflow. A skill presented as an update advisor but capable of scanning local content and reaching out to external URLs can cause users to authorize unexpected data access under false pretenses.
The watchlist design allows arbitrary local commands to be executed for version detection, giving a configuration file the ability to run code on the host. In a skill whose purpose is update monitoring, this is broader than necessary and becomes especially dangerous if an agent, plugin, or attacker can modify the watchlist or supply tool entries.
The skill documentation invokes local scripts, reads and writes files under the user's home directory, uses shell commands, and appears to perform network-backed GitHub checks, but the manifest declares no explicit tool scope or permissions. In an agent environment, missing scope boundaries increases the chance that the skill can access or modify resources beyond what a user expects, especially since it also includes post-approval installation and service actions.
The top-level description says the skill never installs anything without explicit approval and frames it as recommendation-oriented, but the workflow also includes installation, config validation, service restarts, and health changes after approval. Even with approval, combining advisory and operational behaviors in one skill raises the risk of users granting broader execution than intended and blurs the trust boundary between analysis and system modification.
The manifest frames the skill as tracking releases, reviewing notes, and posting recommendations before approval, which implies primarily read-oriented monitoring behavior. This file goes beyond passive checking by creating directories and writing a config file on first run, which is a meaningful side effect not reflected in the manifest description.
The command used for version detection comes from the watchlist config and may be supplied as an arbitrary string or argv list, then executed locally with subprocess.run. Although shell=True is not used, this still permits arbitrary program execution on the host if the config is malicious or tampered with, which exceeds the minimally necessary behavior for an update-checking skill.
if not cmd:
return None
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
output = (result.stdout + result.stderr).strip()
match = re.search(r'\b(\d+\.\d+[\.\d-]*)\b', output)
# Return match or None — never return garbage like the first word
The manifest focuses on checking for available updates and reviewing release information, especially for GitHub-released tools. Querying local npm and pip installations via subprocesses expands the skill into package-manager-specific host inspection, which is not clearly stated in the description.
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def detect_version_npm(package: str) -> Optional[str]:
try:
result = subprocess.run(
["npm", "list", "-g", "--depth=0", package],
capture_output=True, text=True, timeout=10
)
subprocess module calls execute external commands. Without careful input validation, this enables command injection.
def detect_version_pip(package: str) -> Optional[str]:
try:
result = subprocess.run(
["pip", "show", package],
capture_output=True, text=True, timeout=10
)
This script introduces a local skill-auditing capability that scans installed skills and reports on their contents, which is outside the declared update-tracking and release-review purpose of the scout skill. Even though it does not execute inspected files, the added local inspection surface can expose metadata and file structure from arbitrary skill directories and expands the skill's authority beyond user expectations.
Skill enumerates or reads other installed skills. Access to other skills' SKILL.md files or the skills directory reveals prompt instructions, capabilities, and secrets that should be invisible to peer skills.
# Optional: fetch latest skill-creator SKILL.md for context when presenting findings
SKILL_CREATOR_RAW_URL = (
"https://raw.githubusercontent.com/openclaw/openclaw/main/"
"skills/skill-creator/SKILL.md"
)
The code enumerates and recursively inspects arbitrary local skill directories via a user-supplied --skills-dir and rglob/iterdir logic, which is not justified by the advertised release-monitoring functionality. This creates an unintended local discovery capability that can inventory installed skills and associated documentation, potentially revealing sensitive workspace structure or enabling broader reconnaissance by the agent.
This code reads the GITHUB_TOKEN environment variable and, when present, attaches it as a Bearer token to outbound HTTP requests. Although the function docstring notes authenticated requests, there is no user-facing warning, confirmation, or logging that credentials may be sent over the network.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"per_page": 30,
"labels": labels,
})
url = f"https://api.github.com/repos/{repo}/issues?{params}"
try:
return github_request(url)
except urllib.error.HTTPError as e:
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"per_page": 30,
"labels": labels,
})
url = f"https://api.github.com/repos/{repo}/issues?{params}"
try:
return github_request(url)
except urllib.error.HTTPError as e:
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
"per_page": 30,
"labels": labels,
})
url = f"https://api.github.com/repos/{repo}/issues?{params}"
try:
return github_request(url)
except urllib.error.HTTPError as e:
The stated purpose is software update tracking, release-note review, and regression checking for watched tools. The "Skill Health Review" section introduces a different function: reviewing skills against OpenClaw best practices. That is a distinct auditing capability not reflected in the manifest description.
No suspicious patterns detected.