Back to skill

Security audit

loopai-app-publish

Security checks for vulnerabilities and agentic risk

Overview

This App Hub publishing skill is mostly coherent, but it handles workspace credentials unsafely and can send them to an arbitrary API host.

Review this skill before installing. Use it only in an environment where the App Hub token is narrowly scoped and logs are controlled. Avoid --verbose until token redaction is fixed, do not use custom --api-url values unless they are trusted HTTPS App Hub origins, and treat app_url validation as an outbound network request from your agent environment.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/create_app.py:434
Finding

Workspace credential can be transmitted to an arbitrary API origin

Content
View full analysis

Vulnerability Details

File Location: scripts/create_app.py:65-78, scripts/create_app.py:134-161, scripts/create_app.py:207-230, scripts/create_app.py:285-313, and scripts/create_app.py:434-438
Vulnerability Type: User-controlled credential destination
Risk Level: Critical

Vulnerable Code

python
def read_token(token_path: str = ".openclaw/workspace/token") -> str:
    """Read the API token from file."""
    try:
        # Try relative to current workspace
        token_file = Path(token_path)
        if token_file.exists():
            return token_file.read_text().strip()

        # Try absolute path
        abs_token_file = Path("/home/admin/.openclaw/workspace/token")
        if abs_token_file.exists():
            return abs_token_file.read_text().strip()
python
parser.add_argument(
    '--api-url',
    default='https://api.loopspace.xyz',
    help='App Hub API base URL (default: https://api.loopspace.xyz)'
)
python
endpoint = f"{api_base_url.rstrip('/')}/api/app_hub/create"

headers = {
    'Token': token,
    'Content-Type': 'application/json',
    'Accept': 'application/json'
}

response = requests.post(
    endpoint,
    json=app_data,
    headers=headers,
    timeout=30
)

The detail and update operations construct their endpoints from the same user-controlled api_base_url and attach the same token.

Technical Analysis

The command-line --api-url argument accepts an unrestricted base URL. The script reads an authentication token from a workspace file and attaches it to create, detail, and update requests using the Token header.

There is no trusted-host allowlist, HTTPS enforcement, origin comparison, port restriction, or redirect control. Consequently, a caller can replace the intended App Hub API origin with an attacker-controlled server. The option also accepts plaintext HTTP URLs, allowing credentials to ...[truncated 1307 chars]

Remediation
View remediation

Remediation Suggestions

  • Remove --api-url if alternate API deployments are not a required feature.
  • Otherwise, enforce an explicit allowlist of exact HTTPS origins before reading the token.
  • Parse the URL and validate the normalized scheme, hostname, port, username, and password fields.
  • Reject HTTP, IP-literal hosts, embedded credentials, unexpected ports, and hostname suffix tricks.
  • Configure requests not to follow redirects for credential-bearing calls, or verify that every redirect remains on the exact trusted origin.
  • Load a narrowly scoped App Hub token only after the destination has passed validation.
  • Prefer separate credentials for separate approved environments rather than sending one workspace token to configurable hosts.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/create_app.py:151
Finding

Verbose logging exposes the complete authentication token

Content
View full analysis

Vulnerability Details

File Location: scripts/create_app.py:151-156, scripts/create_app.py:222-227, and scripts/create_app.py:304-309
Vulnerability Type: Sensitive information exposure through logs
Risk Level: High

Vulnerable Code

python
headers = {
    'Token': token,
    'Content-Type': 'application/json',
    'Accept': 'application/json'
}

if verbose:
    print(f"[DEBUG] POST {endpoint}", file=sys.stderr)
    print(f"[DEBUG] Headers: {headers}", file=sys.stderr)
    print(f"[DEBUG] Payload: {json.dumps(app_data, indent=2, ensure_ascii=False)}", file=sys.stderr)

The detail and update operations contain equivalent statements:

python
if verbose:
    print(f"[DEBUG] GET {endpoint}", file=sys.stderr)
    print(f"[DEBUG] Headers: {headers}", file=sys.stderr)
python
if verbose:
    print(f"[DEBUG] POST {endpoint}", file=sys.stderr)
    print(f"[DEBUG] Headers: {headers}", file=sys.stderr)
    print(f"[DEBUG] Payload: {json.dumps(payload, indent=2, ensure_ascii=False)}", file=sys.stderr)

Technical Analysis

Each headers dictionary contains the complete value of the Token authentication header. When --verbose is enabled, the dictionary is printed without redaction to standard error.

Standard error is commonly retained in Agent transcripts, CI logs, shell histories captured by automation, centralized logging platforms, and diagnostic reports. Debug output therefore turns a transient credential into persistent plaintext data available to everyone with access to those records.

Logging the token is unnecessary for troubleshooting request routing or response status and violates secret-handling best practices.

Attack Path

  1. An operation is invoked with --verbose or -v.
  2. The script reads the App Hub token and places it in the request headers.
  3. Before issuing the request, it prints the complete headers dictionary to standard erro ...[truncated 517 chars]
