Back to skill

Security audit

redreplier-mean-marketer

Security checks for vulnerabilities and agentic risk

Overview

This skill mostly matches its marketing-notification purpose, but it asks for persistent scheduled polling and uses a disclosed token to mutate remote state beyond its stated network scope.

Install only if you are comfortable with an ongoing background job that uses your RedReplier token every 5 minutes, mutates lead/mention state, and sends server-generated messages to your chosen channel. Before enabling it, use a dedicated limited token, confirm how to disable both the RedReplier loop and the local scheduler, and treat delivered links/messages as untrusted remote content.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • 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)

T01 · Skill Instruction Hijacking

Error
Location
scripts/mean-marketer.js:184
Finding

Untrusted Server-Controlled Content Is Relayed Verbatim to Users

Content
View full analysis
{ const data = await request("POST", "/api/v1/mean-marketer/poll"); // Stay silent when the server says there's nothing worth pinging about. if (!data || data.enabled === false) return; if (!data.message) return; if (data.noNewOpportunity && !data.beMean) return; const lines = [String(data.message)]; const opp = data.opportunity; if (opp) { lines.push(""); lines.push("Opportunity:"); if (opp.title) lines.push(`- Title: ${opp.title}`); if (opp.url) lines.push(`- Link: ${opp.url}`); if (opp.source) { lines.push( `- Source: ${opp.source}${opp.subreddit ? ` (r/${opp.subreddit})` : ""}`, ); } if (opp.relevanceScore != null) lines.push(`- Score: ${opp.relevanceScore}/100`); if (opp.relevanceReason) lines.push(`- Why: ${opp.relevanceReason}`); lines.push(`- ID: ${opp.id} (reply "approve" or "reject" to triage)`); } console.log(lines.join("\n").trim()); }, ``` The associated Skill instructions explicitly require verbatim relay: ```markdown 1. **If `message` is non-null, send it to the user as-is.** It's already written in the right tone at the right meanness — do not rewrite, soften, or embellish it. ``` ### Technical Analysis The `message`, title, URL, relevance reason, source, subreddit, and ID are obtained from a remote API and inserted directly into output. The Skill instructs the host agent to deliver the server-generated message unchanged, without validation or review. This creates an external instruction and output-control channel. If the RedReplier service, an upstream data source, or the API response path is compromised, an attacker can return content containing: - Prompt-inject ...[truncated 1557 chars]
Remediation
View remediation

T06 · System Persistence

Warning
Location
SKILL.md:112
Finding

Skill Requires Indefinite Scheduled Background Persistence

Content
View full analysis
⚠️ **Each poll mutates server state** (it hands the lead over and advances the strike > clock). So the scheduled job must ALWAYS deliver whatever `notify` prints — never run a > poll just to peek and throw it away, or you'll "consume" a lead silently. ### Set it up — pick your host **Hermes (cron):** create one cron job, every 5 minutes, no-agent mode, delivering the script's stdout to your channel. Concretely: - schedule: `5m` - script/command: `node ~/.hermes/skills/redreplier/redreplier-mean-marketer/scripts/mean-marketer.js notify` (adjust ...[truncated 2479 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/mean-marketer.js:235
Finding

Authenticated Triage Request Exceeds the Declared Network Permission Scope

Content
View full analysis
{ const parsed = parseArgs(args); if (!parsed.id || !parsed.status) { error( "Usage: ./scripts/mean-marketer.js triage --id --status APPROVED|REJECTED|NEW", ); process.exit(1); } output( await request("PATCH", `/api/v1/mentions/${parsed.id}/status`, { status: parsed.status, }), ); }, ``` ### Technical Analysis The declared network allowlist is limited to `/ai-app/api/v1/mean-marketer/*`. The `triage` command instead makes an authenticated request to: ```text /ai-app/api/v1/mentions/{id}/status ``` This endpoint is outside the disclosed path boundary. Consequently, the implementation has broader network behavior than the permission manifest and the “and nothing else” documentation claim. The client also does not validate `status` against `APPROVED`, `REJECTED`, or `NEW`, despite presenting those as the permitted values. The mention ID is interpolated into the URL without explicit encoding or format validation. Backend authorization and validation may limit exploitation, but no such guarantees are visible in the audited project. ### Attack Path 1. A user or policy engine approves the documented `mean-marketer/*` API scope. 2. An agent or caller invokes `triage` with an ID and status. 3. The script attaches the stored bearer token to a `PATCH` ...[truncated 775 chars]
Remediation
View remediation
Vulnerability Patterns
  • Rogue AgentSelf-Modification, Session Persistence
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
Findings (13)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 52)May include surrounding context.

md
./scripts/mean-marketer.js setup # prompts, input hidden

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 53)May include surrounding context.

md
./scripts/mean-marketer.js setup # prompts, input hidden

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 91)May include surrounding context.

md
./scripts/mean-marketer.js setup # prompts, input hidden

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 92)May include surrounding context.

md
./scripts/mean-marketer.js setup # prompts, input hidden

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 93)May include surrounding context.

md
./scripts/mean-marketer.js setup # prompts, input hidden

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 94)May include surrounding context.

md
./scripts/mean-marketer.js setup # prompts, input hidden

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 95)May include surrounding context.

md
./scripts/mean-marketer.js setup # prompts, input hidden

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 121)May include surrounding context.

md
./scripts/mean-marketer.js setup # prompts, input hidden

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 147)May include surrounding context.

md
./scripts/mean-marketer.js setup # prompts, input hidden

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 158)May include surrounding context.

md
./scripts/mean-marketer.js setup # prompts, input hidden

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 198)May include surrounding context.

md
./scripts/mean-marketer.js setup # prompts, input hidden

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · SKILL.md (reported line 79)May include surrounding context.

md
The token never appears in a command-line argument. `setup` reads it from a hidden prompt
or from stdin, and refuses a `--key` flag outright. Keep it out of your crontab too: the
cron examples below rely on the stored config file rather than an inline variable, so the
secret stays in one `600` file instead of being copied into `crontab -l` output, process
listings and backups.

To rotate or revoke, delete `~/.config/redreplier/config.json` and generate a new token at

Session Persistence

Medium
Category
Rogue Agent
Confidence
82% confidence
Finding

The skill requires setting up a recurring unattended background job that continuously polls an external service and automatically relays server-generated content. Persistent scheduled execution increases blast radius if the remote service is compromised, abused, or starts returning harmful/spammy content, because the host agent will keep delivering it without per-message approval.

Content

Scanner excerpt · SKILL.md (reported line 108)May include surrounding context.

md
## Run it on a schedule — READ THIS, it's the whole point

This skill does **not** poll by itself, and there is **no webhook** — RedReplier's API is
pull-based. Installing the skill only gives you a command. **You (the host agent) MUST create
a recurring job that runs that command**, or nothing will ever happen. This is a required
setup step, not optional.

Static analysis

No suspicious patterns detected.