Back to skill

Security audit

Word Formula Converter

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly a coherent Word formula converter, but its local web UI exposes and rewrites AI API keys through unauthenticated browser-accessible endpoints.

Install only if you are comfortable reviewing or fixing the local web API first. Prefer the CLI rule-based workflow for sensitive manuscripts. Do not enter real AI API keys into the web UI until raw key readback, wildcard CORS, unauthenticated config writes, and unvalidated base URLs are fixed; rotate any key already used with this web service and update the frontend dependencies before relying on the web UI.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
resources/latex_convert_project/services/api/app/main.py:45
Finding

Unauthenticated Cross-Origin API Key Disclosure

Content
View full analysis
dict: return read_ai_config() ``` ```python def read_ai_config() -> dict: _load_dotenv(overwrite=True) return { "api_key": os.environ.get("OPENAI_API_KEY") or os.environ.get("API_KEY") or "", "base_url": os.environ.get("OPENAI_BASE_URL") or os.environ.get("BASE_URL") or "https://api.openai.com/v1", "model": os.environ.get("OPENAI_MODEL") or os.environ.get("MODEL") or "gpt-4.1-mini", "batch_size": _env_int("AI_BATCH_SIZE", 10), "max_workers": _env_int("AI_MAX_WORKERS", 5), "failure_fallback": os.environ.get("AI_FAILURE_FALLBACK") or "rule", } ``` ### Technical Analysis The local FastAPI service enables wildcard CORS for every origin, method, and request header. It also exposes an unauthenticated configuration endpoint that returns the complete AI API key rather than a redacted value or an indication that a key is configured. Binding the service to `127.0.0.1` does not, by itself, establish an authorization boundary. A malicious website loaded in the user's browser can attempt to access loopback services. Where the browser permits the loopback request, the wildcard `Access-Control-Allow-Origin` response allows the malicious origin to read the API response. No authentication token, origin validation, or user confirmation protects the endpoint. The ...[truncated 1517 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
resources/latex_convert_project/services/api/app/main.py:862
Finding

Unrestricted AI Base URL Enables SSRF and Credential or Manuscript-Data Disclosure

Content
View full analysis
dict[str, str]: endpoint = base_url.rstrip("/") + "/chat/completions" compact = [ { "id": c.id, "text": c.text, "context": c.context, "confidence": c.confidence, "default_action": c.default_action, } for c in candidates ] ``` ```python req = request.Request( endpoint, data=body, headers={ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", }, method="POST", ) payload = _post_ai_request(req, timeout_seconds=timeout_seconds, retries=retries) ``` ```python def _post_ai_request(request_obj: request.Request, *, timeout_seconds: int, retries: int) -> dict[str, Any]: last_exc: Exception | None = None for attempt in range(max(0, retries) + 1): try: with request.urlopen(request_obj, timeout=timeout_seconds, context=_ssl_context()) as resp: return json.loads(resp.read().decode("utf-8")) except (error.HTTPError, error.URLError, TimeoutError) as exc: last_exc = exc if attempt >= retries: break time.sleep(min(2**attempt, 8)) assert last_exc is not None ra ...[truncated 5686 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Note
Location
resources/latex_convert_project/services/api/app/main.py:1013
Finding

AI API Credentials Persisted in a Plaintext Project Environment File

Content
View full analysis
None: env_path = ROOT / ".env" existing: dict[str, str] = {} if env_path.exists(): for raw_line in env_path.read_text(encoding="utf-8").splitlines(): line = raw_line.strip() if not line or line.startswith("#") or "=" not in line: continue key, value = line.split("=", 1) existing[key.strip()] = value.strip().strip('"').strip("'") existing.update( { "API_KEY": config.api_key.strip(), "BASE_URL": config.base_url.strip() or "https://api.openai.com/v1", "MODEL": config.model.strip() or "gpt-4.1-mini", "OPENAI_API_KEY": config.api_key.strip(), "OPENAI_BASE_URL": config.base_url.strip() or "https://api.openai.com/v1", "OPENAI_MODEL": config.model.strip() or "gpt-4.1-mini", "AI_BATCH_SIZE": str(max(1, min(config.batch_size, 50))), "AI_MAX_WORKERS": str(max(1, min(config.max_workers, 10))), "AI_FAILURE_FALLBACK": config.failure_fallback.strip() or "rule", } ) lines = [f"{key}={_quote_env_value(value)}" for key, value in existing.items()] env_path.write_text("\n".join(lines) + "\n", encoding="utf-8") for key, value in existing.items(): os.environ[key] = value ``` ### Technical Analysis The Web UI persists the AI API key directly in a project-level `.env` file. The secret is duplicated under both `API_KEY` and `OPENAI_API_KEY`. The write operation does not explicitly establish owner-only permissions. Actual file permissions depend on the process umask and, for an existing file, its prior mode. On systems with permissive defaults or shared project directories, other ...[truncated 1436 chars]
Remediation
View remediation
Vulnerability Patterns
  • 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
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
Findings (96)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

