Back to skill

Security audit

RDA MSG Board

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to control an LED message board as advertised, but it handles board passwords in ways users may not expect or understand.

Review before installing. Use this only for boards on a trusted network, do not send confidential messages, avoid default or reused passwords, and treat boards.yaml as a secret file. Prefer HTTPS or an isolated management network where possible, and require explicit user confirmation for ambiguous send requests.

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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/send_message.py:67
Finding

Board Credentials and Message Content Transmitted over Plaintext HTTP

Content
View full analysis

Vulnerability Details

File Location: scripts/send_message.py, lines 67-100
Vulnerability Type: Plaintext transmission of credentials and sensitive message data
Risk Level: High

Vulnerable Code

python
# Handle hostname vs IP (simple check, user must provide scheme or we assume http)
if ip.startswith("http"):
    url = f"{ip}/api"
else:
    url = f"http://{ip}/api"

# Build payload
payload = {"MSG": args.message}
if args.repeat is not None: payload["REP"] = args.repeat
if args.buzzer is not None: payload["BUZ"] = args.buzzer
if args.delay is not None: payload["DEL"] = args.delay
if args.brightness is not None: payload["BRI"] = args.brightness
if args.chirp is not None: payload["ALERTCHIRP"] = args.chirp

data = json.dumps(payload).encode('utf-8')

# Setup request
req = urllib.request.Request(url, data=data, method='POST')
req.add_header('Content-Type', 'application/json')

# Basic Auth
auth_str = f"{user}:{password}"
b64_auth = base64.b64encode(auth_str.encode('utf-8')).decode('utf-8')
req.add_header('Authorization', f"Basic {b64_auth}")

Technical Analysis

The script sends the board username, password, and message through an HTTP request. Base64 encoding is required by HTTP Basic Authentication, but it provides no confidentiality and can be trivially decoded.

When the supplied destination does not begin with http, the script explicitly constructs a plaintext http:// URL. It also accepts a caller-controlled URL through --ip without restricting the destination to approved board addresses or requiring HTTPS. Consequently, credentials loaded from environment variables or boards.yaml can be sent to a caller-selected endpoint.

The network transmission itself is necessary for the Skill's declared message-board functionality. The use of plaintext HTTP and unrestricted destinations, however, does not follow least-trust principles and exposes more sensitive information ...[truncated 1234 chars]

Remediation
View remediation

Remediation Suggestions

  • Require HTTPS for authenticated connections and retain standard TLS certificate and hostname validation.
  • Reject plaintext HTTP unless the user explicitly enables a documented legacy mode after receiving a security warning.
  • Parse destinations with urllib.parse.urlsplit and permit only approved schemes, hosts, and ports.
  • Maintain an explicit allowlist of configured board addresses rather than accepting arbitrary credential-bearing URLs.
  • Reject URL user information, unexpected paths, fragments, and malformed hostnames.
  • Ensure authorization headers are never forwarded across redirects to a different origin; preferably disable redirects for this API request.
  • Avoid default credentials and require users to configure unique board passwords.
  • Where the target hardware cannot support TLS, use a trusted local HTTPS gateway, an authenticated VPN, or an isolated management network.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/manage_boards.py:29
Finding

Board Passwords Stored in Plaintext without Explicit Permission Hardening

Content
View full analysis

Vulnerability Details

File Location: scripts/manage_boards.py, lines 29-32 and 64-68
Vulnerability Type: Insecure local storage of authentication credentials
Risk Level: Medium

Vulnerable Code

python
def save_profiles(profiles):
    """Save board profiles to boards.yaml."""
    config_path = get_config_path()
    try:
        import yaml
        with open(config_path, 'w') as f:
            yaml.dump({'profiles': profiles}, f, default_flow_style=False)
python
profiles[args.name] = {
    'ip': args.ip,
    'user': args.user,
    'pass': args.password
}

save_profiles(profiles)

Technical Analysis

Board passwords are serialized directly into boards.yaml as plaintext. The script opens the file using the process's normal creation behavior and does not explicitly set restrictive permissions, verify the permissions of an existing file, or protect updates through an atomic secure-write procedure.

The resulting access mode depends on the operating-system environment, current umask, and any preexisting file permissions. In a permissive or shared environment, other local users and processes may be able to read the stored credentials.

Profile storage is part of the declared functionality, but unrestricted plaintext credential storage is not the minimum secure mechanism necessary to implement profiles.

Attack Path

  1. A user runs manage_boards.py add and supplies a board password.
  2. The script places that password into the profile dictionary.
  3. save_profiles writes the dictionary to boards.yaml without explicitly enforcing owner-only permissions.
  4. In an environment with permissive file permissions, another local user or compromised process reads the file.
  5. The attacker extracts the plaintext board username, password, and address.
  6. The attacker uses those credentials to authenticate to the board.

Impact Assessment

Exploitation requires l ...[truncated 389 chars]

Remediation
View remediation

