Back to skill

Security audit

境外投资补税测算

Security checks for vulnerabilities and agentic risk

Overview

The skill does not show malware behavior, but it needs review because it handles sensitive financial statements and its tax calculations and raw outputs are not safely scoped enough for reliable tax use.

Install only if you are comfortable reviewing the code and limiting inputs to local, redacted copies of the minimum brokerage statements needed. Do not rely on its tax number without manual verification, especially for mixed years, mixed currencies, partial records, non-HTSC brokers, or bank statements. Treat CLI JSON output as sensitive because it can include names, full account identifiers, addresses, holdings, balances, and transactions.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/crs_generator.py:64
Finding

Unnecessary Extraction and Unredacted Output of Personal Financial Data

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/offset.py:4
Finding

Annual Tax Aggregation Does Not Enforce Year Boundaries

Content
View full analysis
OffsetResult: transfer_net = max(sum(sale.gain for sale in matched_sales), 0) dividend_net = sum( item.amount for item in income_items if item.kind == "dividend" ) interest_net = sum( item.amount for item in income_items if item.kind == "interest" ) return OffsetResult( transfer_net=transfer_net, dividend_net=dividend_net, interest_net=interest_net, ) ``` The model includes year fields that the aggregation does not use: ```python @dataclass class MatchedSale: sale: Trade buy: Trade qty: float proceeds: float cost: float gain: float year: int | None = None ``` ```python @dataclass class IncomeItem: kind: str amount: float year: int ``` ### Technical Analysis Despite being named `offset_year`, the function does not accept a target year and does not filter either matched sales or income items by year. All supplied gains, dividends, and interest are aggregated into one result. The pipeline also provides no target-year boundary before invoking this function. Consequently, records from different tax years can be offset or taxed together, contrary to the Skill's annual-estimation behavior. ### Attack Path 1. The caller supplies trades or income records from more than one year. 2. Trade matching returns all matched sales without imposing a target tax year. 3. `offset_year()` sums every gain and every qualifying income item. 4. Losses from one year may offset gains from another, while dividends and interest from multiple years are combined. 5. The resulting value is passed to `estimate_tax()` and presented as a single annual estimate. ### Impact Assessment ...[truncated 302 chars]
Remediation
View remediation
OffsetResult: ``` - Derive sale years from validated sale dates or populate and validate `MatchedSale.year`. - Filter all sales and income items against `target_year` before aggregation. - Reject records with missing, malformed, or contradictory dates rather than silently including them. - Ensure purchase records from earlier years may establish cost basis without causing their year to become the sale-tax year. - Add regression tests containing mixed-year gains, losses, dividends, and interest. - Include the selected year in `OffsetResult` and `ClientReport` so downstream code cannot lose the calculation scope. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/matching.py:20
Finding

Partially Unmatched Sales Are Incorrectly Included in the Tax Basis

Content
View full analysis
0 and buys[key]: buy, available_qty = buys[key][0] match_qty = min(remaining_qty, available_qty) proceeds = trade.amount * match_qty / trade.qty cost = buy.amount * match_qty / buy.qty matched.append( MatchedSale( sale=trade, buy=buy, qty=match_qty, proceeds=proceeds, cost=cost, gain=proceeds - cost, ) ) remaining_qty -= match_qty available_qty -= match_qty if available_qty == 0: buys[key].popleft() else: buys[key][0][1] = available_qty if remaining_qty > 0: unmatched.append( UnmatchedSale( sale=trade, qty=remaining_qty, proceeds=trade.amount * remaining_qty / trade.qty, ) ) ``` The partially matched result is then taxed: ```python def run_pipeline(trades: list[Trade], income_items: list[IncomeItem]) -> ClientReport: matched, unmatched = match_trades(trades) offset = offset_year(matched, income_items) taxed = estimate_tax(offset) report = build_client_report(taxed, unmatched) report.unmatched = unmatched report.tax_due_inputs = taxed return report ``` ### Technical Analysis The matching implementation commits each matched lot immediately. If the available purchase history covers only part of a sale, the matched portion remains in `matched`, while only the residual quantity is placed in `unmatched`. This contradicts the documented policy that a sale lacking complete acquisition records must be excluded in full. Because `run_pipeline()` taxes all entries in `matched`, an incompletely supported sale contributes to the tax estimate. ...[truncated 1017 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/matching.py:7
Finding

Trade Matching and Tax Aggregation Combine Incompatible Currencies

Content
View full analysis
tuple[list[MatchedSale], list[UnmatchedSale]]: buys: dict[tuple[str, str | None], deque[list[float | Trade]]] = defaultdict(deque) matched: list[MatchedSale] = [] unmatched: list[UnmatchedSale] = [] for trade in sorted(trades, key=lambda item: item.date): key = (trade.symbol, trade.account) if trade.side == "buy": buys[key].append([trade, trade.qty]) continue if trade.side != "sell": continue remaining_qty = trade.qty while remaining_qty > 0 and buys[key]: buy, available_qty = buys[key][0] match_qty = min(remaining_qty, available_qty) proceeds = trade.amount * match_qty / trade.qty cost = buy.amount * match_qty / buy.qty matched.append( MatchedSale( sale=trade, buy=buy, qty=match_qty, proceeds=proceeds, cost=cost, gain=proceeds - cost, ) ) ``` ```python def offset_year( matched_sales: list[MatchedSale], income_items: list[IncomeItem] ) -> OffsetResult: transfer_net = max(sum(sale.gain for sale in matched_sales), 0) dividend_net = sum( item.amount for item in income_items if item.kind == "dividend" ) interest_net = sum( item.amount for item in income_items if item.kind == "interest" ) ``` The trade model contains a currency field that is not enforced: ```python @dataclass class Trade: side: str symbol: str qty: float amount: float currency: str date: str source: str ``` ### Technical Analysis The FIFO k ...[truncated 1420 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/matching.py:20
Finding

