T09 · Insecure Skill Coding Practices
- Location
config/dingtalk.yaml:5- Finding
Hardcoded DingTalk application secret and webhook access token
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill performs the advertised rank-monitoring workflow, but it ships real DingTalk credentials/default notification targets and uses plaintext login/session storage plus persistent browser automation.
Do not install this version as-is. Require the publisher to remove and rotate the committed DingTalk secrets, ship only example config files, store Diandian credentials/cookies in a protected secret store, avoid long-lived CDP browser sessions, and make any scheduled execution clearly opt-in with uninstall steps.
config/dingtalk.yaml:5Hardcoded DingTalk application secret and webhook access token
tools/diandian_auto_login.py:23Account passwords and reusable session cookies are persisted in plaintext
tools/keep_browser_alive.sh:4Long-lived authenticated browser exposes an unauthenticated Chrome DevTools control interface
docs/定时任务配置说明.md:22Documented launchd persistence runs a credentialed data-export workflow across sessions
requirements.txt:1Dependencies use open-ended minimum versions without a reproducible lock file
The README instructs users to run rm -rf /var/folders/.../playwright-artifacts-* as a cleanup step. Although the intent is troubleshooting, forceful recursive deletion commands are risky in agent or copy-paste workflows because path mistakes, shell expansion, or environmental differences can remove unintended files.
rm -rf /var/folders/hv/ky0s2_b96k1f69x2z0my_8nc0000gn/T/playwright-artifacts-*
The README instructs users to run rm -rf /var/folders/.../playwright-artifacts-* as a cleanup step. Although the intent is troubleshooting, forceful recursive deletion commands are risky in agent or copy-paste workflows because path mistakes, shell expansion, or environmental differences can remove unintended files.
rm -rf /var/folders/hv/ky0s2_b96k1f69x2z0my_8nc0000gn/T/playwright-artifacts-*
Referencing automatic login from config/credentials.yaml indicates the skill relies on stored reusable credentials for unattended access. In a task automation skill, this is more dangerous because the credentials are likely long-lived and repeatedly accessed by background jobs, making compromise of the workspace or logs materially more impactful.
**现象**: 获取数据失败,提示未登录
**解决**: 脚本已集成自动登录功能,会自动使用 `config/credentials.yaml` 中的账号密码重新登录
#### 3. 浏览器未运行
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.
if cwd is None:
cwd = SRC_DIR
env = {**os.environ, "PYTHONPATH": str(SRC_DIR)}
try:
result = subprocess.run(
This code path persists credentials to credentials.yaml, enabling long-term local storage of reusable secrets. In the context of an auto-login skill, the presence of a save_credentials helper increases risk because it normalizes secret persistence and may be invoked by future code or operators without safeguards.
def save_credentials(username, password):
"""保存账号密码"""
config_path = CONFIG_DIR / "credentials.yaml"
config = {}
if config_path.exists():
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_credentials() -> dict:
"""加载账号凭证"""
cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
if not cred_path.exists():
raise FileNotFoundError(f"凭证文件不存在:{cred_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_credentials() -> dict:
"""加载账号凭证"""
cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
if not cred_path.exists():
raise FileNotFoundError(f"凭证文件不存在:{cred_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_credentials() -> dict:
"""加载账号凭证"""
cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
if not cred_path.exists():
raise FileNotFoundError(f"凭证文件不存在:{cred_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_credentials() -> dict:
"""加载账号凭证"""
cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
if not cred_path.exists():
raise FileNotFoundError(f"凭证文件不存在:{cred_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_credentials() -> dict:
"""加载账号凭证"""
cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
if not cred_path.exists():
raise FileNotFoundError(f"凭证文件不存在:{cred_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_credentials() -> dict:
"""加载账号凭证"""
cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
if not cred_path.exists():
raise FileNotFoundError(f"凭证文件不存在:{cred_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_credentials() -> dict:
"""加载账号凭证"""
cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
if not cred_path.exists():
raise FileNotFoundError(f"凭证文件不存在:{cred_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_credentials() -> dict:
"""加载账号凭证"""
cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
if not cred_path.exists():
raise FileNotFoundError(f"凭证文件不存在:{cred_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_credentials() -> dict:
"""加载账号凭证"""
cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
if not cred_path.exists():
raise FileNotFoundError(f"凭证文件不存在:{cred_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_credentials() -> dict:
"""加载账号凭证"""
cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
if not cred_path.exists():
raise FileNotFoundError(f"凭证文件不存在:{cred_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_credentials() -> dict:
"""加载账号凭证"""
cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
if not cred_path.exists():
raise FileNotFoundError(f"凭证文件不存在:{cred_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_credentials() -> dict:
"""加载账号凭证"""
cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
if not cred_path.exists():
raise FileNotFoundError(f"凭证文件不存在:{cred_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_credentials() -> dict:
"""加载账号凭证"""
cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
if not cred_path.exists():
raise FileNotFoundError(f"凭证文件不存在:{cred_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_credentials() -> dict:
"""加载账号凭证"""
cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
if not cred_path.exists():
raise FileNotFoundError(f"凭证文件不存在:{cred_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_credentials() -> dict:
"""加载账号凭证"""
cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
if not cred_path.exists():
raise FileNotFoundError(f"凭证文件不存在:{cred_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_credentials() -> dict:
"""加载账号凭证"""
cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
if not cred_path.exists():
raise FileNotFoundError(f"凭证文件不存在:{cred_path}")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_credentials() -> dict:
"""加载账号凭证"""
cred_path = Path(__file__).parent.parent / "config" / "credentials.yaml"
if not cred_path.exists():
raise FileNotFoundError(f"凭证文件不存在:{cred_path}")
The code extracts the full browser cookie string and writes it to a YAML configuration file, effectively persisting an authenticated session token. If that file is read by another process, committed to source control, or accessed by an attacker, the token could be replayed to impersonate the user and access the account.
This code accesses credentials from a local configuration file for automated authentication. In a skill environment, credential harvesting and use are especially sensitive because the skill may be run with access to secrets the user did not intend to expose to automation, and those credentials enable account access on an external service.
print(" ❌ 检测到未登录状态,开始自动登录...")
# 加载账号密码
credentials_path = CONFIG_DIR / "credentials.yaml"
if credentials_path.exists():
with open(credentials_path, 'r', encoding='utf-8') as f:
credentials = yaml.safe_load(f)
Referencing a local credentials.yaml file for account secrets is a credential-access pattern that enables the skill to retrieve and use stored authentication material. In this context, the danger is amplified because the credentials are immediately used to access a real external service, creating risk of unauthorized account use if the skill runs unexpectedly or is repurposed.
REPORTS_DIR.mkdir(exist_ok=True)
def load_credentials():
config_path = CONFIG_DIR / "credentials.yaml"
if config_path.exists():
with open(config_path, 'r', encoding='utf-8') as f:
config = yaml.safe_load(f)
No suspicious patterns detected.