Back to skill

Security audit

Free Scaling

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does the advertised model-ensemble work, but it also silently reuses local GitHub/Copilot credentials and can send or retain prompts outside the user's selected scope.

Review before installing. Use it only with data you are comfortable sending to NVIDIA NIM and possibly GitHub Copilot, avoid sensitive prompts unless provider use and logging are acceptable, and do not enable Copilot/audit features unless you trust the local OpenClaw credential environment and workspace paths.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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
Findings (5)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
nim_ensemble/voter.py:50
Finding

Cross-Agent GitHub Credential Discovery and Automatic Reuse

Content
View full analysis
bool: """Refresh the Copilot session token using GitHub OAuth token. Uses the stored GitHub user token (ghu_*) to request a fresh Copilot session token from api.github.com/copilot_internal/v2/token. """ import urllib.request import urllib.error import glob token_path = os.path.expanduser( "~/.openclaw/credentials/github-copilot.token.json" ) # Find GitHub OAuth token from auth-profiles oauth_token = None for profile_path in glob.glob( os.path.expanduser("~/.openclaw/agents/*/agent/auth-profiles.json") ): try: with open(profile_path) as f: data = json.load(f) profiles = data.get("profiles", data) for profile_name, profile_data in profiles.items(): if isinstance(profile_data, dict): tok = profile_data.get("token", "") if isinstance(tok, str) and tok.startswith("ghu_"): oauth_token = tok break if oauth_token: break except Exception: continue if not oauth_token: return False try: req = urllib.request.Request( "https://api.github.com/copilot_internal/v2/token", headers={ "Authorization": f"token {oauth_token}", "Editor-Version": "vscode/1.96.0", "Editor-Plugin-Version": "copilot/1.250.0", "User-Agent": "GithubCopilot/1.250.0", }, ) with urllib.request.urlopen(req, timeout=10) as resp: data = json.loads(resp.read().decode("utf-8")) if "token" not in data: return False with open(to ...[truncated 2205 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
nim_ensemble/cascade.py:665
Finding

Shadow Challenger Can Disclose Inputs to an Unselected Copilot Provider

Content
View full analysis
str | None: state = load() exclude = set(exclude or []) candidates = [ (alias, stats) for alias, stats in state["models"].items() if alias not in exclude ] if not candidates: from .models import MODELS unseen = [ a for a in MODELS if a not in exclude and a not in state["models"] and not MODELS[a].get("thinking") ] return unseen[0] if unseen else None ``` The registry includes Copilot-backed aliases such as `cp-4.1`, `cp-mini`, and `cp-flash`. ### Technical Analysis For `scale()` calls with two or more models, online learning automatically sends the same question and effective system prompt to a shadow challenger. Challenger selection is not constrained to the backend or provider explicitly selected by the caller. Consequently, a caller that requests an NVIDIA NIM en ...[truncated 1659 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
nim_ensemble/health.py:75
Finding

Default Health Check Accesses Optional Copilot Credentials

Content
View full analysis
ModelHealth: """Probe a single model with a trivial question.""" from .voter import call_model info = get_model(alias) t0 = time.time() try: ans, raw = call_model( HEALTH_PROBE, alias, max_tokens=10, temperature=0.0 ) latency = time.time() - t0 if ans == "ERROR": if any(code in raw for code in ["HTTP 404", "HTTP 410"]): _mark_dead(alias) return ModelHealth( alias, info["id"], "dead", latency, raw[:100] ) return ModelHealth( alias, info["id"], "error", latency, raw[:100] ) if latency > 10: return ModelHealth(alias, info["id"], "slow", latency) _dead_models.pop(alias, None) return ModelHealth(alias, info["id"], "ok", latency) except Exception as e: return ModelHealth( alias, info["id"], "error", time.time() - t0, str(e)[:100] ) def health(models: list[str] = None, parallel: bool = True) -> dict[str, ModelHealth]: aliases = models or list(MODELS.keys()) results = {} if parallel: with ThreadPoolExecutor(max_workers=min(len(aliases), 10)) as pool: futures = {pool.submit(probe_model, a): a for a in aliases} for fut in as_completed(futures): alias = futures[fut] try: results[alias] = fut.result() except Exception as e: results[alias] = ModelHealth( alias, "", "error", 0, str(e)[:100] ) ``` ### Technical Analysis The documented zero-argument `health()` operation probes every entry in `MODELS`. The ...[truncated 1429 chars]
Remediation
View remediation

