Back to skill

Security audit

george

Security checks for vulnerabilities and agentic risk

Overview

This banking skill mostly matches its stated purpose, but it includes real payment upload/signing authority, persistent session tokens, and a token-forwarding flaw that users should review carefully before installing.

Install only if you intentionally want an agent to automate a live George banking session, including portfolio access and data-carrier upload/signing. Treat the workspace george directory as sensitive credential storage, use logout when done, avoid shared or synced workspaces, and do not use data-carrier upload/sign unless you have independently reviewed the payment file and destination details.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/george.py:3078
Finding

Banking Bearer Token Forwarded to an Unvalidated Polling URL

Content
View full analysis
0: interval_s = max(poll_interval / 1000.0, 1.0) start = time.time() last_state = state while time.time() - start < args.timeout: time.sleep(interval_s) try: poll_result = page.evaluate("""async ([url, token]) => { try { const resp = await fetch(url, { headers: {'Authorization': token} }); ``` ### Technical Analysis The data-carrier signing API response supplies `poll.url`. The Skill uses that value as a request destination without validating its scheme, hostname, port, or path. It attaches the captured George bearer token to the resulting request through the `Authorization` header. This differs from the account and transaction functions, which use fixed official Sparkasse URLs. If a compromised or manipulated signing response returns an attacker-controlled polling URL, the browser may send the banking bearer token to that destination. Because an `Authorization` header triggers a CORS preflight for cross-origin requests, an attacker-controlled server can permit th ...[truncated 1371 chars]
Remediation
View remediation
str: parts = urlsplit(url) if parts.scheme != "https": raise ValueError("Signing poll URL must use HTTPS") if parts.hostname != "api.sparkasse.at": raise ValueError("Unexpected signing poll URL host") if parts.port not in (None, 443): raise ValueError("Unexpected signing poll URL port") if parts.username or parts.password or parts.fragment: raise ValueError("Malformed signing poll URL") if not parts.path.startswith("/rest/netbanking/"): raise ValueError("Unexpected signing poll URL path") return url ``` Apply this check before the polling loop and never attach the bearer token to an unapproved destination. ]]>

T08 · Insecure Dependencies

Warning
Location
SETUP.md:8
Finding

Unpinned Playwright Package and Browser Installation

Content
View full analysis
Remediation
View remediation
``` 2. Place dependencies in a committed lock file. 3. Use hash verification, such as a requirements file generated with hashes and installed using: ```bash pip install --require-hashes -r requirements.txt ``` 4. Pin the matching Playwright container image by immutable digest when containerized. 5. Document and pin the expected Chromium revision or use the browser revision tied to the reviewed Playwright release. 6. Download packages only from an approved, authenticated package index. 7. Run dependency installation and browser downloads in a restricted build environment. 8. Periodically update pins through a controlled review process that includes vulnerability scanning and regression testing. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (33)

Context-Inappropriate Capability

Critical
Category
Not specified by scanner
Confidence
100% confidence
Finding

The code implements end-to-end payment/order initiation behavior via data-carrier upload and subsequent signing, which materially exceeds the stated read-oriented banking purpose. In banking context this is highly dangerous because a caller expecting account-read automation could unintentionally submit or authorize transfers or other financial orders.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The documented purpose emphasizes login/logout, account listing, and transactions, but the skill also covers portfolio holdings, securities-related transactions, and datacarrier management beyond the stated scope. In a financial environment, under-describing these capabilities increases the risk of inappropriate use, overbroad trust, and accidental exposure of additional sensitive assets and workflows.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The documented purpose emphasizes login/logout, account listing, and transactions, but the skill also covers portfolio holdings, securities-related transactions, and datacarrier management beyond the stated scope. In a financial environment, under-describing these capabilities increases the risk of inappropriate use, overbroad trust, and accidental exposure of additional sensitive assets and workflows.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The manifest text limits the skill to basic banking retrieval functions, while the command list includes datacarrier upload and signing that can enable payment submission or authorization. In a banking skill, this discrepancy is especially dangerous because users may treat the skill as read-only when it actually supports potentially irreversible financial actions.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
99% confidence
Finding

The declared skill description says the skill logs in, lists accounts, and fetches transactions, but the code also implements data-carrier upload and signing workflows. That mismatch is dangerous because operators may grant or invoke the skill believing it is read-only when it can actually initiate or authorize payment-related actions.

Content

No source excerpt is available for this finding.

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 · scripts/george.py (reported line 370)May include surrounding context.

python
# Ensure the state dir exists and is private.
    _ensure_dir(STATE_DIR)

    # No .env loading — use GEORGE_USER_ID env var or --user-id flag

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The setup documentation explicitly states that session tokens are cached locally in {workspace}/george/token.json but does not warn users that this creates persistent local credentials. If that file is readable by other local users, included in backups, copied from a shared workspace, or accidentally committed, an attacker may be able to reuse the authenticated session and access banking data without redoing 2FA.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill exposes capabilities consistent with shell, filesystem, environment, and network access but does not declare any explicit tool scope or permission boundary in the manifest. In a banking automation context, this is dangerous because the skill handles highly sensitive financial data and can initiate browser-driven actions against a live bank session without clear least-privilege constraints.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The summary omits portfolio access even though the documentation states the skill fetches stock portfolio data. While less severe than hidden payment functionality, this still expands the data sensitivity and privacy scope beyond what a user or policy engine might reasonably infer from the summary.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The payment upload and signing commands are documented like ordinary operations and lack a prominent warning that they may create or authorize real banking instructions. In the context of online banking automation, insufficient warning and friction can lead to accidental execution of irreversible or financially harmful actions.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The document explicitly proposes storing banking access tokens on disk in a predictable local file (token.json) to avoid re-authentication, but it does not require encryption, strict file permissions, OS keychain storage, or user consent. For a banking automation skill, cached bearer tokens can grant direct account access if the host is compromised, another local user can read the file, or logs/backups inadvertently capture it.

Content

No source excerpt is available for this finding.

Tainted flow: 'p' from os.environ.get (line 410, credential/environment) → pathlib.Path.write_text (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/george.py (reported line 418)May include surrounding context.

python
"expiresAt": expires_at,
            "source": source,
        }
        p.write_text(json.dumps(data, ensure_ascii=False, indent=2))
        _chmod(p, 0o600)
        _chmod(p.parent, 0o700)
    except Exception:

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The docstrings for load_config and save_config state that George no longer uses or writes a local config.json/accounts cache. But elsewhere the code still reads CONFIG_PATH for user_id resolution and cmd_setup writes config.json with user_id and accounts, so the documentation actively misrepresents current behavior.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/george.py (reported line 1027)May include surrounding context.

python
url = request.url or ""
            # George uses multiple backends; tokens are sent to both netbanking and proxy/g APIs.
            if not (
                url.startswith("https://api.sparkasse.at/rest/netbanking/")
                or url.startswith("https://api.sparkasse.at/proxy/g/api/")
                or url.startswith("https://api.sparkasse.at/sec-trading/")
            ):

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/george.py (reported line 1028)May include surrounding context.

python
url = request.url or ""
            # George uses multiple backends; tokens are sent to both netbanking and proxy/g APIs.
            if not (
                url.startswith("https://api.sparkasse.at/rest/netbanking/")
                or url.startswith("https://api.sparkasse.at/proxy/g/api/")
                or url.startswith("https://api.sparkasse.at/sec-trading/")
            ):

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/george.py (reported line 1029)May include surrounding context.

python
url = request.url or ""
            # George uses multiple backends; tokens are sent to both netbanking and proxy/g APIs.
            if not (
                url.startswith("https://api.sparkasse.at/rest/netbanking/")
                or url.startswith("https://api.sparkasse.at/proxy/g/api/")
                or url.startswith("https://api.sparkasse.at/sec-trading/")
            ):

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/george.py (reported line 1079)May include surrounding context.

python
url = request.url or ""
            # George uses multiple backends; tokens are sent to both netbanking and proxy/g APIs.
            if not (
                url.startswith("https://api.sparkasse.at/rest/netbanking/")
                or url.startswith("https://api.sparkasse.at/proxy/g/api/")
                or url.startswith("https://api.sparkasse.at/sec-trading/")
            ):

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/george.py (reported line 1085)May include surrounding context.

python
url = request.url or ""
            # George uses multiple backends; tokens are sent to both netbanking and proxy/g APIs.
            if not (
                url.startswith("https://api.sparkasse.at/rest/netbanking/")
                or url.startswith("https://api.sparkasse.at/proxy/g/api/")
                or url.startswith("https://api.sparkasse.at/sec-trading/")
            ):

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/george.py (reported line 1086)May include surrounding context.

python
url = request.url or ""
            # George uses multiple backends; tokens are sent to both netbanking and proxy/g APIs.
            if not (
                url.startswith("https://api.sparkasse.at/rest/netbanking/")
                or url.startswith("https://api.sparkasse.at/proxy/g/api/")
                or url.startswith("https://api.sparkasse.at/sec-trading/")
            ):

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/george.py (reported line 1117)May include surrounding context.

python
url = request.url or ""
            # George uses multiple backends; tokens are sent to both netbanking and proxy/g APIs.
            if not (
                url.startswith("https://api.sparkasse.at/rest/netbanking/")
                or url.startswith("https://api.sparkasse.at/proxy/g/api/")
                or url.startswith("https://api.sparkasse.at/sec-trading/")
            ):

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/george.py (reported line 1132)May include surrounding context.

python
url = request.url or ""
            # George uses multiple backends; tokens are sent to both netbanking and proxy/g APIs.
            if not (
                url.startswith("https://api.sparkasse.at/rest/netbanking/")
                or url.startswith("https://api.sparkasse.at/proxy/g/api/")
                or url.startswith("https://api.sparkasse.at/sec-trading/")
            ):

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/george.py (reported line 1158)May include surrounding context.

python
url = request.url or ""
            # George uses multiple backends; tokens are sent to both netbanking and proxy/g APIs.
            if not (
                url.startswith("https://api.sparkasse.at/rest/netbanking/")
                or url.startswith("https://api.sparkasse.at/proxy/g/api/")
                or url.startswith("https://api.sparkasse.at/sec-trading/")
            ):

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/george.py (reported line 3165)May include surrounding context.

python
url = request.url or ""
            # George uses multiple backends; tokens are sent to both netbanking and proxy/g APIs.
            if not (
                url.startswith("https://api.sparkasse.at/rest/netbanking/")
                or url.startswith("https://api.sparkasse.at/proxy/g/api/")
                or url.startswith("https://api.sparkasse.at/sec-trading/")
            ):

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/george.py (reported line 3207)May include surrounding context.

python
url = request.url or ""
            # George uses multiple backends; tokens are sent to both netbanking and proxy/g APIs.
            if not (
                url.startswith("https://api.sparkasse.at/rest/netbanking/")
                or url.startswith("https://api.sparkasse.at/proxy/g/api/")
                or url.startswith("https://api.sparkasse.at/sec-trading/")
            ):

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/george.py (reported line 3553)May include surrounding context.

python
url = request.url or ""
            # George uses multiple backends; tokens are sent to both netbanking and proxy/g APIs.
            if not (
                url.startswith("https://api.sparkasse.at/rest/netbanking/")
                or url.startswith("https://api.sparkasse.at/proxy/g/api/")
                or url.startswith("https://api.sparkasse.at/sec-trading/")
            ):

Static analysis

Detected: suspicious.exposed_secret_literal

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
scripts/george.py:285