Back to skill

Security audit

Signet Guide

Security checks for vulnerabilities and agentic risk

Overview

This mental health companion is not clearly malicious, but it makes privacy and encryption claims that the bundled code does not uphold for sensitive user data.

Review this carefully before installing. Do not rely on the encryption claims unless the storage implementation is fixed, and avoid entering highly sensitive mental-health information unless you are comfortable with local plaintext databases and the Signet CLI receiving prompt/history context. The publisher should add clear consent, deletion controls, accurate privacy wording, authenticated or pinned CLI execution, and region-aware crisis guidance.

Vulnerability Patterns
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
database.py:19
Finding

Sensitive mental-health data is stored in plaintext despite explicit encryption claims

Content
View full analysis

Vulnerability Details

File Location: database.py:19-28, database.py:33-55, database.py:62-76, database.py:81-99, and database.py:132-150
Related Misleading Claims: README.md:23, README.md:40, and signet-mind.py:37
Vulnerability Type: Plaintext storage of sensitive information and ineffective encryption implementation
Risk Level: High

Vulnerable Code

python
def get_encryption_key() -> str:
    """Get or generate encryption key for local data."""
    if ENCRYPTION_KEY_FILE.exists():
        return ENCRYPTION_KEY_FILE.read_text().strip()
    
    # Generate new key (in production, this would be derived from user password)
    key = os.urandom(32).hex()
    ENCRYPTION_KEY_FILE.write_text(key)
    os.chmod(ENCRYPTION_KEY_FILE, 0o600)
    return key

The generated key is not used when creating or accessing either database:

python
def init_conversation_db():
    """Initialize the conversations database."""
    conn = sqlite3.connect(DB_PATH)
    cursor = conn.cursor()
    
    cursor.execute("""
        CREATE TABLE IF NOT EXISTS conversations (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            timestamp TEXT NOT NULL,
            user_message TEXT NOT NULL,
            ai_response TEXT NOT NULL,
            mood_before TEXT,
            mood_after TEXT,
            tags TEXT
        )
    """)
    
    cursor.execute("""
        CREATE TABLE IF NOT EXISTS user_profile (
            key TEXT PRIMARY KEY,
            value TEXT NOT NULL,
            updated_at TEXT NOT NULL
        )
    """)
    
    conn.commit()
    conn.close()

Conversation content is inserted directly into ordinary SQLite columns:

