Back to skill

Security audit

Runstr analytics

Security checks for vulnerabilities and agentic risk

Overview

This RUNSTR analytics skill appears purpose-aligned, but it handles a full Nostr private key in unsafe and partly misdescribed ways, so users should review it carefully before installing.

Install only if you are comfortable giving this skill and the local nak binary access to your full Nostr private key and decrypted RUNSTR health/journal data. Avoid pasting the key into chat or using --nsec on shared or monitored systems, review the cron job before enabling it, and consider removing cached data under ~/.cache/runstr-analytics when no longer needed.

Vulnerability Patterns
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • 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
Findings (3)

T06 · System Persistence

Warning
Location
setup_cron.sh:20
Finding

User-Level Persistence Through a Recurring Cron Job

Content
View full analysis
> $HOME/.cache/runstr-analytics/cron.log 2>&1" # Check if cron job already exists if crontab -l 2>/dev/null | grep -q "runstr-analytics/daily_update.sh"; then echo "⚠️ Cron job already exists." echo "" echo "Current crontab entry:" crontab -l | grep "runstr-analytics" echo "" read -p "Replace existing job? (y/N): " REPLACE if [[ $REPLACE =~ ^[Yy]$ ]]; then # Remove old entry crontab -l 2>/dev/null | grep -v "runstr-analytics/daily_update.sh" | crontab - echo "Old job removed." else echo "Setup cancelled." exit 0 fi fi # Add new cron job (crontab -l 2>/dev/null; echo "$CRON_JOB") | crontab - ``` ### Technical Analysis The setup script modifies the current user's crontab and registers `daily_update.sh` to execute every day at 07:00. This creates execution that survives the current Skill invocation and user session, meeting the definition of system persistence. The behavior is disclosed, supports the declared optional automated-update feature, and requires interactive confirmation before installation. It is therefore not covert. Nevertheless, scheduled execution exceeds the privileges needed for one-shot fitness analysis and expands the security boundary from an explicitly invoked program to persistent background execution. The cron entry references the project script by path rather than an immutable, integrity-checked installed artifact. Any future modification or replacement of `daily_update.sh` at that path will affect what cron executes. The command also does not quote the script and log paths for cron, which may cause incorrect interpretation when installation paths contain spaces or shell meta ...[truncated 1102 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
daily_update.sh:62
Finding

Nostr Private Keys Are Exposed Through Process Command-Line Arguments