T07 · Tool Hijacking and Spoofing

Error
Location
presets/audit.py:22
Finding

Environment-Controlled Python Import Path Enables External Code Hijacking

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
nim_ensemble/voter.py:94
Finding

Refreshed Copilot Bearer Token Is Written Without Explicit File Protection

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (41)

Tainted flow: 'req' from os.environ.get (line 256, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · nim_ensemble/voter.py (reported line 94)May include surrounding context.

python
"User-Agent": "GithubCopilot/1.250.0",
            },
        )
        with urllib.request.urlopen(req, timeout=10) as resp:
            data = json.loads(resp.read().decode("utf-8"))
        
        if "token" not in data:

Tainted flow: 'req' from os.environ.get (line 256, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
91% confidence
Finding

User prompts and system prompts are transmitted to an external GitHub Copilot endpoint, and the bearer token used to authorize that access is sourced from local cached credentials. In the context of an ensemble voting skill, silently forwarding potentially sensitive user data to a third-party backend creates a real data-exposure risk, especially because the Copilot backend is optional and not obviously necessary to core functionality.

Content

Scanner excerpt · nim_ensemble/voter.py (reported line 190)May include surrounding context.

python
},
        )
        
        with urllib.request.urlopen(req, timeout=45) as resp:
            raw = resp.read().decode("utf-8")
        
        if not raw or not raw.startswith("{"):

Tainted flow: 'req' from os.environ.get (line 256, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · nim_ensemble/voter.py (reported line 266)May include surrounding context.

python
)
        
        try:
            with urllib.request.urlopen(req, timeout=curl_timeout) as resp:
                raw = resp.read().decode("utf-8")
        except urllib.error.HTTPError as e:
            body = e.read().decode("utf-8", errors="replace")

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

This variant includes a substantive undeclared behavior: access to local GitHub/Copilot credential files and token endpoints despite no declared permissions. Undisclosed credential discovery or token handling can expose secrets and silently extend the skill's trust boundary to third-party services, especially dangerous in an agent context where users may pass sensitive repositories or environments.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

This variant includes a substantive undeclared behavior: access to local GitHub/Copilot credential files and token endpoints despite no declared permissions. Undisclosed credential discovery or token handling can expose secrets and silently extend the skill's trust boundary to third-party services, especially dangerous in an agent context where users may pass sensitive repositories or environments.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

This variant includes a substantive undeclared behavior: access to local GitHub/Copilot credential files and token endpoints despite no declared permissions. Undisclosed credential discovery or token handling can expose secrets and silently extend the skill's trust boundary to third-party services, especially dangerous in an agent context where users may pass sensitive repositories or environments.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

This variant includes a substantive undeclared behavior: access to local GitHub/Copilot credential files and token endpoints despite no declared permissions. Undisclosed credential discovery or token handling can expose secrets and silently extend the skill's trust boundary to third-party services, especially dangerous in an agent context where users may pass sensitive repositories or environments.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

This variant includes a substantive undeclared behavior: access to local GitHub/Copilot credential files and token endpoints despite no declared permissions. Undisclosed credential discovery or token handling can expose secrets and silently extend the skill's trust boundary to third-party services, especially dangerous in an agent context where users may pass sensitive repositories or environments.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

This variant includes a substantive undeclared behavior: access to local GitHub/Copilot credential files and token endpoints despite no declared permissions. Undisclosed credential discovery or token handling can expose secrets and silently extend the skill's trust boundary to third-party services, especially dangerous in an agent context where users may pass sensitive repositories or environments.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

This variant includes a substantive undeclared behavior: access to local GitHub/Copilot credential files and token endpoints despite no declared permissions. Undisclosed credential discovery or token handling can expose secrets and silently extend the skill's trust boundary to third-party services, especially dangerous in an agent context where users may pass sensitive repositories or environments.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

