Back to skill

Security audit

Gstack Skills

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a coherent development workflow suite, but its local state manager can read, modify, or delete JSON files outside its intended folder through unvalidated workflow IDs.

Install only if you are comfortable with a workflow skill that can inspect repositories, suggest or apply code fixes, and guide release workflows. Avoid using its state manager on shared workspaces until workflow IDs are validated and state files are permission-restricted; do not store secrets in its workflow state. Use explicit slash commands and require confirmation before fixes, merges, PR creation, deployment, or state deletion.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/state_manager.py:71
Finding

Workflow Identifier Path Traversal Enables Arbitrary JSON File Access

Content
View full analysis

Vulnerability Details

File Location: scripts/state_manager.py, lines 71, 91, and 201
Vulnerability Type: Unvalidated path construction and directory traversal
Risk Level: High

The application constructs workflow file paths directly from the caller-controlled workflow_id without validating its format or ensuring that the resolved path remains inside the configured state directory.

python
def load_workflow(self, workflow_id: str) -> Optional[Dict]:
    """
    Load a workflow's state.

    Args:
        workflow_id: Workflow ID

    Returns:
        Workflow state dictionary, or None if not found
    """
    workflow_file = self.state_dir / f"{workflow_id}.json"
    
    if not workflow_file.exists():
        return None
    
    with open(workflow_file, "r") as f:
        return json.load(f)
python
def save_workflow(self, workflow_id: str, state: Dict) -> bool:
    """
    Save a workflow's state.

    Args:
        workflow_id: Workflow ID
        state: State dictionary

    Returns:
        True if saved successfully, False otherwise
    """
    workflow_file = self.state_dir / f"{workflow_id}.json"
    
    state["updated_at"] = datetime.now().isoformat()
    
    try:
        with open(workflow_file, "w") as f:
            json.dump(state, f, indent=2)
        return True
    except Exception as e:
        print(f"Error saving workflow: {e}")
        return False
python
def delete_workflow(self, workflow_id: str) -> bool:
    """
    Delete a workflow.

    Args:
        workflow_id: Workflow ID

    Returns:
        True if deleted successfully, False otherwise
    """
    workflow_file = self.state_dir / f"{workflow_id}.json"
    
    if workflow_file.exists():
        workflow_file.unlink()
        return True
    
    return False

Technical Analysis

workflow_id is accepted from com ...[truncated 2251 chars]

Remediation
View remediation

Remediation Suggestions

  1. Enforce the identifier format used by create_workflow() before any filesystem operation:

    python
    import re
    
    WORKFLOW_ID_PATTERN = re.compile(r"^[0-9a-f]{8}$")
    
    def validate_workflow_id(workflow_id: str) -> None:
        if not WORKFLOW_ID_PATTERN.fullmatch(workflow_id):
            raise ValueError("Invalid workflow ID")
    
  2. Centralize safe path generation and verify containment after canonicalization:

    python
    def get_workflow_path(self, workflow_id: str) -> Path:
        validate_workflow_id(workflow_id)
    
        base = self.state_dir.resolve()
        candidate = (base / f"{workflow_id}.json").resolve()
    
        if candidate.parent != base:
            raise ValueError("Workflow path escapes the state directory")
    
        return candidate
    
  3. Replace every direct construction of self.state_dir / f"{workflow_id}.json" with the centralized safe helper.

  4. Reject symlink-based file targets, especially before writes and deletions. Where supported, use operating-system APIs that avoid following symlinks.

  5. Add tests for identifiers containing .., absolute paths, path separators, encoded traversal sequences, unexpected Unicode, and symlink targets.

  6. Run the state manager under a minimally privileged account so that any remaining filesystem flaw has limited impact.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/state_manager.py:39
Finding

Workflow Context Is Persisted in Plaintext with Default Filesystem Permissions

Content
View full analysis

Vulnerability Details

File Location: scripts/state_manager.py, lines 39-44, 96-101, and 117-125
Vulnerability Type: Insecure storage of potentially sensitive workflow data
Risk Level: Low

The state manager creates its storage directory and JSON files using default permission behavior, then persists arbitrary workflow context as unencrypted plaintext.

python
if state_dir is None:
    # Default to .workbuddy/gstack-state in the project
    self.state_dir = Path(".workbuddy/gstack-state")
else:
    self.state_dir = Path(state_dir)

# Ensure state directory exists
self.state_dir.mkdir(parents=True, exist_ok=True)
python
def save_workflow(self, workflow_id: str, state: Dict) -> bool:
    workflow_file = self.state_dir / f"{workflow_id}.json"
    
    state["updated_at"] = datetime.now().isoformat()
    
    try:
        with open(workflow_file, "w") as f:
            json.dump(state, f, indent=2)
        return True
    except Exception as e:
        print(f"Error saving workflow: {e}")
        return False
python
def set_data(self, workflow_id: str, key: str, value: Any) -> bool:
    state = self.load_workflow(workflow_id)
    
    if state is None:
        print(f"Workflow {workflow_id} not found")
        return False
    
    state["data"][key] = value
    return self.save_workflow(workflow_id, state)

Technical Analysis

set_data() accepts arbitrary keys and values, while add_step() can similarly persist arbitrary step information. The code does not classify, redact, encrypt, expire, or restrict the stored content.

Directory creation relies on the process umask, and open(..., "w") creates files using default operating-system permissions. In environments with a permissive umask, other local users or processes may be able to inspect workflow files. Existing files with overly broad permissions are not corrected when rewritten.

Si ...[truncated 1269 chars]

Remediation
View remediation

