T09 · Insecure Skill Coding Practices
- Location
scripts/tryhair.py:43- Finding
Server-Side Request Forgery Through Unrestricted Image URL Fetching
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill mostly does what it claims, but it handles face photos and user IDs with broad automatic triggers and has concrete implementation risks around URL fetching and output file writes.
Install only if you are comfortable sending face photos and a TryHair UID to tryhair.ai. Use explicit commands, avoid relying on automatic or 'try this' style triggers, avoid image_url inputs from untrusted sources, and manually delete generated files from output/ when done.
scripts/tryhair.py:43Server-Side Request Forgery Through Unrestricted Image URL Fetching
scripts/tryhair.py:178Path Traversal in Generated Image Filename
scripts/tryhair.py:31Unbounded Image Download, Decoding, and Disk Write
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
}
try:
response = requests.post(
TRYHAIR_API,
files=files,
data=data,
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
data = {'uid': args.uid}
try:
response = requests.post(
FACESHAPE_API,
files=files,
data=data,
A description-behavior mismatch is dangerous because it prevents informed consent and accurate review. If the packaged skill actually performs publishing/deployment or other unrelated actions while claiming to only analyze hairstyles, users may expose images, identifiers, or runtime context to code operating outside the advertised purpose.
Using generic phrases like "try this" or "try it" as action triggers can cause accidental invocation when the user did not intend to send an image for external processing. In a skill handling photos and UIDs, ambiguous triggers materially raise the risk of surprise data processing, privacy violations, and unintended API calls.
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.
---
## Image Output Guidelines
- Images are displayed without exposing internal paths
- Outputs are presented clearly and in a user-friendly format
Allowing activation from a bare style name is too ambiguous because ordinary conversation can mention a hairstyle without requesting processing. In this context, the skill could initiate image handling or remote requests based on weak intent signals, which is especially risky for biometric-style face analysis workflows.
The README describes uploading a face photo and supplying a UID, but it does not clearly disclose that these sensitive inputs are sent to a third-party online service for processing. Since facial photos can be highly sensitive biometric data and the UID is an account-linked identifier, inadequate disclosure undermines informed consent and raises privacy and compliance risk.
The hairstyle workflow is triggered by broad natural-language cues like "Change my hairstyle" or general hairstyle requests paired with a photo. In a multimodal agent, this can cause unintended activation and transmission of a sensitive face image and UID to a third-party service without sufficiently explicit user confirmation.
The face-shape workflow uses ambiguous prompts such as asking which hairstyles might suit the user, which could overlap with ordinary styling advice requests. Because this skill processes biometric facial imagery via an external service, ambiguous activation increases the risk of unexpected analysis of a user's face and disclosure of their photo and UID.
Large whitespace padding was detected (a block of blank lines or a long run of spaces). This can push injected instructions below or to the right of the visible area so a human reviewer never sees them while the agent still reads them. Manual review of the hidden content is recommended.
## Error Recovery
| Error Type | Suggested Response |
| -------------------- | --------------------------------------------------------------------------------------------------- |
| Missing UID | "I need your UID to continue. You can get it by logging in at https://tryhair.ai." |
| Insufficient credits | "You're out of free credits. Would you like to purchase a plan to continue?" |
The skill declares no explicit tool scope even though its behavior implies environment and network access. That creates an unnecessary trust gap: a runtime may grant broader capabilities than users or reviewers expect, increasing the chance of unintended data access or external transmission of uploaded images and UIDs.
The skill permits reuse of previously provided images or identifiers with only optional confirmation, which is inadequate for sensitive visual and identity-linked data. Reprocessing or carrying forward a user's photo across requests can violate expectations and consent boundaries, especially when the image may reveal biometric characteristics.
The skill enables implicit invocation while advertising a broad natural-language use case around analyzing photos and trying hairstyles, but it does not define clear scope constraints or invocation boundaries. This can cause the agent to invoke the skill automatically in ambiguous contexts involving images or appearance-related requests, potentially leading to unintended photo processing, privacy exposure, or user actions occurring without sufficiently explicit consent.
The skill uploads a user-provided face image and uid to an external third-party API, which is sensitive biometric/personal data. While this is core functionality, the code provides no explicit user-facing consent notice or disclosure at the point of transmission, increasing privacy and compliance risk.
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
}
try:
response = requests.post(
TRYHAIR_API,
files=files,
data=data,
Face-shape analysis sends a face image and user identifier to a remote endpoint without any explicit notice in the script output or runtime interaction. Because facial imagery is sensitive biometric data, silent transfer to a third party is a meaningful privacy vulnerability in this skill context.
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
data = {'uid': args.uid}
try:
response = requests.post(
FACESHAPE_API,
files=files,
data=data,
The script writes a file using a filename component derived from remote API response data: data['hairstyle']. Only spaces are replaced, so path separators or traversal sequences in a malicious or compromised API response could escape the output directory and overwrite unintended files.
os.makedirs("output", exist_ok=True)
filepath = f"output/output_{safe_style}_{timestamp}.jpg"
with open(filepath, 'wb') as f:
f.write(base64.b64decode(data['image_base64']))
style = data['hairstyle']
No suspicious patterns detected.