Back to skill

Security audit

huawei-cloud-skill-tester

Security checks across malware telemetry and agentic risk

Overview

This Huawei Cloud testing skill has a legitimate testing purpose, but it can run generated shell/Python commands and cloud write operations with credentials without the user confirmation its docs promise.

Install only after review, and run only in an isolated test account with least-privilege temporary Huawei Cloud credentials. Do not use production AK/SK or valuable billing resources. Treat tested skills as code that may be executed by this framework, and inspect generated phase files before phase 4 or phase 6.

SkillSpector

By NVIDIA
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
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (25)

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill declares significant capabilities—environment access, file read/write, and shell execution—but does not declare permissions or bound them in the metadata. That creates a transparency and review failure: users may invoke a skill that can access credentials, modify local files, and run commands without an explicit permission contract.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The documented purpose understates risky behavior while the skill instructions include credential harvesting, filesystem mutation, skill uninstall/reinstall flows, report deletion, and real-environment destructive operations. This mismatch is dangerous because users may consent to 'testing' without realizing the skill can read secrets, alter installed skills, delete artifacts, and operate on live cloud resources.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The script header promises that write operations require per-item user confirmation, but the implementation later auto-confirms and executes them with active cloud credentials. This creates a dangerous trust mismatch: operators may believe the phase is safe/read-only while the code can create, modify, or delete Huawei Cloud resources non-interactively.

Context-Inappropriate Capability

Medium
Confidence
87% confidence
Finding
The template includes a destructive test flow for deleting BSS vouchers, which are billing-related assets and not ordinary ephemeral infrastructure. In a generic end-to-end testing framework, embedding such deletion steps can normalize high-risk operations and lead to accidental execution against real financial/account resources, especially if safeguards, scoping, or mock/test-environment constraints are absent.

Vague Triggers

Medium
Confidence
88% confidence
Finding
The trigger list includes broad phrases such as 'verification', 'e2e', '跑测试', and 'skill test', which can cause accidental invocation in unrelated conversations. In this skill's context, unintended activation is more dangerous because the skill is designed to inspect local skills, access credentials, write files, and potentially execute real cloud operations.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The script reads the secret key using plain read -r, so the credential is echoed visibly on the terminal and may be exposed to shoulder-surfing, screen recording, terminal logging, or shared session capture. Because this skill is explicitly designed to handle Huawei Cloud credentials and run real-environment tests, exposing SK material in interactive flows is materially risky.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This phase automatically installs, uninstalls, and reinstalls skills, including a recursive `rm -rf` of the target directory, without any interactive confirmation, dry-run mode, or prominent safety guard. In a testing framework this behavior is expected, but it is still risky because user-controlled `skill_name` and environment-controlled installation paths can cause unintended deletion or modification of local skill data under `~/.hermes/skills`, especially when the script is run against untrusted or mistaken targets.

Missing User Warnings

High
Confidence
99% confidence
Finding
For test cases marked as writes, the code sets user_confirmed=True unconditionally and proceeds after printing that it will execute non-interactively with AK/SK credentials. In a testing framework that consumes generated test cases, this can directly trigger destructive cloud actions without informed consent from the operator.

Missing User Warnings

Medium
Confidence
98% confidence
Finding
CLI command text is taken from test-case data and passed directly to bash -c. Because the command source is effectively untrusted pipeline data, an attacker can inject arbitrary shell commands that run with the user's environment and cloud credentials.

Missing User Warnings

High
Confidence
99% confidence
Finding
The script executes Python, shell, and script commands derived from phase-1 JSON metadata without any trust boundary enforcement or interactive confirmation. Because phase-1 command data is treated as executable input, a malicious or compromised skill can inject arbitrary code or shell commands that run with the user's environment and cloud credentials.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
snippet_lines.append('from huaweicloudsdk%s.%s import %s, %s' % (svc, sdk_ver, client_cls, request_class))
    snippet_lines.append('')
    snippet_lines.append('ak, sk = "", ""')
    snippet_lines.append('for k, v in os.environ.items():')
    snippet_lines.append('    u = k.upper()')
    snippet_lines.append("    if not (u.startswith('HUAWEI') or u.startswith('HW') or u.startswith('HWC')): continue")
    snippet_lines.append("    if 'ACCESS_KEY' in u or u.endswith('_AK') or u == 'AK': ak = v or ak")
Confidence
96% confidence
Finding
for k, v in os.environ.items()

Env Variable Harvesting

High
Category
Data Exfiltration
Content
client_class = getattr(mod, client_cls_name)
                        from huaweicloudsdkcore.auth.credentials import BasicCredentials, GlobalCredentials
                        ak, sk = '', ''
                        for k, v in os.environ.items():
                            u = k.upper()
                            if not (u.startswith('HUAWEI') or u.startswith('HW') or u.startswith('HWC')): continue
                            if 'ACCESS_KEY' in u or u.endswith('_AK') or u == 'AK': ak = v or ak
Confidence
83% confidence
Finding
for k, v in os.environ.items()

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
Iterate over each test case:
  if is_write == false:
    Auto-execute → Record pass/fail
  if is_write == true:
    Show command and expected → Wait for user y/N confirmation
    if confirmed:
Confidence
86% confidence
Finding
Auto-execute

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
- Extend with additional verification steps
  6b: Lifecycle execution
      - Execute each step against real Huawei Cloud environment
      - Read-only steps auto-execute; write steps prompt user
  6c: State consistency verification
      - After each write operation, verify via read operation
      - Confirm resource state matches expected
Confidence
86% confidence
Finding
auto-execute

Unvalidated Output Injection

