Back to skill

Security audit

huawei-cloud-cdn-export-obs

Security checks for vulnerabilities and agentic risk

Overview

The skill performs its advertised export job, but unsafe dependency installation and incomplete upload verification need review.

Avoid the dependency helper until certificate verification is restored; install reviewed, pinned dependencies in an isolated environment. Use narrowly scoped cloud credentials and a trusted destination bucket, and approve overwrites explicitly. Independently verify uploaded file sizes because the advertised check can be skipped. All bundled files were reviewed, but live cloud behavior and downloaded code remain unverified.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (1)

T08 · Insecure Dependencies

Error
Location
scripts/ensure_env.py
Finding

Dependency installation disables TLS certificate verification

Content
View full analysis

Impact

Arbitrary code execution with the helper's privileges, potentially exposing cloud credentials accessible to that account.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (23)

Description-Behavior Mismatch

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The supplied script implements the CDN query portion, but not the declared export-to-OBS workflow. Its only result destination is stdout, and its default format is a plain-text table rather than a JSON file. The missing file export, upload, overwrite protection, and verification constitute a materially narrower primary workflow, not merely implementation differences. No undeclared malicious behavior is evident. The claimed unit-test coverage cannot be verified from this code chunk.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

In verify_object(), the SDK fallback calls sdk_object_exists(), which discards the head_object response metadata, then returns size=null and size_match=null. The CLI path likewise returns without error when no size is parsed. Thus verification can complete without the size comparison explicitly promised by the description. Otherwise, the module's OBS access and upload behavior align with the declared purpose. CDN querying, serialization, and tests cannot be assessed from this upload-only chunk; their absence here is not treated as a mismatch.

Content

No source excerpt is available for this finding.

Skill declares no tool scope ('permissions' or 'allowed-tools') but code capabilities were detected: env, file_write, shell.

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Live verification uploads CDN inventory and explicitly overwrites a cloud object without a user-facing warning.

Medium
Category
Output Handling
Confidence
90% confidence
Finding

The live-verification workflow instructs the agent to upload CDN domain data to OBS and then repeat the upload with --overwrite. Although overwrite refusal is noted, there is no warning that these commands transmit account inventory to cloud storage and can replace an existing object at the selected key.

Content

No source excerpt is available for this finding.

Cloud Storage Exfiltration

Medium
Category
Data Exfiltration
Confidence
55% confidence
Finding

Data is uploaded to cloud storage (S3 / GCS / Azure Blob). This may be a legitimate backup or exfiltration to an external bucket. Manual review is recommended.

Content

Scanner excerpt · scripts/cdn_export_obs.py (reported line 11)May include surrounding context.

python
list_cdn.list_domains(...)          # hcloud CDN ListDomains  (SDK fallback)
        → export_results.export(...)    # local json / csv / md (atomic write)
        → upload_obs.upload_file(...)    # hcloud OBS cp          (SDK fallback)
              → upload_obs.verify_object(...)   # hcloud OBS stat read-back

The CDN side is strictly read-only; the only write is the single OBS object

Cloud Storage Exfiltration

Medium
Category
Data Exfiltration
Confidence
55% confidence
Finding

Data is uploaded to cloud storage (S3 / GCS / Azure Blob). This may be a legitimate backup or exfiltration to an external bucket. Manual review is recommended.

Content

Scanner excerpt · tests/test_cdn_export_obs.py (reported line 345)May include surrounding context.

python
size = os.path.getsize(local)
            fake = FakeHcloud(self._handler(exists=False, verify_size=size))
            with mock.patch("subprocess.run", fake):
                result = upload_obs.upload_file(
                    "cn-north-4", local, "bk", "k", verify=True)
        self.assertFalse(result["existed_before"])
        self.assertEqual(result["uploaded_via"], "cli")

Cloud Storage Exfiltration

Medium
Category
Data Exfiltration
Confidence
55% confidence
Finding

Data is uploaded to cloud storage (S3 / GCS / Azure Blob). This may be a legitimate backup or exfiltration to an external bucket. Manual review is recommended.

Content

Scanner excerpt · tests/test_cdn_export_obs.py (reported line 360)May include surrounding context.

