Back to skill

Security audit

Clinical Trial Sample Size & Power / 临床试验样本量与检验效能专家

Security checks for vulnerabilities and agentic risk

Overview

This is a mostly disclosed cloud sample-size calculator, but it needs review because it sends sensitive trial-design parameters to preapproved cloud services and trusts remote response content too broadly.

Install only if you are comfortable sending trial-design parameters, hostname hash, locale, and skill version to the ct-samplesize cloud endpoint. Use --dry-run or CTSS_COZE_MOCK=1 when you want no network send, avoid patient or identifying data in prompts and bug-report descriptions, and be cautious opening generated HTML reports until remote SVG and URL allowlisting are hardened.

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
adapters/rendering.py:217
Finding

Remote SVG Content Is Embedded into Browser-Viewed HTML Without Sanitization

Content
View full analysis

Vulnerability Details

File Location: adapters/rendering.py:217-261, with the reachable invocation path at adapters/rendering.py:817-836 and scripts/samplesize_power.py:850-872
Vulnerability Type: Active-content injection through untrusted SVG
Risk Level: Medium

Complete Code Snippet

python
# adapters/rendering.py:217-261
def build_figure_widget(
    figures: list, titles: list[str], pad: float = 8.0, pad_y: float = 24.0
) -> str:
    """figures: [{svg, type}, ...] → complete inline HTML fragment."""
    blocks = []
    for fig, t in zip(figures, titles):
        inner, vb = extract_svg(fig.get("svg") or "")
        inner = _strip_clip(inner)
        min_x, min_y, max_x, max_y = content_bbox(inner, pad=pad, pad_y=pad_y)
        vb_fit = f"{min_x:g} {min_y:g} {max_x - min_x:g} {max_y - min_y:g}"
        w = max_x - min_x
        blocks.append(
            f'<div><div style="font-size:16px;font-weight:600;margin:0 0 6px;'
            f'color:var(--color-text-primary);">{t}</div>'
            f'<div style="overflow-x:auto;max-width:100%;background:#fff;'
            f'border:0.5px solid var(--color-border-tertiary);'
            f'border-radius:var(--border-radius-md);">'
            f'<svg viewBox="{vb_fit}" style="width:{w:g}px;height:auto;display:block;'
            f'margin:0 auto;">'
            f"{_wrap_points(inner)}</svg></div></div>"
        )
python
# adapters/rendering.py:817-836
svg_figs, titles = [], []
for i, f in enumerate(inline, 1):
    svg_figs.append({"svg": f.get("content", "")})
    ftype = f.get("type") or ""
    grp = type_names.get(ftype)
    titles.append(f"{grp} — {f.get('caption')}" if grp
                  else (f.get("caption") or "Figure %d" % i))
widget = build_figure_widget(svg_figs, titles)
figures_card = (
    '<section class="card"><h2>Figures (%d)</h2>' % len(inline)
    + widget + "</section>"
)
python
# scripts/samplesize_power.py:850-872
render_figures(res.figures, test, figure_mod
...[truncated 3961 chars]
Remediation
View remediation

Remediation Suggestions

  1. Sanitize SVG with a strict allowlist parser

    • Permit only required graphical elements such as svg, g, path, rect, line, circle, polyline, polygon, and text.
    • Permit only known-safe presentation and geometry attributes.
    • Reject scripts, animation elements, foreignObject, embedded HTML, and unknown namespaces.
    • Remove every on* event-handler attribute.
  2. Restrict URL-bearing attributes

    • Reject javascript:, data:, remote HTTP(S), and other active or external URL schemes unless explicitly required and safely handled.
    • Disallow external references in href, xlink:href, CSS url(...), image references, and filter resources.
  3. Escape captions

    • Apply _html_escape() to every remote caption before interpolation.
    • Treat captions exclusively as text rather than trusted markup.
  4. Prefer non-active rendering

    • Rasterize remote SVG into PNG using a hardened converter before browser display.
    • If SVG must remain interactive, place it in a sandboxed iframe without allow-scripts or allow-same-origin.
  5. Apply browser containment

    • Add a restrictive Content Security Policy that blocks scripts, plugins, frames, external connections, and remote resources.
    • Avoid inline scripts in the report so that script-src 'none' can be enforced.
    • Where the host controls the viewer, assign generated reports an isolated opaque origin.
  6. Validate before persistence and display

    • Perform validation immediately after parsing the remote response.
    • Reject unsafe figures rather than saving and advertising them through the __FIGURE__ marker.
    • Record a non-sensitive warning explaining that an unsafe remote figure was omitted.
  7. Add regression tests

    • Test payloads containing <script>, onload, foreignObject, unsafe href, CSS URLs, malformed nested SVG, and caption-based tag injection.
    • Assert that no active content survives into ...[truncated 15 chars]
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
Findings (106)

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

Critical
Category
Data Flow
Confidence
97% confidence
Finding

The code fetches repro['url'] directly from a server-supplied response without validating the scheme, host, or path and without applying the outbound authorization gate used for the primary endpoint. If the remote service or its response is compromised, this creates an SSRF-style primitive that can force the client to make arbitrary outbound requests to internal or attacker-controlled hosts, potentially leaking network metadata or retrieving maliciously large content.

Content

Scanner excerpt · adapters/coze_client.py (reported line 678)May include surrounding context.

python
repro = parsed.get("repro")
    if isinstance(repro, dict) and repro.get("url") and not repro.get("r"):
        try:
            with urllib.request.urlopen(repro["url"], timeout=timeout) as r:
                repro["r"] = r.read().decode("utf-8")
        except Exception:  # noqa: BLE001
            repro["_repro_fetch_failed"] = True

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
95% confidence
Finding

The confidentiality section states A-tier skills can run fully locally with network off, but elsewhere this release says it has no local compute fallback and relies exclusively on the remote coze endpoint. For a tool aimed at clinical-trial users handling potentially sensitive designs, this contradiction can cause users to rely on a nonexistent local-only mode and disclose proprietary trial parameters to a remote service under false assumptions.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 167)May include surrounding context.

