Back to skill

Security audit

Gangtise知识库

Security checks for vulnerabilities and agentic risk

Overview

This is an authenticated Gangtise knowledge-base search and download skill, but its credential-bearing network requests and local file writes need careful review before installation.

Install only in an environment where Gangtise credentials are intended for this skill, endpoint environment variables are controlled, and downloaded files can be safely written to a contained workspace. Avoid using broad or long-lived credentials, and review or patch filename handling before relying on direct downloads.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/utils.py:47
Finding

Authorization Token and Sensitive Query Disclosure Through Configurable API Origins

Content
View full analysis

Vulnerability Details

File Location: scripts/utils.py:47-55, scripts/kb.py:70-81, scripts/get_file.py:54-101
Vulnerability Type: Unvalidated configurable network destination receiving authorization credentials
Risk Level: High

Vulnerable Code

python
GANGTISE_DATA_DOMAIN = os.getenv(
    "GANGTISE_DOMAIN",
    "https://open.gangtise.com/application/open-data"
)
GANGTISE_INSIGHT_DOMAIN = os.getenv(
    "GANGTISE_INSIGHT_DOMAIN",
    "https://open.gangtise.com/application/open-insight"
)

RAG_URL = f"{GANGTISE_DATA_DOMAIN}/ai/search/knowledge_base"
FILE_URL = f"{GANGTISE_DATA_DOMAIN}/ai/resource/download"
SUMMARY_DOWNLOAD_URL = f"{GANGTISE_INSIGHT_DOMAIN}/summary/v2/download/file"
COMPANY_ANNOUNCEMENT_DOWNLOAD_URL = f"{GANGTISE_INSIGHT_DOMAIN}/announcement/download/file"
REPORT_DOWNLOAD_URL = f"{GANGTISE_INSIGHT_DOMAIN}/broker-report/download/file"
FOREIGN_REPORT_DOWNLOAD_URL = f"{GANGTISE_INSIGHT_DOMAIN}/foreign-report/download/file"

The search endpoint is subsequently given the bearer token and user query:

python
headers = {
    "Authorization": GTS_AUTHORIZATION,
}

payload = {
    "query": query,
    "startTime": datetime.datetime.strptime(start_date, "%Y-%m-%d").timestamp() * 1000 if start_date else None,
    "endTime": datetime.datetime.strptime(end_date, "%Y-%m-%d").timestamp() * 1000 if end_date else None,
    "resourceTypes": [FILE_TYPE_MAP[file_type] for file_type in file_types] if file_types else None,
    "top": limit,
}
response = requests.post(RAG_URL, headers=headers, json=payload, timeout=300)

Download endpoints receive the same token:

python
headers = {
    "Authorization": GTS_AUTHORIZATION,
}

# ...
if file_type in url_map:
    response = requests.get(
        url_map[file_type],
        headers=headers,
        params=params_map[file_type],
        timeout=300,
    )