python
fake = FakeHcloud(self._handler(exists=True))
            with mock.patch("subprocess.run", fake):
                with self.assertRaises(upload_obs.OverwriteError):
                    upload_obs.upload_file("cn-north-4", local, "bk", "k")
        # no cp issued
        self.assertTrue(all(obs_subop(c) != "cp" for c in fake.obs_calls))

Cloud Storage Exfiltration

Medium
Category
Data Exfiltration
Confidence
55% confidence
Finding

Data is uploaded to cloud storage (S3 / GCS / Azure Blob). This may be a legitimate backup or exfiltration to an external bucket. Manual review is recommended.

Content

Scanner excerpt · tests/test_cdn_export_obs.py (reported line 372)May include surrounding context.

python
size = os.path.getsize(local)
            fake = FakeHcloud(self._handler(exists=True, verify_size=size))
            with mock.patch("subprocess.run", fake):
                result = upload_obs.upload_file(
                    "cn-north-4", local, "bk", "k", overwrite=True)
        self.assertTrue(result["existed_before"])
        self.assertTrue(result["overwritten"])

Cloud Storage Exfiltration

Medium
Category
Data Exfiltration
Confidence
55% confidence
Finding

Data is uploaded to cloud storage (S3 / GCS / Azure Blob). This may be a legitimate backup or exfiltration to an external bucket. Manual review is recommended.

Content

Scanner excerpt · tests/test_cdn_export_obs.py (reported line 395)May include surrounding context.

python
fake = FakeHcloud(handler)
            with mock.patch("subprocess.run", fake):
                with self.assertRaises(upload_obs.ObsError) as ctx:
                    upload_obs.upload_file("cn-north-4", local, "bk", "k")
        self.assertIn("大小", str(ctx.exception))

    def test_missing_local_file(self):

Cloud Storage Exfiltration

Medium
Category
Data Exfiltration
Confidence
55% confidence
Finding

Data is uploaded to cloud storage (S3 / GCS / Azure Blob). This may be a legitimate backup or exfiltration to an external bucket. Manual review is recommended.

Content

Scanner excerpt · tests/test_cdn_export_obs.py (reported line 412)May include surrounding context.

python
side_effect=[False, True]), \
                    mock.patch("upload_obs.sdk_put_object",
                               return_value={"etag": "x", "size": 2}) as put:
                result = upload_obs.upload_file("cn-north-4", local, "bk", "k")
        self.assertEqual(result["uploaded_via"], "sdk")
        put.assert_called_once()

Cloud Storage Exfiltration

Medium
Category
Data Exfiltration
Confidence
55% confidence
Finding

Data is uploaded to cloud storage (S3 / GCS / Azure Blob). This may be a legitimate backup or exfiltration to an external bucket. Manual review is recommended.

Content

Scanner excerpt · tests/test_cdn_export_obs.py (reported line 424)May include surrounding context.

python
fake = FakeHcloud(self._handler(exists=False, cp_ok=False))
            with mock.patch("subprocess.run", fake):
                with self.assertRaises(upload_obs.ApiError):
                    upload_obs.upload_file("cn-north-4", local, "bk", "k", verify=False)


# --------------------------------------------------------------------------

Cloud Storage Exfiltration

Medium
Category
Data Exfiltration
Confidence
55% confidence
Finding

Data is uploaded to cloud storage (S3 / GCS / Azure Blob). This may be a legitimate backup or exfiltration to an external bucket. Manual review is recommended.

Content

Scanner excerpt · scripts/cdn_export_obs.py (reported line 134)May include surrounding context.