Missing Numeric Validation Allows Calculation Failure and Negative Tax Results

Content
View full analysis
0 and buys[key]: buy, available_qty = buys[key][0] match_qty = min(remaining_qty, available_qty) proceeds = trade.amount * match_qty / trade.qty cost = buy.amount * match_qty / buy.qty ``` ```python def estimate_tax(offset: OffsetResult) -> OffsetResult: return OffsetResult( transfer_net=offset.transfer_net, dividend_net=offset.dividend_net, interest_net=offset.interest_net, dividend_tax=round((offset.dividend_net + offset.interest_net) * DIVIDEND_RATE, 2), transfer_tax=round(offset.transfer_net * TRANSFER_RATE, 2), ) ``` ### Technical Analysis The data classes accept unrestricted floating-point values, and the calculation functions do not validate them. Zero quantities can cause division by zero. Negative, infinite, or `NaN` quantities and amounts can produce invalid matching behavior or non-finite results. Negative dividend or interest amounts are directly multiplied by the tax rate and can create negative tax. Validation cannot be delegated safely to Python type annotations because dataclass annotations are not runtime constraints. The pipeline accepts already-created model objects without an intervening validation layer. ### Attack Path 1. A parser, integration, or direct caller creates a `Trade` with a zero or invalid quantity, or an `IncomeItem` with a negative or non-finite amount. 2. `match_trades()` divides by the invalid quantity, causing a runtime exception or corrupt arithmetic. 3. Alternatively, `estimate_tax()` multiplies negative or non-finite income directly by the tax rate. 4. The pipeline either terminates, producing denial of service, or returns a negative/non-finite tax estimate. ### Impact Assessm ...[truncated 260 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (10)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description promises a tax-focused workflow: estimating China IIT, preparing documents for the tax bureau, and offsetting gains/losses based on overseas brokerage or bank statements and local chat inputs. The supplied code does something narrower and different: it parses local PDF brokerage statements into structured JSON. It extracts fields such as client/account info, holdings, balances, transactions, and margin interest, and only for supported broker formats detected in the PDF text. There is no tax calculation logic, no tax-form/document generation, no gain/loss aggregation or offset engine, no bank statement parser, and no integration with WorkBuddy or 豆包 Work. While statement parsing could be a supporting component for a tax tool, this code chunk by itself materially underdelivers relative to the declared primary purpose, so it is a clear description-behavior mismatch.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: pytest==8.3.5 — 2 advisory(ies): CVE-2025-71176 (pytest has vulnerable tmpdir handling); CVE-2025-71176 (pytest has vulnerable tmpdir handling)

High
Category
Supply Chain
Confidence
95% confidence
Finding

The file pins pytest==8.3.5, which is reported as affected by a tmpdir-handling vulnerability. Even though pytest is typically a development/test dependency rather than a production runtime component, keeping a known-vulnerable version can still expose developer or CI environments if untrusted tests, plugins, or filesystem interactions are involved. In this skill context, the dependency appears unrelated to the core tax-report functionality, which makes the issue less dangerous than a vulnerable runtime package but still a real supply-chain hygiene problem.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The entire document is written only in Chinese and does not indicate that the skill is region-specific or that users may choose another language. Under the language/locale policy rule, this can be a natural-language policy violation when a specific language is imposed without user opt-in or justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The entire interview script is written as mandatory Chinese-language instructions and does not indicate that users may choose another language or locale. This is a natural-language policy concern because it imposes a specific language experience without documented opt-in or justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

Line L6 states that the default result shown to customers is a 'plain-language number' using Chinese phrasing, and the entire document is written only in Chinese with no indication that users may choose another language or locale. This can violate a language/locale policy when the skill implicitly enforces one language without user opt-in.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The file docstring narrows the script's behavior to parsing broker statement PDFs and explicitly says not to generate official tax filings. In contrast, the manifest presents the skill as a CRS/report generator used to prepare tax-bureau documents and support gain/loss offset workflows, which this code does not implement.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

This is an active contradiction between inline documentation and the declared skill intent. The code itself only parses PDF content into JSON, and the docstring warns against official filing generation despite the skill being positioned for tax-report preparation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

SQP-3 applies to all file types and covers language or locale policy violations. All user-facing prompts are written only in Chinese, which forces a specific language experience without any indication that the user can choose another language or has opted in to Chinese.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The workflow explicitly asks users to upload monthly and annual brokerage statements, which commonly contain highly sensitive financial and personal information. Without a user-facing warning, minimization guidance, or clear indication of what is necessary, users may overshare confidential data and increase privacy and data-handling risk.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The report section titles and user-facing report strings are hard-coded in Chinese, which imposes a specific language on users. The file does not indicate any user opt-in, language selection, or documented region-specific requirement that would justify the locale restriction.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.