Back to skill

Security audit

Multi-Source Feed

Security checks for vulnerabilities and agentic risk

Overview

This skill is a daily tech-brief tool, but it handles live browser session cookies, runs authenticated X/Twitter scraping, and installs recurring jobs in ways users should review carefully before installing.

Install only if you are comfortable with a local automation job saving reusable X/Twitter session cookies, scraping your personalized timeline, and running recurring scheduled jobs. Use a dedicated browser profile, protect and exclude .env and x_session.json from version control, revoke the X session if exposed, pin the code and dependencies, and disable the cron jobs when you no longer need the brief.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
login_save_session.py:4
Finding
Broad browser-session capture and plaintext storage of reusable credentials<![CDATA[ ## Vulnerability Details **File Location**: `login_save_session.py:4-9`; related setup instructions in `SKILL.md:45-57` and credential reuse in `scrape_feed.py:77-81` **Vulnerability Type**: Excessive browser-session capture and insecure local secret storage **Risk Level**: High ### Vulnerable Code ```python # login_save_session.py:4-9 # Connect to the user's already-open Chrome via CDP browser = p.chromium.connect_over_cdp("http://127.0.0.1:9222") context = browser.contexts[0] # Save the login session/cookies to a file context.storage_state(path="x_session.json") ``` The resulting file is subsequently loaded into an automated browser: ```python # scrape_feed.py:77-81 context = browser.new_context( storage_state="x_session.json", viewport={"width": 1280, "height": 900}, ) ``` The setup instructions explicitly tell the user to expose Chrome through the Chrome DevTools Protocol: ```text # SKILL.md:45-57 1. Open Chrome with remote debugging enabled by running: open -a 'Google Chrome' --args --remote-debugging-port=9222 2. Log in to X/Twitter in that Chrome window 3. Once logged in, I'll run a script that connects to that browser and saves your session cookies. ``` ### Technical Analysis Playwright's `storage_state()` serializes cookies and origin-specific browser storage from the selected browser context. The implementation selects `browser.contexts[0]` without confirming that it is a dedicated X-only context and saves the result to a plaintext file. The instructions tell the user to launch Chrome with remote debugging but do not require a separate browser profile. If the selected context contains active sessions for services other than X, their cookies or origin storage may also be copied into `x_session.json`. Even if only X credentials are captured, the file contains reusable authentication material. No permission hardening is applied when the file is created, and no `.gitignore` was present in the audited project tree to exclude ...[truncated 1875 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Launch a dedicated Chrome profile used exclusively for X, for example with a new `--user-data-dir` that contains no unrelated sessions. 2. Do not attach to an arbitrary first context. Identify and validate the expected dedicated context explicitly. 3. Where supported, filter exported cookies and storage to the minimum required X domains, such as `x.com` and necessary authentication domains. 4. Create the session file atomically and enforce owner-only permissions (`0600`) immediately. 5. Add `.env`, `x_session.json`, `feed_raw*.json`, and other generated credential or feed artifacts to `.gitignore`. 6. Display a clear warning that the file is equivalent to a credential and must not be shared or committed. 7. Document how to delete the session file and revoke active X sessions. 8. Instruct the user to terminate the remote-debugging browser after session capture. Avoid leaving CDP exposed longer than necessary. 9. Prefer a fresh Playwright persistent context dedicated to this application instead of connecting to the user's general-purpose browser. ]]>

T01 · Skill Instruction Hijacking

Warning
Location
SKILL.md:91
Finding
Indirect prompt injection through attacker-controlled feed content<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:91-99`; related push workflow at `SKILL.md:161-173`, data propagation at `src/pipeline.py:177-187`, and `push/push.py:107-112` **Vulnerability Type**: Missing trust boundary between external feed data and tool-capable agent instructions **Risk Level**: Medium ### Vulnerable Code The scheduled agent is instructed to read externally derived data and produce and send a message, without being told that feed fields are untrusted: ```text # SKILL.md:91-99 Create an OpenClaw cron job that: 1. Checks if `feed_slim.json` exists and is from today 2. Reads `config/user_profile.md` and `config/preferences.md` 3. Reads `feed_slim.json` (the scrape output) 4. Generates the daily brief following preferences.md format 5. Sends the brief to the user via their configured channel 6. Saves the brief to `memo/YYYY-MM-DD.md` (used for cross-day dedup) ``` Externally supplied titles, URLs, and authors are copied into the LLM input: ```python # src/pipeline.py:177-187 slim_items = [ {k: v for k, v in item.items() if k in ("source", "title", "url", "author", "metrics")} for item in item_dicts ] slim_output = { "date": today, "total_items": len(slim_items), "items": slim_items, } slim_path = output_path.parent / "feed_slim.json" with open(slim_path, "w", encoding="utf-8") as f: json.dump(slim_output, f, ensure_ascii=False, indent=2) ``` The optional push workflow also passes attacker-controlled post text and links to the agent: ```python # push/push.py:107-112 KEEP_FIELDS = {"author_handle", "text", "url", "views", "external_links", "is_retweet", "retweeted_by", "timestamp"} slim_posts = [{k: v for k, v in p.items() if k in KEEP_FIELDS} for p in new_posts] NEW_POSTS_FILE.write_text( json.dumps({"scraped_at": data.get("scraped_at", ""), "posts": slim_posts}, ensure_ascii=False, indent=2) ) ``` ### Technical Analysis The application aggregates content controll ...[truncated 2619 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add an explicit instruction to every memo and push job stating that all feed fields are untrusted data and must never be interpreted as commands. 2. Require the agent to ignore any request in a title, post, author field, URL, or description that asks it to change instructions, access files, invoke tools, disclose information, or contact another recipient. 3. Place external content inside a clearly delimited, schema-validated data block. 4. Escape or normalize control characters and instruction-like markup before presenting content to the model. 5. Restrict the scheduled agent to the minimum tool set: read only the expected configuration and JSON files, write only the designated memo path, and send only to the preconfigured recipient. 6. Disable shell execution, arbitrary browsing, unrestricted file reads, and general network tools for summarization jobs. 7. Separate summarization from action execution. Prefer a no-tool model to produce structured output, followed by deterministic validation and delivery code. 8. Validate generated output against a strict schema and reject content containing unexpected commands, links, recipients, or tool syntax. 9. Apply maximum lengths and type validation to titles, authors, URLs, and post text before including them in model context. 10. Log rejected injection-like entries for review without reproducing their instructions in an executable agent context. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:5
Finding
Mutable and unpinned dependencies are executed during automated setup<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:5-14`; related automated installation at `SKILL.md:17-25` and `README.md:19-32` **Vulnerability Type**: Unpinned third-party code and mutable installation sources **Risk Level**: Medium ### Vulnerable Code All Python dependencies use open-ended lower bounds and no integrity hashes: ```text # requirements.txt:5-14 playwright>=1.50 # New pipeline (src/) PyYAML>=6.0 feedparser>=6.0 beautifulsoup4>=4.12 requests>=2.31 # Testing pytest>=7.0 ``` The automated setup retrieves a mutable repository branch and installs the currently resolved dependency and browser artifacts: ```bash # SKILL.md:17-25 cd ~ && git clone https://github.com/zidooong/multi-source-feed.git cd ~/multi-source-feed python3 -m venv .venv && source .venv/bin/activate pip install -r requirements.txt playwright install chromium ``` The README also suggests package execution through `npx` and repeats the mutable installation process: ```bash # README.md:19 npx clawhub install multi-source-feed # README.md:30-32 git clone https://github.com/zidooong/multi-source-feed.git && cd multi-source-feed python3 -m venv .venv && source .venv/bin/activate pip install -r requirements.txt && playwright install chromium ``` ### Technical Analysis Version specifiers using only `>=` allow future versions to be selected without further review. No lock file or hash verification binds installations to artifacts assessed during this audit. Cloning the default branch similarly retrieves whatever code is current at installation time rather than a reviewed commit. `playwright install chromium` downloads an additional mutable browser artifact, while `npx` may retrieve and execute package code selected at invocation time. Python installation and package-manager lifecycle behavior execute code with the privileges of the user or agent running setup. A compromised upstream account, malicious package release, dependency takeover, or changed reposito ...[truncated 1759 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin every production dependency to an exact reviewed version. 2. Generate a lock file containing transitive dependencies and cryptographic hashes. 3. Install with hash enforcement, such as `pip install --require-hashes`, using a reviewed requirements lock file. 4. Separate test-only packages such as `pytest` from production runtime dependencies. 5. Pin the Git repository to a signed release tag or immutable reviewed commit rather than the default branch. 6. Pin the exact ClawHub package version and avoid unrestricted `npx` execution during unattended setup. 7. Pin and verify Playwright browser artifacts where the ecosystem permits it. 8. Perform dependency vulnerability and provenance scanning in continuous integration. 9. Require explicit user confirmation before cloning repositories or executing package installers. 10. Rebuild and re-review lock files through a controlled update process rather than accepting arbitrary future versions during normal setup. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • 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 (75)

Tainted flow: 'token' from os.environ.get (line 51, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
).strftime("%Y-%m-%dT00:00:00Z")
        variables["postedAfter"] = posted_after

        resp = requests.post(
            _GRAPHQL_URL,
            json={"query": _QUERY, "variables": variables},
            headers={
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'payload' from os.environ.get (line 37, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
"days": days,
        }

        resp = requests.post(self.API_URL, json=payload, timeout=30)
        resp.raise_for_status()
        data = resp.json()
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Credential Access

High
Category
Privilege Escalation
Content
Some sources require API keys — you'll need to register and fill them into `.env`. Tavily powers web search (catching trending topics not covered by any feed), and Product Hunt requires an API token for its GraphQL endpoint.

```bash
cp .env.example .env
# Fill in TAVILY_API_KEY (free: https://tavily.com)
# Fill in PRODUCTHUNT_API_TOKEN (free: https://api.producthunt.com/v2/docs)
```
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
Some sources require API keys — you'll need to register and fill them into `.env`. Tavily powers web search (catching trending topics not covered by any feed), and Product Hunt requires an API token for its GraphQL endpoint.

```bash
cp .env.example .env
# Fill in TAVILY_API_KEY (free: https://tavily.com)
# Fill in PRODUCTHUNT_API_TOKEN (free: https://api.producthunt.com/v2/docs)
```
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill includes undeclared execution of local scraper workflows tied to X/Twitter access, which goes beyond a simple configuration assistant. Even if functionally related, hidden subprocess-style scraping behavior is riskier in an agent skill because it obscures what code will run and what data will be accessed.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill includes undeclared execution of local scraper workflows tied to X/Twitter access, which goes beyond a simple configuration assistant. Even if functionally related, hidden subprocess-style scraping behavior is riskier in an agent skill because it obscures what code will run and what data will be accessed.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The skill includes undeclared execution of local scraper workflows tied to X/Twitter access, which goes beyond a simple configuration assistant. Even if functionally related, hidden subprocess-style scraping behavior is riskier in an agent skill because it obscures what code will run and what data will be accessed.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The skill includes undeclared execution of local scraper workflows tied to X/Twitter access, which goes beyond a simple configuration assistant. Even if functionally related, hidden subprocess-style scraping behavior is riskier in an agent skill because it obscures what code will run and what data will be accessed.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The skill includes undeclared execution of local scraper workflows tied to X/Twitter access, which goes beyond a simple configuration assistant. Even if functionally related, hidden subprocess-style scraping behavior is riskier in an agent skill because it obscures what code will run and what data will be accessed.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill includes undeclared execution of local scraper workflows tied to X/Twitter access, which goes beyond a simple configuration assistant. Even if functionally related, hidden subprocess-style scraping behavior is riskier in an agent skill because it obscures what code will run and what data will be accessed.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The skill includes undeclared execution of local scraper workflows tied to X/Twitter access, which goes beyond a simple configuration assistant. Even if functionally related, hidden subprocess-style scraping behavior is riskier in an agent skill because it obscures what code will run and what data will be accessed.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill includes undeclared execution of local scraper workflows tied to X/Twitter access, which goes beyond a simple configuration assistant. Even if functionally related, hidden subprocess-style scraping behavior is riskier in an agent skill because it obscures what code will run and what data will be accessed.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The skill instructs the agent to attach to the user’s already-open Chrome instance via remote debugging and extract/save X/Twitter session cookies. This is highly sensitive credential material: anyone obtaining those cookies may be able to impersonate the user’s account, and the use of CDP against a live browser can expose more than just the intended site if misused.

Missing User Warnings

High
Confidence
98% confidence
Finding
The skill describes extracting and saving X/Twitter session cookies without any explicit privacy or account-security warning. Session cookies are often equivalent to live authentication; storing them on disk dramatically increases the risk of account takeover if the file is stolen, reused, or mishandled by later automation.

YARA rule 'info_stealer': Information stealer patterns (credential harvesting, browser data theft) [malware]

High
Category
YARA Match
Content
window
> 3. Once logged in, I'll run a script that connects to that browser and saves your session cookies."

After the user confirms they are logged in to X in Chrome, run:

```bash
cd ~/multi-source-feed && source .venv/bin/activate && python login_save_session.py
```

This script connects to the already-open Chrome instance via CDP (Chrome DevTools Protocol) on port 9222, extracts the session/cookies, and saves them to `x_session.json` in the project root. It does **not** open a new browser window — it requires Chrome to already be running with `--remote-debugging-port=9222`.

### Step 4: Customize

**This step directly affects the quality of the daily brief.** Strongly encourage the user to customize before proceeding.

Ask the user:
> "The default profile is a generic template. I strongly recommend customizing these files to match your interests — this directly determines the quality of your daily brief. What topics do you care about? What should be filtered out?"

Based on t
Confidence
97% confidence
Finding
This heuristic correctly flags behavior consistent with information stealing: the skill directs extraction of live browser cookies and writes them to x_session.json. Even if intended for convenience, collecting reusable authentication material from a live browser is a high-risk credential-access pattern that can enable account hijacking.

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

High
Category
YARA Match
Content
summary (number of sources, items fetched, any errors). If successful, show 5-10 sample titles from `feed_slim.json`.

### Step 6: Schedule

The system runs in two phases. Phase 1 (scraping) must complete before Phase 2 (memo generation) starts.

**Phase 1: Scrape (crontab)** — Pure Python job that fetches all sources, deduplicates, and writes `feed_slim.json`. Set up a daily cron job:
```bash
(crontab -l 2>/dev/null; echo "0 9 * * * cd ~/multi-source-feed && .venv/bin/python3 -m src.pipeline >> /tmp/msf-scrape.log 2>&1") | crontab -
```

**Phase 2: Memo (OpenClaw cron)** — LLM-powered job that generates the daily brief and sends it to the user. Must run ~20 min after Phase 1 to ensure scraping is complete.

Create an OpenClaw cron job that:
1. Checks if `feed_slim.json` exists and is from today
2. Reads `config/user_profile.md` and `config/preferences.md`
3. Reads `feed_slim.json` (the scrape output)
4. Generates the daily brief following preferences.md format
5. Sends the brief t
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Credential Access

High
Category
Privilege Escalation
Content
pip install -r requirements.txt && playwright install chromium

# 3. Configure
cp .env.example .env
# Edit .env with your API keys

# 4. X Login
Confidence
72% confidence
Finding
The manual setup fallback instructs users to create and populate a .env file with API keys, which is a form of credential handling. While commonplace, it is still sensitive because the skill offers no safeguards around permissions, secure storage, or preventing accidental disclosure.

Credential Access

High
Category
Privilege Escalation
Content
# 3. Configure
cp .env.example .env
# Edit .env with your API keys

# 4. X Login
python login_save_session.py
Confidence
95% confidence
Finding
The adjacent X login step compounds the credential risk by pairing local secret storage with a script that saves browser session state. In context, this creates a concentrated local repository of both API keys and authenticated session material, substantially increasing the blast radius of any local compromise.

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

High
Category
YARA Match
Content
│
└───────────────────────────────┬─────────────────────────────────────┘
                                │
                                ▼
                     User's configured channel
```

## Daily Brief Pipeline

```
09:00 — crontab triggers python -m src.pipeline
     │
     ├── X/Twitter (Playwright)           ──┐
     ├── Hacker News (Algolia API)          │
     ├── GitHub Trending (BeautifulSoup)    │
     ├── AI blogs & tech media (RSS)        ├── raw items
     ├── Indie blogs & VC blogs (RSS)       │
     ├── arXiv (RSS)                        │
     ├── Reddit subs (API)                  │
     ├── Product Hunt (GraphQL)             │
     └── Tavily web searches (API)        ──┘
     │
     ▼
  Dedup
     ├──
Confidence
75% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
The code connects to an already-running Chrome instance over the local DevTools protocol and exports the active browser storage state, including authentication cookies, to disk. For a skill whose stated purpose is configuring a multi-source daily tech brief, this is unrelated capability that can enable credential or session theft and downstream account takeover.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The skill implements browser session extraction by accessing the user's existing Chrome context and serializing its session data to x_session.json. In the context of a news/briefing configuration skill, this behavior is unjustified and especially dangerous because it covertly repurposes unrelated authenticated state from the user's browser for potential impersonation.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The script includes anti-bot evasion measures such as launching Chrome with '--disable-blink-features=AutomationControlled' and redefining 'navigator.webdriver' to hide automation. In a skill advertised for configuring a tech brief, concealment of browser automation is not necessary for legitimate setup and strongly suggests an attempt to bypass platform detection or policy controls.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The implementation directly opens an authenticated X session via a local storage state, navigates to the user's home/following/trending feeds, and bulk-scrapes personalized content. That behavior exceeds the stated purpose of 'setup/configuration' for a daily brief and collects potentially sensitive account-specific data, making the mismatch itself a significant security and privacy risk.

Context-Inappropriate Capability

High
Confidence
99% confidence
Finding
The code launches Chrome with automation-evasion flags and injects a script to hide navigator.webdriver, which are classic anti-detection techniques. In a skill whose purpose is benign feed setup, such stealth behavior is unjustified and increases the likelihood that the code is trying to bypass platform safeguards while accessing user-account content.

Credential Access

High
Category
Privilege Escalation
Content
"""Centralized configuration — reads from .env if present."""

from __future__ import annotations
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Static analysis

No suspicious patterns detected.