Back to skill

Security audit

Ms Todo Oauth

Security checks for vulnerabilities and agentic risk

Overview

The skill appears to manage Microsoft To Do as advertised, but it ships a reusable OAuth client secret and persists sensitive login tokens without permission hardening.

Review before installing. Use your own Azure app registration, do not rely on the embedded fallback secret, avoid passing client secrets on the command line, restrict or remove the local token cache when done, treat exported task JSON as private data, and confirm any delete-list or delete-task action before allowing an agent to run it.

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 (4)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/ms-todo-oauth.py:36
Finding
Published OAuth Client Secret Used as an Automatic Fallback<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ms-todo-oauth.py:36-38` **Vulnerability Type**: Hardcoded OAuth application credentials **Risk Level**: High ### Vulnerable Code ```python # Built-in fallback OAuth app credentials. Override with CLI options or env vars. DEFAULT_CLIENT_ID = "ca6ec244-002c-435b-bafd-06e470d37edc" DEFAULT_CLIENT_SECRET = "TwQ8Q~mHv6C_scYqI7PC2dZKWFeM931.8AczhasR" ``` ### Technical Analysis The source code contains an OAuth confidential-client secret and automatically uses it when the caller does not provide another value. Because the project distributes this source, the secret must be considered publicly compromised. Documentation acknowledging that the secret is public does not make its continued use secure. An OAuth client secret authenticates the registered application to Microsoft Entra ID. Anyone with access to this project can copy the client ID and secret and attempt to impersonate the application. Actual access to user data would still depend on successfully completing an applicable OAuth flow and on the registered application's tenant, redirect URI, and permission configuration; possession of the secret alone does not directly grant access to every user account. The embedded credential is not required for the Skill's minimum functionality. Users can register their own application, and an interactive CLI can use a public-client authorization flow with PKCE or device authorization rather than distributing a confidential-client secret. ### Attack Path 1. An attacker downloads or otherwise obtains the project. 2. The attacker extracts the embedded client ID and client secret. 3. The attacker submits the credentials to Microsoft Entra endpoints while impersonating the registered application. 4. Depending on the application's registration and redirect configuration, the attacker abuses supported OAuth flows, application quotas, or consent associated with that application. 5. If the attacker can also obtain ...[truncated 641 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Revoke the exposed client secret immediately in Microsoft Entra ID and review the application's sign-in and audit logs. 2. Remove the secret from the source tree, documentation, tests, release artifacts, and version-control history. 3. Do not provide another shared fallback secret. 4. For an installed CLI, use a public-client OAuth flow with PKCE or device authorization where supported, avoiding the need to distribute a confidential-client credential. 5. If a confidential-client flow is strictly required, require each operator to supply credentials through an approved secret manager or protected environment injection. 6. Fail closed with a clear setup error when required credentials are absent. 7. Add automated secret scanning and pre-commit checks to prevent future credential commits. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/ms-todo-oauth.py:64
Finding
OAuth Token Cache Is Persisted Without Enforced Owner-Only Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ms-todo-oauth.py:64-80` **Vulnerability Type**: Insecure plaintext storage of OAuth token material **Risk Level**: High ### Vulnerable Code ```python # Set cache file path if cache_file is None: cache_file = os.path.join(Path.home(), ".mstodo_token_cache.json") self.cache_file = cache_file # Initialize token cache self.cache = msal.SerializableTokenCache() if os.path.exists(self.cache_file): with open(self.cache_file, "r") as f: self.cache.deserialize(f.read()) # Register cache saving on exit atexit.register(self._save_cache) def _save_cache(self): """Save token cache to file""" if self.cache.has_state_changed: with open(self.cache_file, "w") as f: f.write(self.cache.serialize()) ``` ### Technical Analysis The serialized MSAL cache is stored in `~/.mstodo_token_cache.json` using ordinary file operations. The implementation does not explicitly create the file with owner-only permissions, verify the permissions of an existing cache, reject symbolic links, or use an operating-system credential vault. The final permissions therefore depend on the user's umask and on any pre-existing file at that path. In an environment with a permissive umask, shared home directory, compromised local process, or attacker-precreated path, token material may become readable or replaceable by another principal. MSAL token caches can contain access tokens, refresh tokens, account metadata, and related authentication state. A stolen refresh token may remain useful beyond the lifetime of a single access token, subject to Microsoft revocation and conditional-access controls. ### Attack Path 1. The victim authenticates through the Skill. 2. MSAL changes the cache state, causing `_save_cache()` to serialize it to `~/.mstodo_token_cache.json`. 3. The file is created with permissions inherited from the process umask, or an attacker has already influenced the file or path. 4. ...[truncated 805 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer an operating-system credential store, such as Windows Credential Manager, macOS Keychain, or Secret Service, and encrypt persistent authentication state where practical. 2. On POSIX systems, create the cache atomically with mode `0600`, for example using `os.open()` with `O_CREAT | O_EXCL | O_WRONLY` and explicit permissions. 3. Enforce owner-only permissions on existing cache files before reading them; reject files owned by another user or with group/world access. 4. Reject symbolic links and validate that the resolved cache path is a regular file owned by the current user. 5. Write updates through a securely created temporary file in the same directory, flush and synchronize it, then atomically replace the cache. 6. Apply restrictive permissions to the parent directory where possible. 7. On logout, clear in-memory authentication state and securely remove or invalidate cached credentials. 8. Document the sensitivity of both the cache and exported task data. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/ms-todo-oauth.py:1267
Finding
OAuth Client Secret Can Be Supplied Through Process Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/ms-todo-oauth.py:1267-1268` **Vulnerability Type**: Sensitive credential exposure through command-line arguments **Risk Level**: Medium ### Vulnerable Code ```python parser.add_argument("--client-id", help="Azure app client ID. Overrides MS_TODO_CLIENT_ID and the built-in fallback") parser.add_argument("--client-secret", help="Azure app client secret. Overrides MS_TODO_CLIENT_SECRET and the built-in fallback") ``` The documentation actively recommends this usage pattern: ```text python scripts/ms-todo-oauth.py --client-id "<id>" --client-secret "<secret>" lists ``` ### Technical Analysis Command-line arguments are not an appropriate channel for long-lived secrets. Depending on the operating system and execution environment, command arguments may be observable through process inspection, shell history, terminal recording, job-control metadata, CI/CD logs, agent tool transcripts, telemetry, or crash diagnostics. The parser does not mask the argument, warn the user at execution time, or erase it from surrounding logging systems. Although the code does not intentionally print the secret, exposure can occur before the application processes the argument. ### Attack Path 1. A user follows the documented example and places the client secret after `--client-secret`. 2. The shell records the full command in command history, or an orchestration/agent platform records it in execution logs. 3. Alternatively, a local user inspects process arguments while the command is running. 4. An attacker with access to that history, log, transcript, or process metadata retrieves the secret. 5. The attacker uses the credential to impersonate the registered OAuth application, subject to the application's Entra configuration. ### Impact Assessment The exposed value compromises the OAuth application credential rather than directly authenticating as a Microsoft user. Potential scope includes application impersonation, ...[truncated 247 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove the `--client-secret` command-line option. 2. Accept secrets from a protected secret manager, a restricted configuration file, or controlled environment injection. 3. If interactive entry is necessary, use a no-echo prompt such as `getpass.getpass()`. 4. Ensure diagnostic output reports only whether a credential source was selected, never the credential value. 5. Update all documentation and examples to stop recommending command-line secrets. 6. Warn users to remove any existing secret-bearing commands from shell history and retained automation logs. 7. Rotate credentials that may already have appeared in histories, transcripts, or CI/CD logs. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:6
Finding
Dependencies Are Installed from Unbounded Version Ranges Without Integrity Pinning<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt:6-9` **Vulnerability Type**: Non-reproducible dependency resolution without hash verification **Risk Level**: Medium ### Vulnerable Code ```text # Microsoft Authentication Library (MSAL) - for Azure AD authentication msal>=1.34.0 # HTTP library for API requests requests>=2.32.5 ``` The documented installation command is: ```bash python -m pip install -r requirements.txt ``` ### Technical Analysis The manifest defines only minimum versions. Any future release satisfying these ranges can be selected at installation time, including versions that were never reviewed with this project. No lock file or package hashes are present, and installation is recommended in the active Python environment. The package names are legitimate and no dependency-confusion or typosquatting package was identified. The risk arises from non-reproducible resolution and lack of artifact integrity enforcement rather than from a presently confirmed malicious dependency. Because these libraries participate directly in OAuth processing and HTTP communication, an unsafe or compromised future release would execute in the CLI process and could access client credentials, authorization codes, cached tokens, task data, and network requests. ### Attack Path 1. A dependency publisher account, package index, distribution artifact, or future compatible release is compromised or becomes unsafe. 2. A user runs the documented `pip install -r requirements.txt` command at a later date. 3. Pip resolves a newer release permitted by the `>=` constraint. 4. Package installation or imported runtime code executes in the user's environment. 5. Malicious or compromised code accesses the same process privileges and sensitive OAuth/task data available to the Skill. This path depends on an upstream supply-chain compromise or unsafe future release; the audit did not find evidence that the currently named packages are malicious. ### Impa ...[truncated 428 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin all direct and transitive dependencies to reviewed exact versions using a lock file. 2. Generate and verify cryptographic hashes, such as with pip's `--require-hashes` workflow. 3. Rebuild the lock file through a controlled review process when dependencies are upgraded. 4. Install dependencies in a dedicated virtual environment rather than the global or active shared environment. 5. Configure an approved package index and disable unintended fallback indexes where organizational policy requires it. 6. Add automated dependency vulnerability and provenance scanning. 7. Review release notes and authentication/network behavior before accepting new MSAL or Requests versions. 8. Periodically update pinned versions after security review so reproducibility does not prevent timely security patching. ]]>
Vulnerability Patterns
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (113)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The README admits built-in fallback Azure app credentials and a committed client secret in the script, which is materially more sensitive than a normal task-management CLI. Shipping embedded OAuth confidential-client credentials enables anyone with the skill to reuse the application identity and can facilitate unauthorized Graph access attempts, tenant abuse, or secret leakage and replay.

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
3. Built-in fallback values in `scripts/ms-todo-oauth.py`
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Static analysis

Detected: suspicious.dynamic_code_execution, suspicious.exposed_secret_literal

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
scripts/test_ms_todo_oauth.py:43

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/ms-todo-oauth.py:38

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/test_ms_todo_oauth.py:123