T09 · Insecure Skill Coding Practices
- Location
scripts/clickzetta-discovery.mjs:666- Finding
Reusable ClickZetta Credentials and Bearer Tokens Are Persisted Without Explicit Access Controls
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill is for ClickZetta Studio, but it deserves review because it can use stored credentials for live operations, auto-approve remote actions, and cache sensitive credential material locally.
Install only if you trust the ClickZetta environment and understand that the skill can operate on live workspaces using the stored JDBC credential. Prefer a least-privileged ClickZetta account, avoid enabling always-allow or auto-approve behavior, inspect local cache/config permissions, and be cautious with SQL/job/task requests that can consume compute or change data.
scripts/clickzetta-discovery.mjs:666Reusable ClickZetta Credentials and Bearer Tokens Are Persisted Without Explicit Access Controls
scripts/cz-agent-proxy.mjs:1100Remote Tool and Action Confirmation Requests Are Automatically Approved by Default
scripts/cz-agent-proxy.mjs:544Bearer Tokens and User Data Can Be Redirected to Arbitrary or Insecure WebSocket Endpoints
Referenced artifact was not completely inspected
- Default path for normal user requests: run `scripts/cz-agent-oneshot.mjs` once and return its result.
The skill advertises creating or running ClickZetta jobs from a stored JDBC secret without an explicit safety notice or confirmation model for potentially destructive or billable actions. Because the skill is designed to execute against a live environment, users may trigger job creation, SQL execution, or workspace operations without appreciating the operational impact.
The trigger phrase '用 Studio Agent 帮我 ...' is effectively open-ended and can capture a wide range of unrelated or underspecified requests. In combination with the policy to immediately run the one-shot command, this can cause unintended access to ClickZetta resources or actions against the user's environment.
The skill's activation scope is broad enough to match many general ClickZetta-related requests, then immediately execute a backend runner. That increases the chance of the skill being invoked for ambiguous requests and performing real operations or queries without a clear, narrowly scoped user confirmation boundary.
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
- For queries like "我当前有哪些clickzetta任务", "帮我查任务", "查 workspace/project", "执行 SQL", default to the one-shot runner immediately.
- For workspace management queries like "当前 workspace 是什么", "列出 workspace", "切换到 workspace 101201", also default to the one-shot runner immediately.
- `列出 workspace` / `切换 workspace` 会使用短 TTL 的 workspace 列表缓存;`刷新 workspace 列表` 会强制刷新。
- For SQL execution requests, forward the user's original wording once. Do not rewrite it into follow-up variants such as "创建一个 SQL 任务", "运行刚才创建的任务", or other speculative recovery prompts.
## Install then Configure (Required)
This execution policy directs the agent to assume credentials are configured and to run the backend tool by default for normal requests, reducing opportunities for the user to review or approve execution. That is a form of autonomous action against an authenticated external system, which can lead to unintended data access or operational side effects.
## Execution policy
- Do not ask the user for `CZ_*` env vars up front.
- Assume the JDBC secret is already wired via the Skills page unless runtime errors prove otherwise.
- When runtime reports missing/invalid Studio config, tell the user to open the Skills page and set `CZ_STUDIO_JDBC_URL`.
- Default path for normal user requests: run `scripts/cz-agent-oneshot.mjs` once and return its result.
The skill explicitly instructs immediate execution for requests like creating a lakehouse task, using runtime-discovered credentials and returning the result. This creates a direct path from natural-language input to authenticated job/task creation without a mandatory confirmation step, increasing risk of accidental or unauthorized changes.
- Assume the JDBC secret is already wired via the Skills page unless runtime errors prove otherwise.
- When runtime reports missing/invalid Studio config, tell the user to open the Skills page and set `CZ_STUDIO_JDBC_URL`.
- Default path for normal user requests: run `scripts/cz-agent-oneshot.mjs` once and return its result.
- When describing connection status to the user, refer to the runtime-discovered Studio connection; do not ask the user to manually provide internal `CZ_AGENT_*` values.
- For requests like "帮我创建个 lakehouse 任务", immediately:
1. run one-shot runner command
2. read runner JSON output
This code persists discovery data to local JSON cache files, including environment values that contain a reusable WebSocket URL with the x-clickzetta-token embedded, plus instance, tenant, user, and workspace metadata. In this skill’s context, that token is effectively a bearer credential for ClickZetta Studio APIs, so local file disclosure, backup leakage, or cross-user access on a shared host could enable unauthorized access without re-entering credentials.
Natural-language behavior in this file is biased toward Chinese: several command patterns and many user-facing messages are fixed in Chinese, while no locale selection or user opt-in is provided. This can violate language/locale policy when a skill forces a specific language experience by default.
When apiKey and CZ_STUDIO_JDBC_URL differ, the script silently updates and persists the config file with fs.writeFileSync. There is no prompt, log, or disclosure in this file that informs the user their local configuration will be modified during a one-shot run.
The script defaults CZ_INTERRUPT_DECISION_MODE to "auto_approve", which weakens the safety boundary by allowing downstream agent actions to proceed without an explicit approval gate when the environment does not set a stricter mode. In the context of a Studio agent that can query tasks, inspect workspaces, and create or run ClickZetta jobs using configured credentials, this increases the risk of unintended or prompt-induced remote operations against the user's data platform.
env.CZ_REQUEST_TIMEOUT_SECONDS = String(opts.requestTimeoutSeconds);
env.CZ_STARTUP_CONNECT_TIMEOUT_SECONDS = String(opts.startupTimeoutSeconds);
if (!asTrimmedString(env.CZ_INTERRUPT_DECISION_MODE)) {
env.CZ_INTERRUPT_DECISION_MODE = "auto_approve";
}
if (!asTrimmedString(env.CZ_EMIT_ASSISTANT_DELTAS)) {
env.CZ_EMIT_ASSISTANT_DELTAS = "false";
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.
return fallback;
}
const normalized = value.toLowerCase();
if (["auto_approve", "approve", "auto-approve"].includes(normalized)) {
return "auto_approve";
}
if (["auto_reject", "reject", "auto-reject"].includes(normalized)) {
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.
return fallback;
}
const normalized = value.toLowerCase();
if (["auto_approve", "approve", "auto-approve"].includes(normalized)) {
return "auto_approve";
}
if (["auto_reject", "reject", "auto-reject"].includes(normalized)) {
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.
return fallback;
}
const normalized = value.toLowerCase();
if (["auto_approve", "approve", "auto-approve"].includes(normalized)) {
return "auto_approve";
}
if (["auto_reject", "reject", "auto-reject"].includes(normalized)) {
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.
return fallback;
}
const normalized = value.toLowerCase();
if (["auto_approve", "approve", "auto-approve"].includes(normalized)) {
return "auto_approve";
}
if (["auto_reject", "reject", "auto-reject"].includes(normalized)) {
The proxy copies CZ_ALWAYS_ALLOW_TOOLS from the environment into request metadata, allowing blanket tool approval behavior to be silently injected outside the user's request. Because this skill is intended to operate jobs and workspace/project actions, an environment-level override can permanently bypass interactive approval boundaries and expand what the remote agent may execute.
The proxy defaults CZ_INTERRUPT_DECISION_MODE to auto_approve, so any interrupt_request from the remote Studio agent is approved without an explicit user confirmation step. In a skill that can create or run ClickZetta jobs, this turns a nominally conversational proxy into an action-authorizing component and can permit unintended or over-privileged operations if the upstream agent is mistaken, compromised, or prompt-injected.
Automatic interrupt approval is enabled by default and there is no user-facing confirmation before the proxy returns approve decisions to the Studio agent. This undermines the safety boundary that interrupts are supposed to provide, especially in a job-management skill where approved actions may have side effects on data, compute, or tenant resources.
This constructor initializes interruptDecisionMode with a default of auto_approve, enabling autonomous authorization of tool/action requests without user confirmation. In the context of a Studio agent that can query, create, and run jobs from a configured secret, this can authorize impactful operations across connected resources if the agent behaves unexpectedly or is influenced by malicious input.
this.reconnectMaxAttempts = readIntEnv("CZ_RECONNECT_MAX_ATTEMPTS", 3, 1, this.env);
this.interruptDecisionMode = readInterruptDecisionModeEnv(
"CZ_INTERRUPT_DECISION_MODE",
"auto_approve",
this.env,
);
this.emitAssistantDeltas = readBoolEnv("CZ_EMIT_ASSISTANT_DELTAS", false, this.env);
DEFAULT_LANGUAGE is set to zh_CN and is automatically applied as the cz-lang header in common request headers. This forces a specific locale in the skill's behavior without any opt-in, configurability, or documented region-specific justification in the file.
No suspicious patterns detected.