Back to skill

Security audit

Privacy Shield

Security checks for vulnerabilities and agentic risk

Overview

This privacy-marking skill is understandable, but its core permission check can mistakenly treat some sensitive paths as public, so it should be reviewed before use.

Install only if you are comfortable treating this as an advisory helper, not a reliable privacy gate. Before using it to approve sharing or exporting data, fix or compensate for the path-matching bug with normalized, component-aware path checks, and avoid running it with elevated privileges or attacker-controlled WORKSPACE values.

Vulnerability Patterns
  • 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
  • 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)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/registry.py:63
Finding

Unsafe Path-Prefix Matching Can Misclassify Sensitive Resources as Public

Content
View full analysis

Vulnerability Details

File Location: scripts/registry.py, lines 63–68
Vulnerability Type: Improper resource path matching
Risk Level: Medium

Vulnerable Code

python
for r in registry["resources"]:
    path = r["path"]
    # Exact prefix match
    if resource.startswith(path) or path.startswith(resource):
        priority = LEVEL_PRIORITY.get(r["level"], 0)
        if priority > match_priority:
            matched = r
            match_priority = priority

Technical Analysis

The privacy registry compares resource paths as raw strings rather than normalized filesystem paths. It also treats either string being a prefix of the other as a match:

  • resource.startswith(path) allows sibling names sharing a textual prefix to inherit a privacy classification. For example, a public mark on exports/public also matches exports/public-backup/secrets.json.
  • path.startswith(resource) allows a parent resource query to inherit the classification of a registered child.
  • The comparison does not enforce path-component boundaries, distinguish files from directories, normalize . or .., resolve relative and absolute aliases, or account for symlinks.

After a resource is incorrectly classified as public, check_action_allowed() at lines 166–172 authorizes every supported action:

python
def check_action_allowed(level, action):
    """Check whether an operation is allowed."""
    rules = {
        "owner_only": ["internal_use"],
        "private": ["internal_use"],
        "no_export": ["internal_use", "display"],
        "public": ["share", "export", "display", "internal_use"]
    }
    return action in rules.get(level, [])

This behavior violates the documented conservative policy for unmarked resources. The flaw affects callers that rely on this CLI's result as an authorization decision. The CLI itself is advisory and does not directly perform or intercept expo ...[truncated 1344 chars]

Remediation
View remediation

Remediation Suggestions

  1. Normalize configured and requested paths before comparison. For local resources, use Path.resolve(strict=False) where resolving against the filesystem is appropriate.
  2. Replace raw string-prefix checks with path-component-aware containment checks, such as Path.is_relative_to() on supported Python versions.
  3. Remove path.startswith(resource). A parent query must not inherit a child entry's classification.
  4. Use exact matching for entries representing files. Apply descendant matching only to entries explicitly identified as directories.
  5. Define a clear policy for relative paths, absolute paths, nonexistent paths, case sensitivity, and symlinks. Reject ambiguous paths when they cannot be safely canonicalized.
  6. Default to unmarked and deny sharing or export whenever matching is ambiguous.
  7. Add regression tests covering:
    • Sibling names with shared textual prefixes, such as public and public-backup
    • Parent queries when only a child is marked
    • . and .. path segments
    • Relative and absolute aliases
    • Trailing separators
    • Symlink traversal
    • File entries versus directory entries
Vulnerability Patterns
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (3)

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This code file contains natural-language strings for the tool description, command help, status messages, and warnings entirely in Chinese. Because the skill does not offer any language selection or explain that it is intended only for a Chinese-speaking context, it imposes a locale choice on users, which matches the language-policy violation criteria.

Content

No source excerpt is available for this finding.

Tainted flow: 'REGISTRY_PATH' from os.environ.get (line 14, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
88% confidence
Finding

The registry file path is derived from the WORKSPACE environment variable and then written without validation or confinement. If an attacker can influence the process environment, they can redirect writes to arbitrary filesystem locations, causing file overwrite, corruption of unrelated files, or misuse of privileged execution contexts.

Content

Scanner excerpt · scripts/registry.py (reported line 39)May include surrounding context.

python
"""保存隐私注册表"""
    REGISTRY_PATH.parent.mkdir(parents=True, exist_ok=True)
    registry["updated_at"] = datetime.now(timezone(timedelta(hours=8))).isoformat()
    with open(REGISTRY_PATH, "w", encoding="utf-8") as f:
        json.dump(registry, f, ensure_ascii=False, indent=2)

Tainted flow: 'AUDIT_PATH' from os.environ.get (line 15, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
88% confidence
Finding

The audit log path is also derived from the WORKSPACE environment variable and opened in append mode without path validation. A manipulated environment can redirect audit output to arbitrary files, enabling log forgery, unintended file growth, or corruption of sensitive files if the tool runs with elevated permissions.

Content

Scanner excerpt · scripts/registry.py (reported line 54)May include surrounding context.

python
"result": result,
        "detail": detail
    }
    with open(AUDIT_PATH, "a", encoding="utf-8") as f:
        f.write(json.dumps(entry, ensure_ascii=False) + "\n")