Back to skill

Security audit

KallyAI Executive Assistant (Claude Code)

Security checks for vulnerabilities and agentic risk

Overview

This skill matches its stated assistant purpose, but it gives a broadly triggered agent authority to perform real-world actions like calls, emails, bookings, outreach, phone-number changes, and billing-related changes without clear enough confirmation boundaries.

Review this carefully before installing. Use it only if you are comfortable granting KallyAI access to private communications and account actions, and avoid letting the broad ask command run without first confirming recipients, costs, affected accounts, and any irreversible actions such as releasing a phone number or changing connected channels/subscriptions.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (2)

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/kallyai.py:108
Finding
OAuth Tokens Exposed Through URL Query Parameters and Non-Atomic Credential File Permissions## Vulnerability Details **File Location**: `scripts/kallyai.py:108-115` and `scripts/kallyai.py:145-148` **Vulnerability Type**: OAuth credential exposure and insecure local secret-file creation **Risk Level**: Medium ### Vulnerable Code ```python def save_token(access_token: str, refresh_token: str = None, expires_in: int = 3600): """Save token securely (0600 permissions).""" TOKEN_FILE.write_text(json.dumps({ "access_token": access_token, "refresh_token": refresh_token, "expires_at": time.time() + expires_in - 60, })) TOKEN_FILE.chmod(0o600) ``` ```python if "access_token" in params: auth_result["access_token"] = params["access_token"][0] auth_result["refresh_token"] = params.get("refresh_token", [None])[0] auth_result["state"] = params.get("state", [None])[0] ``` The corresponding OAuth flow is documented in `references/api-reference.md:47-57`: ```text GET /v1/auth/cli?redirect_uri=http://localhost:PORT&state=OPTIONAL_STATE After authentication, redirects to: http://localhost:PORT?access_token=TOKEN&refresh_token=TOKEN&expires_in=3600&state=STATE ``` ### Technical Analysis The OAuth callback transports both the access token and refresh token in the URL query string. Query parameters can be retained in browser history and may be visible to browser extensions, local browser telemetry, diagnostic tooling, or other software that records navigated URLs. A refresh token is particularly sensitive because it can potentially be exchanged for new access tokens after the original access token expires. The credential file is also written before restrictive permissions are applied. `Path.write_text()` creates or truncates the file using permissions derived from the process umask, and `chmod(0o600)` is only called after the sensitive contents have been written. This produces a race window during which another local account may be able to ...[truncated 2075 chars]
Remediation
## Remediation Suggestions 1. Replace query-parameter token delivery with OAuth 2.0 Authorization Code Flow with PKCE. - Return only a short-lived, single-use authorization code to the localhost callback. - Exchange the code and PKCE verifier for tokens through a TLS-protected back-channel request. - Do not place access tokens or refresh tokens in URLs. 2. Make the OAuth state parameter mandatory at both the client and server. 3. Return browser responses with restrictive headers, including `Cache-Control: no-store`, `Pragma: no-cache`, and an appropriate `Referrer-Policy`. 4. Create credential files with restrictive permissions before writing secrets. Use `os.open()` with mode `0o600`, then wrap the descriptor for writing. 5. Prefer an atomic update strategy: - Create a temporary file in the same directory with mode `0o600`. - Write and flush the credential data. - Optionally call `fsync()`. - Atomically replace the destination with `os.replace()`. 6. Before reading an existing credential file, verify that it is owned by the current user, is a regular file rather than a symbolic link, and has no group or world permissions. 7. Where available, store refresh tokens in the operating system's credential manager instead of a plaintext JSON file. 8. Revoke existing tokens after migrating the authentication flow.

T08 · Insecure Dependencies