python
return result

    key = args.key or os.path.basename(local_path)
    upload = upload_obs.upload_file(
        args.region, local_path, args.bucket, key,
        endpoint=args.endpoint, overwrite=args.overwrite,
        verify=not args.no_verify,

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
97% confidence
Finding

The subprocess call uses an argument list rather than a shell, so it is not itself shell injection. However, its callers install unpinned executable Python dependencies from third-party mirrors with --trusted-host, disabling normal certificate verification for those hosts; a network or mirror attacker could supply malicious packages that execute during installation or later import in the credential-bearing environment.

Content

Scanner excerpt · scripts/ensure_env.py (reported line 58)May include surrounding context.

python
def run_cmd(cmd, timeout=300):
    try:
        proc = subprocess.run(
            cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
            timeout=timeout, universal_newlines=True,
        )

Markdown exports impose Chinese-language headings without a language choice.

Medium
Category
Output Handling
Confidence
90% confidence
Finding

The fixed Chinese headings are applied to every Markdown export, regardless of the user's language; the optional summary is also hardcoded in Chinese. The file provides no language selection or documented regional justification for this constraint.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/list_cdn.py (reported line 235)May include surrounding context.

python
cmd.extend(extra)
    cmd.extend(argv)
    try:
        proc = subprocess.run(
            cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
            timeout=120, universal_newlines=True,
        )

Tainted flow: 'cmd' from os.environ.get (line 229, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/list_cdn.py (reported line 235)May include surrounding context.

python
cmd.extend(extra)
    cmd.extend(argv)
    try:
        proc = subprocess.run(
            cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
            timeout=120, universal_newlines=True,
        )

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/upload_obs.py (reported line 193)May include surrounding context.

python
cmd.extend(extra)
    cmd.extend(argv)
    try:
        proc = subprocess.run(
            cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
            timeout=300, universal_newlines=True,
        )

Tainted flow: 'cmd' from os.environ.get (line 187, credential/environment) → subprocess.run (code execution)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/upload_obs.py (reported line 193)May include surrounding context.

python
cmd.extend(extra)
    cmd.extend(argv)
    try:
        proc = subprocess.run(
            cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
            timeout=300, universal_newlines=True,
        )

Cloud Storage Exfiltration

Medium
Category
Data Exfiltration
Confidence
55% confidence
Finding

Data is uploaded to cloud storage (S3 / GCS / Azure Blob). This may be a legitimate backup or exfiltration to an external bucket. Manual review is recommended.

Content

Scanner excerpt · scripts/upload_obs.py (reported line 334)May include surrounding context.

python
with open(local_path, "rb") as handle:
            request = PutObjectRequest(
                stream=handle, bucket_name=bucket, object_key=key)
            response = client.put_object(request)
    except OSError as exc:
        raise ObsError("无法读取本地文件 %s:%s" % (local_path, exc))
    except Exception as exc:  # noqa: BLE001

Read-back verification does not compare object size on the SDK fallback path and silently skips comparison when CLI metadata lacks a parseable size.

Medium
Category
Excessive Agency
Confidence
99% confidence
Finding

With verification enabled, the SDK fallback checks only existence and returns no size comparison; the CLI path also accepts metadata with no parseable size. This weakens the advertised archive-integrity safeguard: a truncated or otherwise wrong-sized object can leave the operation successful without the requested verification being completed.

Content

No source excerpt is available for this finding.

Cloud Storage Exfiltration

Medium
Category
Data Exfiltration
Confidence
55% confidence
Finding

Data is uploaded to cloud storage (S3 / GCS / Azure Blob). This may be a legitimate backup or exfiltration to an external bucket. Manual review is recommended.

Content

Scanner excerpt · tests/test_cdn_export_obs.py (reported line 400)May include surrounding context.

python
def test_missing_local_file(self):
        with self.assertRaises(upload_obs.ObsError):
            upload_obs.upload_file("cn-north-4", "/no/such/file", "bk", "k")

    def test_sdk_fallback(self):
        with tempfile.TemporaryDirectory() as tmp:

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · tests/test_cdn_export_obs.py (reported line 424)May include surrounding context.

python
fake = FakeHcloud(self._handler(exists=False, cp_ok=False))
            with mock.patch("subprocess.run", fake):
                with self.assertRaises(upload_obs.ApiError):
                    upload_obs.upload_file("cn-north-4", local, "bk", "k", verify=False)


# --------------------------------------------------------------------------

Credential errors are surfaced in Chinese without a user language choice or documented locale justification.

Low
Category
Output Handling
Confidence
87% confidence
Finding

The guide specifies the fixed Chinese message 缺少 AK/SK 凭证 for credential failures, irrespective of the user's language. No language opt-in or locale-specific justification is provided for this user-facing output.

Content

No source excerpt is available for this finding.