Back to skill

Security audit

Klientenportal

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent portal automation tool, but it handles tax-portal credentials and documents with under-scoped safeguards that users should review before installing.

Install only if you trust the machine and workspace. Prefer short-lived environment variables or a secret manager over plaintext config.json, keep any config file out of version control, do not add a custom portal_url, review files before upload, store downloads in a private directory, and run logout after use to clear the browser session.

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/klientenportal.py:156
Finding

Unvalidated Portal URL Can Redirect Credentials to an Attacker-Controlled Origin

Content
View full analysis
dict: """Load config from config.json or env vars. Env vars (override config.json values): KLIENTENPORTAL_PORTAL_ID, KLIENTENPORTAL_USER_ID, KLIENTENPORTAL_PASSWORD """ cfg: dict = {} if CONFIG_FILE.exists(): cfg = json.loads(CONFIG_FILE.read_text(encoding="utf-8")) _harden_path(CONFIG_FILE) # Env vars override / supplement config.json portal_id = os.environ.get("KLIENTENPORTAL_PORTAL_ID") or cfg.get("portal_id", "") user_id = os.environ.get("KLIENTENPORTAL_USER_ID") or cfg.get("user_id", "") password = os.environ.get("KLIENTENPORTAL_PASSWORD") or cfg.get("password", "") if not portal_id or not user_id or not password: missing = [n for n, v in [("portal_id", portal_id), ("user_id", user_id), ("password", password)] if not v] print(f"ERROR: Missing config: {', '.join(missing)}") print(f"Create {CONFIG_FILE} or set env vars. See SKILL.md for details.") sys.exit(1) cfg["portal_id"] = portal_id cfg["portal_url"] = cfg.get("portal_url") or f"https://klientenportal.at/prod/{portal_id}" cfg["user_id"] = user_id cfg["password"] = password return cfg ``` The resulting URL is used as the authentication origin: ```python def login(page, config: dict) -> bool: """Log in to the portal.""" portal_url = config["portal_url"] # Already logged in? if page.locator("text=Abmelden").count() > 0: print("[login] Session still valid", flush=True) dismiss_modals(page) return True page.goto(f"{portal_url}/account/login", wait_until="networkidle") time.sleep(1) if page.locator("text=Abmelden").count() > 0: print("[login] Alr ...[truncated 2405 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
SETUP.md:19
Finding

Portal Password Is Stored in a Plaintext Configuration File

Content
View full analysis
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
SETUP.md:8
Finding

Installation Instructions Use Unpinned Third-Party Dependencies

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/klientenportal.py:776
Finding

Downloaded ZIP Extraction Lacks Resource Limits and Safe Destination Creation

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (6)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The setup instructions explicitly direct users to place a portal password in a plaintext config.json file, which increases the chance of credential exposure through source control, backups, shared workspaces, logs, or overly broad file permissions. In this skill's context, the credentials grant access to a tax/accounting document portal, so compromise could expose sensitive financial records and enable unauthorized document access or submission.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
86% confidence
Finding

The skill advertises capabilities that use environment variables, file access, and shell execution, but it does not declare an explicit tool scope such as permissions or allowed-tools. In an agent setting, this weakens least-privilege controls and makes it harder for users or the platform to understand and constrain what the skill can access, especially since it handles portal credentials and local document upload/download operations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill description clearly indicates handling of portal credentials and financial documents, but it does not prominently warn users that sensitive accounting data will be accessed, uploaded, downloaded, and potentially stored locally or in browser session state. This increases the risk of accidental exposure, misuse on shared systems, or users invoking the skill without understanding the sensitivity of the data flow.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The section header at L139 says 'no .env, config.json only', but _load_config explicitly documents and implements reading KLIENTENPORTAL_PORTAL_ID, KLIENTENPORTAL_USER_ID, and KLIENTENPORTAL_PASSWORD from environment variables at L153-L164. This is an active documentation contradiction about how credentials are sourced.

Content

No source excerpt is available for this finding.

Tainted flow: 'extracted' from os.environ.get (line 782, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · scripts/klientenportal.py (reported line 783)May include surrounding context.

python
names = zf.namelist()
                    if len(names) == 1:
                        extracted = out_dir / _safe_filename(names[0])
                        with zf.open(names[0]) as src, open(extracted, "wb") as dst:
                            dst.write(src.read())
                        print(f"[received-download] Extracted: {extracted}", flush=True)
                        dest.unlink()  # Remove zip wrapper

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script implements received-file listing and selective download features beyond the skill description. Hidden or undocumented data-access capabilities increase risk because users, reviewers, and policy controls may grant trust based on an incomplete manifest, while the code can retrieve additional accountant-provided documents containing sensitive financial data.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.