Back to skill

Security audit

BrickLink

Security checks for vulnerabilities and agentic risk

Overview

This is a real BrickLink store CLI, but it can immediately change live store data and save customer details to predictable local files without built-in confirmations.

Install only if you trust the agent and account permissions you will use with BrickLink. Prefer read-only or least-privilege API credentials when possible, manually review every write command before running it, and avoid default /tmp invoice/detail exports when they contain customer personal information.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/bricklink.py:1159
Finding

Authenticated BrickLink mutations execute without enforced confirmation

Content
View full analysis

Vulnerability Details

File Location: scripts/bricklink.py:1159-1269; supporting behavior documented at SETUP.md:51-54
Vulnerability Type: Missing authorization confirmation for destructive and externally visible operations
Risk Level: Medium

Technical Analysis

The CLI exposes authenticated operations that immediately delete inventory, alter order and payment state, send customer email, or publish feedback. The implementation does not require an interactive confirmation, an explicit --confirm capability, or a dry-run stage.

Representative destructive operation:

python
def cmd_delete_inventory(args) -> int:

    creds = load_creds(args)
    base = API_BASE_DEFAULT
    inv_id = int(args.inventory_id)
    url = f"{base}/inventories/{inv_id}"
    data = api_call(creds, "DELETE", url)
    print(json.dumps(data, ensure_ascii=False, indent=2))
    return 0

Other authenticated mutations follow the same immediate-execution pattern:

python
def cmd_update_order_status(args) -> int:

    creds = load_creds(args)
    base = API_BASE_DEFAULT
    oid = int(args.order_id)
    url = f"{base}/orders/{oid}/status"
    body = {"field": "status", "value": args.status}
    data = api_call(creds, "PUT", url, body_json=body)
    print(json.dumps(data, ensure_ascii=False, indent=2))
    return 0


def cmd_update_payment_status(args) -> int:

    creds = load_creds(args)
    base = API_BASE_DEFAULT
    oid = int(args.order_id)
    url = f"{base}/orders/{oid}/payment_status"
    body = {"field": "payment_status", "value": args.payment_status}
    data = api_call(creds, "PUT", url, body_json=body)
    print(json.dumps(data, ensure_ascii=False, indent=2))
    return 0


def cmd_send_drive_thru(args) -> int:

    creds = load_creds(args)
    base = API_BASE_DEFAULT
    oid = int(args.order_id)

    params = {}
    if args.mail_me:
        params["mail_me"] = "true"


...[truncated 2349 chars]
Remediation
View remediation

Remediation Suggestions

  • Make mutating commands dry-run by default and print the HTTP method, resource, target identifier, and proposed changes without sending the request.
  • Require a deliberate --confirm or --execute flag before any POST, PUT, or DELETE operation.
  • For destructive operations such as inventory deletion, require an operation-specific confirmation value, such as --confirm-delete <inventory_id>, and verify that it matches the target.
  • When an interactive terminal is available, display a concise mutation summary and require explicit approval. Do not treat documentation or an Agent-generated response as confirmation.
  • Separate read-only and write-capable entry paths so deployments can disable mutation commands entirely.
  • Consider using separate OAuth credentials or API permissions with the minimum required write scope where supported.
  • Add regression tests proving that every mutating handler refuses to send a network request unless the confirmation gate has been satisfied.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (19)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The supplied code chunk does not implement BrickLink API access, OAuth signing, CLI commands for store operations, or any network interaction. Instead, it provides local path sanitization logic for output files. While such a helper could be a supporting utility within a larger CLI, this chunk’s actual behavior is materially different from the declared purpose and represents an unrelated filesystem safety component rather than BrickLink Store API functionality.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
Use `scripts/bricklink.py`.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The HTML export writes customer PII including name, address, email, and phone number to disk, defaulting to a predictable file in /tmp. On multi-user systems or shared agent environments, this can expose sensitive customer data through world-readable temp locations, backups, or later unintended access.

Content

No source excerpt is available for this finding.

Missing User Warnings

High
Category
Not specified by scanner
Confidence
97% confidence
Finding

The invoice generator persists a printable document containing customer identity and contact details to disk without any warning, again using a predictable /tmp filename by default. This materially increases the chance of unauthorized disclosure in shared or sandboxed environments where temp files may be accessible or retained.

Content

No source excerpt is available for this finding.

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
90% confidence
Finding

The tool exposes a direct destructive 'delete-inventory' operation with no built-in confirmation, dry-run, or additional safeguards. In agentic or automated contexts, parameter mistakes or prompt-induced misuse could cause irreversible deletion of store inventory records through valid authenticated API calls.

Content

Scanner excerpt · scripts/bricklink.py (reported line 1468)May include surrounding context.

