Back to skill

Security audit

Skill

Security checks for vulnerabilities and agentic risk

Overview

This skill is a Japan-focused M&A analysis helper that fetches or parses financial filings and writes reports, with no evidence of hidden persistence, destructive behavior, or unrelated data transfer.

Before installing, treat this as a Japanese public-filing due diligence assistant, not professional financial advice. Use it in a controlled workspace, avoid exposing unrelated environment variables, and be aware that EDINET_API_KEY could appear in logs on request failures unless errors are sanitized. Pin or review dependencies if reproducibility matters.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/fetch_edinet.py:45
Finding

EDINET API key may be disclosed through request URLs and exception logs

Content
View full analysis

Vulnerability Details

File Location: scripts/fetch_edinet.py:45-46, scripts/fetch_edinet.py:70-71, scripts/fetch_edinet.py:138-140, and scripts/fetch_edinet.py:157-159
Vulnerability Type: Credential exposure through URL query parameters and unsanitized error logging
Risk Level: Medium

Vulnerable Code

python
def search_documents(api_key: str, target_date: str) -> list[dict]:
    """指定日の書類一覧を取得."""
    url = f"{EDINET_BASE}/documents.json"
    params = {"date": target_date, "type": 2, "Subscription-Key": api_key}
    resp = httpx.get(url, params=params, timeout=30)
    resp.raise_for_status()
    data = resp.json()
    return data.get("results", [])
python
def download_pdf(api_key: str, doc_id: str, output_dir: Path) -> Path | None:
    """EDINET から書類 PDF をダウンロード (ZIP展開)."""
    url = f"{EDINET_BASE}/documents/{doc_id}"
    params = {"type": 2, "Subscription-Key": api_key}  # type=2: PDF
    resp = httpx.get(url, params=params, timeout=60)
    resp.raise_for_status()
python
try:
    results = search_documents(api_key, check_date)
except httpx.HTTPError as e:
    print(f"  API エラー: {e}", file=sys.stderr)
    continue
python
try:
    path = download_pdf(api_key, doc_id, output_dir)
    if path:
        downloaded.append(str(path))
        print(f"  ✓ 保存: {path}", file=sys.stderr)
except httpx.HTTPError as e:
    print(f"  ダウンロードエラー: {e}", file=sys.stderr)

Technical Analysis

The EDINET_API_KEY secret is inserted into the URL query string as Subscription-Key. HTTP client exceptions may include the complete request URL in their string representation. The code writes raw httpx.HTTPError objects to standard error without redaction, creating a potential path for the API key to enter terminal histories, Agent logs, CI logs, centralized logging systems, or diagnostic records.

Query-string credentials can also be retained ...[truncated 1767 chars]

Remediation
View remediation

Remediation Suggestions

  1. Do not print raw exception objects for authenticated HTTP requests. Log a sanitized error type and status code instead:

    python
    except httpx.HTTPStatusError as e:
        print(
            f"EDINET API error: HTTP {e.response.status_code}",
            file=sys.stderr,
        )
    except httpx.RequestError:
        print("EDINET API request failed", file=sys.stderr)
    
  2. If the EDINET API supports authentication through an HTTP header, place the subscription key in that header rather than the query string:

    python
    headers = {"Subscription-Key": api_key}
    params = {"date": target_date, "type": 2}
    resp = httpx.get(url, params=params, headers=headers, timeout=30)
    
  3. If EDINET requires the key as a query parameter, introduce explicit URL sanitization before any request-related information is logged. Replace the value of Subscription-Key with [REDACTED].

  4. Configure CI, Agent, and centralized logging systems to redact known credential parameter names, including Subscription-Key.

  5. Rotate the current API key if existing logs may contain failed EDINET request URLs. Restrict access to historical logs and remove exposed copies where feasible.

  6. Add automated tests that induce request and HTTP status failures, capture standard error, and assert that the configured API key never appears in output.

T08 · Insecure Dependencies

Note
Location
SKILL.md:5
Finding

Third-party Python dependencies are installed without version or integrity constraints

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:5
Vulnerability Type: Unpinned third-party dependencies and non-reproducible installation
Risk Level: Low

Vulnerable Code

yaml
metadata: {"openclaw":{"requires":{"bins":["python3"],"anyBins":["uv","pip3"]},"emoji":"🦞","os":["darwin","linux"],"install":[{"type":"uv","packages":["httpx","pdfplumber","openpyxl"]}]}}

Technical Analysis

The Skill requests installation of httpx, pdfplumber, and openpyxl without exact versions, hashes, or a committed dependency lock file. Each installation can therefore resolve to a different package release.

The package names are consistent with the Skill's declared network, PDF-processing, and spreadsheet-export functionality. No typo-squatted name, untrusted registry, or known malicious dependency is shown in the audited files. The risk arises from mutable dependency resolution: a compromised upstream release or an unsafe future release could be selected automatically without a new review of this Skill.