else:
    params = {
        "sou
...[truncated 2468 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin API origins to the documented Gangtise HTTPS endpoints whenever custom deployments are not explicitly required.
  2. If endpoint configuration is necessary, parse each URL with urllib.parse.urlparse and enforce:
    • The https scheme.
    • An exact allowlist of approved hostnames.
    • An expected port.
    • No embedded username or password.
    • An approved base path.
  3. Reject IP literals, loopback addresses, link-local addresses, and unexpected subdomains unless explicitly required and approved.
  4. Disable redirects for credential-bearing requests or validate every redirect destination before following it.
  5. Do not forward the Authorization header across origins.
  6. Use short-lived, narrowly scoped tokens and revoke potentially exposed tokens.
  7. Add tests proving that arbitrary HTTP origins, lookalike domains, and cross-origin redirects are rejected.
  8. Avoid including sensitive server response bodies in user-facing error messages.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/get_file.py:131
Finding

Directory Traversal and Arbitrary File Overwrite Through Server-Controlled Download Filenames

Content
View full analysis

Vulnerability Details

File Location: scripts/get_file.py:27-37, scripts/get_file.py:131-140
Vulnerability Type: Path traversal through an insufficiently sanitized Content-Disposition filename
Risk Level: High

Vulnerable Code

The filename sanitizer does not remove forward slashes, absolute path prefixes, or .. path components:

python
def safe_file_title(file_item):
    title = file_item["title"]
    not_allow_title_symbol = [
        "\\", ":", "*", "?", "\"", "<", ">", "|",
        "=", "&", "\0"
    ]
    for symbol in not_allow_title_symbol:
        title = title.replace(symbol, "")
    title = title.replace(" ", "_")
    return title

In the default download branch, the server-provided filename is decoded and joined directly to the local download directory without applying os.path.basename() or verifying the resolved path:

python
else:
    if len(response.headers["Content-Disposition"].split("filename=")) > 1:
        output = os.path.join(
            file_dir,
            unquote(
                response.headers["Content-Disposition"]
                .split("filename=")[1]
            ),
        )
    else:
        output = os.path.join(
            file_dir,
            unquote(
                response.headers["Content-Disposition"]
                .split("filename*=utf-8''")[1]
            ),
        )
    if output.startswith("\""):
        output = output[1:-1]
    output = safe_file_title(
        {"title": output, "url": "." + output.split(".")[-1]}
    )
    return_message = f"文件已保存到:{output}"

with open(output, "wb") as f:
    f.write(response.content)

Technical Analysis

The response's Content-Disposition filename is controlled by the remote server. Although safe_file_title() removes several special characters, it preserves / and ... On Unix-like systems, these characters are suffic ...[truncated 2039 chars]

Remediation
View remediation

Remediation Suggestions

  1. Parse Content-Disposition with a standards-compliant parser rather than manually splitting header strings.

  2. Apply os.path.basename() to every server-provided filename in every download branch.

  3. Prefer locally generated filenames based on validated document IDs instead of trusting remote filenames.

  4. Resolve and validate the final destination before writing:

    python
    base = os.path.realpath(file_dir)
    name = os.path.basename(server_filename)
    destination = os.path.realpath(os.path.join(base, name))
    
    if os.path.commonpath([base, destination]) != base:
        raise ValueError("Unsafe download path")
    
  5. Reject empty names, . and .., absolute paths, path separators, control characters, and filenames exceeding a conservative length limit.

  6. Use exclusive creation mode where overwriting is unnecessary, or require explicit authorization before replacing an existing file.

  7. Restrict downloaded extensions to the requested and expected document types, while validating content type and file signatures.

  8. Add regression tests for relative traversal, absolute paths, percent-encoded separators, quoted filenames, Unicode separator variants, and pre-existing destination files.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (23)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill is ներկայացված as a semantic knowledge-base search tool, but the documented parameters also enable downloading and saving files locally, including multiple resource types and alternate retrieval interfaces. This mismatch is dangerous because reviewers, users, or policy layers may approve it for low-risk retrieval while it actually performs higher-risk data export and local persistence operations.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill metadata describes semantic retrieval of relevant content snippets, but this code downloads full source documents and writes them to disk. That expands the data-access surface beyond the declared purpose, increases the chance of over-collection of sensitive internal material, and can surprise users or downstream agents that expected only excerpted retrieval.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding

The skill declares no explicit tool scope or permission boundaries even though the documented behavior relies on environment variables, local file reads/writes, and network access. In an agent setting, missing scope declarations can cause overbroad execution privileges, making unintended data access or exfiltration more likely if the skill is invoked in the wrong context.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The markdown explicitly presents the skill guidance in Chinese and labels it as a Chinese explanation, with no indication that other languages are supported or that the user can choose the interaction language. This can violate a language/locale policy when skills should not force a specific language absent user opt-in or documented justification.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The code automatically attaches a sensitive authorization token to outbound HTTP requests and performs document retrieval without any user-visible notice that privileged credentials are being used. In a skill that appears to be simple KB retrieval, this reduces transparency and can lead to unintentional privileged access or export of internal documents.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The argparse description says this command retrieves related files based on a query sentence, implying search behavior. However, the implementation accepts only a concrete file ID and file type, then downloads the corresponding file, which directly contradicts the documented intent.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
89% confidence
Finding

This code performs an outbound HTTP POST to a configured service using user-supplied search content and internal authorization headers. External transmission is expected for a retrieval skill, but in this context it still constitutes a real security concern because it moves potentially sensitive internal search intent and metadata outside the immediate execution boundary.

Content

Scanner excerpt · scripts/kb.py (reported line 81)May include surrounding context.

python
"resourceTypes": [FILE_TYPE_MAP[file_type] for file_type in file_types] if file_types else None,
            "top": limit,
        }
        response = requests.post(RAG_URL, headers=headers, json=payload, timeout=300)
        if response.status_code != 200:
            return format_response({"state": "error", "message": response.text}, "rag")
        response = response.json()

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill sends the user's query, date filters, and file-type filters to an external RAG endpoint, but this file provides no user-facing disclosure or consent step. In an internal knowledge-base context, queries themselves may contain sensitive business intent, names, tickers, or confidential research topics, so undisclosed transmission creates a privacy and data-handling risk.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill is described as a semantic knowledge-base retriever, but it also supports automatically downloading files based on search results. This expands the capability from search to content exfiltration/local file materialization, which is security-relevant because a caller may trigger retrieval of potentially sensitive internal documents without a separate explicit workflow or consent boundary.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The function transmits access credentials to a remote authentication endpoint automatically, without any user-facing disclosure or confirmation in this code path. In a skill for querying an internal knowledge base, undisclosed credential transmission is sensitive because operators may not expect secrets from the local environment or file system to be sent immediately on import/use.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
90% confidence
Finding

External transmission is expected for an API-backed skill, but this specific transmission includes raw access credentials to obtain a token. In context, that is security-relevant because the module sends sensitive secrets to a remote service and does so without visible consent or strong transport/timeout/error controls in the function.

Content

Scanner excerpt · scripts/utils.py (reported line 25)May include surrounding context.

python
"accessKey": ak,
        "secretAccessKey": sk
    }
    response = requests.post(AUTHORIZATION_URL, json=payload)
    if response.status_code != 200:
        return None
    try:

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/utils.py (reported line 33)May include surrounding context.

