T09 · Insecure Skill Coding Practices
- Location
auth.py:24- Finding
OAuth Access and Refresh Tokens Exposed Through Standard Output
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This MyAnimeList skill is mostly purpose-aligned, but it handles powerful OAuth tokens and account-changing actions in ways users should review before installing.
Install only if you are comfortable granting this skill MyAnimeList OAuth access that can read and modify your MAL data. Prefer storing secrets in the OpenClaw vault, avoid shell profiles or project .env files, do not run token commands where output is logged, and manually confirm anime IDs before using update or delete commands.
auth.py:24OAuth Access and Refresh Tokens Exposed Through Standard Output
requirements.txt:1Unpinned Runtime Dependencies Create a Non-Reproducible Supply-Chain Boundary
This skill performs authenticated requests to a third-party service and includes state-changing operations like PATCH and DELETE against the user's MyAnimeList account. In a skill with no documented trust boundary, authorization model, or user-consent flow, this creates account integrity risk because invoking the skill can modify or remove remote user data.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
token_data = response.json()
print(f"ACCESS_TOKEN={token_data['access_token']}")
print(f"REFRESH_TOKEN={token_data['refresh_token']}")
logger.info("Access token refreshed successfully.")
return True
else:
logger.error(f"Failed to refresh token: {response.text}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Ensure we are in the skill directory
os.chdir(os.path.dirname(os.path.abspath(__file__)))
load_dotenv(dotenv_path=".env")
# Validate required env variables
REQUIRED_ENV = ["ACCESS_TOKEN"]
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# Ensure we are in the skill directory
os.chdir(os.path.dirname(os.path.abspath(__file__)))
load_dotenv(dotenv_path=".env")
# Validate required env variables
REQUIRED_ENV = ["ACCESS_TOKEN"]
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
]
},
"env": {
"ACCESS_TOKEN": { "description": "OAuth2 access token for MAL API" },
"REFRESH_TOKEN": { "description": "OAuth2 refresh token for MAL API" },
"CLIENT_ID": { "description": "MAL Application Client ID" },
"CLIENT_SECRET": { "description": "MAL Application Client Secret" }
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
]
},
"env": {
"ACCESS_TOKEN": { "description": "OAuth2 access token for MAL API" },
"REFRESH_TOKEN": { "description": "OAuth2 refresh token for MAL API" },
"CLIENT_ID": { "description": "MAL Application Client ID" },
"CLIENT_SECRET": { "description": "MAL Application Client Secret" }
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
]
},
"env": {
"ACCESS_TOKEN": { "description": "OAuth2 access token for MAL API" },
"REFRESH_TOKEN": { "description": "OAuth2 refresh token for MAL API" },
"CLIENT_ID": { "description": "MAL Application Client ID" },
"CLIENT_SECRET": { "description": "MAL Application Client Secret" }
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
{
"openapi": "3.0.0",
"info": {
"description": "MyAnimeList.net is the property of MyAnimeList Co., Ltd. All Rights Reserved.\n\n## Versioning\n\nThere are multiple versions of the API.\nYou can specify a version by including it in the request uri:\n\n(During closed beta, version starts with '0.')\n\n`https://api.myanimelist.net/v2`\n\nThe version is incremented if any backwards incompatible changes are introduced to the API.\n\nExamples of backwards incompatible changes are belows:\n\n- Removing existing endpoints.\n- Removing existing fields of API responses.\n- Changing mean of the existing fields.\n\nOn the other hand, belows are examples of backwards compatible changes:\n\n- Adding new endpoints.\n- Adding new optional request parameters.\n- Adding new fields to existing API responses.\n- Changing the order of fields in existing API responses.\n- Changing the contents of fields that suffixed by `_formatted`.\n\n\n## Common formats\n\n### List / Pagination\n\n```json\n{\n \"data\": [\n {...},\n {...},\n ...\n ],\n \"paging\": {\n \"previous\": \"https://xxx\",\n \"next\": \"https://xxx\"\n }\n}\n```\n \n### Date / Time format\n\n<table>\n<tr>\n<th>Format\n<th>Type\n<th>Sample\n<th>Description\n</tr>\n\n<tr>\n<td>date-time\n<td>string\n<td>\"2015-03-02T06:03:11+00:00\" \n<td>ISO 8601\n</tr>\n \n<tr>\n<td>date\n<td>string\n<td>\"2017-10-23\" or <br>\"2017-10\" or <br>\"2017\"\n<td>\n</tr>\n\n<tr>\n<td>time\n<td>string\n<td>\"01:35\"\n<td>\n</tr>\n\n</table>\n\n### Error format\n\n\n```json\n{\n \"error\": \"invalid_token\"\n \"message\": \"token is invalid\",\n}\n```\n\n## Common parameters\n\n### Parameters for endpoints that return a list\n\n| Name | Description |\n| --- | --- |\n| limit | |\n| offset | |\n\n### Choosing fields\n\nBy default, the API doesn\u2019t return all fields.\n\nYou can choose the fields that you want returned with the `fields` parameter.\n\nExample:\n\n```\nfields=synopsis,my_list_status{priority,comme
...[truncated 25 chars]
The setup instructions direct users to place long-lived OAuth credentials and secrets in environment variables or a vault, but provide no warning about secure storage, least-privilege handling, shell history exposure, or avoiding accidental logging and commits. In a skill that automates API access and token refresh, mishandling these values could let an attacker take over the user's MAL account or abuse the integration.
The CLI reference documents a deletion command that removes anime from a personal list without any caution that it is destructive or whether the underlying tool prompts for confirmation. In an automation-oriented skill, users may invoke or script this command incorrectly, causing unintended data loss in their MAL library.
With no manifest or declared purpose, this skill's code introduces a nontrivial capability to read local secret material from a .env file and process API credentials. That may be necessary for a MyAnimeList client, but in the absence of any stated scope it is an unjustified capability under this audit's criteria.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
CLIENT_ID = os.getenv("CLIENT_ID")
CLIENT_SECRET = os.getenv("CLIENT_SECRET")
BASE_URL = "https://api.myanimelist.net/v2"
class MALClient:
def __init__(self, token):
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
CLIENT_ID = os.getenv("CLIENT_ID")
CLIENT_SECRET = os.getenv("CLIENT_SECRET")
BASE_URL = "https://api.myanimelist.net/v2"
class MALClient:
def __init__(self, token):
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
CLIENT_ID = os.getenv("CLIENT_ID")
CLIENT_SECRET = os.getenv("CLIENT_SECRET")
BASE_URL = "https://api.myanimelist.net/v2"
class MALClient:
def __init__(self, token):
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
CLIENT_ID = os.getenv("CLIENT_ID")
CLIENT_SECRET = os.getenv("CLIENT_SECRET")
BASE_URL = "https://api.myanimelist.net/v2"
class MALClient:
def __init__(self, token):
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
CLIENT_ID = os.getenv("CLIENT_ID")
CLIENT_SECRET = os.getenv("CLIENT_SECRET")
BASE_URL = "https://api.myanimelist.net/v2"
class MALClient:
def __init__(self, token):
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
CLIENT_ID = os.getenv("CLIENT_ID")
CLIENT_SECRET = os.getenv("CLIENT_SECRET")
BASE_URL = "https://api.myanimelist.net/v2"
class MALClient:
def __init__(self, token):
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
CLIENT_ID = os.getenv("CLIENT_ID")
CLIENT_SECRET = os.getenv("CLIENT_SECRET")
BASE_URL = "https://api.myanimelist.net/v2"
class MALClient:
def __init__(self, token):
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
CLIENT_ID = os.getenv("CLIENT_ID")
CLIENT_SECRET = os.getenv("CLIENT_SECRET")
BASE_URL = "https://api.myanimelist.net/v2"
class MALClient:
def __init__(self, token):
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
CLIENT_ID = os.getenv("CLIENT_ID")
CLIENT_SECRET = os.getenv("CLIENT_SECRET")
BASE_URL = "https://api.myanimelist.net/v2"
class MALClient:
def __init__(self, token):
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
CLIENT_ID = os.getenv("CLIENT_ID")
CLIENT_SECRET = os.getenv("CLIENT_SECRET")
BASE_URL = "https://api.myanimelist.net/v2"
class MALClient:
def __init__(self, token):
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
CLIENT_ID = os.getenv("CLIENT_ID")
CLIENT_SECRET = os.getenv("CLIENT_SECRET")
BASE_URL = "https://api.myanimelist.net/v2"
class MALClient:
def __init__(self, token):
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
CLIENT_ID = os.getenv("CLIENT_ID")
CLIENT_SECRET = os.getenv("CLIENT_SECRET")
BASE_URL = "https://api.myanimelist.net/v2"
class MALClient:
def __init__(self, token):
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
CLIENT_ID = os.getenv("CLIENT_ID")
CLIENT_SECRET = os.getenv("CLIENT_SECRET")
BASE_URL = "https://api.myanimelist.net/v2"
class MALClient:
def __init__(self, token):
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
CLIENT_ID = os.getenv("CLIENT_ID")
CLIENT_SECRET = os.getenv("CLIENT_SECRET")
BASE_URL = "https://api.myanimelist.net/v2"
class MALClient:
def __init__(self, token):
No suspicious patterns detected.