Back to skill

Security audit

TRO风险提示专家

Security checks across malware telemetry and agentic risk

Overview

The skill mostly matches a TRO/IP-risk workflow, but it deserves review because it handles account credentials, payments, public uploads, local result retention, configurable network endpoints, and automatic feedback reporting.

Install only if you are comfortable sending product images, listing text, account/login data, API keys, and paid-search requests to LinkFox services. Keep LINKFOX_TOOL_GATEWAY and other LINKFOX_*_API_URL overrides unset unless you fully trust the endpoint, avoid uploading private images because uploads become publicly accessible URLs, and periodically review or delete the local linkfox result/cache directories.

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 (147)

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

Critical
Category
Data Flow
Content
}
    req = Request(url, data=data, headers=headers, method="POST")
    try:
        with urlopen(req, timeout=HTTP_TIMEOUT) 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=HTTP_TIMEOUT) as response:

Tainted flow: 'url' from os.environ.get (line 235, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
except RuntimeError as e:
        return {"_error": str(e)}
    try:
        r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)
        return r.json()
    except Exception as e:
        body_text = ""
Confidence
89% confidence
Finding
r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)

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

Critical
Category
Data Flow
Content
headers["Content-Type"] = "application/json"
        req = Request(url, method=method, data=body_bytes, headers=headers)
        try:
            with urlopen(req, timeout=30) as resp:
                return json.loads(resp.read().decode())
        except urllib.error.HTTPError as e:
            status = e.code
Confidence
88% confidence
Finding
with urlopen(req, timeout=30) as resp:

Tainted flow: 'url' from os.environ.get (line 235, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
except RuntimeError as e:
        return {"_error": str(e)}
    try:
        r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)
        return r.json()
    except Exception as e:
        body_text = ""
Confidence
91% confidence
Finding
r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)

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

Critical
Category
Data Flow
Content
headers["Content-Type"] = "application/json"
        req = Request(url, method=method, data=body_bytes, headers=headers)
        try:
            with urlopen(req, timeout=30) as resp:
                return json.loads(resp.read().decode())
        except urllib.error.HTTPError as e:
            status = e.code
Confidence
89% confidence
Finding
with urlopen(req, timeout=30) as resp:

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: 'url' from os.environ.get (line 235, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
except RuntimeError as e:
        return {"_error": str(e)}
    try:
        r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)
        return r.json()
    except Exception as e:
        body_text = ""
Confidence
92% confidence
Finding
r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)

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

Critical
Category
Data Flow
Content
headers["Content-Type"] = "application/json"
        req = Request(url, method=method, data=body_bytes, headers=headers)
        try:
            with urlopen(req, timeout=30) as resp:
                return json.loads(resp.read().decode())
        except urllib.error.HTTPError as e:
            status = e.code
Confidence
89% confidence
Finding
with urlopen(req, timeout=30) as resp:

Tainted flow: 'url' from os.environ.get (line 235, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
except RuntimeError as e:
        return {"_error": str(e)}
    try:
        r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)
        return r.json()
    except Exception as e:
        body_text = ""
Confidence
91% confidence
Finding
r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)

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

Critical
Category
Data Flow
Content
headers["Content-Type"] = "application/json"
        req = Request(url, method=method, data=body_bytes, headers=headers)
        try:
            with urlopen(req, timeout=30) as resp:
                return json.loads(resp.read().decode())
        except urllib.error.HTTPError as e:
            status = e.code
Confidence
92% confidence
Finding
with urlopen(req, timeout=30) as resp:

Tainted flow: 'url' from os.environ.get (line 235, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
except RuntimeError as e:
        return {"_error": str(e)}
    try:
        r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)
        return r.json()
    except Exception as e:
        body_text = ""
Confidence
92% confidence
Finding
r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)

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

Critical
Category
Data Flow
Content
headers["Content-Type"] = "application/json"
        req = Request(url, method=method, data=body_bytes, headers=headers)
        try:
            with urlopen(req, timeout=30) as resp:
                return json.loads(resp.read().decode())
        except urllib.error.HTTPError as e:
            status = e.code