python
except Exception as e:
        return None

GTS_AUTHORIZATION = None
if GTS_ACCESS_KEY and GTS_SECRET_KEY:
    GTS_AUTHORIZATION = get_authorization(GTS_ACCESS_KEY, GTS_SECRET_KEY)
elif os.path.exists(GTS_AUTHORIZATION_PATH):

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/utils.py (reported line 42)May include surrounding context.

python
except Exception as e:
        return None

GTS_AUTHORIZATION = None
if GTS_ACCESS_KEY and GTS_SECRET_KEY:
    GTS_AUTHORIZATION = get_authorization(GTS_ACCESS_KEY, GTS_SECRET_KEY)
elif os.path.exists(GTS_AUTHORIZATION_PATH):

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module automatically reads a local .authorization file and uses its contents for remote authentication without user disclosure. This is especially risky in agent/skill environments because merely importing the module can cause local secrets to be consumed and sent off-host, expanding the trust boundary beyond the manifest's stated retrieval purpose.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill description says it semantically retrieves and returns relevant snippets, but the utility code also creates local workspace directories and persists usage data. That broader data-retention behavior increases privacy and data-handling risk because retrieved internal knowledge and usage metadata may remain on disk beyond the immediate task.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This code can export retrieved knowledge-base content to local files, which exceeds the stated search-and-return purpose of the skill. In the context of an internal knowledge-base tool, silent local copying of retrieved content raises data leakage and retention concerns, especially for sensitive reports or meeting notes.

Content

No source excerpt is available for this finding.

Tainted flow: 'process_path' from os.getenv (line 225, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/utils.py (reported line 232)May include surrounding context.

python
return_message = "错误信息:文件存储系统繁忙,请稍后再试"
                        return return_message
                if GTS_SAVE_EXTENSION == "md":
                    with open(process_path, "w", encoding="utf-8") as f:
                        for i, file in enumerate(data):
                            f.write(f"### 查询结果 {i+1}\n\n")
                            f.write(f"标题:{file['标题']}\n")

Tainted flow: 'process_path' from os.getenv (line 225, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/utils.py (reported line 243)May include surrounding context.

python
return_message = "错误信息:文件存储系统繁忙,请稍后再试"
                        return return_message
                if GTS_SAVE_EXTENSION == "md":
                    with open(process_path, "w", encoding="utf-8") as f:
                        for i, file in enumerate(data):
                            f.write(f"### 查询结果 {i+1}\n\n")
                            f.write(f"标题:{file['标题']}\n")

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

The script unconditionally wraps stdout with UTF-8 encoding, imposing a specific output locale/encoding choice rather than respecting the user's environment. Under the policy, forcing a language or locale-related setting without opt-in can be a natural-language policy issue even when implemented in code.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The argument help text states that --output is 'temporarily ineffective' and managed by the backend, suggesting the parameter has no operational effect. But later the code passes output=args.output into rag_files_finder, which in turn forwards it to format_response, so the documentation contradicts the implemented behavior.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
76% confidence
Finding

The skill's stated purpose is semantic retrieval over an internal knowledge base, but this module additionally loads access credentials from environment variables and a local .authorization file. While authentication may be operationally necessary, credential-source handling is not part of the declared user-facing scope and is an extra capability relative to the manifest description.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

The file contains many user-visible Chinese strings and labels used in generated output, but there is no indication that users can opt into this locale or that the skill is intentionally Chinese-only. This can violate language/locale policy when a skill forces one language by default.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

When GTS_SAVE_FILE is enabled, retrieved content is written to disk without an operation-time warning or confirmation. Because the skill handles internal reports, opinions, and meeting notes, silent persistence can create unintended local copies of sensitive material and broaden exposure beyond the original retrieval session.

Content

No source excerpt is available for this finding.