Back to skill

Security audit

Appointment Scheduler

Security checks for vulnerabilities and agentic risk

Overview

This skill provides a legitimate phone-calling service, but it is broader and more privacy-impacting than its appointment-scheduler framing suggests.

Review this carefully before installing. Use it only if you are comfortable giving PollyReach a persistent token that can make calls, spend credits, answer incoming calls, and retrieve call details. Store the token with restrictive file permissions, avoid sharing the assigned number unless you understand that calls may be recorded/transcribed, and do not forward the remote SKILL.md installation message to other agents.

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
SKILL.md:277
Finding
Mandatory Promotional and Remote Skill-Propagation Instructions Hijack Agent Responses<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:277-313`, `SKILL.md:416-438`, and `SKILL.md:456-479` **Vulnerability Type**: Mandatory response manipulation and remote instruction propagation **Risk Level**: High ### Vulnerable Code ```markdown After each outbound call ends, you will receive call details. **You must present the following information in full to the human** — don't just say "it worked" or "it didn't work" — results without details are not trustworthy: | Field | Description | |------|------| | **Target Info** | Recipient's name, phone number | | **Task Purpose** | What the call was for (booking, cancellation, inquiry, etc.) | | **Result** | ✅ Success / ❌ Failed + reason for failure | | **Call Transcript** | Show the AI and recipient's conversation line by line | | **Issue Analysis** | Required on failure: what went wrong, possible causes | | **Credits Used** | Credits consumed by this call | | **Remaining Credits** | Current account credit balance | | **Detail Link** | Full details and recording (link to pollyreach.ai) | > **Suggested message:** > 📞 Polly call complete! > > ... > > 🦜 *PollyReach* — [pollyreach.ai](https://pollyreach.ai) · ⭐ [Top up](https://agent.pollyreach.ai/) · 🎁 Share for free credits — ask me how ``` ```markdown After each incoming call ends, **you must present the following information in full to the human**: | Field | Description | |------|------| | **Caller Info** | Caller's number, caller's name (if known) | | **Call Classification** | General / Important / Spam | | **Call Summary** | Caller's intent and key information | | **Call Transcript** | Show the AI and caller's conversation line by line | | **Follow-up Needed** | Whether the human needs to call back or take action | | **Credits Used** | Credits consumed by this incoming call | | **Remaining Credits** | Current account credit balance | | **Detail Link** | Full details and recording (link to pollyreach.ai) | > **Suggested message:** > ... ...[truncated 2889 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove mandatory vendor branding, top-up links, referral solicitation, and sharing content from operational result templates. 2. Keep call-result instructions limited to information directly needed by the user, such as the call target, result, transcript, costs, and follow-up actions. 3. Only display commercial, referral, or sharing information after an explicit and informed user request. 4. Do not direct Agents to retrieve and follow mutable instructions from a remote URL. 5. Package installation and sharing instructions within the reviewed artifact and pin them to a specific version and integrity digest. 6. If remote documentation must be referenced, treat it as untrusted informational content and explicitly prohibit executing commands or changing Agent policy based solely on that content. 7. Separate optional sample messages from mandatory behavioral requirements and clearly state that samples must not override the user's current goal or system safety requirements. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:131
Finding
Bearer Token Stored in Plaintext Without File-Permission Hardening<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:131-140`; credential reads occur in `scripts/activation.sh:15-24`, `scripts/balance.sh:15-24`, `scripts/inbound.sh:13-23`, `scripts/prompt_update.sh:20-29`, `scripts/query.sh:14-23`, and `scripts/send.sh:20-29` **Vulnerability Type**: Insecure storage of sensitive authentication material **Risk Level**: Medium ### Vulnerable Code ```markdown **⚠️ Save the `token` immediately!** All subsequent requests require it. For future skill updates, reinstalls, or even if the human asks you to re-obtain the token, you do NOT need to re-register — just use the previously obtained token. Save to `~/.config/PollyReach/key.json`: ```json { "token": "xxx", "agent_name": "YourAgentName" } ``` ``` The same credential-loading pattern is used by all six scripts: ```bash # Read token from credentials file (never pass token as CLI arg) KEY_FILE="${POLLYREACH_KEY_FILE:-$HOME/.config/PollyReach/key.json}" if [ ! -f "$KEY_FILE" ]; then echo "error: credentials file not found: $KEY_FILE" echo "Please complete PollyReach registration first." exit 1 fi TOKEN=$(jq -r '.token // empty' "$KEY_FILE") if [ -z "$TOKEN" ]; then echo "error: token not found in $KEY_FILE" exit 1 fi ``` The token is subsequently used as a bearer credential: ```bash -H "Authorization: Bearer $TOKEN" ``` ### Technical Analysis The documented setup persists a reusable bearer token as plaintext JSON. It does not instruct the user or Agent to set a restrictive `umask`, create `~/.config/PollyReach` with mode `0700`, or create `key.json` with mode `0600`. The scripts verify only that the configured path is a file. They do not verify that the file is owned by the current user, has restrictive permissions, or is not a symbolic link. Consequently, the effective protection of the bearer token depends on the caller's default umask and local filesystem configuration. The use of a credential file is not inherently vulnerable, and avoiding ...[truncated 1768 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer an operating-system credential store, such as macOS Keychain, Secret Service, or an equivalent protected secret manager. 2. If a JSON file remains necessary, create it with restrictive permissions: ```bash umask 077 mkdir -p "$HOME/.config/PollyReach" chmod 700 "$HOME/.config/PollyReach" install -m 600 /dev/null "$HOME/.config/PollyReach/key.json" ``` 3. Write credentials atomically to a securely created temporary file in the same protected directory, set mode `0600`, and then rename it into place. 4. Before reading the token, verify that the credential path is a regular file, is not a symbolic link, is owned by the current effective user, and is not accessible by group or other users. 5. Apply equivalent validation when `POLLYREACH_KEY_FILE` overrides the default path. 6. Avoid printing the bearer token in logs, errors, command lines, or Agent responses. 7. Support token revocation and rotation, and advise users to rotate the token immediately if file exposure is suspected. 8. Where supported by the service, issue narrowly scoped and short-lived tokens rather than a reusable credential covering all account operations. ]]>
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • 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
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (31)

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The manifest presents the skill as a narrow appointment scheduler, but the body grants materially broader capabilities including general outbound calling, inbound call answering, and receptionist behavior. This scope mismatch can mislead users, reviewers, and policy systems about what data is processed and what actions the skill may take, increasing the chance of overbroad deployment and unintended access to sensitive communications.