python
def save_conversation(user_msg: str, ai_msg: str, mood_before: Optional[str] = None,
                      mood_after: Optional[str] = None, tags: Optional[List[str]] = None):
    """Save 
...[truncated 3686 chars]
Remediation
View remediation

Remediation Suggestions

  1. Replace standard plaintext SQLite storage with a reviewed encrypted database solution that provides authenticated encryption.
  2. Ensure encryption is actually applied to the database or to every sensitive field before insertion. Do not treat key generation alone as encryption.
  3. Store or protect the encryption key using the operating system credential store. If password-derived keys are supported, use a modern password-based key derivation function with a unique salt and appropriate cost settings.
  4. Apply restrictive permissions to DATA_DIR, conversations.db, mood.db, their journal or WAL files, and any temporary database files.
  5. Account for SQLite side files such as -wal, -shm, and journals, which may otherwise retain plaintext records.
  6. Add automated tests that insert a unique sensitive marker and verify that it cannot be found in the raw database or associated files.
  7. Define a migration procedure for existing plaintext databases, securely replacing old files after encrypted migration where the platform permits.
  8. Until effective encryption is implemented and verified, remove all statements claiming that records are encrypted and clearly inform users that storage is plaintext.

T07 · Tool Hijacking and Spoofing

Warning
Location
signet.py:23
Finding

PATH-resolved Signet executable receives sensitive conversation and profile data without identity verification

Content
View full analysis

Vulnerability Details

File Location: signet.py:23-33, signet.py:49-72, and signet.py:74-98
Vulnerability Type: Executable search-path hijacking and unverified external tool trust
Risk Level: Medium

Vulnerable Code

Availability is determined by executing the first program named signet resolved through the process environment:

python
def _check_signet(self) -> bool:
    """Check if Signet is available on the system."""
    try:
        result = subprocess.run(
            ["signet", "--version"],
            capture_output=True,
            timeout=5
        )
        return result.returncode == 0
    except (FileNotFoundError, subprocess.TimeoutExpired):
        return False

The same implicitly resolved executable is then given the user's prompt and assembled context:

python
def get_personalized_response(self, user_message: str, conversation_history: List[Dict]) -> str:
    """
    Generate a personalized response using Signet.
    Falls back to basic response if Signet unavailable.
    """
    if not self.signet_available:
        return self._fallback_response(user_message)
    
    # Build context from conversation history and user profile
    context = self._build_context(user_message, conversation_history)
    
    try:
        # Use signet CLI to generate response
        result = subprocess.run(
            ["signet", "chat", "--context", context, "--prompt", user_message],
            capture_output=True,
            timeout=30,
            text=True
        )
        
        if result.returncode == 0:
            return result.stdout.strip()
        else:
            return self._fallback_response(user_message)
            
    except (subprocess.TimeoutExpired, FileNotFoundError):
        return self._fallback_response(user_message)

The supplied context contains profile information and recent conversations:

...[truncated 3601 chars]

Remediation
View remediation

Remediation Suggestions

  1. Require an explicitly configured absolute path to the trusted Signet executable rather than invoking a bare executable name.
  2. Resolve the configured path once and reject symlinks or unexpected path changes where appropriate.
  3. Verify that the executable and its parent directories have trusted ownership and are not writable by unauthorized users.
  4. On supported platforms, verify a code signature or a trusted cryptographic digest before execution.
  5. Do not rely on successful --version output as proof of executable identity.
  6. Ask for informed user consent before sharing profile or conversation history with an external process, even when that process is local.
  7. Minimize the supplied context to the information strictly necessary for the current response.
  8. Avoid transmitting sensitive content through command-line arguments, which may be observable through process inspection on some systems. Prefer a protected standard-input channel or another authenticated local interface.
  9. Treat external tool output as untrusted and clearly communicate the external-tool trust boundary to users.
  10. If the executable cannot be authenticated, disable the integration and use the local fallback behavior.
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 (16)

Lp1

High
Category
MCP Least Privilege
Confidence
75% confidence
Finding

The skill uses 'shell' capability that is not listed in its permissions. This may indicate deceptive intent or missing permission declarations.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The module explicitly claims to handle encrypted local storage, but conversation logs, mood history, notes, triggers, and profile data are stored directly in plaintext SQLite tables. In a mental health companion, this data is highly sensitive, so anyone with local file access, backups, or malware access can read intimate user information despite the misleading security claim.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
93% confidence
Finding

The implementation contradicts the stated local-only expectation by probing for and relying on an external Signet CLI for personalized responses. This mismatch is security-relevant because users may disclose crisis or mental-health information under the assumption it stays local when it may be forwarded elsewhere.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

Executing an external subprocess in a skill marketed as a local mental health companion materially changes the trust boundary. The subprocess receives sensitive conversation data and may itself call remote services, exposing users to privacy loss, unreviewed data flows, or behavior outside the host application's guarantees.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

Mental-health conversation content and user-profile-derived context are forwarded to the external Signet CLI without any visible warning, consent flow, or privacy control. Because the data may include crisis disclosures, mood history, and other intimate details, undisclosed transfer is especially dangerous in this skill context.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill hard-codes crisis contacts for only Ireland and the UK plus a generic fallback, without any locale detection, user selection flow, or prominent limitation notice. In a mental-health companion, presenting regionally incorrect emergency resources can delay access to appropriate help during a crisis, making this a real safety vulnerability even though it is not a traditional code-execution issue.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The code persistently stores sensitive mental health conversations, mood scores, notes, triggers, and activities without any evidence of explicit user consent, retention controls, or user-facing disclosure. In this skill context, silent collection and retention of mental health data increases privacy risk because users may reasonably expect ephemeral support rather than durable local profiling.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The chat flow sends highly sensitive mental-health disclosures to an external Signet connection via get_personalized_response(user_input, history) without a clear just-in-time warning or explicit consent at the moment of transmission. In a mental-health companion context, users may reasonably expect local/private handling, especially because the tool also claims local encrypted storage, making undisclosed off-device transmission materially dangerous to privacy and trust.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
83% confidence
Finding

The top-level docstring says the module 'Connects to Signet AI' and the class says it 'Handles connection to Signet AI,' which suggests an application-level or API integration. In reality, the code does not implement such a connection directly; it invokes a local signet command via subprocess, which is a materially different mechanism.

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 · signet.py (reported line 27)May include surrounding context.

python
def _check_signet(self) -> bool:
        """Check if Signet is available on the system."""
        try:
            result = subprocess.run(
                ["signet", "--version"],
                capture_output=True,
                timeout=5

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The function writes injected context to a local file without any consent, retention policy, or access-control handling. For a mental-health companion, even local persistence of sensitive context can expose private information to other local users, backups, logs, or malware on the device.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
96% confidence
Finding

The code sends the user's message plus built conversation context and profile data to an external 'signet' CLI, despite the skill being presented as a local mental health companion. In a mental-health context this is highly sensitive data, and routing it to another executable without explicit consent, disclosure, or trust validation creates a serious privacy and data-handling risk.

Content

Scanner excerpt · signet.py (reported line 62)May include surrounding context.

python
try:
            # Use signet CLI to generate response
            result = subprocess.run(
                ["signet", "chat", "--context", context, "--prompt", user_message],
                capture_output=True,
                timeout=30,

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

Hardcoding crisis resources for only Ireland and the UK can provide dangerously incorrect guidance for users in other regions during self-harm or suicide crises. In a mental-health skill, inaccurate or geographically mismatched emergency instructions can delay access to appropriate urgent help.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The breathing and relaxation guidance includes prescriptive physical exercises without any caution that breath-holding, rapid breathing, muscle tensing, or similar techniques may be unsuitable for some users, such as those with panic symptoms, asthma, cardiovascular issues, pregnancy, or trauma sensitivities. In a mental-health-oriented skill, users may rely on the advice during distress, increasing the risk of dizziness, symptom worsening, or other adverse effects.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The mood-based coping logic treats terms like 'depressed' and 'hopeless' as routine self-help scenarios and returns only generic coping suggestions, with no crisis screening, warning, or escalation to trusted contacts, crisis lines, or emergency services. In a local mental health companion, this omission is particularly dangerous because users in serious emotional distress may interpret the tool as sufficient support and delay seeking urgent help.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
94% confidence
Finding

The comments and user-facing output say the CLI can log mood with signet-mind --checkin --mood 7, but the argument parser defines no --mood option and run_checkin() only displays prompts. This actively misrepresents the implemented behavior and could mislead users about what the tool actually does.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.