md
ring reaching server-side R is validated against a strict allowlist. History → `CHANGELOG.md`.

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Confidence
80% confidence
Finding

YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Content

Scanner excerpt · adapters/bug_report.py (reported line 54)May include surrounding context.

python
�能版本(如 "4.0.7")
    "test": str,             # 出错检验(如 "ttest_ind");未知为 "unknown"
    "error_type": str,       # error | engine_error | numerical_suspect | crash
    "error_code": str,       # 技能定义的错误码(如 "COZE_UNREACHABLE");无则 ""
    "engine_status": str,    # 引擎状态摘要(如 "coze ok" / "r_engine error");无则 ""
    "description": str,      # 用户把关制问题描述(现象/复现/算法或函数/可含数值与研究设计;不含可识别身份信息)
    "locale": str,           # 会话语言("zh"/"en")
    "query_origin": str,     # §8.6 客户端标识(sha256(hostname))
    "session_hash": str,     # 会话指纹(sha256(hostname+date)),不含会话内容
    "attempts": int,         # 同检验重试次数(1 = 首次失败)
}

# ── 统一报告端点(已发布 2026-08-21,ct-bugreport 正式域名)──────────────
# 全库共用�

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The manifest reassembly path performs unrestricted downloads from _coze_manifest['url'], which is fully controlled by the remote response. In a client-side skill, this broad outbound-fetch capability is more dangerous because it can be abused to probe internal services, contact arbitrary Internet hosts, or deliver oversized/untrusted content back into local processing.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The CLI help text tells users that --yes is needed to execute and that the default is a safe preview, but the code only enforces --yes for gated local backends and still sends requests to the remote Coze service by default. This mismatch is dangerous because users or higher-level agents may rely on the documented behavior and unintentionally trigger external processing or data disclosure.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The code comments state that dry-run is the safe default and execution requires explicit opt-in, but the actual logic still performs remote backend computation by default unless --dry-run is supplied. In a security-sensitive agent setting, this can cause unreviewed transmission of user-supplied trial parameters to an external service, violating operator expectations and weakening consent and data-handling guarantees.

Content

No source excerpt is available for this finding.

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding

Suspicious Unicode normalization or mixed-script content

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The file states outputs switch between Chinese and English according to the OS language, which is a locale policy choice applied automatically. Under the policy rule, forcing or auto-selecting a language/locale without explicit user opt-in can be a natural-language policy violation unless clearly justified as region-specific.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
96% confidence
Finding

The changelog documents a default external NLP/LLM endpoint at api.longcat.chat, with automatic fallback behavior and embedded key-loading support. This is a genuine outbound data-flow risk because natural-language inputs may contain sensitive trial design or research information, and making the remote endpoint default increases the chance of silent transmission beyond the core compute service.

Content

Scanner excerpt · CHANGELOG.md (reported line 90)May include surrounding context.

md
- 背景:页面「智能识别并计算」实为确定性正则(classify_test/param_aliases),与对话模式的
    Agent LLM 理解力差距大;旧版 `nl_llm.py` LLM 兜底默认禁用(需显式 CTSS_NL_ENDPOINT),云端无 env → 死路径。
  - 改造:按 ct-base §12(2026-09-25 家族统一 LongCat-2.0)——`nl_llm.py` 默认端点
    `https://api.longcat.chat/openai/chat/completions`、模型 `LongCat-2.0`,key 经
    `load_llm_key()`(env `LONGCAT_API_KEY` > 混淆 `config/llm_key.py`);新增 `adapters/llm_loader.py`
    + `config/llm_key.py`(自 ct-base 拷贝,入载荷白名单,载荷 35→37 文件);
    max_tokens 800→2048 + 超时 25→45s(LongCat 为推理模型,reasoning 吃预算,meta-analysis 实测教训)。

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

At L0170-L0172 the documentation explicitly says LLM fallback is disabled by default and that the published app will not call any local or external model unless an endpoint is explicitly configured. However, L0089-L0093 later describe a default https://api.longcat.chat/... endpoint and model configuration, which means NL fallback can invoke an external model by default. These statements actively contradict each other about whether default model/network use occurs.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This line says the interface defaults to Chinese for navigator.language starting with zh, otherwise English. That is an automatic locale policy based on environment signals rather than explicit user choice, which falls under the language/locale policy check.

Content

No source excerpt is available for this finding.

Ssd 3

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The changelog describes prefilling bug reports with current test, session, parameters, and other context, then sending them to a remote endpoint. Even if intended for debugging, this creates a real risk of leaking user-provided study details, workflow context, or sensitive metadata to an external service, especially if users approve without carefully editing the prefilled content.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The README makes strong confidentiality claims such as 'never touches original data' and 'does not send patient data', but the bug-report flow explicitly allows a user-reviewed description to be transmitted externally and says it may include study design details or numeric values. In a clinical-trial context, even partial protocol parameters or study numbers can be sensitive business information, so this is a misleading disclosure boundary that can cause unintended data egress.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The skill states that output language defaults based on the OS language and can be force-switched by prompt, which means local environment metadata influences behavior without explicit user opt-in. While not a direct code-execution risk, it creates an unnecessary privacy and policy boundary issue because locale-derived behavior can leak system characteristics and override the user's explicit preferences or expectations.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
94% confidence
Finding

The skill declares a pre-whitelisted remote endpoint with auto-approval, so trial parameters plus metadata such as a hostname hash, locale, and skill version can leave the machine without an interactive authorization step. This weakens user control over outbound data flow and makes accidental exfiltration more likely, especially because the skill is designed to trigger remote compute in normal usage.

Content

Scanner excerpt · SKILL.md (reported line 28)May include surrounding context.

md
permissions:
  scope: "user-space-only"
  network: "required"
  network_note: "v5 requires the remote coze compute endpoint (CTSS_COZE_ENDPOINT, or CTSS_COZE_MOCK=1 for a local demo) — the published skill has no local compute fallback. Only trial-design parameters leave the machine (no patient data); every request also carries a hostname hash `query_origin` (sha256, for server attribution/rate-limit) and the OS-language-derived `locale`, and the skill version `skill_version` (read from the local SKILL.md, for per-version attribution of cloud usage). Outbound authorization gate: the public endpoint is pre-whitelisted in config/config.json auto_approve_endpoints (never prompts, but the assistant states what is sent on first use); user-custom endpoints trigger a one-time AUTH-BLOCK user confirmation before any data leaves the machine. Payloads are sanitized (PII stripped) before sending."
  filesystem: "writes figures to CTSS_OUTPUT_DIR (default ./outputs) and optional curve PNGs; otherwise read-only"
  data: "no patient/external data leaves the boundary — only trial-design parameters plus the hostname hash (query_origin), the skill version (skill_version) and locale metadata are sent to the coze service"

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The locale-selection logic automatically maps the session to only "zh" or "en" using environment variables or system locale, and the surrounding user-facing messages are then chosen accordingly. This enforces a language choice without an explicit user opt-in, which matches the language/locale policy violation criteria.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The adapter embeds a reversibly obfuscated token, decodes it at runtime, and sends it in an Authorization Bearer header to a remote endpoint. Obfuscation with XOR+base64 is not secret storage, so anyone with the code can recover and reuse the credential, enabling unauthorized submissions or abuse of the shared reporting service.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The code is designed to use an embedded bearer token and endpoint as a fallback, meaning the skill can contact an external service using bundled credentials even when the user has not explicitly configured one. In this context, hidden built-in credentials reduce transparency and increase the chance that users unknowingly send data to a third-party service.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · CHANGELOG.md (reported line 1036)May include surrounding context.

