T09 · Insecure Skill Coding Practices
- Location
scripts/setup_and_auth.py:136- Finding
OAuth Access and Refresh Tokens Stored in Plaintext by Setup Workflow
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill does what it claims for Outlook calendar sync, but it stores long-lived Microsoft calendar tokens in plaintext and can delete calendar events without a confirmation step.
Review before installing. Use it only if you are comfortable granting Calendars.ReadWrite and offline_access to a Microsoft app and having calendar tokens stored as a plaintext token_store.json file in the skill directory. Avoid shared machines, verify the Azure tenant/client ID before authenticating, confirm the timezone, and revoke the Microsoft app grant plus delete the token file when you stop using it.
scripts/setup_and_auth.py:136OAuth Access and Refresh Tokens Stored in Plaintext by Setup Workflow
scripts/authenticate.py:105OAuth Access and Refresh Tokens Stored in Plaintext by Authentication Workflow
scripts/calendar_sync.py:122OData Filter Injection Through Unescaped Calendar Search Input
The declared purpose says the skill syncs reminders to Outlook, but the documented behavior includes Azure app registration guidance, persistent storage of access/refresh tokens, and auto-refresh of those credentials. That hidden expansion of capability is dangerous because users may authorize long-lived account access and local secret storage without understanding the real trust boundary or operational behavior.
The trigger phrases are broad enough to match ordinary conversation about reminders or calendars, which can cause the skill to activate unexpectedly. In this skill's context, accidental activation is more dangerous because activation can lead to OAuth login, network calls, local token storage, and calendar modifications or reads affecting a real user account.
The skill performs sensitive actions—network access to Microsoft Graph and local credential persistence—without declaring an explicit tool scope or permission boundary. This increases the chance that an agent invokes it without clear user awareness of file and network side effects, especially because it stores authentication material locally.
The invocation rules are ambiguous about when the skill should handle calendar-related requests versus when the agent should merely discuss or draft a reminder. Ambiguity is risky here because this skill has side effects in an external account, so unclear routing can result in unintended calendar reads/writes or deletion requests being executed.
Hard-coding Asia/Shanghai and assuming a Chinese-user locale without opt-in can cause events to be scheduled at the wrong time, leading to missed meetings or reminders. In a calendar-writing skill, incorrect timezone handling directly affects integrity of user data and can silently propagate to all synced devices.
The code silently creates config.txt with a hardcoded Azure Tenant ID when the file is absent, contradicting the documented behavior that it only reads configuration. This can bind authentication to an unexpected Microsoft tenant, causing users to authenticate against the wrong organization and potentially expose calendar access to an unintended Azure app registration context.
The script stores access and refresh tokens in token_store.json on disk in plaintext without any warning, encryption, or permission hardening. Anyone or any local process with filesystem access to that file can reuse the tokens to access and modify the user's Outlook calendar, and the refresh token can prolong that access beyond the current session.
The skill metadata says it syncs reminders to Outlook calendar, but this file also exposes listing, searching, and deletion capabilities over the user's calendar. That expands the granted behavior beyond the described purpose, which can mislead users or higher-level agents into authorizing broader calendar access than expected.
The CLI explicitly advertises a delete command even though the stated purpose is reminder/calendar sync. Undisclosed destructive functionality is dangerous because an orchestrating agent or user may invoke the skill assuming it only creates reminders, while it can also remove existing calendar data.
Deletion is available as a direct command path and executes immediately without any confirmation, dry-run, or warning. In an agent setting, ambiguous parsing, prompt injection, or accidental invocation could permanently remove calendar events with no user verification step.
The module docstring and all user-facing setup instructions are written exclusively in Chinese, and the script later continues with Chinese-only prompts and status messages. This creates a language-policy concern because the skill imposes a specific language on users without any opt-in, fallback, or explanation that the skill is region-specific.
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
"scope": " ".join(SCOPES),
}).encode()
req = urllib.request.Request(AUTH_URL, data=data)
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read().decode())
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
"scope": " ".join(SCOPES),
}).encode()
req = urllib.request.Request(AUTH_URL, data=data)
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read().decode())
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
"scope": " ".join(SCOPES),
}).encode()
req = urllib.request.Request(AUTH_URL, data=data)
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read().decode())
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
}).encode()
req = urllib.request.Request(TOKEN_URL, data=data)
try:
with urllib.request.urlopen(req) as resp:
new_token = json.loads(resp.read().decode())
new_token["_expires_at"] = time.time() + new_token.get("expires_in", 3600)
save_token(new_token)
The manifest description says the skill is used to sync reminders or add items to the calendar, emphasizing direct writing to the user's Outlook calendar. However, the file later documents additional capabilities to query weekly schedules and delete existing events, which go beyond a narrow 'sync reminders' description.
The workflow section describes a single operational flow ending with calling calendar_sync.py add and telling the user the event was created. Elsewhere, the document explicitly states the skill also supports querying calendars and deleting events, so this workflow description contradicts the broader documented behavior rather than merely omitting details.
Natural-language strings in the module docstring and runtime output are written only in Chinese, with no option for the user to choose another language. The policy explicitly calls out language or locale constraints as violations when they are forced without opt-in or justification.
User-facing docstrings, CLI descriptions, help text, and printed output are all hard-coded in Chinese throughout the file. This forces a specific language/locale experience without indicating user choice or documenting a justified region-specific constraint.