Back to skill

Security audit

Linkfox 电商合规检测

Security checks across malware telemetry and agentic risk

Overview

This skill mostly matches its compliance-checking purpose, but it adds a risky external skill-install fallback and handles credentials and stored results more broadly than its documentation suggests.

Install only if you trust LinkFox with the product images, listing text, patent queries, API key use, and locally saved API responses. Avoid using LINKFOX_TOOL_GATEWAY unless it points to a trusted HTTPS LinkFox endpoint, review or remove the external onboarding-skill install instruction, and be aware that responses may be cached or saved outside the project directory in some environments.

SkillSpector

By NVIDIA
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (81)

Tainted flow: 'req' from os.environ.get (line 72, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST",
    )
    try:
        with urlopen(req, timeout=120) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        body = e.read().decode("utf-8") if e.fp else ""
Confidence
91% confidence
Finding
with urlopen(req, timeout=120) as response:

Tainted flow: 'req' from os.environ.get (line 72, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST",
    )
    try:
        with urlopen(req, timeout=120) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        body = e.read().decode("utf-8") if e.fp else ""
Confidence
91% confidence
Finding
with urlopen(req, timeout=120) as response:

Tainted flow: 'req' from os.environ.get (line 72, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST",
    )
    try:
        with urlopen(req, timeout=120) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        body = e.read().decode("utf-8") if e.fp else ""
Confidence
94% confidence
Finding
with urlopen(req, timeout=120) as response:

Tainted flow: 'req' from os.environ.get (line 72, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST",
    )
    try:
        with urlopen(req, timeout=120) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        body = e.read().decode("utf-8") if e.fp else ""
Confidence
87% confidence
Finding
with urlopen(req, timeout=120) as response:

Tainted flow: 'req' from os.environ.get (line 72, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST",
    )
    try:
        with urlopen(req, timeout=120) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        body = e.read().decode("utf-8") if e.fp else ""
Confidence
93% confidence
Finding
with urlopen(req, timeout=120) as response:

Tainted flow: 'req' from os.environ.get (line 72, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST",
    )
    try:
        with urlopen(req, timeout=120) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        body = e.read().decode("utf-8") if e.fp else ""
Confidence
96% confidence
Finding
with urlopen(req, timeout=120) as response:

Tainted flow: 'req' from os.environ.get (line 72, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST",
    )
    try:
        with urlopen(req, timeout=120) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        body = e.read().decode("utf-8") if e.fp else ""
Confidence
94% confidence
Finding
with urlopen(req, timeout=120) as response:

Tainted flow: 'req' from os.environ.get (line 72, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST",
    )
    try:
        with urlopen(req, timeout=120) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        body = e.read().decode("utf-8") if e.fp else ""
Confidence
95% confidence
Finding
with urlopen(req, timeout=120) as response:

Tainted flow: 'req' from os.environ.get (line 72, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST",
    )
    try:
        with urlopen(req, timeout=120) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        body = e.read().decode("utf-8") if e.fp else ""
Confidence
95% confidence
Finding
with urlopen(req, timeout=120) as response:

Tainted flow: 'req' from os.environ.get (line 72, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST",
    )
    try:
        with urlopen(req, timeout=120) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        body = e.read().decode("utf-8") if e.fp else ""
Confidence
95% confidence
Finding
with urlopen(req, timeout=120) as response:

Tainted flow: 'req' from os.environ.get (line 72, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST",
    )
    try:
        with urlopen(req, timeout=120) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        body = e.read().decode("utf-8") if e.fp else ""
Confidence
93% confidence
Finding
with urlopen(req, timeout=120) as response:

Tainted flow: 'req' from os.environ.get (line 77, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST",
    )
    try:
        with urlopen(req, timeout=120) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        body = e.read().decode("utf-8") if e.fp else ""
Confidence
94% confidence
Finding
with urlopen(req, timeout=120) as response:

Tainted flow: 'req' from os.environ.get (line 72, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST",
    )
    try:
        with urlopen(req, timeout=120) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        body = e.read().decode("utf-8") if e.fp else ""
