T08 · Insecure Dependencies
- Location
README.md:8- Finding
Unpinned Remote Skill Installation Creates a Supply-Chain Risk
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill appears to be a real Pexo explainer-video wrapper, but it has review-worthy security weaknesses around shell config execution, API key handling, and mutable installation.
Install only if you are comfortable sending prompts and uploaded media to Pexo's hosted service. Protect ~/.pexo/config with restrictive permissions, avoid putting shell syntax in it, keep PEXO_BASE_URL set to https://pexo.ai unless you intentionally use a trusted development endpoint, and prefer installing from a pinned commit or reviewed release.
README.md:8Unpinned Remote Skill Installation Creates a Supply-Chain Risk
scripts/_common.sh:7Configuration Files Are Executed as Arbitrary Shell Code
scripts/_common.sh:14Bearer API Key Can Be Sent to an Untrusted or Plaintext Endpoint
scripts/pexo-asset-get.sh:46Backend-Controlled Asset URLs Permit Unrestricted Outbound Requests
references/SETUP-CHECKLIST.md:8Plaintext API Key Configuration Is Created Without Restrictive Permissions
The declared description says this skill creates finished explainer/tutorial videos from a prompt or product description. The actual code does not generate scripts, visuals, voiceover, captions, or compose any video. Its sole purpose is asset retrieval: calling /api/biz/projects/{project_id}/assets/{asset_id}, inspecting downloadUrl, downloading the file locally, and returning metadata. This is a materially different primary purpose and exposes an undeclared capability of local asset download/caching, so the description does not accurately represent the code.
The declared description presents a high-level skill that creates an explainer/tutorial video end-to-end in Pexo. The supplied code does not implement that workflow. Instead, it is a narrow helper script that sends a chat message to an already existing Pexo project, with optional preview selection and validation for tagged asset IDs. It neither initiates nor completes explainer video generation on its own, and its primary purpose is generic asynchronous message submission plus acknowledgement handling. That is a materially different behavior from the declared purpose, so this is a mismatch.
The code does not create videos, scripts, visuals, narration, captions, or any explainer content. Its primary purpose is operational troubleshooting for a Pexo shell environment. It reads a local config file, inspects installed commands, tests connectivity to the base URL, and validates an API key via a projects endpoint. These are materially different capabilities from the declared end-user function of generating explainer videos, so the description does not accurately represent the code.
The code does not perform any explainer video generation tasks such as script creation, visual/storyboard generation, TTS narration, captioning, or video assembly. Its sole purpose is account/credit introspection via /api/biz/auth/entitlements, plus a zero-balance warning and top-up instructions. That is a materially different primary purpose from the declared explainer-video functionality, so this is a clear description-behavior mismatch.
The declared description promises an end-to-end explainer-video creation capability, including creative generation and media assembly. The supplied code chunk performs a much narrower setup action: it creates an empty Pexo project by POSTing a project name to /api/biz/projects and returning the resulting projectId. This is a materially different primary purpose from the declared behavior. While project creation could be a supporting step in a larger workflow, this chunk by itself does not implement the advertised video-generation functionality.
The code chunk’s primary purpose is project inspection and agent orchestration, not explainer video creation. It calls Pexo project and history endpoints, strips internal fields, computes nextAction based on execution/service status, and returns recent actionable messages. None of the described user-facing generation behaviors—writing a script, generating shot-by-shot visuals, adding TTS narration/captions, or assembling an explainer video—are implemented in this chunk. This is a material description/behavior mismatch rather than a mere supporting detail.
There is a clear mismatch between the declared purpose and the actual code. The description promises a content-generation workflow for producing explainer/tutorial videos, but the code only parses pagination arguments, validates them, and performs an authenticated API request to list projects. No functionality related to script generation, visual creation, narration, captions, or video assembly appears in this code chunk. The actual behavior is project listing, which is materially different from the declared video-creation purpose.
The declared description promises an end-to-end explainer-video creation capability, including content generation and assembly. The actual code only performs media asset upload to a Pexo project in three API steps. While asset upload could be a supporting utility within a larger video workflow, this code chunk by itself does not create explainer videos, generate narration, captions, or assemble output. Therefore the code's primary purpose is materially different from the declared purpose.
Code or instructions that leak agent conversation context to external services, potentially exposing sensitive user interactions.
- `400`: the file was rejected — possible reasons: file exceeds the size limit, file format is not supported, or the file content does not match its extension. Convert or compress the file and re-upload from scratch using `pexo-upload.sh`.
- `401`: auth failure — see Auth and Proxy Errors above.
- `404`: the file record was not found. The upload session may have been cleaned up. Re-upload from scratch using `pexo-upload.sh`.
- `412`: the upload session has already expired or been completed. Re-upload from scratch using `pexo-upload.sh`.
- `500`: an unexpected server error occurred. Retry in a moment; if the problem persists, contact support at pexo.ai.
Code or instructions that leak agent conversation context to external services, potentially exposing sensitive user interactions.
- `400`: the file was rejected — possible reasons: file exceeds the size limit, file format is not supported, or the file content does not match its extension. Convert or compress the file and re-upload from scratch using `pexo-upload.sh`.
- `401`: auth failure — see Auth and Proxy Errors above.
- `404`: the file record was not found. The upload session may have been cleaned up. Re-upload from scratch using `pexo-upload.sh`.
- `412`: the upload session has already expired or been completed. Re-upload from scratch using `pexo-upload.sh`.
- `500`: an unexpected server error occurred. Retry in a moment; if the problem persists, contact support at pexo.ai.
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
# Explainer Video — Pexo
Create explainer videos with narration. Pexo writes the script, generates visuals shot by shot,
adds TTS voiceover and captions, and assembles a clear, finished explainer — no prompt
engineering, no editing.
npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.
The README explicitly states that all creative work happens server-side via a hosted backend and requires an API key, but it does not clearly warn users that their prompts, product details, scripts, and possibly other content will be sent to a third-party service. This can lead to unintentional disclosure of sensitive business information, internal concepts, or regulated data by users who assume processing is local.
The skill explicitly instructs use of shell scripts (pexo-project-create.sh, pexo-chat.sh, pexo-upload.sh, polling helpers) but does not declare any tool scope or allowed-tools boundary. That creates an authorization gap: the runtime may expose shell capability more broadly than intended, making it harder to constrain what the skill can execute and increasing blast radius if the skill or downstream scripts are modified.
The workflow depends on persistent server-side project state and reuses the same project_id for revisions, which means user content, assets, and conversation history persist across interactions. Without explicit retention, isolation, and deletion guidance, this can expose prior session data longer than users expect and increase the impact of account compromise or accidental cross-session reuse.
---
name: explainer-video-maker
description: >
Create an explainer video with narration using Pexo. Describe your product, concept, or process
and Pexo writes the script, generates visuals shot by shot, adds TTS voiceover and captions, and
assembles a clear, finished explainer. Use for explainer/tutorial content: "explainer video",
"explain this as a video", "tutorial video", "how it works video". NOT for ads or promos (use
The skill instructs the agent to send the user's request 'verbatim' to a hosted backend, yet it provides no privacy notice, consent step, or guidance to avoid transmitting sensitive data. Users may disclose proprietary product plans, internal processes, files, or personal information believing they are interacting locally, and that data would be forwarded to a third party without clear warning.
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
mkdir -p ~/.pexo
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
- `403`: the download link has expired. Re-run `pexo-asset-get.sh` to get a fresh link.
- `000`: network request failed before receiving a response. Check network connectivity and retry.
- local filesystem write failure: the temp directory (`~/.pexo/tmp/`) is not writable or the disk is full. Free up space or set `PEXO_TMP_DIR` to a writable path.
Notes:
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
err_file=$(mktemp)
if [[ -n "$body" ]]; then
http_code=$(curl -sS \
--connect-timeout "$_PEXO_CONNECT_TIMEOUT" \
--max-time "$_PEXO_REQUEST_TIMEOUT" \
-X "$method" \
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
err_file=$(mktemp)
if [[ -n "$body" ]]; then
http_code=$(curl -sS \
--connect-timeout "$_PEXO_CONNECT_TIMEOUT" \
--max-time "$_PEXO_REQUEST_TIMEOUT" \
-X "$method" \
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
set +o pipefail
if [[ -n "$body" ]]; then
curl -sS -N \
--connect-timeout "$_PEXO_CONNECT_TIMEOUT" \
--max-time "$timeout" \
-X POST \
The script executes source "$config_path", which runs arbitrary shell code from a user-controlled config file in the current shell context. Even though this is framed as loading configuration, any command placed in that file will execute when the diagnostic runs, making code execution possible if the file is modified by an attacker or copied from an untrusted source.
The diagnostic gives no explicit warning that running it will execute shell code from ~/.pexo/config via source. Users may reasonably expect a health check to only read configuration, so this omission increases the risk that unsafe config content is executed without informed consent.
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.
source "$config_path"
else
echo "$FAIL Config file not found: $config_path"
echo " Create it with:"
echo ' mkdir -p ~/.pexo && cat > ~/.pexo/config << EOF'
echo ' PEXO_BASE_URL="https://pexo.ai"'
echo ' PEXO_API_KEY="sk-<your-api-key>"'
This code performs HTTP requests to retrieve project state and full message history, which may include user conversation content and project metadata, but the script provides no runtime notice, confirmation, or explicit warning about accessing and returning that data. The usage text describes functionality but does not clearly warn that conversation/history content will be fetched and emitted to stdout, which is relevant for privacy and safe handling.
No suspicious patterns detected.