Python packages may execute package-controlled behavior during installation or when imported by the scripts. Consequently, dependency resolution is part of the Skill's effective trusted code base.

Attack Path

  1. An attacker compromises an upstream package distribution channel or publishes a malicious release through a compromised maintainer account.
  2. A user installs the Skill after that release becomes the version selected by the resolver.
  3. Because no reviewed version or integrity hash is specified, the installation accepts the newly resolved artifact.
  4. Malicious package behavior executes during installation or when fetch_edinet.py, parse_pdf.py, or export_excel.py imports the dependency.
  5. The package obtains the privileges of the user or Agent process performing the installation or running the script.

This is a supply-chain risk rather than evidence that any currently named dependency is ...[truncated 688 chars]

Remediation
View remediation

Remediation Suggestions

  1. Pin every runtime dependency to an exact, reviewed version:

    yaml
    packages:
      - "httpx==<reviewed-version>"
      - "pdfplumber==<reviewed-version>"
      - "openpyxl==<reviewed-version>"
    
  2. Generate and commit a lock file using the selected package-management workflow so that direct and transitive dependencies resolve reproducibly.

  3. Use hash verification where supported. Maintain hashes for all resolved wheels or source distributions and reject artifacts whose hashes do not match.

  4. Install only from an explicitly configured trusted package index. Disable unneeded extra indexes to reduce dependency-confusion exposure.

  5. Add automated dependency vulnerability and provenance scanning to the release process. Review updates before changing pinned versions.

  6. Perform installation in an isolated virtual environment under a non-privileged account. Do not expose unrelated credentials or sensitive directories during dependency installation.

  7. Retain only the three dependencies required by the declared features and periodically verify that each remains necessary.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (11)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The description promises substantive M&A analysis functionality: analyzing companies, valuating businesses, parsing 決算書, using public filings, and generating acquisition simulation reports. The actual code only formats input JSON into an Excel workbook. While the sheet names and labels are finance/M&A related, the code assumes all financial and valuation data are already provided. Its primary purpose is file export, not analysis. This is a material description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents a full analytical M&A/due-diligence capability. The supplied code only retrieves EDINET filings and stores downloaded PDFs/binary files. It does not inspect document contents, extract financial data, compute valuations, analyze companies, or generate reports. It also mentions 官報 in the description, but the implementation only calls the EDINET API and merely prints a manual 官報 URL on failure. This is a material description-behavior mismatch in primary purpose and capabilities.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description promises high-level financial and M&A analysis capabilities: company valuation, due diligence, acquisition simulations, and analysis of public filings. The supplied code does not perform any of those domain-specific tasks. Instead, it only parses a local PDF using pdfplumber, extracts text and tables, and outputs the raw content. While PDF extraction could be a supporting component for a due diligence system, this code chunk’s primary purpose is generic document extraction rather than M&A analysis. There is no valuation logic, no financial-statement interpretation, no EDINET/官報 integration, no company analysis, and no report generation. Therefore the description materially overstates and misrepresents the actual behavior.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill declares no explicit tool scope or permission boundaries despite instructing use of environment variables, network retrieval, and local file write operations. That creates an over-privileged execution surface where the agent may access network, environment secrets, or write files without clear user-visible constraints, increasing the risk of unintended data access or exfiltration.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The invocation text uses broad trigger phrases like company valuation and due diligence without clear exclusion rules, making accidental or over-broad auto-invocation more likely. In a skill that can fetch network resources, parse user-supplied documents, and write outputs, overly permissive triggering can cause unintended tool use, unnecessary data processing, or surprising access patterns.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The instructions define the persona, tone, and reporting format entirely in Japanese and require specific Japanese phrasing conventions, but do not state that the user may choose another language. This can force a locale/language preference without user opt-in, which is a natural-language policy concern.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The file's docstring, CLI descriptions, error messages, sheet titles, and exported labels are all hard-coded in Japanese, which imposes a specific language/locale on all users. The file does not provide any opt-in, language selection, or justification that this is intentionally limited to a Japanese-only regional context.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/fetch_edinet.py (reported line 34)May include surrounding context.

python
print("ERROR: httpx が必要です。`uv pip install httpx` を実行してください。", file=sys.stderr)
    sys.exit(1)

EDINET_BASE = "https://api.edinet-fsa.go.jp/api/v2"


def search_documents(api_key: str, target_date: str) -> list[dict]:

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

This markdown reference forces a specific language/locale for users by providing all instructions and mappings only in Japanese. Under the policy, language constraints should either offer user choice or be clearly documented as a justified region-specific limitation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The entire guide is written only in Japanese, with no indication that users may choose another language or locale. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy violation unless the locale constraint is explicitly justified.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The module docstring and CLI description describe the tool as extracting PDF text with page numbers, but the implementation also calls extract_tables() and emits table-specific sections. That is broader behavior than the stated purpose, even though it remains related to PDF parsing.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.