Note
Location
scripts/kallyai.py:31
Finding
Unpinned Third-Party Dependency Installation Guidance## Vulnerability Details **File Location**: `scripts/kallyai.py:31-35` **Vulnerability Type**: Unconstrained third-party dependency installation **Risk Level**: Low ### Vulnerable Code ```python try: import httpx except ImportError: print(json.dumps({"error": {"code": "missing_dependency", "message": "httpx is required. Install with: pip install httpx"}})) sys.exit(1) ``` ### Technical Analysis When `httpx` is unavailable, the CLI instructs users to install it using an unconstrained `pip install httpx` command. The project does not provide a reviewed lockfile, an exact version constraint, or package hashes in the audited directory. Consequently, the version installed at execution time is determined by the package index and dependency resolver rather than by the version reviewed with this project. A future compromised, malicious, or behaviorally incompatible release of `httpx` or one of its transitive dependencies could therefore be installed. The package name itself is correctly spelled, and the reviewed project does not configure an untrusted package index or automatically execute the installation command. This lowers the severity, but it does not eliminate the supply-chain risk. ### Attack Path 1. A user runs the CLI in an environment where `httpx` is not installed. 2. The CLI displays the recommendation to run `pip install httpx`. 3. The user executes that command against the configured Python package index. 4. The resolver installs the latest available package and transitive dependencies without validating them against project-controlled hashes. 5. If a resolved package version or transitive dependency has been compromised, malicious code can execute during installation, import, or normal HTTP-client operation. 6. That code runs with the privileges of the user who performed the installation or invoked the CLI. Exploitation depends on compromise of the package source, dependency chain, or pack ...[truncated 684 chars]
Remediation
## Remediation Suggestions 1. Add a dependency manifest that constrains `httpx` and all relevant transitive dependencies to reviewed versions. 2. Generate a lockfile or hash-locked requirements file and require integrity verification during installation. 3. Replace the generic instruction with a project-specific installation command, such as installation from the audited lockfile. 4. Use an isolated virtual environment rather than recommending global or privileged package installation. 5. Configure trusted package indexes explicitly in deployment documentation and avoid dependency resolution from untrusted extra indexes. 6. Establish automated dependency scanning and a controlled update process so pinned versions receive timely security updates after review.
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
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (26)

Vague Triggers

High
Confidence
98% confidence
Finding
The manifest description includes extremely broad trigger phrases like email, search, book, find, schedule, messages, research, assistant, and calendar, which overlap heavily with ordinary user requests. This makes accidental or overly broad invocation likely, and because the skill can perform charged, privacy-sensitive, and account-affecting actions, unintended activation can lead directly to external actions without sufficient user intent.

Missing User Warnings

High
Confidence
97% confidence
Finding
The skill advertises capabilities to send emails, handle calls, manage messages, provision phone numbers, order services, and perform billing-related tasks, but it does not prominently warn about privacy exposure, financial cost, external communications, or irreversible side effects before those actions occur. In this context, users may unknowingly authorize actions that affect third parties, spend credits or money, or expose sensitive personal data.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
### Delete Inbound Calls

**`DELETE /v1/inbound/calls`**

Bulk delete inbound call records.
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
### Delete Routing Rule

**`DELETE /v1/inbound/rules/{rule_id}`**

---
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
### Delete Contact

**`DELETE /v1/inbound/contacts/{contact_id}`**

---
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
### Remove Call Forwarding

**`DELETE /v1/phone-numbers/{phone_number_id}/forwarding`**

---
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
### Release Phone Number

**`DELETE /v1/phone-numbers/{phone_number_id}`**

Releases the phone number (cannot be undone).
Confidence
80% confidence
Finding
This endpoint permanently releases a phone number and the documentation notes it 'cannot be undone,' yet no explicit warning about confirmation or anti-automation safeguards is shown. In an agentic skill that manages telephony resources, destructive irreversible actions are more dangerous because a misinterpreted prompt or prompt injection could disrupt inbound/outbound communications and cause loss of a business-critical number.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
#### Delete Event

**`DELETE /v1/actions/calendar/events/{event_id}`**

---
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
#### Cancel Booking

**`DELETE /v1/actions/bookings/{booking_id}`**

---
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
### Disconnect Account

**`DELETE /v1/email/accounts/{account_id}`**

---
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
### Delete Contact Email

**`DELETE /v1/channels/email/{email_id}`**

Remove a contact email address. Returns 204 on success.
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
**`POST /v1/channels/disconnect/{channel}`**

Disconnect a connected messaging channel (WhatsApp or Telegram). For email, use `DELETE /v1/channels/email/{email_id}` instead.