Remediation
View remediation

Remediation Suggestions

  • Never print authentication dictionaries directly.
  • Build a redacted copy for diagnostics, for example by replacing the token with "[REDACTED]".
  • Centralize request logging in a helper that always removes authorization, token, cookie, and API-key headers.
  • Review payload logging as well, because author and application metadata may contain sensitive information.
  • Add automated tests asserting that known test-token values never appear in verbose output.
  • Rotate any real credential that may already have appeared in retained logs.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/create_app.py:17
Finding

Application URL validation permits unrestricted server-side request forgery

Content
View full analysis

Vulnerability Details

File Location: scripts/create_app.py:17-52 and scripts/create_app.py:99-112
Vulnerability Type: Server-side request forgery and internal network probing
Risk Level: High

Vulnerable Code

python
def check_url_accessible(url: str, timeout: int = 10, verbose: bool = False) -> tuple:
    """
    Use curl to verify that a URL is accessible.

    Returns:
        (is_accessible: bool, status_code_or_error: str)
    """
    try:
        result = subprocess.run(
            [
                'curl', '-s', '-o', '/dev/null',
                '-w', '%{http_code}',
                '-L',  # follow redirects
                '--max-time', str(timeout),
                url
            ],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            timeout=timeout + 5
        )
python
if 'app_url' in data and data['app_url']:
    if not data['app_url'].startswith(('http://', 'https://')):
        errors.append("'app_url' must start with http:// or https://")
    else:
        # Use curl to verify the URL is actually accessible
        accessible, detail = check_url_accessible(data['app_url'])
        if not accessible:
            errors.append(
                f"app_url '{data['app_url']}' is not accessible ({detail}). "
                f"Please check the URL and ensure it is reachable."
            )

Technical Analysis

app_url is user-controlled and is fetched from the host running the Skill. Validation only checks whether the string begins with http:// or https://.

No controls block loopback, private, link-local, reserved, multicast, or cloud metadata addresses. The -L option instructs curl to follow redirects, so even a URL that initially resolves to a public host can redirect to an internal destination. There is also no validation after DNS resolution, leaving the implementation exposed to DNS re ...[truncated 1430 chars]

Remediation
View remediation

Remediation Suggestions

  • Permit HTTPS only unless HTTP validation is explicitly required.
  • Resolve the hostname before connecting and reject loopback, private, link-local, multicast, unspecified, reserved, and metadata address ranges for both IPv4 and IPv6.
  • Validate every redirect destination with the same policy or disable redirects entirely.
  • Protect against DNS rebinding by binding validation and connection to approved resolved addresses.
  • Reject URLs containing embedded credentials, ambiguous host encodings, or unexpected ports.
  • Prefer performing reachability checks in an isolated external validation service with no access to internal networks.
  • Apply strict outbound firewall rules as defense in depth.

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/create_app.py:564
Finding

Validation-only mode unnecessarily reads a sensitive workspace credential

Content
View full analysis

Vulnerability Details

File Location: scripts/create_app.py:564-568 and scripts/create_app.py:654-665
Vulnerability Type: Violation of least privilege during non-authenticated validation
Risk Level: Medium

Vulnerable Code

python
args = parser.parse_args()

try:
    # Load token
    token = read_token(args.token_file)
    if args.verbose:
        print(f"[DEBUG] Token loaded (length: {len(token)})", file=sys.stderr)
python
# Validate only mode
if args.validate_only:
    errors = validate_app_data(app_data)
    if errors:
        print("Validation failed:", file=sys.stderr)
        for error in errors:
            print(f"  ✗ {error}", file=sys.stderr)
        sys.exit(1)
    else:
        print("✓ Validation passed - app data is valid", file=sys.stderr)
        print(json.dumps(app_data, indent=2, ensure_ascii=False))
        sys.exit(0)

Technical Analysis

The token is loaded immediately after parsing arguments and before operation dispatch. Therefore, --validate-only reads a sensitive credential even though this mode exits without making an authenticated App Hub request.

This unnecessarily broadens the period during which the secret exists in process memory and forces a validation operation to have filesystem access to the credential. It also means validation fails if the token is unavailable, despite authentication not being needed for the documented task.

In addition, validate_app_data performs the external URL request described in the SSRF finding, so the mode is not purely offline validation.

Attack Path

  1. A caller invokes the documented --validate-only operation.
  2. Before checking the selected mode, the script accesses the token file or the hardcoded fallback token path.
  3. The credential is loaded into process memory despite never being used for authentication.
  4. Any concurrent process compromise, instrumentation, crash collec ...[truncated 729 chars]
Remediation
View remediation

