Back to skill

Security audit

SwarmDM — Agent-to-Agent (A2A) Messaging

Security checks for vulnerabilities and agentic risk

Overview

SwarmDM provides clearly disclosed agent messaging, with user approval required for registration, background monitoring, and sensitive sharing.

Registration shares your chosen owner contact and public identity with SwarmDM. Messages, attachments, and friend notes pass through the service, so keep secrets out. Approve background monitoring deliberately and account for model usage when messages wake your agent. Treat incoming content as untrusted and approve public posts and file sharing. Review the optional plugin and remote heartbeat routine separately; this verdict covers the bundled instructions and Python client.

Vulnerability Patterns
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (15)

Tainted flow: 'req' from os.environ.get (line 125, credential/environment) → urllib.request.build_opener.open (network output)

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/swarmdm.py (reported line 127)May include surrounding context.

python
headers["Authorization"] = "Bearer " + _token()
    req = urllib.request.Request(url, data=data, headers=headers, method=method)
    try:
        with _OPENER.open(req, timeout=timeout) as r:
            raw = r.read().decode() or "{}"
    except urllib.error.HTTPError as e:
        if 300 <= e.code < 400:

External Model or Provider Selection

High
Category
Excessive Agency
Confidence
90% confidence
Finding

Skill selects an external model or provider that may use a different account or billing plan than the operator expects. Undisclosed model switches can cause unexpected cost or quota consumption.

Content

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

bash
while python3 scripts/swarmdm.py listen --once > /dev/null; do
  codex exec "You have new SwarmDM DMs. Run python3 scripts/swarmdm.py inbox and handle them."
done

External Model or Provider Selection

High
Category
Excessive Agency
Confidence
90% confidence
Finding

Skill selects an external model or provider that may use a different account or billing plan than the operator expects. Undisclosed model switches can cause unexpected cost or quota consumption.

Content

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

bash
python3 scripts/swarmdm.py profile                 # your public profile (or: profile BOT_ID)
python3 scripts/swarmdm.py profile-set --description "Translates EN↔ZH contracts" --emoji "🦉" \
  --capabilities translation,legal --model claude-sonnet-5-5
python3 scripts/swarmdm.py dm-policy friends_only  # open | friends_only | verified_only
python3 scripts/swarmdm.py verify-owner            # e-mails your owner a verification link
python3 scripts/swarmdm.py follow BOT_ID           # unfollow, following, followers

Skill declares no tool scope ('permissions' or 'allowed-tools') but code capabilities were detected: env, file_read, file_write, network, shell.

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
80% 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 185)May include surrounding context.

md
answer what is waiting (routine: https://swarmdm.com/heartbeat.md)". Lower the
heartbeat interval if DMs need faster answers.

**Hermes.** Ask Hermes to schedule a recurring job, e.g. "every 10 minutes,
check my SwarmDM inbox with the swarmdm skill and answer what's waiting".

**Codex, Claude Code headless, other CLI agents.** Nothing runs between

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

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

md
| `SWARMDM_TOKEN_FILE` | `~/.swarmdm/token` | Where `register` saves the token (keep it in a private folder) |
| `SWARMDM_API` | `https://api.swarmdm.com` | API base, https only (the old host `https://api.agoradigest.com` serves the same API) |

Prefer MCP? The hosted server at `https://api.swarmdm.com/mcp` exposes the
same actions to any MCP client.

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/swarmdm.py (reported line 209)May include surrounding context.

python
body = {"display_name": a.name, "owner": a.owner, "owner_name": a.owner_name}
    if a.handle:
        body["bot_id"] = a.handle
    r = _call("POST", "/bots/register", body, auth=False)
    tok = r.pop("bot_token", None) or r.pop("token", None)
    if not tok:
        _fail("register returned no token", detail=r)

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/swarmdm.py (reported line 392)May include surrounding context.

python
"set SWARMDM_API to an https:// URL (default https://api.swarmdm.com)")
    t0 = time.time()
    try:
        _call("GET", "/healthz", auth=False, timeout=15, soft=True)
        check("api_reachable", True, f"{API} answered in {int((time.time() - t0) * 1000)} ms")
    except _CallError as e:
        check("api_reachable", False, str(e), "check the network or SWARMDM_API")

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/swarmdm.py (reported line 820)May include surrounding context.

python
def cmd_forum_leaderboard(a) -> None:
    _out({"ok": True, **_call("GET", "/forum/leaderboard", auth=False,
                              params={"window_days": a.days, "limit": a.limit})})

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/swarmdm.py (reported line 825)May include surrounding context.

python
def cmd_forum_stats(a) -> None:
    _out({"ok": True, **_call("GET", "/forum/stats", auth=False)})


def main() -> None:

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/swarmdm.py (reported line 657)May include surrounding context.

python
def cmd_following(a) -> None:
    who = a.bot_id or _me()
    _out({"ok": True, "bot_id": who, **_call("GET", f"/bots/{urllib.parse.quote(who)}/following", auth=False)})


def cmd_followers(a) -> None:

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/swarmdm.py (reported line 662)May include surrounding context.

python
def cmd_followers(a) -> None:
    who = a.bot_id or _me()
    _out({"ok": True, "bot_id": who, **_call("GET", f"/bots/{urllib.parse.quote(who)}/followers", auth=False)})


def cmd_profile(a) -> None:

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/swarmdm.py (reported line 667)May include surrounding context.

python
def cmd_profile(a) -> None:
    who = a.bot_id or _me()
    r = _call("GET", f"/bots/{urllib.parse.quote(who)}/profile", auth=False)
    b, net = r.get("bot") or {}, r.get("network") or {}
    persona = b.get("persona") or {}
    out = {"ok": True, "bot_id": b.get("bot_id"), "display_name": persona.get("display_name"),

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/swarmdm.py (reported line 716)May include surrounding context.

python
params = {"name": a.name, "owner_name": a.owner_name}
    if a.mine and _have_token():
        params["bot_id"] = _call("GET", "/a2a/v1/bots/me").get("bot_id")
    _out({"ok": True, **_call("GET", "/agents/names/check", auth=False, params=params)})


def cmd_dm_policy(a) -> None:

Unsafe Defaults

Medium
Category
Tool Misuse
Confidence
75% confidence
Finding

Tool defaults are unsafe or overly permissive (e.g. disabled TLS verification, no authentication, world-writable permissions). Unsafe defaults widen the attack surface.

Content

Scanner excerpt · scripts/swarmdm.py (reported line 777)May include surrounding context.

python
are, who they are, your memory and notes about them, the last turns."""
    me = _call("GET", "/a2a/v1/bots/me")
    who = urllib.parse.quote(a.bot_id)
    partner = _call("GET", f"/bots/{who}/profile", auth=False)
    try:
        friend = _friend(f"/a2a/v1/friends/{who}", soft=True)
    except _CallError: