Back to skill

Security audit

AFOL BrickOwl

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its BrickOwl marketplace purpose, but it has conflicting approval guidance and can send the API key to a caller-selected API URL.

Review before installing. Use only with a BrickOwl API key you are comfortable granting to an agent, do not rely on it for unattended marketplace changes, and avoid BRICKOWL_BASE_URL or --base-url overrides unless you fully trust the destination. Confirm every create, update, delete, wishlist, and bulk write in the current conversation, and treat order addresses, buyer email, inventory costs, and personal notes as private.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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)

T01 · Skill Instruction Hijacking

Error
Location
references/prompts/brickowl-tools.txt:13
Finding

Checked-in guidance instructs the Agent to bypass approval for marketplace mutations

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/brickowl_cli.py:29
Finding

Unrestricted API base URL can exfiltrate the BrickOwl API key

Content
View full analysis
Any: query = {"key": self.api_key, **clean_params(params)} url = f"{self.base_url}{path}?{urllib.parse.urlencode(query, doseq=True)}" return self._request("GET", url) def post_form(self, path: str, fields: dict[str, Any]) -> Any: body = urllib.parse.urlencode({"key": self.api_key, **clean_params(fields)}, doseq=True).encode() url = f"{self.base_url}{path}" headers = {"Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json"} return self._request("POST", url, body, headers) def _request(self, method: str, url: str, body: bytes | None = None, headers: dict[str, str] | None = None) -> Any: request = urllib.request.Request(url, data=body, headers=headers or {"Accept": "application/json"}, method=method) ``` The destination can be supplied through either a command-line option or an environment variable at `scripts/brickowl_cli.py:92-94`: ```python def add_common(parser: argparse.ArgumentParser) -> None: parser.add_argument("--base-url", default=os.getenv("BRICKOWL_BASE_URL", DEFAULT_BASE_URL), help="BrickOwl API base URL") parser.add_argument("--timeout", type=int, default=READ_TIMEOUT_SECONDS, help="HTTP timeout in seconds") ``` The unvalidated value is passed directly into the credential-bearing client at `scripts/brickowl_cli.py:188-189`: ```python def client ...[truncated 3063 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (16)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- CLI source: `scripts/brickowl_cli.py`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- CLI source: `scripts/brickowl_cli.py`

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The prompt explicitly states that BrickOwl listing operations execute immediately without an approval flow, even though the available tools include create, update, and delete actions that can alter a live marketplace inventory. This is dangerous because a misunderstood prompt, prompt injection elsewhere, or accidental user phrasing could cause irreversible listing changes or deletions without a final confirmation checkpoint.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
97% confidence
Finding

The skill declares and documents capabilities that require environment-variable access and outbound network access, but it does not explicitly scope or declare those permissions. That weakens platform-level least-privilege controls and makes it harder for reviewers or runtime policy to constrain secret access and API egress.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The OpenAPI spec documents an inventory update operation with a delete flag, which directly enables destructive marketplace writes despite the skill being described as supporting 'safe marketplace writes'. Without explicit safeguards, an agent using this spec could remove inventory lots unintentionally or through prompt manipulation, causing business-impacting data loss.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The spec exposes destructive deletion of inventory via a boolean flag but provides no warning, confirmation guidance, or usage constraints. In a transactional marketplace skill, lack of friction around destructive actions increases the risk of accidental deletions or unsafe autonomous execution.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The order detail response includes personal data such as buyer email and shipping/billing addresses, yet the spec gives no privacy or data-handling warning. In an agent-integrated skill, exposing PII without minimization guidance raises the risk of unnecessary collection, display, logging, or downstream disclosure.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The /bulk endpoint allows caller-supplied endpoint, method, and parameters, enabling composition of multiple API calls beyond the narrowly described skill behaviors. In an agent context, this broad primitive can bypass intended per-action restrictions and make it easier for prompt-influenced workflows to perform unexpected or higher-risk operations at scale.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
95% confidence
Finding

Removing user approval for state-changing actions enables autonomous execution over marketplace inventory, including listing creation, modification, and deletion. In this skill context, that materially increases the risk of unauthorized sales changes, pricing mistakes, inventory loss, or business disruption if the agent is induced to act on ambiguous or malicious instructions.

Content

Scanner excerpt · references/prompts/brickowl-tools.txt (reported line 18)May include surrounding context.

text
- **Currency**: Prices are in the store's default currency (set in BrickOwl shop settings, not via API)
- **Condition Values**: "New" or "Used" (case-insensitive, defaults to "New")
- **Rate Limits**: 600 requests per minute for standard operations
- **NO User Approval Required**: Unlike BrickLink tools, these execute immediately without approval flow
- **Inventory Updates**: Update operations only modify fields that are provided (null checks for partial updates)

When to Use BrickOwl:

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The trigger 'User mentions BrickOwl marketplace or selling platform' is broad enough to match general discussion about BrickOwl, not just requests to use this skill. Without clearer constraints or negative examples, the skill could be invoked when the user is merely asking informational questions rather than requesting inventory actions.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · references/prompts/brickowl-tools.txt (reported line 52)May include surrounding context.

text
- Ask which platform they want to use if not specified

4. When user explicitly specifies service:
   - Use only that service without asking
   - Examples: "create BrickOwl listing", "sell on BrickLink"

Examples:

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 31)May include surrounding context.

md
import urllib.request
from typing import Any, Iterable

DEFAULT_BASE_URL = "https://api.brickowl.com/v1"
READ_TIMEOUT_SECONDS = 30
WRITE_COMMANDS = {"inventory-create", "inventory-update", "inventory-delete", "wishlist-create", "bulk"}

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · references/openapi/brickowl.yaml (reported line 20)May include surrounding context.

yaml
import urllib.request
from typing import Any, Iterable

DEFAULT_BASE_URL = "https://api.brickowl.com/v1"
READ_TIMEOUT_SECONDS = 30
WRITE_COMMANDS = {"inventory-create", "inventory-update", "inventory-delete", "wishlist-create", "bulk"}

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · scripts/brickowl_cli.py (reported line 20)May include surrounding context.

python
import urllib.request
from typing import Any, Iterable

DEFAULT_BASE_URL = "https://api.brickowl.com/v1"
READ_TIMEOUT_SECONDS = 30
WRITE_COMMANDS = {"inventory-create", "inventory-update", "inventory-delete", "wishlist-create", "bulk"}

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/brickowl_cli.py (reported line 207)May include surrounding context.

python
def require_any_change(args: argparse.Namespace, field_names: Iterable[str]) -> None:
    if not any(getattr(args, field) is not None for field in field_names):
        raise BrickOwlCliError("provide at least one field to update")

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/brickowl_cli.py (reported line 246)May include surrounding context.

python
require_identifier(args)
    change_fields = ["absolute_quantity", "relative_quantity", "price", "condition", "for_sale", "public_note", "personal_note"]
    require_any_change(args, change_fields)
    fields = {"lot_id": args.lot_id, "external_lot_id": args.external_lot_id, **{field: getattr(args, field) for field in change_fields}}
    path = "/inventory/update"
    if not ensure_write_allowed(args, path, fields):
        return None

Static analysis

No suspicious patterns detected.