Back to skill

Security audit

Amazon 产品定位建议

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly disclosed, but it goes beyond product-positioning analysis into paid account actions, persistent confirmation settings, monitoring, and unrestricted local exports that users should review carefully.

Install only if you trust ARI and are comfortable with the agent using an ARI API key to make paid requests and manage account state. Say "only quote, do not execute" when you want pricing only, keep auto-confirm disabled if you want per-action approval, do not set custom API base environment variables unless you control the host, and avoid using export --out on sensitive paths.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/ari.py:1474
Finding
Arbitrary Local File Overwrite Through the Export Output Path<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ari.py:1474-1475`, with attacker-controlled path propagation at `scripts/ari.py:1590-1604` and argument definition at `scripts/ari.py:1903-1908` **Vulnerability Type**: Unrestricted file write and arbitrary file overwrite **Risk Level**: Medium ### Vulnerable Code ```python def request_download(path, params, dest): """Download a non-JSON response (CSV / HTML / Markdown) to a local file.""" query = {"method": "GET", "path": path, "params": {k: v for k, v in (params or {}).items() if v not in (None, "")}, "payload": None} url = base_url() + path if query["params"]: url += "?" + urllib.parse.urlencode(query["params"], doseq=True) headers = {"Authorization": "Bearer " + require_key(), "User-Agent": user_agent()} try: req = urllib.request.Request(url, headers=headers, method="GET") with urllib.request.urlopen(req, timeout=TIMEOUT_SEC) as resp: note_release(resp.headers) ctype = resp.headers.get("Content-Type", "") body = resp.read() if "application/json" in ctype: out = json.loads(body.decode("utf-8", errors="replace")) if isinstance(out, dict): out["_query"] = query return out tail = body[-300:].decode("utf-8", errors="replace") if "# export error:" in tail: return error_obj("ARI_EXPORT_ERROR", 200, tail.split("# export error:", 1)[1].strip(), "Export failed before completion; the incomplete file was not written.", query) with open(dest, "wb") as fh: fh.write(body) ``` The destination is derived directly from the command-line argument: ```python def cmd_export(args): """Export reviews or reports to a local file.""" if args.report_id: ...[truncated 4381 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Use a dedicated export directory** Resolve all default and user-selected output paths under a controlled directory such as `~/.ari/exports`: ```python export_root = os.path.realpath(os.path.expanduser("~/.ari/exports")) os.makedirs(export_root, mode=0o700, exist_ok=True) ``` 2. **Validate the resolved destination** Reject destinations that resolve outside the approved directory: ```python candidate = os.path.realpath(os.path.join(export_root, requested_name)) if os.path.commonpath([export_root, candidate]) != export_root: raise ValueError("Export path escapes the approved export directory") ``` 3. **Refuse silent replacement** Create new files exclusively rather than truncating existing files: ```python flags = os.O_WRONLY | os.O_CREAT | os.O_EXCL if hasattr(os, "O_NOFOLLOW"): flags |= os.O_NOFOLLOW fd = os.open(candidate, flags, 0o600) with os.fdopen(fd, "wb") as fh: fh.write(body) ``` 4. **Reject symbolic links** Use `O_NOFOLLOW` where available and perform an `lstat`-based check on platforms that do not support it. Security-sensitive code should account for time-of-check/time-of-use races rather than relying solely on a preliminary path check. 5. **Write atomically** Write the response to a securely created temporary file in the approved destination directory, flush and synchronize it, and then rename it atomically. Do not replace an existing destination unless the user has explicitly requested overwrite behavior. 6. **Require explicit overwrite confirmation** If overwriting is a necessary feature, add a separate `--overwrite` option and clearly display the resolved target path before performing the write. Agent instructions should require direct user approval before using this option. 7. **Prefer filenames rather than unrestricted paths** Consider changing `--out` to accept only a filename. If arbitrary paths must remain ...[truncated 107 chars]
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (27)

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

Critical
Category
Data Flow
Content
headers = {"Authorization": "Bearer " + require_key(), "User-Agent": user_agent()}
    try:
        req = urllib.request.Request(url, headers=headers, method="GET")
        with urllib.request.urlopen(req, timeout=TIMEOUT_SEC) as resp:
            note_release(resp.headers)
            ctype = resp.headers.get("Content-Type", "")
            body = resp.read()
Confidence
74% confidence
Finding
The export/download path sends the bearer API key to whatever base_url() resolves to, including operator-approved custom hosts. While the dual-variable gate reduces accidental leakage, this still creates a credential exfiltration path if a session, wrapper, or user is tricked into enabling both variables, and export endpoints may also write attacker-controlled responses to local files.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill invokes shell, network, environment, and file-write capabilities but does not declare permissions or prominently bound those capabilities at the metadata layer. This creates a trust and consent gap: a user selecting a seemingly analytical skill may unknowingly authorize local key storage, network calls, and state-changing operations, which increases the risk of unintended data access or account-impacting actions.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared purpose is narrow product-positioning analysis, but the documented behavior includes credential setup, balance checks, auto-confirmation changes, paid task submission, recurring schedule management, watch CRUD, exports, and other account-affecting operations. This mismatch is dangerous because users and platform policy may treat the skill as low-risk analysis while it can modify account state, trigger charges, persist secrets locally, and perform broader operational workflows.

Description-Behavior Mismatch

High
Confidence
95% confidence
Finding
The file implements broad product-operations workflows that go beyond the declared evidence-driven product-positioning scope. In an agent setting, this expands the action surface from analysis into operational task execution, increasing the chance that the skill is invoked for unintended paid or state-changing actions without users understanding the mismatch.

Description-Behavior Mismatch

Medium
Confidence
93% confidence
Finding
Competitive monitoring, alerts, leaderboard, workbench, and similar features materially exceed the manifest's stated use of positioning discussion based on evidence. This scope creep is dangerous in an agent ecosystem because orchestration layers may grant or invoke capabilities based on the advertised purpose, not the much broader actual behavior.

Context-Inappropriate Capability

High
Confidence
96% confidence
Finding
Generating AI reply or handling advice for negative reviews is an action-oriented remediation capability, not evidence-only positioning analysis. In agent use, this can steer customer communications or policy-sensitive appeal actions without the tighter approvals and guardrails expected for outward-facing or operational content generation.

Context-Inappropriate Capability

Medium
Confidence
90% confidence
Finding
Mutating review workbench status changes operational state and workflow tracking, which is unrelated to evidence-only positioning discussion. This matters because a seemingly analytical skill can silently become a write-capable operations tool, enabling unintended state changes by an agent or confused user.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The module docstring states paid commands require explicit --confirm after a preview, but the code includes multiple auto-confirm paths that can execute and spend credits without that flag. This mismatch can mislead users and calling agents into believing the tool is non-spending unless they opt in, weakening informed consent around paid actions.

Intent-Code Divergence

High
Confidence
93% confidence
Finding
The documented behavior materially exceeds the declared skill scope of evidence-driven product positioning and introduces operational workflows such as monitoring, reports, exports, alerts, and account-scoped actions. This scope mismatch is dangerous because users and reviewers may grant trust, permissions, or approval under a narrower description while the skill can drive materially broader actions and recurring costs.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The README is written entirely in Chinese and directs the user to interact in Chinese, without offering a language choice or documenting a necessary locale restriction. This can exclude or mislead users who operate in other languages, and may cause misunderstanding of pricing, authorization, or workflow steps in a security-relevant setup flow.

Vague Triggers

Medium
Confidence
84% confidence
Finding
The natural-language triggers are broad and encourage automatic extraction of parameters and default execution paths, which can cause the skill to activate on common product-analysis requests without clear boundaries. In this skill's context, broad activation is riskier because the same skill also contains paid and state-changing workflows, so ambiguous matching can lead to unintended network actions or billing-related flows.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
1. 运行 `check`,确认账户、邮箱验证状态和可用积点。
2. 用户要 VOC / 评论分析报告时,默认运行 `voc <ASIN> --site <站点>`。
   **返回里有 `autoConfirmed: true` 就说明已经直接生成了**(1.4.5 起:服务端对前几次小额
   付费操作免确认,用户先拿到结果再谈钱),此时把报告讲给用户,并转述 `autoConfirmNote`
   (本次扣了多少、还剩几次免确认、之后会先问)。**不要在拿到结果后再补问「要不要生成」。**
3. 返回 `confirmationRequired: true` 才需要用户确认:报出 `totalCredits` 与余额,
Confidence
97% confidence
Finding
The workflow instructs the agent to run a paid VOC command by default and accept `autoConfirmed: true` as sufficient to proceed without fresh user confirmation. Even if the backend allows this, it removes an interactive consent checkpoint at execution time and can result in charges or data collection based on inferred intent rather than explicit approval.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
用户只说自然语言。网页是补充视图(图表、分享链接、海报),不是把人送走的地方。

**确认与扣点**
- 报价返回 `autoConfirm: true` 时直接生成,不要再问「要不要」。生成后一句话交代:本次扣了多少、
  还剩几次免确认(或「免费版小额不问」)。策略由服务端决定:免费版小额不问;付费版前几次不问,之后先问。
- 用户说「以后别问了 / 50 以内直接做」→ 运行 `autoconfirm 50`;说「以后每次先问我」→ `autoconfirm off`;
  说「恢复默认」→ `autoconfirm default`。这是唯一需要你代用户设置的东西,设完复述一句当前规则。
Confidence
97% confidence
Finding
This explicitly tells the agent to execute generation when the quote reports `autoConfirm: true`, without asking the user. In a skill that can spend credits and launch data collection, backend-side auto-confirm semantics do not eliminate the need for user-facing consent, so this creates a meaningful risk of unintended paid actions.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
**确认与扣点**
- 报价返回 `autoConfirm: true` 时直接生成,不要再问「要不要」。生成后一句话交代:本次扣了多少、
  还剩几次免确认(或「免费版小额不问」)。策略由服务端决定:免费版小额不问;付费版前几次不问,之后先问。
- 用户说「以后别问了 / 50 以内直接做」→ 运行 `autoconfirm 50`;说「以后每次先问我」→ `autoconfirm off`;
  说「恢复默认」→ `autoconfirm default`。这是唯一需要你代用户设置的东西,设完复述一句当前规则。
- 报价需要确认时,只说两个数:这次多少积点、余额多少,然后等用户一个「好」。采集是**固定单价**:直接说「15 积点/页 × 3 页 = 45 积点」,不要说成「预计 / 最多」——价格不会浮动;商品评论不够这么多页时只收实际采到的页数,差额自动退回(`pricingNote` 已写好这句)。不要罗列参数。
Confidence
95% confidence
Finding
The same line also permits disabling prompts (`autoconfirm off/default` variants) through natural-language interpretation, which is still a sensitive authorization-setting change. This is dangerous because it modifies the safety boundary for subsequent paid commands beyond the immediate task, creating persistent risk from ambiguous or manipulated instructions.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
**确认与扣点**
- 报价返回 `autoConfirm: true` 时直接生成,不要再问「要不要」。生成后一句话交代:本次扣了多少、
  还剩几次免确认(或「免费版小额不问」)。策略由服务端决定:免费版小额不问;付费版前几次不问,之后先问。
- 用户说「以后别问了 / 50 以内直接做」→ 运行 `autoconfirm 50`;说「以后每次先问我」→ `autoconfirm off`;
  说「恢复默认」→ `autoconfirm default`。这是唯一需要你代用户设置的东西,设完复述一句当前规则。
- 报价需要确认时,只说两个数:这次多少积点、余额多少,然后等用户一个「好」。采集是**固定单价**:直接说「15 积点/页 × 3 页 = 45 积点」,不要说成「预计 / 最多」——价格不会浮动;商品评论不够这么多页时只收实际采到的页数,差额自动退回(`pricingNote` 已写好这句)。不要罗列参数。
Confidence
95% confidence
Finding
The same line also permits disabling prompts (`autoconfirm off/default` variants) through natural-language interpretation, which is still a sensitive authorization-setting change. This is dangerous because it modifies the safety boundary for subsequent paid commands beyond the immediate task, creating persistent risk from ambiguous or manipulated instructions.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
voc B0... --site amz_us 是完整 VOC 的入口:先取得报价,已有足够评论时使用当前分析价格;
数据不足时合并采集与分析费用。符合服务端免确认规则且总额不超过上限时可能直接生成,
返回 autoConfirmed。否则返回 confirmationRequired,取得用户同意后追加 --confirm,
自动完成必要采集、等待、分析和归档。只读询价应使用 quote / collect 报价入口。

`collect --asin B0... --site amz_us --pages 3` 只返回报价;确认后追加
Confidence
77% confidence
Finding
This section states that `voc` may directly generate a paid report when server-side auto-confirm rules match, combining collection, waiting, analysis, and archiving without a fresh confirmation in the current interaction. In an agent context, that can lead to unintended billable actions or state changes if the agent treats the command as informational rather than transactional, especially because it may perform multiple operations automatically.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
先调用 `quote --type ...`。报价字段:
`type, basePrice, price, sampledReviews, totalReviews, balance, sufficient`,
另有(1.4.5):`autoConfirm`(true = 服务端首次体验策略允许免确认直接生成)、
`autoConfirmMaxCredits`(免确认单次上限,采集 + 报告合计)、`autoConfirmRemaining`(还剩几次)、
`autoConfirmNote`、`webUrl`(该产品的网页报告页)。`sampleCap` / `degraded` 表示 Free 样本封顶与轻量模型。
`voc` / `analyze` 在 autoConfirm 命中时会直接生成,返回 `autoConfirmed: true` 与 `autoConfirmNote`,
Confidence
84% confidence
Finding
The documented quote response exposes `autoConfirm` and related fields that signal the service may allow immediate execution without another prompt. In an agent skill, this is dangerous because downstream logic may interpret `autoConfirm=true` as sufficient authorization and proceed with paid analysis, causing unauthorized spending or actions on the user's account.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
先调用 `quote --type ...`。报价字段:
`type, basePrice, price, sampledReviews, totalReviews, balance, sufficient`,
另有(1.4.5):`autoConfirm`(true = 服务端首次体验策略允许免确认直接生成)、
`autoConfirmMaxCredits`(免确认单次上限,采集 + 报告合计)、`autoConfirmRemaining`(还剩几次)、
`autoConfirmNote`、`webUrl`(该产品的网页报告页)。`sampleCap` / `degraded` 表示 Free 样本封顶与轻量模型。
`voc` / `analyze` 在 autoConfirm 命中时会直接生成,返回 `autoConfirmed: true` 与 `autoConfirmNote`,
并附 `web.report` / `web.product` 网页链接。
Confidence
84% confidence
Finding
This duplicate finding points to the same direct-execution-on-autoConfirm behavior. The risk is not malicious code execution but loss of user control over paid operations, which is material in an agent workflow capable of invoking external APIs on the user's behalf.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
先调用 `quote --type ...`。报价字段:
`type, basePrice, price, sampledReviews, totalReviews, balance, sufficient`,
另有(1.4.5):`autoConfirm`(true = 服务端首次体验策略允许免确认直接生成)、
`autoConfirmMaxCredits`(免确认单次上限,采集 + 报告合计)、`autoConfirmRemaining`(还剩几次)、
`autoConfirmNote`、`webUrl`(该产品的网页报告页)。`sampleCap` / `degraded` 表示 Free 样本封顶与轻量模型。
`voc` / `analyze` 在 autoConfirm 命中时会直接生成,返回 `autoConfirmed: true` 与 `autoConfirmNote`,
并附 `web.report` / `web.product` 网页链接。
Confidence
84% confidence
Finding
This duplicate finding points to the same direct-execution-on-autoConfirm behavior. The risk is not malicious code execution but loss of user control over paid operations, which is material in an agent workflow capable of invoking external APIs on the user's behalf.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
`type, basePrice, price, sampledReviews, totalReviews, balance, sufficient`,
另有(1.4.5):`autoConfirm`(true = 服务端首次体验策略允许免确认直接生成)、
`autoConfirmMaxCredits`(免确认单次上限,采集 + 报告合计)、`autoConfirmRemaining`(还剩几次)、
`autoConfirmNote`、`webUrl`(该产品的网页报告页)。`sampleCap` / `degraded` 表示 Free 样本封顶与轻量模型。
`voc` / `analyze` 在 autoConfirm 命中时会直接生成,返回 `autoConfirmed: true` 与 `autoConfirmNote`,
并附 `web.report` / `web.product` 网页链接。
Confidence
82% confidence
Finding
The line indicates that auto-confirmed executions return metadata and links after the action has already occurred. That post-hoc signaling is insufficient protection in an agent environment, because the sensitive event is the unreviewed paid execution itself, not merely its reporting.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
if not ok(quote):
        return quote
    q_data = data_of(quote) or {}
    # 首次体验免确认(服务端策略 skill.autoConfirm):前几次小额直接生成,不再多问一轮。
    auto_confirmed = False
    if not confirm and q_data.get("autoConfirm") and q_data.get("sufficient"):
        confirm = True
Confidence
97% confidence
Finding
This code automatically flips a paid analysis from quote-only to confirmed execution when the server reports autoConfirm and sufficient balance, even if the caller did not pass --confirm. In an agent context, that undermines the advertised explicit-consent boundary and can cause unintended spending or analysis runs.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
return quote
    q_data = data_of(quote) or {}
    # 首次体验免确认(服务端策略 skill.autoConfirm):前几次小额直接生成,不再多问一轮。
    auto_confirmed = False
    if not confirm and q_data.get("autoConfirm") and q_data.get("sufficient"):
        confirm = True
        auto_confirmed = True
Confidence
96% confidence
Finding
The auto_confirmed flag marks that the system proceeded with a paid action without the explicit confirmation flag on that invocation. This evidences behavior that can surprise users and downstream agents about when credit-consuming actions occur.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
q_data = data_of(quote) or {}
    # 首次体验免确认(服务端策略 skill.autoConfirm):前几次小额直接生成,不再多问一轮。
    auto_confirmed = False
    if not confirm and q_data.get("autoConfirm") and q_data.get("sufficient"):
        confirm = True
        auto_confirmed = True
    if not confirm:
Confidence
96% confidence
Finding
The condition checks server-provided autoConfirm and sufficient balance to bypass the explicit confirmation step. That creates an execution path where paid actions occur based on stored preferences or server policy rather than immediate user consent.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
if plan is not None and plan["balance"]["note"]:
        combined_quote["siteNote"] = plan["balance"]["note"]
    combined_quote["webUrl"] = analysis_quote.get("webUrl")
    # 首次体验免确认:服务端 autoConfirm=true 且「采集 + 报告」合计不超过单次上限时,直接跑完。
    # 在聊天里多问一句「确认吗」,很多用户就不回了——先让他拿到结果。
    auto_max = int(analysis_quote.get("autoConfirmMaxCredits") or 0)
    auto_confirmed = (not args.confirm and bool(analysis_quote.get("autoConfirm"))
Confidence
98% confidence
Finding
This VOC flow explicitly supports auto-confirming a combined paid workflow that may include both review collection and report generation when autoConfirm is enabled. Because it can trigger multiple billable actions without --confirm, the impact is higher than a single analysis call and contradicts the skill's own stated confirmation guarantees.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
combined_quote["webUrl"] = analysis_quote.get("webUrl")
    # 首次体验免确认:服务端 autoConfirm=true 且「采集 + 报告」合计不超过单次上限时,直接跑完。
    # 在聊天里多问一句「确认吗」,很多用户就不回了——先让他拿到结果。
    auto_max = int(analysis_quote.get("autoConfirmMaxCredits") or 0)
    auto_confirmed = (not args.confirm and bool(analysis_quote.get("autoConfirm"))
                      and sufficient and total_credits <= auto_max)
    if not args.confirm and not auto_confirmed:
Confidence
98% confidence
Finding
The auto_confirmed condition evaluates whether to run collection plus analysis automatically based on server-side policy and available balance. In an agent environment, this can spend credits and collect data without a fresh user approval on the current request.

Static analysis

No suspicious patterns detected.