Back to skill

Security audit

voice2need

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to be a legitimate research tool, but live Discord export gives a sensitive token and the full environment to a configurable external program, which needs careful review before use.

Use the offline demo or existing Discord exports when possible. For live runs, approve only specific sources and budgets, keep raw run directories private, use only trusted API tokens, and for Discord verify the exporter path yourself and run with a minimal environment so unrelated secrets are not exposed.

Vulnerability Patterns
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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)

T07 · Tool Hijacking and Spoofing

Error
Location
scripts/voice2need/adapters/discord.py:190
Finding
Configurable Discord Export Executable Receives the Discord Token and Full Parent Environment<![CDATA[ ## Vulnerability Details **File Location**: `scripts/voice2need/adapters/discord.py:190-201` and `scripts/voice2need/adapters/discord.py:245-250` **Vulnerability Type**: Untrusted local tool selection and excessive environment inheritance **Risk Level**: High ### Vulnerable Code ```python def _export_one(ctx, job, command, token, channel, relative, timeout): """One process and one DCE worker; private output never reaches the console.""" directory = ctx.path(relative) directory.mkdir(parents=True, exist_ok=True) args = command + ["export", "-c", channel, "-f", "Json", "-o", str(directory) + os.sep, "--after", iso_time(job["start"]), "--before", iso_time(job["end"]), "--include-threads", "All", "--parallel", "1", "--media", "false", "--markdown", "false", "--respect-rate-limits", "true"] stdout, stderr, code, status = b"", b"", None, "complete" try: result = subprocess.run(args, env={**os.environ, "DISCORD_TOKEN": token}, capture_output=True, timeout=timeout, check=False) ``` The command is selected from the job configuration using only structural validation: ```python command = job.get("dce_command") if not isinstance(command, list) or not command or not all(isinstance(c, str) for c in command): raise SourceError("invalid-input", "Set dce_command to an installed executable, or dotnet plus its DLL") if not (len(command) == 1 or (len(command) == 2 and Path(command[0]).name == "dotnet" and command[1].lower().endswith(".dll"))): raise SourceError("invalid-input", "dce_command accepts only one executable or dotnet plus its DLL; no flags") ``` ### Technical Analysis The validation limits the number and shape of command elements, which prevents direct shell-argument injection because `subprocess.run` is invoked without a shell. It does not, however, verify that the selected executable is ...[truncated 2413 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Allowlist the expected exporter** - Require an absolute path. - Resolve symlinks before validation. - Verify that the resolved executable is an approved DiscordChatExporter binary. - For DLL execution, allow only an explicitly approved absolute DLL path. 2. **Verify tool integrity** - Pin an expected version. - Validate a cryptographic hash or trusted code signature before execution. - Record the resolved path, version, and hash in the run manifest. - Require renewed authorization if any of those values change. 3. **Use a minimal child environment** - Do not copy all of `os.environ`. - Pass only `DISCORD_TOKEN` and narrowly required runtime variables. - If `PATH` is needed, construct a controlled value rather than inheriting an untrusted one. - Explicitly exclude cloud, CI, package-registry, SSH-agent, and unrelated API credentials. 4. **Strengthen authorization** - Treat a change in `dce_command` as a material scope change. - Require explicit approval of the resolved executable path in addition to Discord account and channel confirmation. - Persist the approved tool identity in the run checkpoint and reject mismatches on resume. 5. **Reduce process privileges** - Run the exporter in a restricted subprocess environment or sandbox. - Limit filesystem access to its output directory. - Restrict outbound network access to Discord endpoints where practical. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/voice2need/adapters/apify.py:57
Finding
Unpinned Third-Party Apify Actors Can Change After Skill Review<![CDATA[ ## Vulnerability Details **File Location**: `scripts/voice2need/adapters/reddit.py:16,133-145`, `scripts/voice2need/adapters/youtube.py:30,482-483`, and `scripts/voice2need/adapters/apify.py:57-68` **Vulnerability Type**: Mutable remote supply-chain dependency **Risk Level**: Medium ### Vulnerable Code The Reddit integration identifies an actor by its mutable name: ```python ACTOR = "fatihtahta/reddit-scraper-search-fast" ``` It starts that actor during collection: ```python def collect(ctx, job): value = actor_input(job) seen = {} def save_page(items, refs): records = [_record(item, raw, job) for item, raw in zip(items, refs)] ctx.add_records(records) seen.update((record["native_id"], record) for record in records) _, _, info = run_actor(ctx, ACTOR, value, max_charge_usd=job.get("max_charge_usd", 1), max_polls=job.get("max_polls", 30), poll_seconds=job.get("poll_seconds", 2), max_dataset_pages=job.get("max_pages", 100), max_items=job.get("max_items", 10000), page_size=job.get("page_size", 1000), on_page=save_page) ``` The generic Apify integration sends the job to the actor name without pinning an immutable build: ```python actor_id = actor_id.replace("/", "~") options = {"maxTotalChargeUsd": max_charge_usd, "maxItems": max_items, "timeout": actor_timeout_seconds, "waitForFinish": 0, "restartOnError": "false"} digest = hashlib.sha256(json.dumps([actor_id, input_data, options], sort_keys=True).encode()).hexdigest() state_key = "apify-" + digest saved = ctx.get_checkpoint(state_key) or {} headers = {"Authorization": "Bearer " + require_env("APIFY_API_TOKEN")} if saved.get("run"): state, last_raw = saved["run"], saved["raw"] else: response = ctx.request("POST", f"{API}/acts/{actor_id}/runs", params=options, headers=headers, json_body=input_data, cache=True) ``` YouTube transcript co ...[truncated 2764 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Pin immutable actor builds** - Use an immutable Apify build or version identifier where the provider supports it. - Do not rely on a mutable latest/default actor build for production research. 2. **Record remote dependency identity** - Store the actor ID, build ID, version, and available image or source digest in checkpoints, manifests, and coverage reports. - Include this identity in the request fingerprint used for caching and resume behavior. 3. **Detect dependency changes** - Reject execution when the resolved remote build differs from the previously approved build. - Require explicit user approval before adopting a new actor version. - Re-run compatibility and security review when a pinned build changes. 4. **Prefer controlled actors** - Mirror or maintain reviewed actor implementations under an organization-controlled account. - Apply source review, release signing, and restricted publisher access. 5. **Preserve existing containment controls** - Continue enforcing per-run charge caps, item caps, polling limits, request limits, and strict input schemas. - Keep actor identifiers non-configurable unless an additional explicit trust policy and approval process are introduced. 6. **Validate result integrity** - Add provider-build metadata to every raw response. - Flag unexplained schema or behavioral changes. - Require independent evidence review before actor-derived content changes a material decision. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (18)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
Undeclared outbound HTTPS, credential retrieval from environment variables, and persistent local storage of raw data/checkpoints materially change the security profile of the skill. These hidden infrastructure behaviors can leak sensitive data, create compliance issues, and leave durable artifacts on disk without informed approval.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
Undeclared outbound HTTPS, credential retrieval from environment variables, and persistent local storage of raw data/checkpoints materially change the security profile of the skill. These hidden infrastructure behaviors can leak sensitive data, create compliance issues, and leave durable artifacts on disk without informed approval.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Undeclared outbound HTTPS, credential retrieval from environment variables, and persistent local storage of raw data/checkpoints materially change the security profile of the skill. These hidden infrastructure behaviors can leak sensitive data, create compliance issues, and leave durable artifacts on disk without informed approval.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
Undeclared outbound HTTPS, credential retrieval from environment variables, and persistent local storage of raw data/checkpoints materially change the security profile of the skill. These hidden infrastructure behaviors can leak sensitive data, create compliance issues, and leave durable artifacts on disk without informed approval.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
Undeclared outbound HTTPS, credential retrieval from environment variables, and persistent local storage of raw data/checkpoints materially change the security profile of the skill. These hidden infrastructure behaviors can leak sensitive data, create compliance issues, and leave durable artifacts on disk without informed approval.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
Undeclared outbound HTTPS, credential retrieval from environment variables, and persistent local storage of raw data/checkpoints materially change the security profile of the skill. These hidden infrastructure behaviors can leak sensitive data, create compliance issues, and leave durable artifacts on disk without informed approval.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
Undeclared outbound HTTPS, credential retrieval from environment variables, and persistent local storage of raw data/checkpoints materially change the security profile of the skill. These hidden infrastructure behaviors can leak sensitive data, create compliance issues, and leave durable artifacts on disk without informed approval.

Anti-Refusal Statement

High
Category
Anti-Refusal
Content
4. Write the findings JSON using [the findings format](references/findings-format.md). Keep each claim beside an exact source snippet and its evidence ID. Keep authors, projects, payers, personal percentages, and historical context separate.
5. Answer all fourteen research questions or state the specific remaining gap. Seek counterexamples and check underrepresented platforms, recent evidence, direct competitors, and ordinary discussions that keyword searches could miss.
6. Independently review the conclusions that change a decision. Check their quotations, authors, time, context, money, action status, and strongest counterexample. Use a separate reviewer when available; otherwise label a separate self-review honestly.
7. Validate, render, and read the final report. Fix structural failures and semantic errors. Trace every important finding to its destination or a reason for deferral; do not judge completion by record counts alone.

For a new domain or a changed analysis prompt, use the invented cases in [semantic evaluation](references/semantic-evaluation.md). Offline unit tests check identities and arithmetic; they cannot certify that an AI understood a conversation.
Confidence
85% confidence
Finding
Skill instructs the agent to omit warnings, disclaimers, or ethical commentary. Stripping safety caveats hides risk from the user and is a common jailbreak preamble.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
"--markdown", "false", "--respect-rate-limits", "true"]
    stdout, stderr, code, status = b"", b"", None, "complete"
    try:
        result = subprocess.run(args, env={**os.environ, "DISCORD_TOKEN": token},
                                capture_output=True, timeout=timeout, check=False)
        stdout, stderr, code = result.stdout, result.stderr, result.returncode
    except subprocess.TimeoutExpired as exc:
Confidence
84% confidence
Finding
The code launches an external process with the entire parent environment plus `DISCORD_TOKEN`, which can expose unrelated secrets to the child process and any of its descendants. In this skill context, the child is a user-configurable exporter command, so a malicious or substituted executable could read and exfiltrate all inherited environment variables, not just the Discord token.

Lp3

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding
The skill advertises operational behaviors that imply access to network, filesystem, shell, and environment-backed credentials, yet it declares no explicit tool or permission scope. This creates a confused-deputy risk where a host may grant broader capabilities than users expect, enabling unauthorized data collection, token use, local persistence, or subprocess execution.

Natural-Language Policy Violations

Medium
Confidence
89% confidence
Finding
The example configuration sets `"language": "en"`, which presents English as the default analysis language in the natural-language reference. Because this file does not indicate that users may choose another language or that English is required for a justified region-specific purpose, it can violate the language/locale policy for skills.

Unbounded Output

Medium
Category
Output Handling
Content
| `retention_days` | Positive integer, default 30; records a retention review date, not automatic deletion |
| `limits.max_requests` | Nonnegative Session HTTP-request cap, default 100; failures also consume attempts |
| `limits.max_units` | Optional nonnegative internal resource-unit cap; `null` leaves it unset; not a dollar cap or a complete provider quota model |
| `analysis` | Packet subject-record/character limits; optional `evidence_ids` selects candidate IDs; context/long records can exceed limits without truncation |
| `questions` | Optional question wording by Q01–Q14, or an array of `{"id":"Q08","question":"..."}`; all fourteen slots remain |

Each job requires a unique `id` of 1–80 letters, digits, `_` or `-`, and `platform` in `reddit`, `youtube`, `x`, `discord`. Specify a mode explicitly. A changed collection scope uses a new config and new run directory; resuming reuses the unchanged config and run directory. Prior source/spend authorization still applies within its stated boundary.
Confidence
60% confidence
Finding
Output size or generation rate is not bounded. Unbounded output enables denial-of-service through resource exhaustion, log flooding, or context-window stuffing.

External Transmission

Medium
Category
Data Exfiltration
Content
from voice2need.runtime import SourceError, require_env

API = "https://api.apify.com/v2"
TERMINAL = {"SUCCEEDED", "FAILED", "ABORTED", "TIMED-OUT"}
ACTIVE = {"READY", "RUNNING", "TIMING-OUT", "ABORTING"}
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"--markdown", "false", "--respect-rate-limits", "true"]
    stdout, stderr, code, status = b"", b"", None, "complete"
    try:
        result = subprocess.run(args, env={**os.environ, "DISCORD_TOKEN": token},
                                capture_output=True, timeout=timeout, check=False)
        stdout, stderr, code = result.stdout, result.stderr, result.returncode
    except subprocess.TimeoutExpired as exc:
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

External Transmission

Medium
Category
Data Exfiltration
Content
from voice2need.runtime import SourceError, require_env
from .apify import positive_int

API = "https://api.x.com/2/tweets"
FIELDS = "id,text,created_at,author_id,conversation_id,referenced_tweets,public_metrics,lang,note_tweet"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Intent-Code Divergence

Medium
Confidence
80% confidence
Finding
The module documentation states "bounded, opt-in HTTP," which implies network access is gated by explicit live enablement. However, the request path only blocks networking when both live is false and transport is None, so any caller-provided transport can execute requests without --live, contradicting the stated opt-in behavior.

Natural-Language Policy Violations

Low
Confidence
94% confidence
Finding
The config sets "language": "en", which is a natural-language locale constraint. In this file there is no indication that the user can choose another language or that the English-only setting is required for a documented region-specific purpose.

Natural-Language Policy Violations

Low
Confidence
95% confidence
Finding
The config hard-codes the language as "en", which is a natural-language locale constraint. Under the policy, locale restrictions should either offer user choice or be clearly documented and justified as region-specific; this file does neither.

Static analysis

No suspicious patterns detected.