Back to skill

Security audit

AI News Aggregator

Security checks for vulnerabilities and agentic risk

Overview

This is a disclosed news-to-Discord skill, but it needs review because it automatically posts AI-generated summaries from untrusted web and social content to Discord.

Install only if you expect this skill to send your requested topics and gathered article, tweet, and video metadata to third-party providers and post generated text to a configured Discord channel. Use dry-run first, provide only the API keys needed for the chosen mode, avoid sensitive topics, and prefer a version with pinned dependencies plus Discord mention/link validation.

Vulnerability Patterns
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (2)

T09 · Insecure Skill Coding Practices

Warning
Location
news_aggregator.py:171
Finding
Untrusted news content is incorporated into an instruction-bearing AI prompt<![CDATA[ ## Vulnerability Details **File Location**: `news_aggregator.py:171-198` **Vulnerability Type**: Indirect prompt injection through external content **Risk Level**: Medium ### Vulnerable Code ```python item_lines = [ f"{i}. Title: {it.get('title','')}\n Source: {it.get('source','')}\n URL: {it.get('url','')}" for i, it in enumerate(items, 1) ] prompt = ( f"You are a news editor. The date is {today}.\n\n" f"Here are the top stories about \"{topic}\" from {period} ({len(items)} items):\n\n" + "\n".join(item_lines) + "\n\nComplete two tasks — output plain text only (no JSON, no extra commentary):\n\n" "## Task 1: Overview\n" "Write a 150-200 word editorial paragraph summarising the most important developments.\n" "- Natural journalistic prose, no bullet points or lists\n" "- Embed the most important story titles as Markdown hyperlinks: [keyword](url)\n" "- Highlight connections between stories and the overall trend\n\n" "## Task 2: Worth Reading\n" "Pick the 3 most worth-reading items. One sentence each explaining why:\n" "🔖 [Title](url) — reason\n\n" "Output only these two sections." ) try: print(f" [AI] Using provider={provider} model={model or PROVIDER_DEFAULTS.get(provider, '')}") editorial = call_ai(prompt, provider, model) except Exception as exc: print(f" [AI:{provider}] Editorial failed: {exc}") editorial = "\n".join( f"• [{it.get('title','')}]({it.get('url','')})" for it in items[:10] ) ``` ### Technical Analysis Article titles, source names, URLs, tweets, and other metadata obtained from RSS, Tavily, Twitter, and YouTube are externally controlled. The code concatenates these fields directly into the same user message that contains instructions for the AI model. There is no structured separation between trusted application instructions and untrusted source content. There is also no explicit instruction telling the model to treat embedded instructi ...[truncated 1770 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Place external records in a clearly delimited data section and identify every field as untrusted. 2. Use a trusted system message for the editorial policy and keep source data in a separate user message or structured payload. 3. Explicitly instruct the model not to execute, repeat, or follow instructions found inside titles, source names, URLs, tweets, or other retrieved content. 4. Normalize and validate URLs before including them in prompts or Discord messages. Permit only expected `https` or `http` schemes. 5. Escape Discord Markdown in externally supplied titles and source names. 6. Disable Discord mentions by including an `allowed_mentions` policy: ```python payload = { "content": header + chunk, "allowed_mentions": {"parse": []}, } ``` 7. Apply output validation before posting. Reject unexpected link schemes, suspicious mass mentions, and links whose displayed text or host differs from the validated source record. 8. Consider producing a deterministic list of validated links separately from AI-generated prose so that the model cannot introduce arbitrary destinations. ]]>

T08 · Insecure Dependencies

Warning
Location
news_aggregator.py:2
Finding
Automatically installed Python dependencies are not version-pinned<![CDATA[ ## Vulnerability Details **File Location**: `news_aggregator.py:2-10` **Vulnerability Type**: Uncontrolled third-party dependency resolution **Risk Level**: Medium ### Vulnerable Code ```python # /// script # requires-python = ">=3.9" # dependencies = [ # "feedparser", # "requests", # "openai", # "anthropic", # "tavily-python", # ] # /// ``` The documented execution procedure in `SKILL.md:161-169` runs the script through `uv`: ```bash uv run "$SCRIPT" [--topic "TOPIC"] [--days N] [--report TYPE] [--provider PROVIDER] [--model MODEL] [--dry-run] ``` ### Technical Analysis The PEP 723 dependency metadata specifies package names without exact versions or hashes. The Skill documentation states that `uv run` installs these dependencies automatically. As a result, the effective code executed by the Skill can change between runs even when the audited project files remain unchanged. Dependency resolution depends on the state of the configured package index and the latest compatible package releases. This is a supply-chain weakness rather than evidence that any currently declared package is malicious. Exploitation would require compromise of a dependency release, package maintainer account, configured package index, or dependency-resolution environment. ### Attack Path 1. An attacker compromises an upstream package, its maintainer account, or the package index used by `uv`. 2. The attacker publishes a malicious release under one of the unpinned dependency names. 3. A user invokes the documented `uv run` command after that release becomes eligible for resolution. 4. `uv` resolves and installs the malicious version because no exact version or integrity hash constrains the selection. 5. The malicious package executes during import or normal library use. 6. The package runs with the same operating-system privileges and environment access as the Skill process. ### Impact Assessment A compromised dependency could execute arbitrary Python code ...[truncated 507 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin every dependency to an exact reviewed version rather than using unconstrained package names. 2. Generate and commit a lockfile containing integrity hashes where the execution environment supports it. 3. Configure `uv` to use an explicitly approved package index rather than inheriting arbitrary resolver or index configuration. 4. Perform dependency updates through a controlled review process that includes vulnerability and provenance checks. 5. Use automated dependency scanning and verify package signatures or attestations when available. 6. Run the Skill under a dedicated, non-privileged account with access only to the environment variables and files required for aggregation. 7. Avoid exposing credentials for unused providers or sources to the process. Inject only the key required for the selected provider and requested report type. 8. Consider installing dependencies into a prebuilt, reviewed environment instead of resolving mutable packages automatically on every invocation. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (30)

YARA rule 'agent_skill_credential_exfiltration_webhook': AI agent skill credential harvesting followed by webhook or external exfiltration [agent_skills]

Critical
Category
YARA Match
Content
OpenClaw passes env vars declared in SKILL.md requires.env automatically.

AI provider selection (pick one):
  Provider   Env var needed          Default model
  ─────────  ──────────────────────  ─────────────────────────
  deepseek   DEEPSEEK_API_KEY        deepseek-chat
  openai     OPENAI_API_KEY          gpt-4o-mini
  claude     ANTHROPIC_API_KEY       claude-3-5-haiku-20241022

  Set AI_PROVIDER=<provider> env var, or pass --provider flag at runtime.
  Override model with AI_MODEL=<model> env var or --model flag.

External endpoints contacted:
  - https://api.deepseek.com/chat/completions     (provider=deepseek)
  - https://api.openai.com/v1/chat/completions    (provider=openai)
  - https://api.anthropic.com/v1/messages         (provider=claude)
  - https://discord.com/api/webhooks/...           (always, required)
  - https://techcrunch.com/.../feed/              (defa
Confidence
85% confidence
Finding
YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Tainted flow: 'TAVILY_API_KEY' from os.getenv (line 76, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
return []
    print(f"  Fetching Tavily: '{topic}' (last {days}d)")
    try:
        resp = requests.post(
            "https://api.tavily.com/search",
            json={
                "api_key":        TAVILY_API_KEY,
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'TWITTER_API_KEY' from os.getenv (line 77, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
return []
    print(f"  Fetching Twitter: '{topic}'")
    try:
        resp = requests.get(
            "https://api.twitterapi.io/twitter/tweet/advanced_search",
            headers={"X-API-Key": TWITTER_API_KEY},
            params={"query": topic, "queryType": "Top"},
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'YOUTUBE_API_KEY' from os.getenv (line 78, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
stats_map = {}
    for i in range(0, len(video_ids), 50):
        try:
            resp = requests.get(
                "https://www.googleapis.com/youtube/v3/videos",
                params={
                    "part": "statistics,contentDetails",
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'YOUTUBE_API_KEY' from os.getenv (line 78, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
resolved = {}
    for handle in handles:
        try:
            resp = requests.get(
                "https://www.googleapis.com/youtube/v3/channels",
                params={
                    "part":      "contentDetails,snippet",
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'YOUTUBE_API_KEY' from os.getenv (line 78, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
for handle, (_, playlist_id) in handle_map.items():
        display = AI_YOUTUBE_CHANNELS.get(handle, handle)
        try:
            resp = requests.get(
                "https://www.googleapis.com/youtube/v3/playlistItems",
                params={
                    "part":       "snippet",
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'YOUTUBE_API_KEY' from os.getenv (line 78, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
print(f"  Searching YouTube: '{topic}'")
    published_after = cutoff.strftime("%Y-%m-%dT%H:%M:%SZ")
    try:
        resp = requests.get(
            "https://www.googleapis.com/youtube/v3/search",
            params={
                "part":           "snippet",
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill description understates and misstates important behavior, especially around what sources are actually used and the fact that it performs broad third-party network access and Discord posting. Description/behavior mismatches are dangerous because users may consent to one workflow while the skill executes another, increasing the chance of unintended data transmission or trust abuse.

External Model or Provider Selection

High
Category
Excessive Agency
Content
uv run "$SCRIPT" --topic "climate change" --days 7 --provider claude

# Use a specific model
uv run "$SCRIPT" --topic "Bitcoin" --provider openai --model gpt-4o

# Trending AI on Twitter and YouTube
uv run "$SCRIPT" --report trending
Confidence
90% confidence
Finding
Allowing runtime selection of external providers and models increases the attack surface and data-governance risk because the destination handling user content can change per invocation. Without strict policy controls, a user or prompt may steer data to a less-trusted provider than the operator intended, causing unapproved external disclosure.

Credential Access

High
Category
Privilege Escalation
Content
AI      : OpenAI (default) · DeepSeek · Anthropic Claude — user's choice
Output  : Discord channel via webhook (formatted markdown)

Credentials: read from environment variables only (no .env file loaded).
OpenClaw passes env vars declared in SKILL.md requires.env automatically.

AI provider selection (pick one):
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
97% confidence
Finding
The skill declares no explicit tool scope or permissions even though it clearly needs environment-variable access and broad outbound network access to multiple third parties, including a Discord webhook. Missing scope declarations weaken platform-level safety controls because users and orchestrators cannot reliably constrain or review what the skill is allowed to do before execution.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger text is overly broad, causing the skill to activate on common phrases like requests for news, digests, trending topics, or updates on any subject. Overbroad activation is risky because it can cause unintended execution of a networked skill that sends content to external services, including Discord, when the user may have only been asking conversationally.

External Transmission

Medium
Category
Data Exfiltration
Content
| Endpoint | Purpose | Condition |
|----------|---------|-----------|
| `https://api.openai.com/v1/chat/completions` | AI editorial summarisation | Only if `provider=openai` (default) |
| `https://api.deepseek.com/chat/completions` | AI editorial summarisation | Only if `provider=deepseek` |
| `https://api.anthropic.com/v1/messages` | AI editorial summarisation | Only if `provider=claude` |
| `https://discord.com/api/webhooks/...` | Post digest to Discord | Always (required) |
Confidence
85% confidence
Finding
This skill sends fetched content and user-selected topics to OpenAI for summarization, which is an external transmission to a third-party model provider. In context this is expected functionality, but it remains a real data-exposure risk if sensitive prompts, private topics, or proprietary feed content are included without clear consent or minimization.

External Transmission

Medium
Category
Data Exfiltration
Content
| Endpoint | Purpose | Condition |
|----------|---------|-----------|
| `https://api.openai.com/v1/chat/completions` | AI editorial summarisation | Only if `provider=openai` (default) |
| `https://api.deepseek.com/chat/completions` | AI editorial summarisation | Only if `provider=deepseek` |
| `https://api.anthropic.com/v1/messages` | AI editorial summarisation | Only if `provider=claude` |
| `https://discord.com/api/webhooks/...` | Post digest to Discord | Always (required) |
| `https://techcrunch.com/.../feed/` | RSS news (AI topic) | Default AI topic only |
Confidence
85% confidence
Finding
This skill can transmit content externally to DeepSeek for summarization when that provider is selected. Although this is part of the advertised feature set, it still creates third-party data handling risk and may expose user interests, collected article text, or other contextual data beyond the local environment.

External Transmission

Medium
Category
Data Exfiltration
Content
|----------|---------|-----------|
| `https://api.openai.com/v1/chat/completions` | AI editorial summarisation | Only if `provider=openai` (default) |
| `https://api.deepseek.com/chat/completions` | AI editorial summarisation | Only if `provider=deepseek` |
| `https://api.anthropic.com/v1/messages` | AI editorial summarisation | Only if `provider=claude` |
| `https://discord.com/api/webhooks/...` | Post digest to Discord | Always (required) |
| `https://techcrunch.com/.../feed/` | RSS news (AI topic) | Default AI topic only |
| `https://www.theverge.com/rss/...` | RSS news (AI topic) | Default AI topic only |
Confidence
85% confidence
Finding
This skill can transmit content externally to Anthropic when Claude is selected, which constitutes third-party disclosure of gathered material and user-request context. The behavior is expected but still security-relevant because model-provider transfers can leak sensitive or regulated data if not constrained.

External Transmission

Medium
Category
Data Exfiltration
Content
| `https://techcrunch.com/.../feed/` | RSS news (AI topic) | Default AI topic only |
| `https://www.theverge.com/rss/...` | RSS news (AI topic) | Default AI topic only |
| `https://www.nytimes.com/svc/collections/...` | RSS news (AI topic) | Default AI topic only |
| `https://api.tavily.com/search` | Custom topic news search | Only if `TAVILY_API_KEY` set |
| `https://api.twitterapi.io/twitter/tweet/advanced_search` | Twitter search | Only if `TWITTERAPI_IO_KEY` set |
| `https://www.googleapis.com/youtube/v3/...` | YouTube search | Only if `YOUTUBE_API_KEY` set |
Confidence
84% confidence
Finding
The skill performs external transmission to Tavily for custom-topic searches, exposing user-requested topics and query context to a third-party search API. This is less severe than credential leakage, but it still matters because user interests or operational research topics may be sensitive.

External Transmission

Medium
Category
Data Exfiltration
Content
| `https://www.theverge.com/rss/...` | RSS news (AI topic) | Default AI topic only |
| `https://www.nytimes.com/svc/collections/...` | RSS news (AI topic) | Default AI topic only |
| `https://api.tavily.com/search` | Custom topic news search | Only if `TAVILY_API_KEY` set |
| `https://api.twitterapi.io/twitter/tweet/advanced_search` | Twitter search | Only if `TWITTERAPI_IO_KEY` set |
| `https://www.googleapis.com/youtube/v3/...` | YouTube search | Only if `YOUTUBE_API_KEY` set |

Exactly one AI endpoint is contacted per run, determined by the active provider. The default provider is OpenAI (`OPENAI_API_KEY` required). Switch providers with `--provider deepseek` or `--provider claude`.
Confidence
84% confidence
Finding
The skill can send queries to a third-party Twitter/X search API, revealing user interests and potentially generating externally visible or provider-logged lookups. In a general news skill this is contextually expected, but still constitutes a real outbound data flow that should be constrained and disclosed.

External Transmission

Medium
Category
Data Exfiltration
Content
Override model with AI_MODEL=<model> env var or --model flag.

External endpoints contacted:
  - https://api.deepseek.com/chat/completions     (provider=deepseek)
  - https://api.openai.com/v1/chat/completions    (provider=openai)
  - https://api.anthropic.com/v1/messages         (provider=claude)
  - https://discord.com/api/webhooks/...           (always, required)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
External endpoints contacted:
  - https://api.deepseek.com/chat/completions     (provider=deepseek)
  - https://api.openai.com/v1/chat/completions    (provider=openai)
  - https://api.anthropic.com/v1/messages         (provider=claude)
  - https://discord.com/api/webhooks/...           (always, required)
  - https://techcrunch.com/.../feed/              (default AI topic only)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
External endpoints contacted:
  - https://api.deepseek.com/chat/completions     (provider=deepseek)
  - https://api.openai.com/v1/chat/completions    (provider=openai)
  - https://api.anthropic.com/v1/messages         (provider=claude)
  - https://discord.com/api/webhooks/...           (always, required)
  - https://techcrunch.com/.../feed/              (default AI topic only)
  - https://www.theverge.com/rss/...              (default AI topic only)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
- https://techcrunch.com/.../feed/              (default AI topic only)
  - https://www.theverge.com/rss/...              (default AI topic only)
  - https://www.nytimes.com/svc/collections/...   (default AI topic only)
  - https://api.tavily.com/search                 (only if TAVILY_API_KEY set)
  - https://api.twitterapi.io/...                 (only if TWITTERAPI_IO_KEY set)
  - https://www.googleapis.com/youtube/v3/...     (only if YOUTUBE_API_KEY set)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
- https://techcrunch.com/.../feed/              (default AI topic only)
  - https://www.theverge.com/rss/...              (default AI topic only)
  - https://www.nytimes.com/svc/collections/...   (default AI topic only)
  - https://api.tavily.com/search                 (only if TAVILY_API_KEY set)
  - https://api.twitterapi.io/...                 (only if TWITTERAPI_IO_KEY set)
  - https://www.googleapis.com/youtube/v3/...     (only if YOUTUBE_API_KEY set)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
- https://www.theverge.com/rss/...              (default AI topic only)
  - https://www.nytimes.com/svc/collections/...   (default AI topic only)
  - https://api.tavily.com/search                 (only if TAVILY_API_KEY set)
  - https://api.twitterapi.io/...                 (only if TWITTERAPI_IO_KEY set)
  - https://www.googleapis.com/youtube/v3/...     (only if YOUTUBE_API_KEY set)

CLI flags:
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
- https://www.theverge.com/rss/...              (default AI topic only)
  - https://www.nytimes.com/svc/collections/...   (default AI topic only)
  - https://api.tavily.com/search                 (only if TAVILY_API_KEY set)
  - https://api.twitterapi.io/...                 (only if TWITTERAPI_IO_KEY set)
  - https://www.googleapis.com/youtube/v3/...     (only if YOUTUBE_API_KEY set)

CLI flags:
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Natural-Language Policy Violations

Medium
Confidence
97% confidence
Finding
The function name and prompt explicitly require generating an English editorial, including the instruction 'Write a 150-200 word editorial paragraph' with no option for user language selection. This is a natural-language policy issue because the skill imposes a specific language rather than offering locale choice or documenting a justified region-specific constraint.

Static analysis

No suspicious patterns detected.