This is the strongest mismatch: the skill appears to expose a networked web API with broad CORS, persistent storage of uploaded documents and metadata, optional remote AI transmission of document-derived content, and .env secret/config handling, while the description emphasizes local conversion. For a document-processing skill handling potentially sensitive manuscripts, these undeclared behaviors materially increase confidentiality and exposure risk.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

This is the strongest mismatch: the skill appears to expose a networked web API with broad CORS, persistent storage of uploaded documents and metadata, optional remote AI transmission of document-derived content, and .env secret/config handling, while the description emphasizes local conversion. For a document-processing skill handling potentially sensitive manuscripts, these undeclared behaviors materially increase confidentiality and exposure risk.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

This is the strongest mismatch: the skill appears to expose a networked web API with broad CORS, persistent storage of uploaded documents and metadata, optional remote AI transmission of document-derived content, and .env secret/config handling, while the description emphasizes local conversion. For a document-processing skill handling potentially sensitive manuscripts, these undeclared behaviors materially increase confidentiality and exposure risk.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: browserslist==4.28.2 — 2 advisory(ies): CVE-2026-73088 (Browserslist: Uncaught crash / prototype write via untrusted browserslist-stats.); CVE-2026-73089 (Browserslist: Unbounded memory growth (no cache eviction) via distinct query res)

High
Category
Supply Chain
Confidence
92% confidence
Finding

browserslist 4.28.2 is flagged for crashes/prototype write from untrusted browserslist-stats input and unbounded memory growth from distinct query results. The issue appears genuine, but in this project it is part of frontend build tooling, so exploitation generally requires attacker influence over build inputs or configuration rather than ordinary use of the skill.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: nanoid==3.3.12 — 2 advisory(ies): CVE-2026-67214 (nanoid: non-secure generators can loop indefinitely with negative size); CVE-2026-67213 (nanoid: custom generators can loop indefinitely when size is zero)

High
Category
Supply Chain
Confidence
83% confidence
Finding

nanoid 3.3.12 is flagged for infinite-loop conditions in non-secure/custom generators under invalid size inputs. This is a real dependency issue, though likely only reachable if application code or a dependency passes attacker-influenced zero or negative sizes to affected APIs; in this lockfile it is transitive via tooling such as PostCSS.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: postcss==8.5.15 — 2 advisory(ies): CVE-2026-69153 (PostCSS: incomplete fix of GHSA-6g55-p6wh-862q — attacker-controlled sourceMappi); CVE-2026-73646 (PostCSS: Path Traversal in Previous Source Map Auto-Loading (sourceMappingURL) l)

High
Category
Supply Chain
Confidence
93% confidence
Finding