Confidence
89% confidence
Finding
with urlopen(req, timeout=30) as resp:

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

Critical
Category
Data Flow
Content
)

    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)
Confidence
88% confidence
Finding
with urlopen(req, timeout=120) as response:

Tainted flow: 'url' from os.environ.get (line 235, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
except RuntimeError as e:
        return {"_error": str(e)}
    try:
        r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)
        return r.json()
    except Exception as e:
        body_text = ""
Confidence
92% confidence
Finding
r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)

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

Critical
Category
Data Flow
Content
headers["Content-Type"] = "application/json"
        req = Request(url, method=method, data=body_bytes, headers=headers)
        try:
            with urlopen(req, timeout=30) as resp:
                return json.loads(resp.read().decode())
        except urllib.error.HTTPError as e:
            status = e.code
Confidence
93% confidence
Finding
with urlopen(req, timeout=30) as resp:

Tainted flow: 'url' from os.environ.get (line 235, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
except RuntimeError as e:
        return {"_error": str(e)}
    try:
        r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)
        return r.json()
    except Exception as e:
        body_text = ""
Confidence
95% confidence
Finding
r = requests.post(url, json=body or {}, headers=headers, timeout=timeout)

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

Critical
Category
Data Flow
Content
headers["Content-Type"] = "application/json"
        req = Request(url, method=method, data=body_bytes, headers=headers)
        try:
            with urlopen(req, timeout=30) as resp:
                return json.loads(resp.read().decode())
        except urllib.error.HTTPError as e:
            status = e.code
Confidence
94% confidence
Finding
with urlopen(req, timeout=30) as resp:

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

Critical
Category
Data Flow
Content
)

    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)
Confidence
84% confidence
Finding
with urlopen(req, timeout=120) as response:

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The declared purpose is a narrow TRO/IP risk advisor, but the underlying behavior reportedly includes authentication flows, payment/order creation, public file upload, report generation, and broader patent-search operations. This mismatch is dangerous because users may grant trust and sensitive inputs under a limited-risk expectation while the skill can route data to unrelated external services or trigger commercial/account actions outside the stated scope.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The referenced API documentation describes a generic AI text-generation service rather than the manifest-declared IP/TRO risk advisory skill. This mismatch can cause the agent to invoke the wrong backend, send sensitive user inputs (including product images/text) to an unrelated service, and produce outputs outside the safety and policy expectations of the declared skill.

Intent-Code Divergence

Medium
Confidence
94% confidence
Finding
The file explicitly identifies the skill as `linkfox-aigc-textgen`, which contradicts the manifest name `linkfox-expert-tro-risk-advisor`. Identity mismatches undermine traceability, can misroute telemetry or operational actions, and increase the chance that the wrong capability is executed under a trusted label.

Intent-Code Divergence

Medium
Confidence
93% confidence
Finding
The feedback API example and field rules instruct clients to report using `linkfox-aigc-textgen` instead of the manifest-declared skill. This can poison audit trails, break incident response and quality monitoring, and conceal misuse or failures by attributing activity to the wrong skill.

Description-Behavior Mismatch

High
Confidence
95% confidence
Finding
The implementation is a generic async text-generation client, while the manifest describes a TRO/IP risk advisor. This capability mismatch is dangerous because users or higher-level agents may grant trust and sensitive inputs under the assumption of narrow risk-analysis behavior, when the code actually forwards arbitrary prompts to a remote generation service.

Description-Behavior Mismatch

Medium
Confidence
84% confidence
Finding
The code persists generated responses to local session files when output exceeds a threshold, which is broader data-handling behavior than the manifest suggests. In this context, users may submit sensitive product descriptions or legal-risk material, so undisclosed local retention increases confidentiality and data-governance risk.

Context-Inappropriate Capability

Medium
Confidence
87% confidence
Finding
The embedded session tracking and archival subsystem creates per-session directories, metadata, and indexes that are not justified by the stated TRO/IP advisor function. This expands the skill's data collection and persistence surface, making accidental retention, correlation of user activity, and later exposure more likely.

VirusTotal

VirusTotal findings are pending for this skill version.

View on VirusTotal

Static analysis

No suspicious patterns detected.