Back to skill

Security audit

drivethru-sanmar

Security checks for vulnerabilities and agentic risk

Overview

This SanMar purchasing skill is purpose-aligned, but it exposes credentials and has several high-impact safety gaps that users should review before installing.

Install only if you are comfortable reviewing and hardening this skill first. At minimum, remove raw credential-bearing payloads from outputs, restrict portal and SFTP destinations, validate SFTP host keys, confine uploaded return images to approved user attachments, and require explicit human approval for purchase orders and any future return submission.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (5)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/sanmar_tools.py:819
Finding

Purchase-order dry-run output discloses plaintext SanMar credentials

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/ftp_resolver.py:154
Finding

SFTP clients authenticate with passwords without verifying the server host key

Content
View full analysis
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/sanmar_browser.py:413
Finding

Return processing accepts unrestricted local file paths for browser upload

Content
View full analysis
ReturnLineRequest: if isinstance(value, ReturnLineRequest): return value if not isinstance(value, dict): raise TypeError(f"Cannot coerce {value!r} to ReturnLineRequest") return ReturnLineRequest( style=value["style"], color=value["color"], size=value["size"], reason=value["reason"], quantity=value.get("quantity"), details=value.get("details"), replacement=value.get("replacement"), image_path=value.get("image_path"), ) ``` ```python # scripts/sanmar_browser.py:413-418 if ( fill.reason == "DEFECTIVE_DAMAGED" and fill.image_path and os.path.isfile(fill.image_path) ): self.page.locator(f"#file{n}").set_input_files(fill.image_path) ``` ### Technical Analysis The CLI accepts `image_path` as an unrestricted string. The only validation before passing the file to Playwright is `os.path.isfile()`. There is no canonical-path check, approved upload directory, image signature verification, file-size limit, extension restriction, or dedicated user confirmation for local-file disclosure. `set_input_files()` gives the selected file to the loaded web page. Depending on page behavior, JavaScript can upload the file immediately or include it in a later form request. This is especially dangerous when combined with the independently configurable portal base URL. The action performs this selection while preparing a dry run. Therefore, `confirm=false` does not prevent the local file from being exposed to the browser page. ### Attack Path 1. An attacker or prompt-injected workflow supplies a sensitive readable path a ...[truncated 1003 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/sanmar_browser.py:93
Finding

Portal credentials can be submitted to an unvalidated configurable base URL

Content
View full analysis
None: self.page = page self.base_url = base_url.rstrip("/") def login(self, credentials: SanMarPortalCredentials) -> None: """Log in via the homepage header form (Spring Security).""" self.page.goto(self.base_url + "/", wait_until="domcontentloaded") try: self.page.fill("#login-header-form #username", credentials.username) self.page.fill("#login-header-form #password", credentials.password) self.page.click( "#login-header-form button.btn-primary-df[type=submit]" ) ``` ### ...[truncated 1712 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/sanmar_client.py:291
Finding

Legacy SOAP methods interpolate unescaped credentials and user input into XML

Content
View full analysis
InventoryResult: creds = self.credentials payload = ( f'' "" "" "" f"{creds.customer_number}" f"{creds.username}" f"{creds.password}" f"{style}" f"{color}" f"{size}" "" "" "" ) ``` ```python # scripts/sanmar_client.py:341-361 creds = self.credentials arg0_chunks = "".join( f"{ln.color}{ln.size}" f"{ln.style}" for ln in lines ) payload = ( f'' "" "" "" f"{arg0_chunks}" "" f"{creds.customer_number}" f"{creds.username}" f"{creds.password}" "" "" "" "" ) ``` ```python # scripts/sanmar_client.py:404-420 arg0_inner = f"{style}" if color: arg0_inner += f"{color}" if size: arg0_inner += f"{size}" payload = ( f'' ...[truncated 2108 chars]
Remediation
View remediation
`, quotes, and attempted closing-tag payloads. - Validate domain-specific fields such as style, color, and size against documented length and character constraints as defense in depth. - Ensure upstream error responses do not reflect sensitive request content into logs or caller-visible messages. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • 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
Findings (16)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/sanmar_client.py (reported line 577)May include surrounding context.