postcss 8.5.15 is flagged for source map related arbitrary file access/path traversal issues. This is meaningful because PostCSS often processes files during builds, and if an attacker can influence CSS or sourceMappingURL content in the build pipeline, local file disclosure could occur; however this remains mostly a build-time exposure in the given project context.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: vite==7.3.3 — 2 advisory(ies): CVE-2026-53571 (vite: `server.fs.deny` bypass on Windows alternate paths); CVE-2026-53632 (launch-editor: NTLMv2 hash disclosure via UNC path handling on Windows)

High
Category
Supply Chain
Confidence
95% confidence
Finding

Vite 7.3.3 is reported vulnerable to Windows path-based server.fs.deny bypass and NTLMv2 hash disclosure through launch-editor UNC path handling. In this project Vite is the main development server, so the issue is credible and more dangerous than other build-only advisories if developers run the server on Windows or expose it beyond localhost.

Content

No source excerpt is available for this finding.

Known Vulnerable Dependency: vite==7.3.3 — 2 advisory(ies): CVE-2026-53571 (vite: `server.fs.deny` bypass on Windows alternate paths); CVE-2026-53632 (launch-editor: NTLMv2 hash disclosure via UNC path handling on Windows)

High
Category
Supply Chain
Confidence
98% confidence
Finding

The manifest allows a Vite version identified as vulnerable to CVE-2026-53571 and CVE-2026-53632. In this skill's context, the web app uses Vite for development/preview on localhost, which somewhat limits exposure, but the Windows-specific flaws can still matter in developer environments by enabling filesystem restriction bypass or NTLM hash disclosure when a vulnerable version is installed and used.

Content

No source excerpt is available for this finding.

Unvalidated Output Injection

High
Category
Output Handling
Confidence
65% confidence
Finding

Model output is used without validation or sanitization. Unvalidated output injected into downstream contexts (SQL, shell, HTML) enables injection attacks and arbitrary code execution.

Content

Scanner excerpt · resources/latex_convert_project/apps/web/src/main.tsx (reported line 768)May include surrounding context.

tsx
return latex;
    }
  }, [latex]);
  return <span className="math-render" dangerouslySetInnerHTML={{ __html: html }} />;
}