md
出站授权(ct-base §5 安全模型 / docs/02-security-model.md,2026-08-19 应用):
  - 任何发往外部端点的请求在真正发送前经 `_check_outbound_authorization` 门控:
    命中 config/config.json 的 auto_approve_endpoints 白名单(公共端点已由作者预置,
    永不弹确认)或本会话内存已授权 → 放行;否则 stderr 输出 AUTH-BLOCK + 全库统一
    确认提示,**不发送**(授权不阻断流程:返回 None,由调用方提示用户)。
  - 发往端点的 payload 经 `sanitize()` 剥离 PII(身份证 / 手机号 / 邮箱)。

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · adapters/coze_client.py (reported line 15)May include surrounding context.

python
出站授权(ct-base §5 安全模型 / docs/02-security-model.md,2026-08-19 应用):
  - 任何发往外部端点的请求在真正发送前经 `_check_outbound_authorization` 门控:
    命中 config/config.json 的 auto_approve_endpoints 白名单(公共端点已由作者预置,
    永不弹确认)或本会话内存已授权 → 放行;否则 stderr 输出 AUTH-BLOCK + 全库统一
    确认提示,**不发送**(授权不阻断流程:返回 None,由调用方提示用户)。
  - 发往端点的 payload 经 `sanitize()` 剥离 PII(身份证 / 手机号 / 邮箱)。

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · adapters/coze_client.py (reported line 220)May include surrounding context.

python
出站授权(ct-base §5 安全模型 / docs/02-security-model.md,2026-08-19 应用):
  - 任何发往外部端点的请求在真正发送前经 `_check_outbound_authorization` 门控:
    命中 config/config.json 的 auto_approve_endpoints 白名单(公共端点已由作者预置,
    永不弹确认)或本会话内存已授权 → 放行;否则 stderr 输出 AUTH-BLOCK + 全库统一
    确认提示,**不发送**(授权不阻断流程:返回 None,由调用方提示用户)。
  - 发往端点的 payload 经 `sanitize()` 剥离 PII(身份证 / 手机号 / 邮箱)。

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · adapters/coze_client.py (reported line 221)May include surrounding context.

python
出站授权(ct-base §5 安全模型 / docs/02-security-model.md,2026-08-19 应用):
  - 任何发往外部端点的请求在真正发送前经 `_check_outbound_authorization` 门控:
    命中 config/config.json 的 auto_approve_endpoints 白名单(公共端点已由作者预置,
    永不弹确认)或本会话内存已授权 → 放行;否则 stderr 输出 AUTH-BLOCK + 全库统一
    确认提示,**不发送**(授权不阻断流程:返回 None,由调用方提示用户)。
  - 发往端点的 payload 经 `sanitize()` 剥离 PII(身份证 / 手机号 / 邮箱)。

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · adapters/coze_client.py (reported line 224)May include surrounding context.

python
出站授权(ct-base §5 安全模型 / docs/02-security-model.md,2026-08-19 应用):
  - 任何发往外部端点的请求在真正发送前经 `_check_outbound_authorization` 门控:
    命中 config/config.json 的 auto_approve_endpoints 白名单(公共端点已由作者预置,
    永不弹确认)或本会话内存已授权 → 放行;否则 stderr 输出 AUTH-BLOCK + 全库统一
    确认提示,**不发送**(授权不阻断流程:返回 None,由调用方提示用户)。
  - 发往端点的 payload 经 `sanitize()` 剥离 PII(身份证 / 手机号 / 邮箱)。

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
85% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · adapters/coze_client.py (reported line 240)May include surrounding context.

python
出站授权(ct-base §5 安全模型 / docs/02-security-model.md,2026-08-19 应用):
  - 任何发往外部端点的请求在真正发送前经 `_check_outbound_authorization` 门控:
    命中 config/config.json 的 auto_approve_endpoints 白名单(公共端点已由作者预置,
    永不弹确认)或本会话内存已授权 → 放行;否则 stderr 输出 AUTH-BLOCK + 全库统一
    确认提示,**不发送**(授权不阻断流程:返回 None,由调用方提示用户)。
  - 发往端点的 payload 经 `sanitize()` 剥离 PII(身份证 / 手机号 / 邮箱)。

Static analysis

Detected: suspicious.dynamic_code_execution

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
adapters/llm_loader.py:52

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
scripts/compute_backend.py:186