Remediation Suggestions

  1. Create the state directory with owner-only permissions and verify permissions for an existing directory:

    python
    self.state_dir.mkdir(parents=True, exist_ok=True, mode=0o700)
    self.state_dir.chmod(0o700)
    
  2. Create state files atomically with mode 0600, rather than relying solely on the process umask.

  3. Document that credentials, access tokens, private keys, and other secrets must not be stored in workflow state.

  4. Add filtering or redaction for commonly sensitive fields such as password, token, secret, api_key, and authorization.

  5. Define a retention period and automatically remove completed or expired workflow files.

  6. Consider encryption at rest when workflow state must contain confidential information on shared systems. Store encryption keys separately from the state directory.

  7. Add a secure cleanup command that removes state when a workflow is complete, and ensure project backups do not unintentionally retain sensitive state.

Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • 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
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (19)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

A description-behavior mismatch is dangerous because users and downstream policy systems may grant trust based on the declared purpose while the actual skill exposes different commands and stateful file operations. If the package really implements undeclared local storage, deletion, or a separate CLI surface, it creates a hidden capability channel that can bypass user expectations and safety review.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

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.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The condition 'User needs guidance' is so ambiguous that it could match many benign interactions and cause the router to insert itself too broadly. In a multi-skill environment, this can lead to unrequested delegation, confusing behavior, and accidental invocation of more capable downstream skills.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

Using broad natural-language triggers like 'brainstorm' or 'I have an idea' can cause the skill to activate unexpectedly during ordinary conversation. Over-broad activation increases the chance of unintended routing into workflows that may consume tools, alter task flow, or steer the user into actions they did not explicitly request.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The documentation describes /ship as a release engineer workflow with automated merge and PR behavior but does not prominently warn that it can perform impactful repository or deployment actions. Missing risk disclosure makes accidental misuse more likely and reduces the chance that users apply appropriate safeguards before invoking it.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

Generic phrases like 'ship', 'deploy', or 'create PR' can appear in ordinary discussion, yet here they map to an automated release workflow. If interpreted as activation triggers, they may initiate high-impact build, merge, versioning, or deployment steps without sufficiently explicit user authorization.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger phrases in metadata are broad enough to match common conversational requests like 'brainstorm' or 'help me think through this', which can cause the skill to activate outside its intended product-ideation scope. In an agentic environment, over-broad routing can misdirect users into a persuasive business-consulting workflow, suppress more appropriate skills, and increase the chance of unintended context capture or unsafe downstream actions.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The 'When to Use' section repeats ambiguous examples such as 'I have an idea' and 'is this a good idea?', which are generic phrases that occur in many unrelated contexts. Because this skill is part of an automatically routed suite, these examples increase the probability of accidental invocation and inappropriate workflow takeover, especially when users are asking for general advice rather than startup or product validation.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill is presented as a pre-merge review tool, but its description and workflow also instruct the agent to fix code. That expands it from passive analysis into write-capable behavior, increasing the chance of unintended code changes, especially when invoked casually for 'review' rather than explicit editing.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The activation phrases are broad and common, making accidental invocation more likely. Because this skill can progress into repository inspection and potentially code-modifying behavior, ambiguous triggers raise the risk of the agent taking higher-impact actions when the user may have intended only a general discussion or lightweight review.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The 'When to Use' section broadens activation further with generic review phrases and lacks explicit scope boundaries. In context, that is more dangerous because the skill is not purely informational; it can inspect repo state and may steer toward applying fixes, so accidental activation can lead to unintended operational changes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill promotes automatic execution and auto-fix behavior without consistently disclosing up front that repository files may be modified. Users may invoke a 'review' expecting read-only analysis, but the hidden write capability creates a trust-boundary violation and increases the chance of unauthorized or surprising code changes.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill gives mixed instructions: earlier sections endorse automatic fixes and even show 'Auto-Fixes Applied', while the safety section says user confirmation is required before applying fixes. In an agentic environment, this ambiguity can cause unauthorized repository modifications because the model may follow the more action-oriented instructions and edit code before explicit approval.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
82% confidence
Finding

The manifest describes a workflow suite that routes to specialized skills for ideation, review, testing, QA, deployment, and generally sharing workflow context. This file implements destructive deletion of stored workflow state, which is not reflected in the manifest or this module’s top-level description about storing and retrieving context for sharing between skills.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This code deletes a persisted workflow state file via unlink() but provides no confirmation prompt, cautionary message, or other user-facing disclosure that the operation is destructive. For a code file, irreversible deletion of stored state should have some visible warning unless explicitly disclosed as part of the operation.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
75% confidence
Finding

The manifest and introductory description focus on code review concerns such as SQL security, trust boundaries, race conditions, structural issues, and git diff analysis. Adding UI design review for visual consistency, responsive design, and broader UX concerns is a distinct capability not clearly supported by that stated purpose.

Content

No source excerpt is available for this finding.

Scope Creep

Low
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.

Content

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

md
],
    "plan-ceo-review": [
        r"think bigger",
        r"expand scope",
        r"strategic review",
        r"rethink",
        r"ceo.*perspective",

Scope Creep

Low
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill's behavior or capabilities extend beyond its stated purpose. Scope creep allows an agent to perform actions unrelated to its documented functionality, increasing the attack surface.

Content

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

python
],
    "plan-ceo-review": [
        r"think bigger",
        r"expand scope",
        r"strategic review",
        r"rethink",
        r"ceo.*perspective",

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
98% confidence
Finding

The module docstring says the CLI usage is python state_manager.py init <workflow_id>, implying callers supply the ID. In the actual implementation, the init command takes no extra argument and calls create_workflow() to generate a fresh UUID-derived ID.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.