T01 · Skill Instruction Hijacking
- Location
SKILL.md:1332- Finding
Meeting instructions override agent task priority and recommend a persistent stop-blocking hook
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This meeting skill is mostly purpose-aligned, but it gives live meeting participants unusually strong influence over an agent with local tool access and documents risky credential, tunnel, and persistence patterns.
Review before installing. Use this only in trusted meetings or a sandboxed project with a narrow tool allowlist. Do not let meeting participants authorize file reads, shell commands, code edits, commits, credential access, or new network destinations without explicit confirmation from the local user. Avoid the Stop hook unless you fully understand and remove it afterward. Prefer environment or OS secret storage over plaintext config, rotate keys if exposed, and be careful not to tunnel sensitive localhost apps or save confidential transcripts in a repository.
SKILL.md:1332Meeting instructions override agent task priority and recommend a persistent stop-blocking hook
scripts/python/agentcall.py:105Long-lived API credentials are transmitted in WebSocket query strings
scripts/python/join.py:20Unvalidated API endpoint overrides can redirect credentials and meeting data
scripts/python/register.py:97Persistent API key file is created without explicit restrictive permissions
scripts/python/requirements.txt:1Runtime dependencies are not reproducibly pinned
examples/meeting-assistant-smart/assistant.py:329Examples persist complete meeting transcripts using default filesystem permissions
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
# ──────────────────────────────────────────────────────────────────────────────
def create_call(meet_url: str, bot_name: str, trigger_words: List[str], context: str, voice: str) -> dict:
resp = requests.post(
f"{API_BASE}/v1/calls",
headers=HEADERS,
json={
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
def create_call(meet_url: str, bot_name: str, trigger_words: List[str], context: str, voice: str) -> dict:
"""Create a collaborative mode call with avatar UI."""
resp = requests.post(
f"{API_BASE}/v1/calls",
headers=HEADERS,
json={
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
def create_call(meet_url: str, bot_name: str, enable_english_stt: bool) -> dict:
"""Create a call with audio streaming enabled."""
resp = requests.post(
f"{API_BASE}/v1/calls",
headers=HEADERS,
json={
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
def create_call(meet_url: str, bot_name: str) -> dict:
"""Create a call via REST API — audio mode, transcription on, direct strategy (listen only)."""
resp = requests.post(
f"{API_BASE}/v1/calls",
headers=HEADERS,
json={
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
def create_call(meet_url: str, bot_name: str) -> dict:
"""Create a call via REST API — audio mode, transcription on, direct strategy (listen only)."""
resp = requests.post(
f"{API_BASE}/v1/calls",
headers=HEADERS,
json={
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
def fetch_transcript(call_id: str) -> Optional[dict]:
"""Fetch the full transcript after the call ends."""
resp = requests.get(
f"{API_BASE}/v1/calls/{call_id}/transcript?format=json",
headers=HEADERS,
)
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
def fetch_transcript(call_id: str) -> Optional[dict]:
resp = requests.get(f"{API_BASE}/v1/calls/{call_id}/transcript?format=json", headers=HEADERS)
if resp.status_code == 200:
return resp.json()
return None
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
# ──────────────────────────────────────────────────────────────────────────────
def create_call(meet_url: str, bot_name: str) -> dict:
resp = requests.post(
f"{API_BASE}/v1/calls",
headers=HEADERS,
json={
The declared description presents a general-purpose skill for joining meetings as an AI bot with voice and visual presence across multiple modes. The supplied code chunk is narrower and materially different: it is a subprocess bridge that connects AgentCall meeting I/O to another AI agent, explicitly stating it has no LLM and is not a standalone agent. It only creates calls with mode='audio' and voice_strategy='direct', so visual presence and webpage/custom UI modes are not represented in this code. Additionally, it exposes a screenshot command/result flow that is not mentioned in the declared description. While core meeting participation features like joining a call, receiving transcripts, TTS speaking, chat, raise hand, and leaving are consistent with the general domain, the actual primary purpose and capabilities differ enough to count as a mismatch.
The declared description focuses on conferencing behavior: joining meetings on Meet/Teams/Zoom and participating with voice, TTS, and visual presence. The supplied code chunk does something materially different: it provides background business tools that can be invoked during a meeting, such as querying company data, searching documents, performing calculations, and creating tickets. These are undeclared capabilities and the primary declared capability—joining and attending a video meeting—is absent from this code. While the code is thematically related to meeting assistance, it does not match the stated purpose of the skill itself.
The declared description presents a general-purpose meeting-joining bot with voice/visual presence and multiple interaction modes. The supplied code chunk instead implements a specific smart note-taker workflow: it joins a meeting in audio mode, collects transcripts, retrieves the full transcript, optionally sends transcript content to third-party LLM providers for summarization, and writes results to a local markdown file. Those are material capabilities not stated in the description, especially transcript processing, summarization, and file output. The code also does not implement the broader visual presence, text-to-speech, or webpage modes highlighted in the description. While joining a meeting is consistent at a high level, the actual primary purpose of this code is automated note-taking and summarization, which is materially narrower and different from the declared description.
This code chunk does not implement meeting participation or conferencing behavior. Its primary purpose is update checking, which is materially different from the declared description of joining video meetings as an AI bot. It also performs undeclared file-system access and outbound network access to GitHub for version checking. While an update checker could be a supporting utility for a broader skill, this specific code chunk's behavior is unrelated to the declared operational purpose and therefore constitutes a description-behavior mismatch.
The declared purpose is a meeting-participation skill for video conferencing, but the code chunk does not join, attend, or interact with any meeting platform, audio/video stream, or UI. Its primary function is account registration and credential provisioning via email OTP against AgentCall APIs, followed by saving the resulting API key locally. That is a materially different purpose and introduces undeclared capabilities related to authentication and local credential storage.
The declared description is about joining video meetings as an AI bot with media/voice presence. The supplied code does not contain any meeting, conferencing, audio, video, TTS, browser automation, or call-joining behavior. Instead, it is infrastructure code for a reverse tunnel that proxies HTTP traffic from a remote WebSocket endpoint to a local server. That is a materially different primary purpose and an undeclared capability, so this is a clear description-behavior mismatch.
The declared description presents a broad AgentCall skill for joining meetings as an AI bot with voice and visual presence across multiple modes. The supplied code only covers one specific component: an audio bridge subprocess that interfaces a meeting bot with an external agent through stdin/stdout. It does support joining a call and speaking/listening, so it partially aligns with the description. However, it does not implement visual presence, webpage modes, or a standalone meeting-participation agent. It also exposes extra capabilities not mentioned in the description, notably screenshot capture and microphone mute/unmute control. Because the code’s actual scope and primary function are materially narrower/different than the declared purpose, and it includes undeclared capabilities, this is a mismatch.
The declared purpose is about attending video meetings as an AI bot. The actual code chunk does not implement any meeting, audio, video, conferencing, or bot participation behavior. Instead, it performs update checking against GitHub, including local file reads, network requests, and cache writes. This is a materially different purpose from the declared description. Although the script may be optional/supporting infrastructure for the broader skill, this specific code chunk's behavior is unrelated to the advertised meeting-joining functionality, so it should be flagged as a description-behavior mismatch for this chunk.
The declared description says this skill joins and participates in video meetings, but the code shown does not interact with any meeting platform, audio/video streams, voice features, or UI modes. Its sole purpose is authentication/bootstrap: contacting AgentCall auth endpoints, verifying an emailed code, creating an API key, and persisting that credential locally. Those are materially different capabilities from the declared meeting-join functionality, so this is a clear description-behavior mismatch.
The declared description focuses on meeting participation capabilities—joining video conferences as an AI bot with voice, visual presence, and related interaction modes. The actual code chunk does not implement meeting joining, conferencing platform integration, voice handling, TTS, or visual bot behavior. Instead, it is infrastructure code for tunneling/proxying remote HTTP/WS requests to localhost. While such tunneling could be a supporting component for custom UI or screensharing, this chunk’s primary behavior is materially different from the declared end-user purpose and introduces undeclared network proxy capabilities.
The skill explicitly encourages obtaining mailbox OTP codes and collecting or storing API keys in plain workflow steps. This creates a natural-language path for credential harvesting and persistence, which is especially dangerous in agent environments because users may be socially engineered into revealing secrets or allowing the agent to handle them insecurely.
Referenced artifact was not completely inspected
Node equivalent: `node scripts/node/register.js send --email ...` and
Referenced artifact was not completely inspected
Node equivalent: `node scripts/node/register.js send --email ...` and
The skill supports exposing localhost-backed webpages to meeting participants through externally reachable shareable URLs. This can unintentionally publish sensitive local content or internal-only apps to anyone with the URL, especially because the feature is framed as a convenience workflow rather than a risky network exposure.
The anti-refusal language pushes the agent to 'always respond' and 'always participate,' even though meeting transcripts are explicitly untrusted input. In practice this pressures the agent to comply under social or adversarial prompting, weakening safety boundaries and increasing the risk of unauthorized actions during a live call.
→ call.ended: exit loop
Agent IS the voice. Every second without checking = silence. Always greet, always respond, always participate.
**Follow THE CALL_LOOP algorithm (see Pattern 5 → Method 1 below — event-driven, recommended).**
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).
exec 3>&- rm /tmp/meeting-stdin
**THE CALL LOOP — follow this algorithm exactly:**
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).
- `POST /v1/calls` — create a call and join a meeting
- `GET /v1/calls/{id}` — check call status (bot_joining, bot_ready, ended)
- `GET /v1/calls/{id}/transcript?format=json` — fetch the transcript at any time
- `DELETE /v1/calls/{id}` — end the call
These endpoints work as standard HTTP tool calls. The agent can create a call,
periodically check the transcript, and end the call — but cannot participate
Detected: suspicious.dynamic_code_execution, suspicious.env_credential_access