python
_add_inventory_common_args(p, include_item=False)
    p.set_defaults(func=cmd_update_inventory)

    p = sub.add_parser("delete-inventory", help="DELETE /inventories/{inventory_id}")
    p.add_argument("inventory_id", type=int)
    p.set_defaults(func=cmd_delete_inventory)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill exposes capabilities that imply access to environment variables, filesystem reads/writes, and network operations, but it does not declare any explicit tool scope or permission boundaries. In a skill that handles OAuth credentials and supports inventory/order mutations, this lack of scoping increases the chance of over-privileged execution and unintended access to sensitive data or destructive actions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill documents multiple state-changing operations such as updating orders, posting feedback, creating/updating/deleting inventory, and sending emails, but it does not prominently warn that these commands modify live BrickLink account data. In an agent-driven context, insufficient warnings can lead to accidental destructive or irreversible actions, especially because the skill also has authenticated API access.

Content

No source excerpt is available for this finding.

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/catalog-api.md (reported line 3)May include surrounding context.

md
import html as _html


API_BASE_DEFAULT = "https://api.bricklink.com/api/store/v1"


def _find_workspace_root() -> Path:

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/inventory-api.md (reported line 3)May include surrounding context.

md
import html as _html


API_BASE_DEFAULT = "https://api.bricklink.com/api/store/v1"


def _find_workspace_root() -> Path:

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/orders-api.md (reported line 56)May include surrounding context.

md
import html as _html


API_BASE_DEFAULT = "https://api.bricklink.com/api/store/v1"


def _find_workspace_root() -> Path:

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/bricklink.py (reported line 21)May include surrounding context.

python
import html as _html


API_BASE_DEFAULT = "https://api.bricklink.com/api/store/v1"


def _find_workspace_root() -> Path:

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Content

Scanner excerpt · scripts/bricklink.py (reported line 1151)May include surrounding context.

python
items = _load_batch_file(args.file)
        for idx, entry in enumerate(items):
            _validate_inventory_required(entry, index=idx)
        print(f"Creating {len(items)} inventory item(s)…", file=__import__('sys').stderr)
        data = api_call(creds, "POST", url, body_json=items)
    else:
        # Single mode: build from flags

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill fetches full order and item details from the BrickLink API, which include buyer email, phone, and shipping address later rendered into HTML. There is no confirmation prompt or explicit user-facing warning near these network operations despite handling sensitive personal data.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

The manifest explains API coverage and OAuth signing, but does not mention local workspace discovery, reading config.json, or consuming credentials from environment variables. While practical for CLI authentication, this is a local file/environment access capability not justified by the stated purpose alone.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

When '--inline-images' is used, the tool performs additional outbound requests to BrickLink's image CDN and embeds the fetched content into generated HTML. This expands network behavior beyond the primary API endpoints and can unexpectedly disclose access patterns or create undeclared external dependencies in restricted environments.

Content

No source excerpt is available for this finding.

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/bricklink.py (reported line 1532)May include surrounding context.

python
if hasattr(args, "is_filed") and isinstance(args.is_filed, str):
        args.is_filed = True if args.is_filed.lower() == "true" else False
    for k in ("box", "instruction", "break_minifigs", "break_subsets"):
        if hasattr(args, k) and isinstance(getattr(args, k), str):
            setattr(args, k, True if getattr(args, k).lower() == "true" else False)
    for k in ("bulk", "is_retain", "is_stock_room"):
        if hasattr(args, k) and isinstance(getattr(args, k), str):

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/bricklink.py (reported line 1535)May include surrounding context.

python
if hasattr(args, "is_filed") and isinstance(args.is_filed, str):
        args.is_filed = True if args.is_filed.lower() == "true" else False
    for k in ("box", "instruction", "break_minifigs", "break_subsets"):
        if hasattr(args, k) and isinstance(getattr(args, k), str):
            setattr(args, k, True if getattr(args, k).lower() == "true" else False)
    for k in ("bulk", "is_retain", "is_stock_room"):
        if hasattr(args, k) and isinstance(getattr(args, k), str):

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/bricklink.py (reported line 1533)May include surrounding context.

python
args.is_filed = True if args.is_filed.lower() == "true" else False
    for k in ("box", "instruction", "break_minifigs", "break_subsets"):
        if hasattr(args, k) and isinstance(getattr(args, k), str):
            setattr(args, k, True if getattr(args, k).lower() == "true" else False)
    for k in ("bulk", "is_retain", "is_stock_room"):
        if hasattr(args, k) and isinstance(getattr(args, k), str):
            setattr(args, k, True if getattr(args, k).lower() == "true" else False)

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/bricklink.py (reported line 1536)May include surrounding context.

python
args.is_filed = True if args.is_filed.lower() == "true" else False
    for k in ("box", "instruction", "break_minifigs", "break_subsets"):
        if hasattr(args, k) and isinstance(getattr(args, k), str):
            setattr(args, k, True if getattr(args, k).lower() == "true" else False)
    for k in ("bulk", "is_retain", "is_stock_room"):
        if hasattr(args, k) and isinstance(getattr(args, k), str):
            setattr(args, k, True if getattr(args, k).lower() == "true" else False)

Static analysis

No suspicious patterns detected.