Remediation Suggestions

  • Determine the requested operation before loading credentials.
  • Run local argument and schema validation without reading any token.
  • Load the token immediately before the authenticated create, detail, or update request and discard references as soon as practical.
  • Separate local format validation from optional public URL reachability validation.
  • Allow validation-only mode to run in a sandbox with no credential-file access and, for offline validation, no network access.
  • Remove the hardcoded fallback token path unless it is operationally essential and explicitly documented.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (29)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared purpose focuses on publishing a new app listing, but the documented behavior also includes reading a local token, retrieving existing app details, and updating existing records. This mismatch undermines informed consent and review, and in practice can enable unauthorized modification or data access if an agent is permitted to run the skill under a narrower expected trust model.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 40)May include surrounding context.

md
python scripts/create_app.py --app-name "应用名称" --app-url "https://example.com" --description "应用描述" --author-name "作者名"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 64)May include surrounding context.

md
python scripts/create_app.py --app-name "应用名称" --app-url "https://example.com" --description "应用描述" --author-name "作者名"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 71)May include surrounding context.

md
python scripts/create_app.py --app-name "应用名称" --app-url "https://example.com" --description "应用描述" --author-name "作者名"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 93)May include surrounding context.

md
python scripts/create_app.py --app-name "应用名称" --app-url "https://example.com" --description "应用描述" --author-name "作者名"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 101)May include surrounding context.

md
python scripts/create_app.py --app-name "应用名称" --app-url "https://example.com" --description "应用描述" --author-name "作者名"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 130)May include surrounding context.

md
python scripts/create_app.py --app-name "应用名称" --app-url "https://example.com" --description "应用描述" --author-name "作者名"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 157)May include surrounding context.

md
python scripts/create_app.py --app-name "应用名称" --app-url "https://example.com" --description "应用描述" --author-name "作者名"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 164)May include surrounding context.

md
python scripts/create_app.py --app-name "应用名称" --app-url "https://example.com" --description "应用描述" --author-name "作者名"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 170)May include surrounding context.

md
python scripts/create_app.py --app-name "应用名称" --app-url "https://example.com" --description "应用描述" --author-name "作者名"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 176)May include surrounding context.

md
python scripts/create_app.py --app-name "应用名称" --app-url "https://example.com" --description "应用描述" --author-name "作者名"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 189)May include surrounding context.

md
python scripts/create_app.py --app-name "应用名称" --app-url "https://example.com" --description "应用描述" --author-name "作者名"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 221)May include surrounding context.

md
python scripts/create_app.py --app-name "应用名称" --app-url "https://example.com" --description "应用描述" --author-name "作者名"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 317)May include surrounding context.

md
python scripts/create_app.py --app-name "应用名称" --app-url "https://example.com" --description "应用描述" --author-name "作者名"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 407)May include surrounding context.

md
python scripts/create_app.py --app-name "应用名称" --app-url "https://example.com" --description "应用描述" --author-name "作者名"

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 414)May include surrounding context.

md
python scripts/create_app.py --app-name "应用名称" --app-url "https://example.com" --description "应用描述" --author-name "作者名"

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

Verbose logging prints the full request headers, including the API Token, to stderr during app creation. Any user, log collector, CI system, or adjacent process with access to console output could capture the credential and reuse it to create, read, or modify App Hub data.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

This is the same underlying issue as the SDI-2 finding at the same location: verbose debug output exposes the API authentication token in logs. In the context of a skill that interacts with a remote publishing API, leaked bearer-like credentials can be replayed for unauthorized access.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The detail retrieval path also logs the full headers containing the API Token when verbose mode is enabled. This unnecessarily exposes a reusable credential during a read operation, creating a straightforward secret leakage path through logs.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

This duplicate pattern correctly identifies that token-bearing headers are exposed during app detail fetches. Even though the operation is read-oriented, the leaked token is still a general credential and could be abused wherever accepted by the backend.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The update path logs request headers with the authentication token, leaking credentials during privileged modification operations. Because this skill can update existing App Hub entries, token compromise could enable unauthorized tampering with published content.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

This duplicate pattern correctly flags secret exposure during update requests. Since the script supports modification of existing entries, disclosure of the token materially increases the risk of unauthorized content changes or further API abuse.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding

The skill advertises and documents capabilities that require shell execution, network access, and likely local file reads, but it does not declare any tool scope or permission boundaries. This creates a confused-deputy risk where an agent may invoke broader capabilities than a reviewer or user expects, especially because the workflow includes curl-based URL validation and a local token read for authentication.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The manifest description is written entirely in Chinese and the rest of the skill instructions are also Chinese-only, with no indication that the user can opt into another language or that the skill is limited to a China-specific audience. Under the policy, a language/locale constraint should either be optional for the user or clearly justified as region-specific.

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/create_app.py (reported line 25)May include surrounding context.

python
(is_accessible: bool, status_code_or_error: str)
    """
    try:
        result = subprocess.run(
            [
                'curl', '-s', '-o', '/dev/null',
                '-w', '%{http_code}',