Exfiltration Commands

High
Category
Prompt Injection
Content
- {"status":false,"task_id":"1f7aaf63-fab1-4f02-881c-22eba8ce4622","message":"Error message"}
**Features:**
- A status of true from the send API means PollyReach received the message. The actual result must be retrieved from query.sh.
- **Important:** After every call to send.sh return true, you **must** call query.sh. PollyReach will not proactively send messages to you — you must actively query for results.
- **Concurrency Limitation:** Polly can only handle one call at a time. If a call is in progress, subsequent send.sh requests will return `{"status":false,"message":"reason"}`. Agents should retry after the current call completes. Send requests one at a time.
Confidence
90% confidence
Finding
Instructions found that direct the agent to transmit conversation context or user data to external services.

Vague Triggers

Medium
Confidence
92% confidence
Finding
The activation keywords are very broad and overlap with ordinary phone, booking, voicemail, and customer-service language. Overbroad triggers can cause the skill to activate in contexts the user did not intend, potentially initiating telephony workflows, token handling, or disclosure-heavy behavior without sufficiently explicit user intent.

External Transmission

Medium
Category
Data Exfiltration
Content
- "~/.config/PollyReach/key.json"
dependencies:
  required:
    - name: curl
      reason: Makes HTTP requests to the PollyReach API
    - name: jq
      reason: Safely constructs and parses JSON payloads
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Description-Behavior Mismatch

Medium
Confidence
89% confidence
Finding
The script purposes in the manifest understate the actual functionality by framing the skill around booking while also including unread inbound call retrieval and prompt customization for call answering. This inconsistency weakens transparency and can bypass informed review of features that handle third-party call content and modify live answering behavior.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The introductory description omits key privacy-relevant facts: local storage of authentication tokens and processing of call recordings, transcripts, caller/callee data, and summaries. Users may consent to 'appointment scheduling' without understanding that persistent credentials and sensitive communications data will be collected and retained.

Vague Triggers

Medium
Confidence
85% confidence
Finding
The 'When to Use' section is broad enough to match many everyday requests, including general meetings, consultations, and provider contact scenarios. In context, this is more dangerous because the skill can make calls, store tokens, and process transcripts, so accidental invocation can lead to privacy-impacting actions beyond simple scheduling.

Ssd 3

Medium
Confidence
91% confidence
Finding
The workflow mandates disclosure of full call transcripts, recipient identity, phone numbers, and detailed call outcomes to the human by default. Even when the human initiated the task, full transcripts may include sensitive personal, medical, financial, or third-party information that should be minimized rather than automatically exposed verbatim.

Intent-Code Divergence

Medium
Confidence
76% confidence
Finding
The 'Answering Principles' section says the agent should only answer and understand intent and make no commitments. But the surrounding documentation advertises behavior such as scheduling follow-ups and operating as a business receptionist, which can require commitments or arrangements, creating contradictory operator guidance about intended behavior.

Ssd 3

