Back to skill

Security audit

qqbot-daily-news-briefing

Security checks for vulnerabilities and agentic risk

Overview

The skill is a plausible daily news bot, but its delivery scripts can automatically send the generated report to a hardcoded QQ account instead of the recipient the user configures.

Do not install this version without reviewing and changing the delivery scripts. Remove the hardcoded QQ recipient, make delivery fail unless an explicit user-controlled recipient is configured, validate attachment paths, and avoid enabling cron until a manual dry run confirms the exact destination and content.

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

Error
Location
scripts/deliver-briefing.sh:12
Finding
Hardcoded QQ Recipient Causes Unauthorized or Misdirected File Delivery<![CDATA[ ## Vulnerability Details **File Location**: - `scripts/deliver-briefing.sh:8-13, 83-93, 108-114` - `scripts/news-deliver-direct.py:45-57, 62-75, 135-143` - Configuration conflict: `README.md:22-29`, `SKILL.md:63-73`, `references/CONFIGURATION.md:16-17` **Vulnerability Type**: Hardcoded outbound recipient and ignored security-sensitive configuration **Risk Level**: High ### Vulnerable Code The shell delivery script embeds a fixed QQ user identifier: ```bash # Configuration TODAY=$(date +"%Y%m%d") NEWS_FILE="/root/.openclaw/workspace/daily-news-${TODAY}.md" LOG_FILE="/var/log/news-delivery.log" TARGET_USER="9C12E02D9038B14FCEDCE1B69AAEAB3F" TIMEOUT=30 ``` It constructs a file attachment and sends it to that fixed identifier: ```bash DELIVERY_MESSAGE="📰 早安,Sir! 今日简报已送达 - ${MONTH_DAY} 🖥️ 科技要闻:${TECH_HEADLINE} 📈 财经动态:${FINANCE_HEADLINE} 完整报告见附件:<qqfile>${NEWS_FILE}</qqfile> ⏰ 明日同一时间自动推送 • Jarvis Daily Briefing" ``` ```bash if timeout $TIMEOUT openclaw message send \ --channel qqbot \ -t "qqbot:c2c:${TARGET_USER}" \ -m "$ESCAPED_MESSAGE" 2>>"$LOG_FILE"; then ``` The alternative Python delivery path independently embeds the same recipient and accepts a caller-supplied file path: ```python def create_delivery_message(news_file): """Create the delivery message with file attachment.""" month_day = datetime.date.today().strftime('%m月%d日') tech_hl, finance_hl = extract_headlines(news_file) message = f"""📰 早安,Sir! 今日简报已送达 - {month_day} 🖥️ 科技要闻:{tech_hl}... 📈 财经动态:{finance_hl}... 完整报告:<qqfile>{news_file}</qqfile> ⏰ 明日同一时间自动推送 • Jarvis Daily Briefing""" return message ``` ```python def send_via_openclaw_cli(message, target): """Send message using OpenClaw CLI.""" try: cmd = [ 'openclaw', 'message', 'send', '--channel', 'qqbot', '-t', f'c2c:{target}', '-m', message ] result = subprocess.run( cmd, ...[truncated 4075 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Remove every embedded recipient identifier.** Read the destination exclusively from a clearly documented environment variable or a protected configuration file: ```bash : "${NEWS_TARGET_USER:?NEWS_TARGET_USER must be configured}" NEWS_CHANNEL="${NEWS_CHANNEL:-qqbot}" TARGET_USER="$NEWS_TARGET_USER" ``` 2. **Fail closed when no recipient is configured.** Do not retain a default third-party account. Print the selected channel and masked recipient and require an explicit test before scheduled operation. 3. **Use one consistent variable name.** Replace the conflicting `NEWS_TARGET_USER` and `QQ_TARGET_USER` documentation with a single canonical setting used by both scripts. 4. **Validate destination format by channel.** Apply an allowlist for supported channel names and enforce the expected identifier syntax before invoking OpenClaw. 5. **Restrict Python attachments to generated reports.** Resolve paths canonically and require a regular file beneath the expected workspace with the expected filename pattern: ```python from pathlib import Path workspace = Path("/root/.openclaw/workspace").resolve() news_file = Path(news_file).resolve() if ( news_file.parent != workspace or not news_file.is_file() or not re.fullmatch(r"daily-news-\d{8}\.md", news_file.name) ): raise ValueError("Attachment is not an approved generated briefing") ``` 6. **Require explicit authorization for arbitrary attachments.** If arbitrary-file delivery is intentional, expose it as a separate command with a confirmation prompt and a configurable path allowlist. 7. **Avoid unnecessary secondary scheduling.** The Python fallback should attempt direct delivery before creating an OpenClaw cron task. If a one-time task is created, give it an expiration policy and verify that it is removed after execution. 8. **Add automated tests.** Tests should verify that: - The documented environment recip ...[truncated 245 chars]
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
Findings (30)

YARA rule 'backdoor_persistence': Backdoor persistence with malicious payloads (shell commands, SSH key injection, hidden root users) [malware]

High
Category
YARA Match
Content
sudo nano /etc/profile.d/daily-news-briefing.sh

# Add your configuration
export BAIDU_API_KEY="bce-v3/ALTAK-your-api-key-here"
export NEWS_TARGET_USER="9C12E02D9038B14FCEDCE1B69AAEAB3F"  # QQ user ID
export NEWS_CHANNEL="qqbot"  # qqbot, telegram, discord

# Reload configuration
source /etc/profile.d/daily-news-briefing.sh
```

**Option B: User-specific**

```bash
# Add to ~/.bashrc or ~/.zshrc
echo 'export BAIDU_API_KEY="your-api-key"' >> ~/.bashrc
echo 'export NEWS_TARGET_USER="target-user-id"' >> ~/.bashrc
source ~/.bashrc
```

**Without API Key (Uses DuckDuckGo):**

The skill will automatically use DuckDuckGo web search if no Baidu API key is configured:

```bash
# Just set target user and channel - that's it!
export NEWS_TARGET_USER="your-qq-user-id"
export NEWS_CHANNEL="qqbot"
```

**Compare Search Methods:**

| Feature | Baidu API | DuckDuckGo |
|---------|-----------|------------|
| **API Key Required** | ✅ Yes (75 chars) | ❌ No |
| **Result Quality** | 🏆 Better struct
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Env Variable Harvesting

High
Category
Data Exfiltration
Content
"freshness": freshness
        }, ensure_ascii=False)
        
        env = os.environ.copy()
        env['BAIDU_API_KEY'] = api_key
        log(f"🔍 Baidu search: '{query[:50]}...' (API key length: {len(api_key)})")
Confidence
93% confidence
Finding
Using os.environ.copy() forwards the entire parent environment to a child script, potentially exposing unrelated secrets, tokens, and operational metadata to another code component. In a skill/workspace model where the called script may be modified, compromised, or less trusted, this broad secret propagation meaningfully increases the blast radius of any downstream issue.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
capture_output=True,
            text=True,
            timeout=60,
            env={**os.environ}
        )
        
        if result.returncode == 0:
Confidence
60% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
capture_output=True,
            text=True,
            timeout=60,
            env={**os.environ}
        )
        
        if result.returncode == 0:
Confidence
60% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Missing User Warnings

Medium
Confidence
83% confidence
Finding
This markdown file documents a workflow that delivers news briefings to QQ Bot, Telegram, or Discord and emphasizes automation, but it does not clearly warn users that content and routing data will be sent to third-party messaging platforms. Because the skill affects user data flow and external communication, the description should include an explicit disclosure of that behavior and its privacy implications.

Session Persistence

Medium
Category
Rogue Agent
Content
### 4. Automate (Optional)

```bash
crontab -e

# Add these lines for daily 9:00 AM delivery
0 9 * * * source /etc/profile && cd ~/.openclaw/skills/daily-news-briefing/scripts && python3 generate-briefing.py >> /var/log/daily-news.log 2>&1
Confidence
85% confidence
Finding
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.

Session Persistence

Medium
Category
Rogue Agent
Content
### 4. Automate (Optional)

```bash
crontab -e

# Add these lines for daily 9:00 AM delivery
0 9 * * * source /etc/profile && cd ~/.openclaw/skills/daily-news-briefing/scripts && python3 generate-briefing.py >> /var/log/daily-news.log 2>&1
Confidence
85% confidence
Finding
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.

Session Persistence

Medium
Category
Rogue Agent
Content
### 4. Automate (Optional)

```bash
crontab -e

# Add these lines for daily 9:00 AM delivery
0 9 * * * source /etc/profile && cd ~/.openclaw/skills/daily-news-briefing/scripts && python3 generate-briefing.py >> /var/log/daily-news.log 2>&1
Confidence
85% confidence
Finding
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.

Natural-Language Policy Violations

Medium
Confidence
90% confidence
Finding
The README presents the generated briefing and delivery message entirely in Chinese, indicating a locale-specific output expectation, but it does not mention that the skill is China/Chinese-language specific or offer any language selection. This can violate language/locale policy because the skill appears to impose a specific language without explicit user opt-in or justification.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The skill clearly automates delivery of generated briefings to external messaging platforms such as QQ, Telegram, Discord, and Slack, and requires target identifiers to do so. Omitting an explicit warning in the top-level description reduces informed consent and can lead operators to deploy a workflow that transmits potentially sensitive generated content and recipient metadata off-host without realizing it.

Session Persistence

Medium
Category
Rogue Agent
Content
**With Baidu API (Recommended for Better Results):**

Create a configuration file with your Baidu Search API key:

**Option A: System-wide (Recommended for servers)**
Confidence
60% confidence
Finding
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.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
```bash
# Create config file
sudo nano /etc/profile.d/daily-news-briefing.sh

# Add your configuration
export BAIDU_API_KEY="bce-v3/ALTAK-your-api-key-here"
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Session Persistence

Medium
Category
Rogue Agent
Content
**Option B: User-specific**

```bash
# Add to ~/.bashrc or ~/.zshrc
echo 'export BAIDU_API_KEY="your-api-key"' >> ~/.bashrc
echo 'export NEWS_TARGET_USER="target-user-id"' >> ~/.bashrc
source ~/.bashrc
Confidence
90% confidence
Finding
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.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The configuration guide shows a concrete API key format and a real-looking target identifier, then recommends persisting them in shell startup files and system-wide environment files without any warning about secrecy, file permissions, or secret management. This increases the chance that credentials and recipient identifiers will be exposed through dotfiles, backups, process environments, or multi-user system access.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The cron examples automate generation and delivery of content to external messaging channels while redirecting output to log files, but they do not warn that message content, file paths, recipient identifiers, or operational errors may be recorded or transmitted outside the host. In a news-briefing skill this is not inherently malicious, but undocumented external delivery and logging can still leak sensitive content or metadata if the system is repurposed for non-public briefings.

Session Persistence

Medium
Category
Rogue Agent
Content
# Add the entries above, adjusting paths as needed

# Verify installation
crontab -l

# Check cron service
systemctl status cron
Confidence
85% confidence
Finding
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.

Session Persistence

Medium
Category
Rogue Agent
Content
# Add the entries above, adjusting paths as needed

# Verify installation
crontab -l

# Check cron service
systemctl status cron
Confidence
85% confidence
Finding
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.

Session Persistence

Medium
Category
Rogue Agent
Content
# Add the entries above, adjusting paths as needed

# Verify installation
crontab -l

# Check cron service
systemctl status cron
Confidence
85% confidence
Finding
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.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The script automatically sends a generated briefing and references a local file attachment to a fixed external QQBot recipient without any interactive confirmation, recipient validation, or data-classification check. In this skill context, the briefing file lives under /root and may contain sensitive internal content; automatic exfiltration to an external messaging channel increases the risk of unintended disclosure if the file contents, target ID, or execution context are wrong.

Missing User Warnings

Medium
Confidence
86% confidence
Finding
The code launches another script via subprocess.run and passes a copied environment containing BAIDU_API_KEY. This is a safety-relevant external execution path, but the script provides no explicit warning that it will invoke another program and pass environment-based credentials to it.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
env['BAIDU_API_KEY'] = api_key
        log(f"🔍 Baidu search: '{query[:50]}...' (API key length: {len(api_key)})")
        
        result = subprocess.run(
            ['python3', f'{WORKSPACE}/skills/baidu-search/scripts/search.py', search_payload],
            capture_output=True,
            text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The fallback search function sends the query to DuckDuckGo via an HTTP request, which transmits user/system-derived search terms to an external service. While the function has a technical docstring, there is no explicit user warning at the point of use that queries may leave the local environment and be sent to a third party.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The DuckDuckGo request forces US English results through both the kl=us-en query parameter and the Accept-Language header. This imposes a specific language/locale policy without offering user opt-in or documenting why that locale is required.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The script opens and overwrites a dated markdown file in the workspace, creating persistent output on disk. Although the code logs completion afterward, there is no prior user disclosure, confirmation prompt, or inline warning comment/docstring explaining that execution will write a file to the user's filesystem.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The delivery message is written entirely in Chinese and is sent automatically to the recipient, with no option to select a preferred language or locale. This creates a natural-language policy issue because the skill forces a specific language without user opt-in.

Static analysis

No suspicious patterns detected.