Content
View full analysis
"$REPORT_FILE" 2>&1 ``` The full analyzer passes the original `nsec` and its decoded secret key to `nak` through command-line arguments: ```python def decode_nsec(self) -> bool: """Decode nsec to hex keys.""" try: result = subprocess.run( ["nak", "decode", self.nsec], capture_output=True, text=True, check=True ) self.hex_sk = result.stdout.strip() result = subprocess.run( ["nak", "key", "public", self.hex_sk], capture_output=True, text=True, check=True ) ``` ```python cmd = ["nak", "encrypt", "--sec", self.hex_sk, self.hex_pk, "--decrypt"] result = subprocess.run( cmd, input=content, capture_output=True, text=True, check=True ) ``` The lightweight analyzer has the same issue: ```python result = subprocess.run( ["nak", "decode", self.nsec], capture_output=True, text=True, check=True ) self.hex_sk = result.stdout.strip() result = subprocess.run( ["nak", "key", "public", self.hex_sk], capture_output=True, text=True, check=True ) ``` ```python cmd = ["nak", "decrypt", "--sec", self.hex_sk, "--sender-pubkey", self.hex_pk, content] result = subprocess.run(cmd, capture_output=True, text=True, check=True) ``` The extended analyzer protects only the initial decode operation. It subsequently exposes the decoded ...[truncated 3201 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
SKILL.md:4
Finding

Mutable and Unpinned Third-Party Dependency Installation

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (42)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The skill presents itself as an analytics tool but also includes system-level automation via cron installation and execution of local update scripts. System modification and scheduled execution materially expand the attack surface because they create persistence and recurring execution, which is more dangerous than one-off local analysis.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding

The skill presents itself as an analytics tool but also includes system-level automation via cron installation and execution of local update scripts. System modification and scheduled execution materially expand the attack surface because they create persistence and recurring execution, which is more dangerous than one-off local analysis.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill presents itself as an analytics tool but also includes system-level automation via cron installation and execution of local update scripts. System modification and scheduled execution materially expand the attack surface because they create persistence and recurring execution, which is more dangerous than one-off local analysis.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill presents itself as an analytics tool but also includes system-level automation via cron installation and execution of local update scripts. System modification and scheduled execution materially expand the attack surface because they create persistence and recurring execution, which is more dangerous than one-off local analysis.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
94% confidence
Finding

The manifest emphasizes analytics, insights, and coaching, but this code connects to multiple external WebSocket relays to request backup events. Network backup retrieval is not an obvious requirement of the described analytical purpose unless explicitly declared as part of the skill's scope.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
93% confidence
Finding

A fitness analytics skill invoking external binaries for key operations increases trust and supply-chain risk because the security of secret handling depends on an external tool outside the script's control. If the nak binary is malicious, replaced in PATH, or behaves unexpectedly, it can exfiltrate private keys or decrypted data.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
84% confidence
Finding

The skill declares no explicit tool scope even though the documentation clearly implies shell execution, local file access, network access to Nostr relays, and cron setup. Missing scope/permission declarations increases the chance that an agent or user will invoke broader capabilities than expected, reducing transparency and making misuse harder to review.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The setup instructions explicitly tell users to provide their Nostr private key directly to the bot. Encouraging users to paste private keys into conversational interfaces is dangerous because bot logs, transcripts, telemetry, prompt history, or downstream tools may capture the secret, enabling account compromise and decryption of protected data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

The quick-start examples pass the private key as a command-line argument, which commonly exposes secrets through shell history, process listings, audit logs, and crash reports. This directly contradicts the later security guidance about stdin and materially increases the risk of credential disclosure during normal use.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The manifest requests a Nostr private key (nsec), which is an extremely sensitive credential because possession of it typically grants full control over the user's identity and signing capabilities. Although the stated purpose is decrypting RUNSTR backup data, the manifest provides no user-facing warning, scope limitation, or safer alternative, increasing the risk that users disclose a credential whose compromise could enable impersonation, unauthorized signing, and account takeover across Nostr-related use.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

The script’s security comment says the secret is passed via stdin, but the implementation actually supplies RUNSTR_NSEC as a command-line argument to python3. Command-line arguments are commonly exposed through process listings, monitoring tools, shell history wrappers, crash reports, and some logging setups, so the NSEC may be disclosed to other local users or collected by system telemetry.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/analyze.py (reported line 46)May include surrounding context.

python
def decode_nsec(self) -> bool:
        """Decode nsec to hex keys."""
        try:
            result = subprocess.run(
                ["nak", "decode", self.nsec],
                capture_output=True, text=True, check=True
            )

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The manifest describes a fitness analytics skill that analyzes workout history and related wellness data, but the implementation depends on spawning the external nak binary for key handling, network retrieval, and decryption. Executing subprocesses is a broader capability than is justified by the stated analytics purpose, especially because it introduces command execution dependency outside the skill's core analysis scope.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/analyze.py (reported line 52)May include surrounding context.

python
)
            self.hex_sk = result.stdout.strip()
            
            result = subprocess.run(
                ["nak", "key", "public", self.hex_sk],
                capture_output=True, text=True, check=True
            )

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
90% confidence
Finding

This call passes the decrypted-account secret key to an external CLI via command-line arguments (--sec, self.hex_sk). On many systems, process arguments are observable by other local processes through tools like ps or /proc, so a local attacker could recover the secret and fully compromise the user's Nostr identity and encrypted backup access.

Content

Scanner excerpt · scripts/analyze.py (reported line 118)May include surrounding context.

python
try:
            # Try nak decrypt first
            cmd = ["nak", "encrypt", "--sec", self.hex_sk, self.hex_pk, "--decrypt"]
            result = subprocess.run(
                cmd, input=content, capture_output=True, text=True, check=True
            )
            decrypted = result.stdout.strip()

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

Multiple coaching tips and summary headings are hard-coded in Norwegian, making the skill's natural-language output locale-specific by default. The file does not offer a language choice or explain that the skill is intentionally limited to Norwegian users, which violates the language/locale policy for all file types.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The --training-plan option allows reading any local file path and processes its contents, even though the feature is only loosely related to workout analytics. In an agent setting, this broadens the skill from backup analysis into arbitrary local file access, creating a path for unintended data exposure if a prompt or wrapper causes the agent to read sensitive files and then summarize or emit their contents.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
99% confidence
Finding

Accepting the Nostr private key through --nsec exposes it in shell history, process listings, audit logs, and agent telemetry. Because this key is the root credential for identity and backup decryption, disclosure can let an attacker impersonate the user, decrypt private content, and potentially publish or manipulate data as that user.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script persistently stores decrypted workout history locally, which expands the data exposure surface beyond transient analytics. Even with restrictive file permissions, local compromise, backups, sync tools, or multi-user misconfiguration can expose sensitive health and behavior data.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

Fetching and decrypting a remote backup using the user's private key is a materially more sensitive capability than local analytics alone. In this skill context, that means the tool handles identity secrets and full historical personal data, increasing the risk if the script is modified, misused, or run in an untrusted environment.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/analyze_extended.py (reported line 99)May include surrounding context.

python
"""Decode nsec to hex keys using stdin to avoid exposing secret in CLI."""
        try:
            # Use stdin instead of CLI args to prevent nsec exposure in ps/process list
            result = subprocess.run(
                ["nak", "decode"],
                input=self.nsec,
                capture_output=True, text=True, check=True

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
84% confidence
Finding

The code passes the derived secret key as a command-line argument to an external process. On many systems, process arguments can be exposed to other local users, monitoring tools, logs, or crash reports, which can leak the user's private key material.

Content

Scanner excerpt · scripts/analyze_extended.py (reported line 106)May include surrounding context.

python
)
            self.hex_sk = result.stdout.strip()
            
            result = subprocess.run(
                ["nak", "key", "public", self.hex_sk],
                capture_output=True, text=True, check=True
            )

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
95% confidence
Finding

The decryption command includes the user's secret key in the argv list via --sec, which can expose the private key to local process inspection, telemetry, shell history wrappers, or diagnostic logs. Because this key protects the user's Nostr identity and encrypted backup, compromise of it has severe downstream impact.

Content

Scanner excerpt · scripts/analyze_extended.py (reported line 163)May include surrounding context.

python
"""Decrypt NIP-44 encrypted content."""
        try:
            cmd = ["nak", "decrypt", "--sec", self.hex_sk, "--sender-pubkey", self.hex_pk, content]
            result = subprocess.run(cmd, capture_output=True, text=True, check=True)
            decrypted = result.stdout.strip()
            
            import base64

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

Accepting a private key through a CLI argument is dangerous because command-line arguments are commonly visible in process listings, job schedulers, shell history wrappers, and logs. In this script, that risk is amplified because the same secret is then used to fetch and decrypt sensitive user backup data.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
93% confidence
Finding

The script passes a user-supplied Nostr private key to an external executable via subprocess, expanding the trust boundary to the local nak binary and the host environment. Even though shell injection is avoided by using an argument list, private key material can still be exposed to process listings, crash logs, telemetry, or a malicious/replaced nak binary.

Content

Scanner excerpt · scripts/analyze_light.py (reported line 34)May include surrounding context.

python
def decode_nsec(self) -> bool:
        """Decode nsec to hex keys."""
        try:
            result = subprocess.run(
                ["nak", "decode", self.nsec],
                capture_output=True, text=True, check=True
            )