python
def pre_submit_po(self, draft: PurchaseOrderDraft) -> CartValidationResult:
        payload = self.build_po_envelope(draft, pre_submit=True)
        text = self._post_soap(
            self.endpoints.po_url(self.credentials.environment),
            payload,
            soap_action="getPreSubmitInfo",
            operation="getPreSubmitInfo",

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/sanmar_client.py (reported line 612)May include surrounding context.

python
def pre_submit_po(self, draft: PurchaseOrderDraft) -> CartValidationResult:
        payload = self.build_po_envelope(draft, pre_submit=True)
        text = self._post_soap(
            self.endpoints.po_url(self.credentials.environment),
            payload,
            soap_action="getPreSubmitInfo",
            operation="getPreSubmitInfo",

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/sanmar_client.py (reported line 981)May include surrounding context.

python
def pre_submit_po(self, draft: PurchaseOrderDraft) -> CartValidationResult:
        payload = self.build_po_envelope(draft, pre_submit=True)
        text = self._post_soap(
            self.endpoints.po_url(self.credentials.environment),
            payload,
            soap_action="getPreSubmitInfo",
            operation="getPreSubmitInfo",

Missing User Warnings

High
Category
Not specified by scanner
Confidence
92% confidence
Finding

The submit_po method performs a live external side effect—placing a purchase order with a vendor—without any in-code guardrail, confirmation gate, or dry-run enforcement. In an agent setting, this is dangerous because a prompt-injected or mistaken workflow could convert untrusted input directly into a real order, causing unauthorized purchases or financial loss.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill advertises capabilities that clearly require environment-variable access, file reads, and outbound network access, but it does not declare an explicit tool/permission scope. That creates a governance gap: a runtime or reviewer cannot easily enforce least privilege, and the agent may receive broader access than intended for sensitive credentials, local files, and external services.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% 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 · SKILL.md (reported line 265)May include surrounding context.

md
**If the script itself returns `config_error` / an auth error**, then the
calling connection did not share the credential (or sharing is misconfigured) —
surface that error message to the caller verbatim. Do not ask the user for a key
(on a delegated turn there is no user to ask), and never print a credential
value into the reply.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The document explicitly permits credentials to be supplied inline via tool stdin JSON, which increases the chance that secrets are exposed through agent logs, traces, shell history, debugging output, or upstream telemetry. In this skill context, the risk is heightened because the credentials grant access to ordering, inventory, invoicing, and SFTP surfaces tied to a customer account, enabling unauthorized reads or writes if leaked.

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/auth_and_patterns.md (reported line 118)May include surrounding context.

md
- Prefer explicit timestamps from feed metadata where available.
- If freshness is critical, include source timestamp/date in response payload.
- Never assume a missing record is permanent without checking relevant active/closeout feeds.

## Error handling contract for this SME

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The examples explicitly encourage supplying SanMar credentials inline in JSON and via environment variables without any warning about secret-handling risks. Inline credentials can be exposed through shell history, process listings, logs, transcripts, CI output, or copied examples, making accidental credential disclosure plausible in real operator workflows.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The notes explicitly instruct a user to paste full live portal HTML from authenticated review and success pages. That HTML can contain customer contact data, order identifiers, RMA numbers, addresses, labels, hidden form fields, session-scoped tokens, and other sensitive business information, creating an unnecessary data-exfiltration path into the agent workflow. In this skill context, the risk is elevated because the content is captured from a real production return flow with no sandbox, so any copied page data is likely to be live and sensitive.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This markdown file documents a skill that can ingest and process shipment-status files containing ship-to names, addresses, tracking numbers, and line-item costs. Under SQP-2 for markdown files, the description should warn users about privacy or sensitive business data implications, but no such warning is present in this section.

Content

No source excerpt is available for this finding.

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

python
<LineItem><StyleNo>PC61</StyleNo><StyleSize>S</StyleSize><Quantity>-1</Quantity>
        <UnitPrice>5.00</UnitPrice><Amount>-5.00</Amount></LineItem>
      </Invoice></S:Body></S:Envelope>"""
    invs = _parse_invoices(__import__("xml.etree.ElementTree", fromlist=["fromstring"]).fromstring(xml))
    assert len(invs) == 1 and invs[0].invoice_number == "999"
    assert invs[0].to_common()["is_credit"] is True  # negative total → credit memo
    assert single  # smoke: replacement produced text

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This client sends authentication credentials and business/order query data to external SanMar SOAP endpoints in multiple methods, but the file provides no user-facing confirmation, print/log disclosure, or inline warning that network transmission occurs. Because the code accesses sensitive credentials and performs outbound HTTP calls, the absence of any visible disclosure meets the missing-warning criterion for code files.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

These sections describe pulling invoices and importing invoice files containing financial and potentially customer-related data, then forwarding normalized invoice data downstream. The documentation does not include any warning about sensitive financial information, retention, or limiting sharing, even though the behavior could affect user data privacy.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

This code file handles sensitive credential inputs, including username, password, customer_number, and ftp_password, and documents fallback to SANMAR_* environment variables. Although the module docstring mentions this behavior, there is no runtime confirmation, warning, or user-facing notice when sensitive credentials are consumed.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

This code file defines credential dataclasses containing usernames and passwords and includes a helper that places the password into request payload fields, but there is no accompanying warning, comment, or user-facing disclosure about handling sensitive credential material. For code files, access to credentials can warrant a finding when there is no visible disclosure at all, and this file provides none beyond basic field descriptions.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.