This variant includes a substantive undeclared behavior: access to local GitHub/Copilot credential files and token endpoints despite no declared permissions. Undisclosed credential discovery or token handling can expose secrets and silently extend the skill's trust boundary to third-party services, especially dangerous in an agent context where users may pass sensitive repositories or environments.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The skill encourages users to pass arbitrary context into classify/generate/verify flows but does not clearly warn that this context is transmitted to external model providers such as NVIDIA NIM and potentially GitHub Copilot. This creates a real risk of unintended disclosure of source code, emails, papers, or other sensitive material to third parties.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The online learning and feedback loop descriptions state that votes, shadow challenger results, deployment outcomes, and Discord reactions are logged, but they do not provide a clear privacy/security warning. Logging interaction data without explicit notice can expose user content, behavioral metadata, and possibly identifiers, and the risk is amplified because the system uses that data for persistent model-ranking and A/B evaluation.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · nim_ensemble/benchmark.py (reported line 126)May include surrounding context.

python
with open(os.path.join('/uploads', filename)) as f:
    return f.read()
```""",
     "VULNERABLE", ["VULNERABLE", "UNSAFE"]),  # ../../../etc/passwd

    ("code",
     """Does this code have a race condition? Answer YES or NO.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill searches unrelated local auth-profile files for GitHub OAuth tokens, then uses any discovered ghu_* token to mint a fresh Copilot session token and stores it locally. This exceeds the expected scope of a voting engine and effectively repurposes credentials from other agent contexts, creating unauthorized credential access and cross-context secret abuse.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The README states that every scale() call automatically logs votes, runs a shadow challenger, and incorporates user feedback, but it does not clearly warn users that deployment interactions and feedback-derived data are being stored and used for ongoing model selection. In a skill intended for code review, moderation, email triage, and agent self-evaluation, this can lead to unintentional transmission or retention of sensitive prompts, outputs, and user reaction metadata, creating privacy, compliance, and data governance risk.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding

The skill documentation describes capabilities involving environment variables, local file access, network calls, and CLI execution, but it does not declare any tool scope or permissions. In an agent environment, this can lead to over-broad execution authority and makes it difficult for users or policy layers to understand what the skill may access or exfiltrate.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

Broad, unspecific invocation language increases the chance that an agent invokes the skill in inappropriate contexts, including with sensitive prompts or data the user did not intend to send to external providers. In a skill that performs networked model calls and optional credential-backed integrations, vague triggering materially raises misuse and data-handling risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

This file sends user questions and optional system/context content to external model backends via call_model() without any user-facing notice or consent mechanism in the API surface. In this skill's context, prompts may include proprietary code, security findings, or internal documents, so undisclosed transmission to remote providers materially increases privacy and confidentiality risk.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The scale() docstring describes a bounded k-model query interface, but the implementation may perform extra challenger calls and feedback logging. This mismatch is security-relevant because integrators may route sensitive material through the function under false assumptions about how many third parties receive the data and whether it is retained.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The core scale() API performs hidden side effects unrelated to answering the user's request: it updates ELO state, issues an additional shadow model request, and logs question/vote data. This creates undisclosed data processing and external transmission that can expose sensitive prompts, code, or context and violates least surprise for callers expecting pure inference.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The shadow challenger performs an additional model API call outside the user's requested panel, silently sending the same question/context to another backend. This increases unauthorized data sharing surface and cost, and it is especially risky when the content includes sensitive code, incidents, or compliance materials that users did not intend to disclose beyond the selected models.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The function logs the full question, final answer, and votes for later feedback resolution without disclosure or visible minimization. Because the question may contain source code, secrets, internal emails, or other sensitive material, this creates a retention/privacy risk and a secondary compromise surface if logs are accessed or mishandled.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The CLI's smart command can emit detailed votes, reasoning, and model outputs in both JSON and verbose modes without any masking, warning, or opt-in guard for sensitive content. If users submit secrets, proprietary prompts, or regulated data, those details may be echoed to terminals, logs, shell history pipelines, or downstream tooling, causing unintended disclosure.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The bench command invokes remote models on arbitrary user questions and prints the first line of each raw model response directly to stdout. This can leak sensitive prompts or returned content into console logs, CI output, transcripts, or monitoring systems, especially because benchmarking is likely to be automated and repeated across many models.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
nim_ensemble/benchmark.py:143

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
nim_ensemble/cascade.py:55

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
tests/test_core.py:221