Remediation Suggestions

  • Store passwords in an operating-system credential manager or dedicated secret-management service rather than YAML.
  • If file-based storage must remain supported, create the file with owner-only mode 0600.
  • Verify ownership and permissions before reading an existing configuration file; reject files writable or readable by unauthorized users.
  • Write updates to a securely created temporary file in the same directory, apply restrictive permissions, flush the data, and atomically replace the destination.
  • Document that boards.yaml contains secrets and must not be committed to source control.
  • Add boards.yaml to .gitignore and provide only a secret-free sample configuration.
  • Use unique, least-privileged credentials for each board.

T08 · Insecure Dependencies

Note
Location
SKILL.md:85
Finding

Unpinned PyYAML Installation Produces a Mutable Dependency Supply Chain

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 85-88
Vulnerability Type: Unpinned third-party dependency installation
Risk Level: Low

Vulnerable Code

markdown
### Optional (for profile support)
- **PyYAML**: `pip install pyyaml` (enables profile-based configuration)

Technical Analysis

The setup instructions install PyYAML without a version constraint, lockfile, package hash, isolated environment, or explicit trusted index. Although PyYAML is a legitimate dependency and the scripts use yaml.safe_load, the installation command resolves a mutable future package release.

This makes installation non-reproducible and means the code ultimately executed by the Skill may differ from the dependency version that was reviewed. Exploitation depends on compromise of the configured package source, account, artifact, or dependency resolution environment; no evidence of an intentionally malicious dependency was found in the audited project.

Attack Path

  1. A user follows the documented pip install pyyaml instruction.
  2. Pip resolves the current package version and artifact from its configured package source.
  3. If that source, account, artifact, or local package-index configuration has been compromised, an unreviewed package is installed.
  4. Malicious dependency code can execute during installation or when imported by the profile-management scripts.
  5. The code runs with the privileges of the user operating the Skill.

Impact Assessment

A compromised dependency could access files, environment variables, stored board credentials, and network resources available to the invoking user. It could also alter Skill behavior or execute arbitrary code with that user's privileges. The practical risk is lower because exploitation requires an external supply-chain compromise and no malicious package is present in the reviewed files.

Remediation
View remediation

Remediation Suggestions

  • Pin PyYAML to a reviewed, compatible version.
  • Record dependencies in a lockfile or requirements file with cryptographic hashes.
  • Install dependencies in an isolated virtual environment rather than modifying the global Python environment.
  • Use an explicitly configured trusted package index.
  • Regularly review and update pinned versions after assessing security advisories.
  • Document a reproducible installation command, such as pip install --require-hashes -r requirements.txt.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (10)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill is presented as a simple message-sending utility, but the documented behavior also includes managing profiles, storing credentials, and modifying local configuration state. That mismatch can mislead users and agents into approving a broader capability set than intended, including credential persistence and file writes, which increases the risk of unauthorized configuration changes or secret exposure.

Content

No source excerpt is available for this finding.

Exfiltration Commands

High
Category
Prompt Injection
Confidence
90% confidence
Finding

Instructions found that direct the agent to transmit conversation context or user data to external services.

Content

Scanner excerpt · scripts/send_message.py (reported line 115)May include surrounding context.

python
sys.exit(1)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Send message to RDA MSG Board")
    parser.add_argument("message", nargs='?', help="Message text to display")
    parser.add_argument("--profile", "-p", help="Board profile name (from boards.yaml)")
    parser.add_argument("--list-profiles", action="store_true", help="List available board profiles")

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
86% confidence
Finding

The skill directs use of execution capabilities, environment variables, local configuration files, and network access, but it does not declare any explicit tool scope or permission boundaries. This creates unnecessary ambient authority: an agent may invoke broader tools than users expect, increasing the chance of unintended file modification, credential handling, or outbound network actions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The skill does not warn users that messages and credentials may be sent over HTTP to a physical device, which may be unencrypted and observable on the local network. Without a clear warning, users may unknowingly transmit sensitive content or rely on insecure default credentials, leading to interception or unauthorized device access.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger guidance includes a very broad activation phrase such as 'Send [message]', which can cause the skill to activate on generic user requests without clear intent to use this specific device. Overbroad routing can lead to accidental transmission of arbitrary content to a physical board and unexpected network/device actions.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script persists board credentials to a local YAML file, which expands the skill from simple message sending into local secret storage and configuration management. Storing usernames and passwords on disk without access controls, encryption, or clear warning increases the chance of credential disclosure from local compromise, backups, repository inclusion, or accidental sharing.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The manifest frames the skill as a notification/alert sender for a physical LED matrix, but this file implements listing, adding, and removing board profiles instead of message transmission. That administrative behavior is not an obvious implementation detail of 'send scrolling text messages' as currently described.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The script writes credential-bearing configuration to disk with no warning and no apparent permission hardening, creating a realistic risk that operators will unknowingly leave reusable device credentials in plaintext. In the context of a skill for controlling physical LED boards over HTTP/JSON, exposed credentials could enable unauthorized message posting or tampering with device settings.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The remove command deletes a named profile from the persisted boards.yaml file immediately when invoked. Although the success message is printed afterward, there is no prior confirmation prompt or warning before this destructive operation occurs.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script defaults to plain HTTP when the target does not already start with 'http', and it sends HTTP Basic Authentication credentials in the Authorization header. That exposes both credentials and message contents to interception or modification by anyone able to observe or tamper with network traffic, especially on shared or untrusted networks.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.