T09 · Insecure Skill Coding Practices
- Location
scripts/main.py:131- Finding
Unrestricted API Endpoint Can Exfiltrate the API Token and Uploaded Documents
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This skill appears intended for cloud PDF-to-Word conversion, but it uploads local files and the SCNet API token to a configurable remote endpoint without strong destination or file-type safeguards.
Install only if you are comfortable sending selected documents to SCNet. Use it only on non-sensitive PDFs, keep the API base set to the official HTTPS SCNet endpoint, protect the API key, and confirm the exact file path before each run.
scripts/main.py:131Unrestricted API Endpoint Can Exfiltrate the API Token and Uploaded Documents
scripts/main.py:276Insufficient Input Validation Allows Arbitrary Readable Files to Be Uploaded
SKILL.md:78Unpinned Dependency Installation Produces a Non-Reproducible Supply Chain
The metadata/description understates critical behavior: the skill claims PDF/image conversion, but the documented invocation only supports PDF_TO_WORD and, more importantly, the top-level description does not clearly disclose that local files are uploaded to a third-party service using an API key from local configuration. This mismatch can cause users or orchestrators to trigger the skill under false assumptions, leading to unintended exfiltration of local document contents and use of stored credentials.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
# 获取技能根目录(脚本所在目录的上一级)
SKILL_ROOT = Path(__file__).parent.parent.absolute()
ENV_FILE = SKILL_ROOT / "config" / ".env"
# 重试配置
MAX_RETRIES = 3
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_config():
"""从 .env 文件加载配置,若文件不存在则抛出友好错误"""
config_from_env = {}
for key in ["SCNET_API_KEY", "SCNET_API_BASE", "SCNET_POLL_INTERVAL", "SCNET_MAX_POLL_TIME"]:
value = os.environ.get(key)
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_config():
"""从 .env 文件加载配置,若文件不存在则抛出友好错误"""
config_from_env = {}
for key in ["SCNET_API_KEY", "SCNET_API_BASE", "SCNET_POLL_INTERVAL", "SCNET_MAX_POLL_TIME"]:
value = os.environ.get(key)
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def load_config():
"""从 .env 文件加载配置,若文件不存在则抛出友好错误"""
config_from_env = {}
for key in ["SCNET_API_KEY", "SCNET_API_BASE", "SCNET_POLL_INTERVAL", "SCNET_MAX_POLL_TIME"]:
value = os.environ.get(key)
This markdown file appears to be a generic GitLab README template rather than a skill description, and it never defines when or how the skill should be invoked. For a skill-facing README, the absence of explicit trigger phrases, scope constraints, or exclusion conditions leaves activation behavior ambiguous.
As a markdown file describing a skill, this README provides no actual description of what the 'pdf_to_word' skill does, including whether it uploads files, writes output files, or handles sensitive document contents. Because document-conversion workflows can affect user data and privacy, the lack of any warning or disclosure is a notable omission.
Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.
* [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/)
* [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
* [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
* [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
* [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
The skill declares capabilities that involve reading local files, accessing environment-based credentials, invoking shell/Python, and sending data over the network, but it does not define any explicit tool scope or permission boundaries. In an agent environment, this weakens reviewability and can allow the skill to be invoked with broader powers than users or operators expect, especially since it uploads local documents to an external service.
This skill is designed to transmit user-specified local files to https://api.scnet.cn/api/llm/v1 for processing. External transmission is expected for the feature, but it remains a real security risk because uploaded documents may contain sensitive data and leave the local trust boundary.
SCNET_API_KEY=your_scnet_api_key_here
# API 基础地址(一般无需修改)
SCNET_API_BASE=https://api.scnet.cn/api/llm/v1
# 轮询配置(可选)
SCNET_POLL_INTERVAL=5
The skill says the AI may auto-trigger based on broad description keywords for requests like '帮我把这份 PDF 转成 Word', without defining strict activation boundaries or mandatory structured consent checks. In a tool-using agent, broad matching increases the chance of accidental invocation on user messages that mention files or conversion, causing unintended local file upload to an external service.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
SCNET_API_KEY=your_scnet_api_key_here
# API 基础地址(一般无需修改)
SCNET_API_BASE=https://api.scnet.cn/api/llm/v1
# 轮询配置(可选)
SCNET_POLL_INTERVAL=5
The documentation instructs users to submit local files or file URLs to a third-party remote conversion service, but it does not disclose that document contents will leave the local environment and be processed externally. This creates a real privacy and data-handling risk, especially for sensitive PDFs, because users may unknowingly transmit confidential material to an external provider.
The file_url parameter accepts publicly accessible document links, but the documentation does not warn that using public URLs can expose documents to unintended parties and to the service operator. Users may assume URL submission is safer than upload when in fact it can widen access and leak sensitive files if links are guessable, long-lived, or broadly shared.
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
" b) 配置文件:\n"
f" mkdir -p {SKILL_ROOT}/config\n"
f" echo 'SCNET_API_KEY=你的密钥' > {ENV_FILE}\n"
f" chmod 600 {ENV_FILE}\n"
"\n配置完成后重新运行。"
)
sys.exit(error_msg)
Commands invoke sudo or root privileges. Verify this elevated access is necessary and justified.
" b) 配置文件:\n"
f" mkdir -p {SKILL_ROOT}/config\n"
f" echo 'SCNET_API_KEY=你的密钥' > {ENV_FILE}\n"
f" chmod 600 {ENV_FILE}\n"
"\n配置完成后重新运行。"
)
sys.exit(error_msg)
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
)
sys.exit(error_msg)
config.setdefault('SCNET_API_BASE', 'https://api.scnet.cn/api/llm/v1')
try:
config.setdefault('SCNET_POLL_INTERVAL', str(DEFAULT_POLL_INTERVAL))
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
)
sys.exit(error_msg)
config.setdefault('SCNET_API_BASE', 'https://api.scnet.cn/api/llm/v1')
try:
config.setdefault('SCNET_POLL_INTERVAL', str(DEFAULT_POLL_INTERVAL))
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
)
sys.exit(error_msg)
config.setdefault('SCNET_API_BASE', 'https://api.scnet.cn/api/llm/v1')
try:
config.setdefault('SCNET_POLL_INTERVAL', str(DEFAULT_POLL_INTERVAL))
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
)
sys.exit(error_msg)
config.setdefault('SCNET_API_BASE', 'https://api.scnet.cn/api/llm/v1')
try:
config.setdefault('SCNET_POLL_INTERVAL', str(DEFAULT_POLL_INTERVAL))
The skill behavior does not match a typical local 'PDF 转 Word' expectation: it uploads the user-supplied file to a third-party SCNet API and returns remote task metadata/results instead of producing a local converted document. This is dangerous because users may disclose sensitive local documents under the assumption of local-only processing, creating a transparency and data-exfiltration risk even though the script does print a warning before upload.
This code sends task identifiers to an external SCNet service over the network. In context, the broader skill already uploads local files to the same service, so the external transmission is intentional but still security-relevant because document content and associated metadata leave the local trust boundary.
payload = {"task_ids": task_ids}
try:
response = requests.post(url, headers=headers, json=payload, timeout=60)
except Exception as e:
sys.exit(f"网络请求失败: {str(e)}")
The manifest description is written entirely in Chinese and presents the skill behavior only in that locale, with no indication that users can choose another language or that the skill is intended solely for a Chinese-language audience. Under the policy, forcing a specific language without user opt-in is a natural-language locale violation.
The entire skill documentation is presented only in Chinese, with no indication that users may choose another language or that the skill is intentionally limited to a Chinese-speaking or region-specific audience. Under the stated policy, forcing a specific language without opt-in is a natural-language policy concern.
No suspicious patterns detected.