Back to skill

Security audit

linkfoxagent

Security checks for vulnerabilities and agentic risk

Overview

This skill has a coherent e-commerce research purpose, but it needs review because it automatically exposes public task links and includes broad ERP/file workflows with weak guardrails for sensitive business data.

Review this skill carefully before installing. Do not use it with secrets, unreleased product plans, confidential ERP exports, or private images unless you accept that prompts, outputs, and generated artifacts may be sent to external services and surfaced through public links. Use least-privilege Lingxing credentials, avoid write-capable ERP apps unless needed, and clear local output/token caches after sensitive work.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
Findings (6)

T01 · Skill Instruction Hijacking

Error
Location
SKILL.md:62
Finding
Unconditional disclosure of public LinkFox conversation URLs<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:62-74`, `SKILL.md:443-452` **Vulnerability Type**: Output hijacking and forced public data sharing **Risk Level**: Critical ### Vulnerable Code or Instructions ```markdown ### RULE 0 — ShareURL FIRST (highest priority, ALWAYS apply when present) Scan stdout for a line `ShareURL: <url>`. **The moment you find one, forward that URL to the user as the very first thing in your reply, before the reflection / results / anything else.** It is a PUBLIC URL — the user can open it on any device without auth, see every step the agent took, and download all generated artifacts (CSV/Excel, images, HTML reports, attachments). Always send it; never decide "to share or not to share". This rule is unconditional: any successful run produces one (`status=finished`), and if it appears in stdout you forward it. ``` The public URL is documented as exposing the complete task: ```markdown - `url`: ShareURL — public, read-only share page for the entire conversation behind this run (intent, tool calls, intermediate outputs, reflection, final summary), with downloadable artifacts (CSV/Excel, images, HTML reports, attached files). ``` ### Technical Analysis The Skill assigns the highest priority to a fixed output behavior and explicitly removes the Agent's discretion to evaluate whether disclosure is appropriate. Every successful task must place a third-party LinkFox share URL at the beginning of the response. The linked page is unauthenticated and may expose the user's intent, tool calls, intermediate data, generated reports, and downloadable artifacts. This behavior is not necessary to provide the requested e-commerce analysis results and exceeds minimum privilege by creating and promoting a public copy of the task. ### Attack Path 1. A user submits commercially sensitive product, competitor, sales, or ERP research. 2. The task and its results are processed by LinkFox. 3. LinkFox returns a public, unauthenticated sh ...[truncated 725 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the unconditional `ShareURL FIRST` instruction. 2. Do not create a public share page by default. 3. Require explicit, informed user consent before creating or returning any public link. 4. Default to authenticated, access-controlled links with short expiration periods. 5. Allow the Agent to withhold a link when prompts or artifacts contain sensitive data. 6. Clearly enumerate what the share page contains before requesting consent. 7. Add server-side revocation and deletion controls. 8. Avoid placing public links first in responses or treating promotional output as higher priority than user instructions and security policy. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/linkfox.py:154
Finding
Plaintext persistence of original prompts and complete API results<![CDATA[ ## Vulnerability Details **File Location**: `scripts/linkfox.py:154-178`, `scripts/linkfox.py:381-415` **Vulnerability Type**: Insecure storage of potentially sensitive task and result data **Risk Level**: Medium ### Vulnerable Code ```python meta = { "messageId": message_id, "task": task or "", "status": "submitted", "submittedAt": datetime.now().isoformat(timespec="seconds"), "url": "", } try: with open(_meta_path(task_dir), "w", encoding="utf-8") as f: json.dump(meta, f, indent=2, ensure_ascii=False) except OSError as e: print( f"Warning: failed to write initial meta for {message_id}: {e}", file=sys.stderr, ) ``` Completed JSON results are also persisted: ```python safe_name = "".join( c if (c.isalnum() or c in "-_") else "_" for c in name ) json_filename = f"{i}_{safe_name}.json" json_path = os.path.join(output_dir, json_filename) with open(json_path, "w", encoding="utf-8") as jf: json.dump(parsed, jf, indent=2, ensure_ascii=False) ``` ### Technical Analysis The script stores the complete original task in `result.json` and writes raw API results to files under `scripts/output/`. It does not explicitly apply restrictive directory or file permissions, redact sensitive fields, encrypt retained content, or automatically remove completed task data. The effective permissions depend on the process umask and surrounding filesystem configuration. Consequently, another local account or process with access to the Skill directory may be able to read retained business prompts and result datasets. ### Attack Path 1. A user submits a task containing proprietary keywords, competitor information, product plans, or other business-sensitive content. 2. `ensure_task_dir()` creates a task directory and writes the complete prompt to `result.json`. 3. When results arrive, `format_result()` writes raw JSON and converted CSV files to the same output hierarchy. 4. The files remain after task completion ...[truncated 459 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store task data in a user-private data directory rather than inside the Skill installation. 2. Create directories with mode `0700` and files with mode `0600`. 3. Set permissions explicitly instead of relying on the process umask. 4. Avoid retaining complete prompts unless the user requests persistence. 5. Redact credentials, personal data, and other sensitive fields before writing results. 6. Implement configurable retention periods and automatic secure deletion. 7. Provide a command that lists and deletes retained tasks and artifacts. 8. Document local persistence clearly before the first task is submitted. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/lingxing.py:714
Finding
Lingxing bearer tokens cached in predictable shared temporary files<![CDATA[ ## Vulnerability Details **File Location**: `scripts/lingxing.py:714-748` **Vulnerability Type**: Insecure temporary-file handling and plaintext credential storage **Risk Level**: High ### Vulnerable Code ```python TOKEN_CACHE_DIR = Path("/tmp") ``` ```python def _token_cache_path(app_id: str) -> Path: return TOKEN_CACHE_DIR / f"lingxing_token_{app_id[:8]}.json" def _load_cached_token(app_id: str) -> Optional[str]: path = _token_cache_path(app_id) if not path.exists(): return None try: data = json.loads(path.read_text()) if data.get("expires_at", 0) > time.time() + 60: return data["access_token"] except Exception: pass return None def _save_token(app_id: str, token: str, expires_in: int) -> None: path = _token_cache_path(app_id) path.write_text(json.dumps({ "access_token": token, "expires_at": time.time() + expires_in })) ``` ### Technical Analysis The script writes a live Lingxing access token to the shared `/tmp` directory. The filename is predictable because it contains only the first eight AppID characters. The write operation does not explicitly enforce mode `0600`, verify ownership, prevent symbolic-link traversal, or use exclusive atomic file creation. Under an unsafe umask, another local account may read the token. A local attacker may also pre-create the predictable path as a symbolic link, potentially redirecting the token write to another file accessible to the attacker. Using only an AppID prefix additionally creates a collision risk between applications sharing the same first eight characters. ### Attack Path 1. A local attacker predicts or discovers `/tmp/lingxing_token_<prefix>.json`. 2. The attacker monitors that file or pre-creates it as a symbolic link to an attacker-readable destination. 3. The legitimate user invokes the Lingxing CLI. 4. The CLI authenticates and writes a live bearer token to the predictable path. 5. The attac ...[truncated 577 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Use a private per-user cache directory with mode `0700`, such as an appropriate platform-specific application data directory. 2. Create token files with mode `0600`. 3. Use atomic creation with `O_CREAT`, `O_EXCL`, and `O_NOFOLLOW` where supported. 4. Verify that the cache file is a regular file owned by the current user before reading or writing it. 5. Derive filenames from a full cryptographic hash of the AppID instead of an eight-character prefix. 6. Avoid disk caching entirely when operationally feasible. 7. Delete expired tokens immediately and provide a cache-clearing command. 8. Consider an operating-system credential store for long-lived deployments. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/lingxing.py:668
Finding
Unpinned installation guidance for a runtime cryptography dependency<![CDATA[ ## Vulnerability Details **File Location**: `scripts/lingxing.py:668-674` **Vulnerability Type**: Unsafe and unpinned third-party dependency installation **Risk Level**: Medium ### Vulnerable Code ```python def _aes_encrypt(key: str, data: str) -> str: try: from Crypto.Cipher import AES except ImportError: print( "Error: pycryptodome is not installed. Run: pip install pycryptodome", file=sys.stderr, ) sys.exit(1) ``` The English rendering above preserves the exact installation behavior of the source message: users are instructed to execute `pip install pycryptodome` without a pinned version or integrity hash. ### Technical Analysis The Skill has no lockfile or requirements file specifying an audited version and hash. When the import fails, users are directed to install the latest package resolved by their configured pip index. Package installation may execute package build or installation logic. A compromised release, package index, configured mirror, or dependency chain could therefore introduce arbitrary code into the environment. Although `pycryptodome` is a known package and no malicious package is embedded in this project, the installation process is not reproducible or integrity-pinned. ### Attack Path 1. The Lingxing integration is invoked on a system without `pycryptodome`. 2. The script instructs the user to run the unpinned pip command. 3. Pip resolves the package and dependencies from the configured package source. 4. A compromised source, mirror, release, or dependency supplies malicious installation content. 5. Installation code runs with the privileges of the user executing pip. 6. The malicious package gains access to that user's files, environment variables, and future Lingxing credentials. ### Impact Assessment Successful supply-chain compromise could execute arbitrary code with the installing user's privileges. This may expose `LINGXING_APP_ID`, `LINGXING_APP_SE ...[truncated 95 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add a reviewed dependency manifest with an exact `pycryptodome` version. 2. Supply cryptographic hashes and require hash verification during installation. 3. Use a lockfile or reproducible package-management workflow. 4. Document installation inside an isolated virtual environment. 5. Use a trusted package index and explicitly warn against unknown mirrors. 6. Regularly audit and update the pinned version in a controlled release process. 7. Do not direct users to install an unconstrained latest package at runtime. ]]>

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/lingxing.py:71
Finding
Query-oriented Lingxing CLI exposes state-changing ERP endpoints<![CDATA[ ## Vulnerability Details **File Location**: `scripts/lingxing.py:71`, `scripts/lingxing.py:87`, `scripts/lingxing.py:829-848`, `scripts/lingxing.py:942-979` **Vulnerability Type**: Excessive capability and missing read-only authorization boundary **Risk Level**: High ### Vulnerable Code The supported endpoint mapping includes operations that appear state-changing: ```python "scOrderSetRemark": "/basicOpen/platformOrder/scOrder/setRemark", ``` ```python "adjustPriceAdjustPriceManual": "/basicOpen/module/adjustPrice/AdjustPriceManual", ``` The generic dispatcher submits any allowlisted non-GET endpoint as a POST request: ```python def call_api(app_id: str, token: str, api_name: str, body: dict, extra_headers: Optional[dict] = None) -> dict: """Single call with signing and automatic GET/POST selection.""" is_get = api_name in GET_APIS qp = _sign_params(app_id, token, extra=None if is_get else body) headers = {"X-API-VERSION": "2"} if extra_headers: headers.update(extra_headers) url = HOST + get_api_path(api_name) if is_get: qp.update(body) return _http_get(url, params=qp, headers=headers) else: return _http_post(url, params=qp, json_body=body, headers=headers) ``` The CLI performs no separate confirmation for mutation-like operations: ```python if args.api not in SUPPORTED_APIS: print(f"Error: unsupported API '{args.api}'", file=sys.stderr) sys.exit(1) result = call_api(app_id, token, args.api, body) ``` ### Technical Analysis The script describes itself as a query interface, and the associated documentation emphasizes retrieving ERP data. Nevertheless, its endpoint allowlist includes names and paths indicating live state changes, including setting order remarks and manually adjusting prices. All supported endpoints pass through a generic dispatcher. Non-GET operations are submitted without a read-only policy, mutation classification, interactive confirm ...[truncated 1179 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove all state-changing endpoints from the query CLI. 2. Maintain a reviewed, explicit allowlist containing only read-only endpoints. 3. Place mutation operations in a separate tool that is disabled by default. 4. Require explicit user confirmation immediately before every state-changing request. 5. Show the endpoint, store identifier, affected objects, old values when available, and proposed new values in the confirmation. 6. Add a dry-run or validation-only mode. 7. Require separate least-privilege Lingxing credentials for read and write operations. 8. Add operation-specific schemas and reject unexpected parameters. 9. Log mutation requests locally without recording reusable credentials or tokens. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/upload_image.py:84
Finding
Local images are uploaded with public-read access<![CDATA[ ## Vulnerability Details **File Location**: `scripts/upload_image.py:84-116`, `scripts/upload_image.py:136-151` **Vulnerability Type**: Public disclosure of user-selected local files **Risk Level**: Medium ### Vulnerable Code ```python def upload_file(presigned_url: str, file_path: str, content_type: str): """Upload the local file to the presigned OSS URL via HTTP PUT.""" with open(file_path, "rb") as f: file_data = f.read() req = Request( presigned_url, data=file_data, headers={ "Content-Type": content_type, "x-oss-object-acl": "public-read", }, method="PUT", ) try: with urlopen(req, timeout=120) as response: if response.status not in (200, 201): print( f"Upload failed with status: {response.status}", file=sys.stderr, ) sys.exit(1) ``` ```python extension = os.path.splitext(file_path)[1].lstrip(".").lower() content_type = CONTENT_TYPE_MAP.get(extension) if not content_type: print( f"Unsupported image format: .{extension}\n" f"Supported formats: {', '.join(CONTENT_TYPE_MAP.keys())}", file=sys.stderr, ) sys.exit(1) presigned_url = get_presigned_url(content_type, extension) upload_file(presigned_url, file_path, content_type) public_url = extract_public_url(presigned_url) print(json.dumps({"url": public_url}, indent=2, ensure_ascii=False)) ``` ### Technical Analysis The helper reads an arbitrary user-selected local path after checking only that it exists and has a supported filename extension. It uploads the complete file to a server-provided presigned URL and explicitly requests a `public-read` object ACL. The implementation does not verify the actual file signature, restrict file size, strip metadata, require an explicit disclosure confirmation, use a private object ACL, or provide a deletion mechanism. The docum ...[truncated 889 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Require explicit user consent immediately before every local-file upload. 2. State the destination, public accessibility, expected retention period, and file path in the confirmation. 3. Use private object storage and short-lived signed download URLs instead of `public-read`. 4. Verify file signatures rather than relying only on extensions. 5. Enforce conservative file-size limits. 6. Decode and re-encode supported images to reject disguised files and remove unnecessary metadata. 7. Restrict presigned upload URLs to approved HTTPS hostnames. 8. Provide server-side expiration, deletion, and revocation controls. 9. Avoid returning permanent public object URLs. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (65)

Ssd 3

High
Confidence
99% confidence
Finding
The skill explicitly requires always forwarding a public ShareURL that can be opened without authentication and reveals the full conversation, execution steps, and downloadable artifacts. Because prompts sent to the service may include business-sensitive research inputs, generated reports, file outputs, and potentially user-provided documents, unconditional disclosure of a public link creates a strong risk of data leakage and unintended third-party access.

Ssd 3

High
Confidence
99% confidence
Finding
This line reinforces that every successful run produces a public, read-only share page containing the full conversation and downloadable artifacts, and instructs the agent to always send it. In the context of a skill handling market research, patent analysis, ERP workflows, PDFs, and uploaded files, that behavior materially increases the likelihood of exposing confidential commercial data or user-supplied content.

Hidden Instructions

High
Category
Prompt Injection
Content
# 亚马逊前台

共 8 个工具。使用 `@工具中文名` 语法在任务提示词中调用。
Confidence
60% confidence
Finding
Hidden instructions were detected in comments or invisible text. These could contain malicious directives. Manual review is recommended.

Credential Access

High
Category
Privilege Escalation
Content
def get_api_key() -> str:
    """Get API key from environment."""
    key = os.environ.get("LINKFOXAGENT_API_KEY")
    if not key:
        print(
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding
The skill exposes broad capabilities through its documented behavior: it reads environment variables, writes local output files, and sends prompts and results to external network services, yet it declares no explicit tool scope or allowed-tools boundary. This weakens least-privilege controls and makes it easier for a caller or future skill changes to access sensitive resources beyond what users expect.

Natural-Language Policy Violations

Medium
Confidence
94% confidence
Finding
The markdown explicitly instructs the agent to tell the user a fixed Chinese message before dispatching a task. This imposes a specific language on user-facing output regardless of the user's language preference, which is a natural-language locale policy violation unless the user opted into Chinese or the skill is clearly constrained to Chinese-only usage.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The skill requires a fixed Chinese message when spawning multiple tasks in parallel. Because this is a mandatory user-facing utterance with no language choice or opt-in, it enforces a locale/language on all users and violates the language-choice policy.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The parameter description states that the product search keyword 'must be Chinese' and instructs the system to translate it first if it is not. This imposes a language constraint on the user's input without offering a choice or opt-in, which matches the locale/language policy violation criteria.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The description says the search keyword 'must be Chinese' and directs translation when it is not. This is a natural-language policy issue because it mandates a specific language rather than offering the user a choice or clearly framing it as an external platform limitation with consent.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The file states that users must invoke tools using the `@工具中文名` syntax, which imposes a specific language requirement. This is a natural-language policy concern because no alternative locale or opt-in is provided for non-Chinese users.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
Line L033 says keywords should be translated to the corresponding country's language, such as using English for the US and German for Germany. This imposes a locale/language behavior by default rather than offering user opt-in or indicating that the user may choose their preferred language.

Vague Triggers

Medium
Confidence
84% confidence
Finding
L129 states that prompts can be organized freely in natural language with no fixed template. In a markdown skill description, this lacks clear trigger boundaries or exclusion conditions and can increase the chance of unintended invocation from ordinary shopping-related requests.

Natural-Language Policy Violations

Medium
Confidence
88% confidence
Finding
The file instructs users to call the tool using the `@工具中文名` syntax, which imposes a Chinese-language naming convention. Under the policy, language constraints should either offer user choice or be clearly justified as region-specific; this file does not provide such justification or an alternative.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
Line L41 states that keywords 'must' be in the country's language and instructs translation if they are not. This is a natural-language locale policy constraint that forces a specific language based on region, without any opt-in, alternative, or documented justification for the restriction.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The instruction requires the keyword to use the language of the selected marketplace, such as English for the US site and German for the DE site. This is a natural-language locale constraint presented as mandatory behavior, with no indication that the user may choose another language or opt in to translation behavior.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
This line states that the keyword is required and must be translated into the language of the selected country. That enforces locale behavior as a policy in the skill description rather than offering translation as an option the user can accept or decline.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The parameter description mandates translating the keyword into the selected country's language. This is a forced locale/language behavior and does not provide a user choice or documented exception.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The file states users must invoke the tools using the `@工具中文名` syntax, which imposes a specific language requirement. This is a natural-language locale policy concern because no alternative language option or opt-in is provided.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The document instructs operators to set Lingxing ERP API credentials and use them to pull live ERP data from an external service, but it does not include any warning about credential sensitivity, data handling, access scope, logging exposure, or the privacy implications of retrieving production business data. In an agent-skill context, this is risky because users may paste secrets or authorize broad data access without understanding that order, inventory, finance, and ads data will be transmitted to a third-party API and may also appear in shell history, task logs, or subprocess output.

Natural-Language Policy Violations

Medium
Confidence
93% confidence
Finding
The parameter description states that the search keyword must be in Russian, which is a language constraint expressed in natural language. Under the policy rule, forcing a specific language without user opt-in or clear documented justification is a reportable locale/language policy violation.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The file explicitly says the category path must be Russian, which imposes a specific language requirement on the user. The document does not offer a language choice, opt-in flow, or policy justification beyond the bare requirement, so it matches the natural-language language-policy violation criteria.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The file instructs users to invoke tools using the `@工具中文名` syntax, which requires Chinese tool names and effectively imposes a specific language for interaction. The document does not offer an alternative language option or note that other locales are supported.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
The file presents all instructions and tool invocation conventions in Chinese, including the required '@工具中文名' syntax, which effectively forces a specific language/locale. Because no opt-in, alternative language support, or documented regional justification is provided, this is a natural-language policy concern.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
This tool accepts Excel download links and processes file contents without any warning about sending external file URLs or potentially sensitive spreadsheet data into the tool pipeline. In a skill centered on e-commerce research and data aggregation, spreadsheets may contain supplier, pricing, customer, or business-sensitive data, so silent transmission/processing increases confidentiality and compliance risk.

Intent-Code Divergence

Medium
Confidence
89% confidence
Finding
The capability description says the Python sandbox supports using an LLM to recognize image URLs, which is an AI analysis capability beyond pure structured-JSON processing. Immediately afterward, the limitations claim it can only process structured JSON data and cannot generate suggestions or analysis reports, which conflicts with the earlier description of LLM-assisted recognition and text-oriented output such as Markdown tables.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/linkfox.py:570

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
SKILL.md:13