High
Category
Output Handling
Content
# Remove leading/trailing pipes and whitespace that might leak from table extraction
            cmd_text = cmd_text.strip().lstrip('|').strip()
            if cmd_text and len(cmd_text) > 5:
                r = subprocess.run(
                    ['bash', '-c', cmd_text],
                    capture_output=True, text=True, timeout=int(os.environ.get('TIMEOUT_CLI', '30')),
                    env=os.environ
Confidence
100% confidence
Finding
subprocess.run( ['bash', '-c', cmd_text], capture_output

Unvalidated Output Injection

High
Category
Output Handling
Content
method_name = tc.get('method_name', '')
            # If command starts with python3 -c, run as bash (it's a one-liner, not a snippet)
            if cmd_text.startswith('python3 -c ') or cmd_text.startswith('python3  -c '):
                r = subprocess.run(
                    ['bash', '-c', cmd_text],
                    capture_output=True, text=True, timeout=int(os.environ.get('TIMEOUT_CLI', '30')),
                    env=os.environ
Confidence
99% confidence
Finding
subprocess.run( ['bash', '-c', cmd_text], capture_output

Unvalidated Output Injection

High
Category
Output Handling
Content
try:
                    sdk_tmp.write(cmd_text)
                    sdk_tmp.close()
                    r = subprocess.run(
                        ['python3', sdk_tmp.name],
                        capture_output=True, text=True, timeout=60,
                        env=os.environ
Confidence
99% confidence
Finding
subprocess.run( ['python3', sdk_tmp.name], capture_output

Unvalidated Output Injection

High
Category
Output Handling
Content
script_args = ' '.join(script_part.split()[1:]) if len(script_part.split()) > 1 else ''
                if os.path.isfile(script_path):
                    full_cmd = f'python3 {script_path} {script_args}'.strip()
                    r = subprocess.run(['bash', '-c', full_cmd], capture_output=True, text=True, timeout=60, env=os.environ)
                    output = (r.stdout[:1000] + '\n' + r.stderr[:500]).strip()
                    status = 'pass' if r.returncode == 0 else 'fail'
                else:
Confidence
96% confidence
Finding
subprocess.run(['bash', '-c', full_cmd], capture_output

Unvalidated Output Injection

High
Category
Output Handling
Content
if not os.path.isfile(script_path):
                    script_path = os.path.join(skill_root, 'scripts', os.path.basename(script_part))
                if os.path.isfile(script_path):
                    r = subprocess.run(['bash', script_path, skill_root], capture_output=True, text=True, timeout=60)
                    output = (r.stdout[:1000] + '\n' + r.stderr[:500]).strip()
                    status = 'pass' if r.returncode == 0 else 'fail'
                else:
Confidence
72% confidence
Finding
subprocess.run(['bash', script_path, skill_root], capture_output

Unvalidated Output Injection

High
Category
Output Handling
Content
# Unknown executor: try running command text as shell command
            cmd_text = tc.get('command', '')
            if cmd_text.strip() and len(cmd_text) > 10:
                r = subprocess.run(
                    ['bash', '-c', cmd_text],
                    capture_output=True, text=True, timeout=30,
                    env={**__import__('os').environ}
Confidence
99% confidence
Finding
subprocess.run( ['bash', '-c', cmd_text], capture_output

Unvalidated Output Injection

High
Category
Output Handling
Content
try:
                sdk_tmp.write(cmd_text)
                sdk_tmp.close()
                r = subprocess.run(
                    ['python3', sdk_tmp.name],
                    capture_output=True, text=True, timeout=60,
                    env=os.environ
Confidence
99% confidence
Finding
subprocess.run( ['python3', sdk_tmp.name], capture_output

Unvalidated Output Injection

High
Category
Output Handling
Content
except OSError:
                    pass
        elif executor == 'cli' and cmd_text:
            r = subprocess.run(
                ['bash', '-c', cmd_text],
                capture_output=True, text=True, timeout=30,
                env=os.environ
Confidence
100% confidence
Finding
subprocess.run( ['bash', '-c', cmd_text], capture_output

Unvalidated Output Injection

High
Category
Output Handling
Content
script_args = ' '.join(script_part.split()[1:]) if len(script_part.split()) > 1 else ''
                if os.path.isfile(script_path):
                    full_cmd = f'python3 {script_path} {script_args}'.strip()
                    r = subprocess.run(['bash', '-c', full_cmd], capture_output=True, text=True, timeout=60, env=os.environ)
                    step['output'] = (r.stdout[:500] + r.stderr[:200]).strip()
                    step['status'] = 'pass' if r.returncode == 0 else 'fail'
                else:
Confidence
98% confidence
Finding
subprocess.run(['bash', '-c', full_cmd], capture_output

Tool Parameter Abuse

High
Category
Tool Misuse
Content
local skill_dir="$1"
  [ -z "$skill_dir" ] && { fail "cleanup_phase_files: skill_dir 为空"; return 1; }
  info "🧹 --fresh: 清理 ${skill_dir}/phase-*.json"
  rm -f "${skill_dir}"/phase-*.json
  info "✅ 已清理"
}
Confidence
92% confidence
Finding
rm -f "${skill_dir}"/

Tool Parameter Abuse

High
Category
Tool Misuse
Content
local phase_files; phase_files=$(ls "$sp"/phase-*.json 2>/dev/null)
    if [ -n "$phase_files" ]; then
      step "删除 ${sn} 中的阶段文件..."
      rm -f "$sp"/phase-*.json
      pass "  phase-*.json 已删除"
    fi
  done
Confidence
90% confidence
Finding
rm -f "$sp"/

VirusTotal

VirusTotal findings are pending for this skill version.

View on VirusTotal

Static analysis

No suspicious patterns detected.