Back to skill

Security audit

聚己社区

Security checks for vulnerabilities and agentic risk

Overview

This skill appears to be a real JuJi community integration, but it handles persistent agent credentials and broad state-changing community actions with weak scoping and security controls.

Install only if you trust the JuJi backend you configure and are comfortable with the skill acting as your Agent for publishing, governance votes, tasks, and asset workflows. Use HTTPS for any non-local backend, restrict ~/.juji/.env to owner-only permissions, avoid storing unrelated secrets in ~/.openclaw/.env, and rotate the JuJi token/key if they may have been exposed.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/juji.py:42
Finding

Sensitive Agent Credentials Stored Without Enforced File Permissions

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/juji.py:91
Finding

Authenticated Traffic and WebSocket Tokens Can Be Sent Over Cleartext Transport

Content
View full analysis
dict: """构建 /ws 查询参数:token +(若已登记 public_key)ts、sig。""" token = load_value("JUJI_AGENT_TOKEN") if not token: raise SystemExit( "JUJI_AGENT_TOKEN 缺失:请先执行注册(如 init / 任意带子命令)以写入 ~/.juji/.env 中的 ws_token" ) q = {"agent_id": agent_id, "token": token} public_key = load_value("JUJI_AGENT_PUBLIC_KEY") private_key = load_value("JUJI_AGENT_PRIVATE_KEY") if public_key: if not private_key: raise SystemExit("已配置 JUJI_AGENT_PUBLIC_KEY 时必须有 JUJI_AGENT_PRIVATE_KEY 才能建立 WebSocket") ts = int(time.time()) msg = f"juji-ws-v1|{agent_id}|{ts}".encode("utf-8") sk = SigningKey(bytes.fromhex(private_key)) q["ts"] = str(ts) q["sig"] = sk.sign(msg).signature.hex() return q ``` ```python def ws_url_from_base(base_url: str, agent_id: int): if base_url.startswith("https://"): ws_base = "wss://" + base_url[len("https://") :] elif base_url.startswith("http://"): ws_base = "ws://" + base_url[len("http://") :] else: raise SystemExit("JUJI_BASE_URL must start with http:// or https://") return f"{ws_base}/ws?{urlencode(_ws_connect_query(agent_id))}" ``` ```python def register_agent(base_url: str): public_key, _private_key = ensure_public_key() name = load_value("JUJI_AGENT_NAME") wallet_address = load_value("JUJI_AGENT_WALLET_ADDRESS") payload = {"public_key": public_key} if wallet_address: payload["wallet_address"] = wallet_address if name: payload["name"] = name r = requests.post(f"{base_url}/agent/register", json=payload, timeout=30) r.raise_for_status() agent = r.json() ws_token = ...[truncated 2773 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding

Security-Sensitive Dependencies Are Not Reproducibly Pinned

Content
View full analysis
=2.28.0 websocket-client>=1.8.0 pynacl>=1.5.0 ``` ### Technical Analysis The requirements specify only minimum versions and no upper bounds, exact versions, lock file, or package hashes. Each installation can therefore resolve to a different future release. These dependencies process highly sensitive data: - `requests` transmits registration data and REST operations. - `websocket-client` transmits the authentication token and all WebSocket actions. - `PyNaCl` generates and uses the Agent's private signing key. This is not evidence that the named packages are currently malicious. The weakness is that the package does not establish a reproducible, reviewed dependency set. A future compromised, malicious, or unexpectedly incompatible release could be installed automatically without any change to the audited Skill package. ### Attack Path 1. A dependency account, package release process, package index, or distribution path is compromised, or a future release introduces a security regression. 2. A user installs the Skill dependencies using the unbounded requirements. 3. The package manager selects the affected release because it satisfies the minimum-version constraint. 4. Package installation code or imported runtime code executes in the user's environment. 5. Malicious or vulnerable dependency code gains access to process memory, environment variables, local credential files, network requests, and cryptographic operations. 6. Agent tokens, private keys, or authenticated action data may be disclosed or modified. ### Impact Assessment Dependency code executes with the same operating-system privileges as the Skill. A compromised dependency could potentially: - Read `~/.juji/.env` and `~/.openclaw/.env`. - Steal the Ed25519 pr ...[truncated 391 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/juji.py:23
Finding

Skill Reads a Shared OpenClaw Credential File Beyond Its Scoped Configuration

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (27)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The description frames the skill as a generic registration/WebSocket shell, but the documentation shows it also performs many predefined high-impact state-changing operations such as publishing content, governance voting, committee actions, task actions, and asset issuance workflows, plus local credential persistence. This mismatch can mislead operators into granting trust and permissions under a narrower mental model than the skill actually needs, which is especially risky because it interacts with remote capabilities discovered dynamically at runtime.

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 46)May include surrounding context.

md
- 仅文档:`GET {JUJI_BASE_URL}/skills/juji/download?format=raw` → 保存为 `SKILL.md`。

Possible Typosquatting: 'pynacl' resembles popular package 'pyyaml'

High
Category
Supply Chain
Confidence
70% confidence
Finding

Package name closely resembles a popular package, suggesting possible typosquatting. Attackers publish malicious packages with similar names to trick developers into installing them.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
93% confidence
Finding

The skill reads secrets and identity material from ~/.juji/.env and ~/.openclaw/.env, including a private key and token, without any trust-boundary restriction between tools. Cross-reading another application's env file increases the blast radius: compromise or confusion in one tool can expose credentials usable by this skill, enabling agent impersonation or unauthorized service access.

Content

Scanner excerpt · scripts/juji.py (reported line 29)May include surrounding context.

python
if v:
        return v
    for env_path in [
        pathlib.Path.home() / ".juji" / ".env",
        pathlib.Path.home() / ".openclaw" / ".env",
    ]:
        if not env_path.exists():

Credential Access

High
Category
Privilege Escalation
Confidence
93% confidence
Finding

Reading credentials from another tool's .env file creates a shared-secret trust boundary that can leak or misuse tokens/private keys across applications. In an agent skill context, this is more dangerous because the credentials are then used automatically to establish authenticated WebSocket sessions and sign messages.

Content

Scanner excerpt · scripts/juji.py (reported line 30)May include surrounding context.

python
return v
    for env_path in [
        pathlib.Path.home() / ".juji" / ".env",
        pathlib.Path.home() / ".openclaw" / ".env",
    ]:
        if not env_path.exists():
            continue

Credential Access

High
Category
Privilege Escalation
Confidence
95% confidence
Finding

The code writes secrets to ~/.juji/.env in plaintext with default permissions and no permission hardening. Any local user or process with filesystem access to that file could recover the WebSocket token or private key and authenticate as the agent.

Content

Scanner excerpt · scripts/juji.py (reported line 42)May include surrounding context.

python
def save_values(values: dict):
    env_path = pathlib.Path.home() / ".juji" / ".env"
    env_path.parent.mkdir(parents=True, exist_ok=True)
    old = env_path.read_text(encoding="utf-8", errors="ignore") if env_path.exists() else ""
    kv = {}

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/juji.py (reported line 66)May include surrounding context.

python
token = load_value("JUJI_AGENT_TOKEN")
    if not token:
        raise SystemExit(
            "JUJI_AGENT_TOKEN 缺失:请先执行注册(如 init / 任意带子命令)以写入 ~/.juji/.env 中的 ws_token"
        )
    q = {"agent_id": agent_id, "token": token}
    public_key = load_value("JUJI_AGENT_PUBLIC_KEY")

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill declares no explicit tool scope or allowed-tools restrictions while its documented behavior requires environment access, file reads/writes, and network communication, including persistent WebSocket connections and writing credentials to local .env files. In an agent framework, this creates an over-privileged installation surface where users and orchestrators cannot accurately constrain what the skill may access, increasing the blast radius if the backend is compromised or the skill is misused.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

This markdown file contains user-facing operational instructions almost exclusively in Chinese, including setup, commands, and safety-relevant behavior notes. That effectively enforces a specific language/locale for users without documenting a choice, which matches the language-policy violation criteria.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill generates a long-lived private signing key and stores it in ~/.juji/.env without setting restrictive filesystem permissions or clearly warning the user. On multi-user systems or misconfigured environments, other local processes or users could read the key and impersonate the agent for WebSocket authentication.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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/juji.py (reported line 146)May include surrounding context.

python
payload["wallet_address"] = wallet_address
    if name:
        payload["name"] = name
    r = requests.post(f"{base_url}/agent/register", json=payload, timeout=30)
    r.raise_for_status()
    agent = r.json()
    ws_token = agent.get("ws_token")

Unbounded Resource Access

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill allows unbounded resource consumption (API calls, storage, compute). Without rate limits or quotas, a compromised or misbehaving agent can cause denial-of-service or cost overruns.

Content

Scanner excerpt · scripts/juji.py (reported line 407)May include surrounding context.

python
session_active = True
        while session_active and not user_done:
            try:
                line = stdin_q.get(timeout=0.5)
            except queue.Empty:
                if connection_lost.is_set():
                    print_out(

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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/juji.py (reported line 673)May include surrounding context.

python
"reason": args.reason,
                "extra_meta": json.loads(args.extra_meta or "{}"),
            }
            r = requests.post(f"{base_url}/asset/issuance/apply", json=payload, timeout=30)
            r.raise_for_status()
            return r.json()
        print_out(with_ws(base_url, run_asset), args.format)

Tainted flow: 'payload' from requests.post (line 430, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/juji.py (reported line 673)May include surrounding context.

python
"reason": args.reason,
                "extra_meta": json.loads(args.extra_meta or "{}"),
            }
            r = requests.post(f"{base_url}/asset/issuance/apply", json=payload, timeout=30)
            r.raise_for_status()
            return r.json()
        print_out(with_ws(base_url, run_asset), args.format)

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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/juji.py (reported line 737)May include surrounding context.

python
"author_agent_id": args.author_agent_id or agent["agent_id"],
                "tags": args.tags or [],
            }
            r = requests.post(f"{base_url}/content/publish", json=payload, timeout=30)
            r.raise_for_status()
            return r.json()
        print_out(with_ws(base_url, run_pub), args.format)

Tainted flow: 'payload' from requests.post (line 430, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/juji.py (reported line 737)May include surrounding context.

python
"author_agent_id": args.author_agent_id or agent["agent_id"],
                "tags": args.tags or [],
            }
            r = requests.post(f"{base_url}/content/publish", json=payload, timeout=30)
            r.raise_for_status()
            return r.json()
        print_out(with_ws(base_url, run_pub), args.format)

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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/juji.py (reported line 753)May include surrounding context.

python
"reward_wallet_address": args.reward_wallet_address,
                "deadline": args.deadline,
            }
            r = requests.post(f"{base_url}/task/create", json=payload, timeout=30)
            r.raise_for_status()
            return r.json()
        print_out(with_ws(base_url, run_task_create), args.format)

Tainted flow: 'payload' from requests.post (line 430, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/juji.py (reported line 753)May include surrounding context.

python
"reward_wallet_address": args.reward_wallet_address,
                "deadline": args.deadline,
            }
            r = requests.post(f"{base_url}/task/create", json=payload, timeout=30)
            r.raise_for_status()
            return r.json()
        print_out(with_ws(base_url, run_task_create), args.format)

Tainted flow: 'payload' from requests.post (line 430, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/juji.py (reported line 762)May include surrounding context.

python
if args.cmd == "task-join":
        def run_task_join(_ws, agent):
            payload = {"task_id": args.task_id, "agent_id": args.agent_id or agent["agent_id"]}
            r = requests.post(f"{base_url}/task/join", params=payload, timeout=30)
            r.raise_for_status()
            return r.json()
        print_out(with_ws(base_url, run_task_join), args.format)

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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/juji.py (reported line 776)May include surrounding context.

python
"result": args.result,
                "proof": args.proof,
            }
            r = requests.post(f"{base_url}/task/submit", json=payload, timeout=30)
            r.raise_for_status()
            return r.json()
        print_out(with_ws(base_url, run_task_submit), args.format)

Tainted flow: 'payload' from requests.post (line 430, network input) → requests.post (network output)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/juji.py (reported line 776)May include surrounding context.

python
"result": args.result,
                "proof": args.proof,
            }
            r = requests.post(f"{base_url}/task/submit", json=payload, timeout=30)
            r.raise_for_status()
            return r.json()
        print_out(with_ws(base_url, run_task_submit), args.format)

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

The dependency is specified with a lower bound only (requests>=2.28.0), which makes builds non-reproducible and allows future installs to pull different versions over time. In a network-facing skill that relies on HTTP communication, this increases supply-chain risk and makes it harder to ensure that a known-safe version is installed.

Content

Scanner excerpt · requirements.txt (reported line 1)May include surrounding context.

text
requests>=2.28.0
websocket-client>=1.8.0
pynacl>=1.5.0

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
83% confidence
Finding

requests has multiple known advisories, and because the manifest does not pin an exact version, there is no way to verify from this file whether the installed version is affected or fixed. In a skill that likely performs backend HTTP calls, this uncertainty meaningfully raises risk even though the file alone does not prove exploitation.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

The dependency websocket-client>=1.8.0 is unpinned, so installations may resolve to different versions depending on when and where the skill is deployed. Because this skill maintains a persistent WebSocket connection, uncontrolled dependency drift can expose it to newly introduced or unreviewed package behavior.

Content

Scanner excerpt · requirements.txt (reported line 2)May include surrounding context.

text
requests>=2.28.0
websocket-client>=1.8.0
pynacl>=1.5.0

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

Using pynacl>=1.5.0 without an upper bound or exact pin permits arbitrary newer releases to be installed, which weakens build reproducibility and supply-chain assurance. Since this package is commonly used for cryptographic operations, version ambiguity can be especially risky if an affected release is later pulled in.

Content

Scanner excerpt · requirements.txt (reported line 3)May include surrounding context.

text
requests>=2.28.0
websocket-client>=1.8.0
pynacl>=1.5.0

Static analysis

No suspicious patterns detected.