Confidence
92% confidence
Finding
with urlopen(req, timeout=120) as response:

Tainted flow: 'req' from os.environ.get (line 72, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST",
    )
    try:
        with urlopen(req, timeout=120) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        body = e.read().decode("utf-8") if e.fp else ""
Confidence
94% confidence
Finding
with urlopen(req, timeout=120) as response:

Tainted flow: 'req' from os.environ.get (line 77, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
method="POST",
    )
    try:
        with urlopen(req, timeout=120) as response:
            return json.loads(response.read().decode("utf-8"))
    except HTTPError as e:
        body = e.read().decode("utf-8") if e.fp else ""
Confidence
95% confidence
Finding
with urlopen(req, timeout=120) as response:

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
The skill explicitly instructs the agent to download and install another skill package from an external URL when authentication or quota issues occur. That introduces an unnecessary software installation pathway unrelated to the core compliance lookup purpose, expanding trust to unaudited remote content and creating a supply-chain execution risk if the package is malicious or later replaced.

Context-Inappropriate Capability

Medium
Confidence
97% confidence
Finding
The skill instructs the agent to download and install another skill from an external URL when authentication or quota problems occur, which expands trust and execution scope far beyond the stated purpose of fetching patent abstract images. This creates a supply-chain risk because a failure path can trigger retrieval of unreviewed code/content from the internet and cause the agent to follow additional instructions unrelated to the user’s original request.

Intent-Code Divergence

Medium
Confidence
84% confidence
Finding
The document frames the tool as only returning abstract images, but elsewhere mandates writing the full API response to persistent local storage. That mismatch can cause agents or users to underestimate what data is retained, leading to unnecessary persistence of metadata and response contents in the project directory beyond the minimal data needed for the task.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The skill instructs the agent to download and install another skill from a remote URL when authentication or quota issues occur. This extends behavior far beyond the documented patent-citation lookup purpose and introduces a supply-chain risk, because the newly installed skill could execute arbitrary additional logic under the agent's privileges.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The markdown explicitly authorizes remote retrieval and installation of a skill package unrelated to the core query function. Allowing network-fetched code/content to be installed during ordinary tool use creates an unjustified remote code/configuration expansion path and can be abused if the distribution endpoint or package is compromised.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The skill instructs the agent to download and install another skill from an external URL when authentication or quota problems occur, even though this skill’s stated purpose is only to retrieve patent PDF links. This expands the trust boundary and creates a supply-chain risk: a compromised or replaced ZIP could introduce arbitrary new instructions, data access, or unsafe behaviors unrelated to the original task.

Context-Inappropriate Capability

Medium
Confidence
92% confidence
Finding
The skill introduces a separate feedback-submission API unrelated to the patent image search task, expanding the skill's effective capabilities beyond user-requested search. This creates a channel for transmitting user-derived content to a third-party endpoint, which can lead to unauthorized data sharing, privacy issues, or abuse if invoked without explicit consent and clear need.

Intent-Code Divergence

Medium
Confidence
78% confidence
Finding
The skill claims it only supports utility-model patents, but later API documentation and examples include design-patent parameters and sample requests. This inconsistency can cause the agent to invoke unsupported modes or route queries incorrectly, undermining policy boundaries and increasing the risk of unintended actions or misleading results.

Intent-Code Divergence

Medium
Confidence
88% confidence
Finding
The module documentation promises that data will not be written to /tmp and that failure should occur if the current directory is not writable, but the implementation silently falls back to home and temporary directories. This mismatch can cause sensitive API responses to be stored in less secure or unintended locations, violating operator expectations and potentially exposing data to other local users or cleanup processes.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The module docstring promises that writing to /tmp is forbidden and that failure should occur if the current directory is not writable, but the implementation silently falls back to the home directory and then the temp directory. This breaks operator expectations and can cause sensitive API responses to be persisted in less controlled locations, including temporary storage explicitly described as prohibited.

VirusTotal

65/65 vendors flagged this skill as clean.

View on VirusTotal

Static analysis

No suspicious patterns detected.