**Response:**
```json
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
### Cancel Pending Change

**`DELETE /v1/subscriptions/pending-change`**

Cancel a scheduled downgrade, keeping the current plan.
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Credential Access

High
Category
Privilege Escalation
Content
| `goal_already_cancelled` | 409 | Goal already cancelled |
| `action_not_undoable` | 409 | Action cannot be reversed |
| `missing_token` | 401 | No Authorization header |
| `token_expired` | 401 | Access token expired |
| `forbidden` | 403 | Insufficient permissions |
| `call_not_found` | 404 | Call doesn't exist |
| `transcript_not_found` | 404 | No transcript available |
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def _refresh_token(refresh_tok: str) -> str | None:
    """Refresh an expired access token."""
    with httpx.Client() as client:
        resp = client.post(
            f"{API_BASE}/v1/auth/refresh",
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Exfiltration Commands

High
Category
Prompt Injection
Content
coord_p = sub.add_parser("coord", help="Coordination & goal management")
    coord_sub = coord_p.add_subparsers(dest="coord_cmd")

    c = coord_sub.add_parser("message", help="Send message to coordination AI")
    c.add_argument("text", nargs="+")
    c.add_argument("--conversation-id", help="Conversation to continue")
    c.set_defaults(func=cmd_coord_message)
Confidence
90% confidence
Finding
Instructions found that direct the agent to transmit conversation context or user data to external services.

Lp3

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding
The skill exposes broad capabilities including file access, shell execution, and network operations but does not declare any explicit tool scope or permission boundaries. In a skill that can send emails, place calls, manage messages, and affect external accounts, this creates a materially increased risk of over-privileged execution, unintended side effects, and abuse if the skill is invoked unexpectedly or handles adversarial input.

Vague Triggers

Medium
Confidence
93% confidence
Finding
The guidance states that for most requests, the user should use `ask`, and describes it as routing automatically through a coordination AI that may create goals, make calls, or send emails as needed. That ambiguous activation model hides operational boundaries and encourages a catch-all pathway for sensitive actions, increasing the chance that loosely specified requests trigger impactful external behavior.

Session Persistence

Medium
Category
Rogue Agent
Content
```bash
outreach tasks [--status X]      # List outreach tasks
outreach task <id>               # Task details
outreach create --channel call --target "+1..." "description"
outreach retry <id>              # Retry failed task
outreach cancel <id>             # Cancel task
```
Confidence
60% confidence
Finding
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.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The coordination API explicitly says it 'automatically creates goals and dispatches actions,' which can trigger real-world side effects such as calls, bookings, or messages from a single natural-language prompt. In an agent skill context, documenting this behavior without strong warnings or confirmation requirements increases the risk of unintended actions on behalf of the user or contacts.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The outreach task API supports creating third-party contact tasks and allows automatic execution, meaning an agent can contact external people without a separate consent checkpoint. In a delegation/assistant skill, this materially raises the risk of privacy violations, spam, harassment, or reputational damage if instructions are misinterpreted or prompt-injected.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
"recipient_name": "Dr. Smith",
  "urgency": "high",
  "coordination_message_id": "optional-msg-id",
  "auto_execute": true
}
```
Confidence
95% confidence
Finding
The `auto_execute` parameter enables autonomous initiation of outreach without a mandatory review step, which is risky because it can convert model output directly into phone calls or messages to real people. In this skill, that capability is especially dangerous because the product is designed to act across multiple channels and make contact decisions on the user's behalf.

Intent-Code Divergence

Medium
Confidence
93% confidence
Finding
The docstring says email sending is 'queued for approval,' but the implementation directly calls a send endpoint. If users or higher-level agents rely on the approval guarantee, this mismatch can cause unintended outbound email actions, creating a consent and integrity risk for a high-impact communication channel.

Natural-Language Policy Violations

Medium
Confidence
96% confidence
Finding
The backward-compatibility argument parser sets the default language to "es", causing calls invoked via legacy flags to use Spanish unless the user explicitly overrides it. This is a natural-language locale policy issue because the tool imposes a specific language by default rather than offering a neutral default or requiring user choice.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
The main calls make command defines --language with default="es", so outbound calls will be conducted in Spanish unless the user notices and changes the setting. This forces a specific language choice in a general-purpose skill and can violate language/locale policy expectations.

Static analysis

No suspicious patterns detected.