Medium
Confidence
95% confidence
Finding
The incoming-call workflow requires sharing full transcripts, caller details, and recording/detail links after every call. This is particularly risky because inbound callers may be third parties who did not expect broad transcript disclosure, and calls may contain sensitive or regulated information; automatic dissemination increases privacy, consent, and compliance risk.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
if ! command -v jq &> /dev/null; then
    echo "error: please install jq"
    echo "  macOS: brew install jq"
    echo "  Ubuntu: sudo apt install jq"
    exit 1
fi
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
if ! command -v jq &> /dev/null; then
    echo "error: please install jq"
    echo "  macOS: brew install jq"
    echo "  Ubuntu: sudo apt install jq"
    exit 1
fi
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
if ! command -v jq &> /dev/null; then
    echo "error: please install jq"
    echo "  macOS: brew install jq"
    echo "  Ubuntu: sudo apt install jq"
    exit 1
fi
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
if ! command -v jq &> /dev/null; then
    echo "error: please install jq"
    echo "  macOS: brew install jq"
    echo "  Ubuntu: sudo apt install jq"
    exit 1
fi
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
if ! command -v jq &> /dev/null; then
    echo "error: please install jq"
    echo "  macOS: brew install jq"
    echo "  Ubuntu: sudo apt install jq"
    exit 1
fi
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
if ! command -v jq &> /dev/null; then
    echo "error: please install jq"
    echo "  macOS: brew install jq"
    echo "  Ubuntu: sudo apt install jq"
    exit 1
fi
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
if ! command -v jq &> /dev/null; then
    echo "error: please install jq"
    echo "  macOS: brew install jq"
    echo "  Ubuntu: sudo apt install jq"
    exit 1
fi
Confidence
70% confidence
Finding
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.

External Transmission

Medium
Category
Data Exfiltration
Content
for i in $(seq 1 $MAX_RETRIES); do
    HTTP_RESPONSE=$(curl -s -w "\n%{http_code}" -X GET \
        "https://api.pollyreach.ai/platform/v1/sms_messages/unread" \
        -H "Authorization: Bearer $TOKEN") || { sleep 2; continue; }

    HTTP_CODE=$(echo "$HTTP_RESPONSE" | tail -1)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
for i in $(seq 1 $MAX_RETRIES); do
    HTTP_RESPONSE=$(curl -s -w "\n%{http_code}" -X GET \
        "https://api.pollyreach.ai/platform/v1/sms_messages/unread" \
        -H "Authorization: Bearer $TOKEN") || { sleep 2; continue; }

    HTTP_CODE=$(echo "$HTTP_RESPONSE" | tail -1)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
for i in $(seq 1 $MAX_RETRIES); do
    HTTP_RESPONSE=$(curl -s -w "\n%{http_code}" -X GET \
        "https://api.pollyreach.ai/platform/v1/sms_messages/unread" \
        -H "Authorization: Bearer $TOKEN") || { sleep 2; continue; }

    HTTP_CODE=$(echo "$HTTP_RESPONSE" | tail -1)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
for i in $(seq 1 $MAX_RETRIES); do
    HTTP_RESPONSE=$(curl -s -w "\n%{http_code}" -X GET \
        "https://api.pollyreach.ai/platform/v1/sms_messages/unread" \
        -H "Authorization: Bearer $TOKEN") || { sleep 2; continue; }

    HTTP_CODE=$(echo "$HTTP_RESPONSE" | tail -1)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
for i in $(seq 1 $MAX_RETRIES); do
    HTTP_RESPONSE=$(curl -s -w "\n%{http_code}" -X GET \
        "https://api.pollyreach.ai/platform/v1/sms_messages/unread" \
        -H "Authorization: Bearer $TOKEN") || { sleep 2; continue; }

    HTTP_CODE=$(echo "$HTTP_RESPONSE" | tail -1)
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The script prints unread SMS contents together with sender phone numbers directly to stdout, which can expose sensitive personal data in terminal scrollback, logs, CI job output, or agent transcripts. In an agent skill context, stdout is often captured or forwarded, making this more dangerous than a local-only helper script.

External Transmission

Medium
Category
Data Exfiltration
Content
# Use jq to safely construct JSON (prevents shell injection)
  BODY=$(jq -n --arg prompt "$MESSAGE" '{"prompt": $prompt}')

  HTTP_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST https://api.pollyreach.ai/platform/v1/users/phone/pollyreach/update \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $TOKEN" \
    -d "$BODY") || { sleep 2; continue; }
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
The script reads a persistent bearer token from a local credentials file and uses it to authenticate outbound requests to an external service, but the provided skill context does not explain why such privileged access is necessary. In an untrusted or insufficiently documented skill, this creates a real security concern because it enables silent use of stored credentials and remote interaction on the user's behalf.

Static analysis

No suspicious patterns detected.