function aiResultTitle(candidate: Candidate) {

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · resources/latex_convert_project/latex_convert/cli.py (reported line 322)May include surrounding context.

python
candidates.append(path)
    candidates.extend(
        [
            Path.cwd() / ".env",
            Path(__file__).resolve().parents[1] / ".env",
        ]
    )

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · resources/latex_convert_project/latex_convert/cli.py (reported line 323)May include surrounding context.

python
candidates.append(path)
    candidates.extend(
        [
            Path.cwd() / ".env",
            Path(__file__).resolve().parents[1] / ".env",
        ]
    )

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · resources/latex_convert_project/latex_convert/document_converter.py (reported line 319)May include surrounding context.

python
raise ConversionError("LibreOffice/soffice was not found")
    with tempfile.TemporaryDirectory(prefix="latex-convert-lo-") as tmp:
        tmp_path = Path(tmp)
        env = os.environ.copy()
        env["HOME"] = str(tmp_path / "home")
        env["UserInstallation"] = f"file://{tmp_path / 'profile'}"
        result = subprocess.run(

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · resources/latex_convert_project/latex_convert/document_converter.py (reported line 353)May include surrounding context.

python
raise ConversionError("LibreOffice/soffice was not found")
    with tempfile.TemporaryDirectory(prefix="latex-convert-lo-") as tmp:
        tmp_path = Path(tmp)
        env = os.environ.copy()
        env["HOME"] = str(tmp_path / "home")
        env["UserInstallation"] = f"file://{tmp_path / 'profile'}"
        result = subprocess.run(

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The /api/ai-config endpoint allows callers to overwrite the project-level .env file and process environment, changing API keys, base URLs, model settings, and worker behavior. Because there is no authentication, an attacker can reconfigure the service to use attacker-controlled endpoints, poison future processing, or break other components that rely on the same .env.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
97% confidence
Finding

The code directly reads and rewrites the project .env file to manage API credentials and operational settings from a web-facing route. In practice this is sensitive credential access and modification through application code, expanding the blast radius to any secrets colocated in the same file.

Content

Scanner excerpt · resources/latex_convert_project/services/api/app/main.py (reported line 1014)May include surrounding context.

python
def write_ai_config(config: AiConfig) -> None:
    env_path = ROOT / ".env"
    existing: dict[str, str] = {}
    if env_path.exists():
        for raw_line in env_path.read_text(encoding="utf-8").splitlines():

Credential Access

High
Category
Privilege Escalation
Confidence
86% confidence
Finding

The helper loads secrets from both ROOT/.env and cwd/.env, broadening the set of files from which credentials can be imported into process environment. This increases the chance of unintended secret ingestion, configuration confusion, and abuse if an attacker can influence the working directory or plant a .env file.

Content

Scanner excerpt · resources/latex_convert_project/services/api/app/main.py (reported line 1095)May include surrounding context.

python
def _load_dotenv(*, overwrite: bool = False) -> None:
    for path in [ROOT / ".env", Path.cwd() / ".env"]:
        if not path.exists():
            continue
        for line in path.read_text(encoding="utf-8").splitlines():

Possible Typosquatting: 'uvicorn' resembles popular package 'gunicorn'

High
Category
Supply Chain
Confidence
70% confidence
Finding

Package name closely resembles a popular package, suggesting possible typosquatting. Attackers publish malicious packages with similar names to trick developers into installing them.

Content

No source excerpt is available for this finding.

Possible Typosquatting: 'uvicorn' resembles popular package 'gunicorn'

High
Category
Supply Chain
Confidence
70% confidence
Finding

Package name closely resembles a popular package, suggesting possible typosquatting. Attackers publish malicious packages with similar names to trick developers into installing them.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
86% confidence
Finding

The skill declares capabilities that include shell, file I/O, environment access, networking, and process launching, but it does not constrain them with an explicit tool scope. That creates an over-privileged execution model where an agent may invoke broader actions than the user would reasonably infer from the skill metadata, increasing the chance of unintended file, network, or process activity.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The AI review request transmits the configured API key, model settings, and likely document-derived candidate content to a backend AI-processing workflow, but this code path provides no user-facing warning about external transmission, privacy impact, or credential handling before starting. Although the UI labels the action as "AI 转换", that does not clearly disclose that uploaded document content may be sent to a third-party model service.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The modal states that configuration will be saved to the project root .env, and the saved configuration includes api_key, but there is no warning that sensitive credentials will be stored locally in plaintext-like project configuration. This is a sensitive credential-handling operation and the current text explains persistence but not the security implications.

Content

No source excerpt is available for this finding.

Ae4

Medium
Category
analysis-evasion
Confidence
80% confidence
Finding

Suspicious Unicode normalization or mixed-script content

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The optional AI decision path sends document-derived formula text, surrounding context, confidence values, and default actions to a remote API endpoint specified by base_url. Even if transport uses TLS, this can expose sensitive manuscript content, unpublished research, or proprietary formulas to external services, and the code does not enforce locality, trust boundaries, data minimization, or explicit consent at this layer.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This code constructs and submits an AI request containing candidate formula text and context to a remote chat completions endpoint, but there is no user-facing warning, consent capture, or in-code disclosure mechanism here. In the context of a document-processing skill for academic manuscripts, silent exfiltration of content is especially risky because formulas and nearby text may contain confidential, unpublished, or regulated information.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The manifest centers this skill on local Word/LibreOffice conversion and direct OOXML edits, with AI assistance described only as optional formula classification. This CLI adds explicit remote-provider configuration via API key, base URL, and model selection, enabling outbound API use that is not necessary for the document-conversion task itself and is a context-sensitive capability for a converter tool.

Content

No source excerpt is available for this finding.

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
resources/latex_convert_project/apps/web/src/main.tsx:273