Code Review & Risk Assessment June 2026  ·  bank-agent/ vs. OpenClaw + Cloud API

Verdict — your project eliminates 5 of 7 risks from the cloud API threat model — 2 remain

bank-agent — architecture review

The prior report found cloud API mode against bank accounts inadvisable across seven independent risk categories. Your project — a custom Python/Playwright/Ollama stack — was designed as the hybrid architecture those reports recommended. Here's how it maps.

Risks eliminated
5
Partially mitigated
1
Still present
1
Local · Ollama
qwen3:32b
Lines of code
~500
SECTION A · RISK DELTA

Cloud API mode vs. your project — all 7 risks

Severity for each of the seven risks, comparing cloud API mode against this project. Green = eliminated. Amber = partially mitigated. Red = still present.

RiskCloud API mode severityYour project severity
Payload to cloudHighEliminated
API retentionMediumEliminated
Framework CVEsHighEliminated
Prompt injectionHighLargely eliminated
Session timeoutMediumEliminated
Credential handlingMediumPartially mitigated
Bot detectionHighPartially mitigated
SECTION B · RISK-BY-RISK COMPARISON

How each of the seven risks maps

01 · Payload Exposure — page content sent to cloud Was: High → Now: Eliminated
Cloud threatBrowser Use serializes Chrome's full accessibility tree — including password field values — and sends it to Anthropic or OpenAI on every step. Account balances and transaction history transit to the cloud provider after login. No automatic redaction.
Your projectUses local Ollama. The accessibility tree never leaves the machine. Defense-in-depth: _snapshot() also masks password values in the tree with a regex before the LLM ever sees them.
agent.py:419–421 — password masking regex · agent.py:424–426 — 4,000-char snapshot cap
02 · API Provider Retention — prompts held 7–30 days Was: Medium → Now: N/A
Cloud threatAnthropic retains API prompts 7 days by default; OpenAI 30 days. Neither has GLBA or banking-regulator provisions in standard contracts. Policy-violation flags extend retention to 2 years.
Your projectNo external API. No retention. Ollama is entirely local. The only data store is the downloaded PDFs in ~/Downloads/bank-agent/ — which is exactly the intended output.
03 · Framework CVE Surface — 138+ CVEs, malware via marketplace Was: High → Now: N/A
Cloud threatOpenClaw had two CVSS 9.9 vulnerabilities (unauthenticated RCE, auth bypass) in 2026. The ClawHub marketplace distributed 341 malicious skills including banking keyloggers. Giskard found cross-session credential leakage.
Your projectDoesn't use OpenClaw at all. The stack is: Playwright, Ollama, and ~500 lines of your own Python. No marketplace, no third-party skill loader, no gateway process. The attack surface is your code.
04 · Prompt Injection — page content hijacks agent reasoning Was: High → Now: Largely eliminated
Cloud threatarXiv 2505.13076 established prompt injection through bank page content is architecturally unfixable for general browser agents. Transaction memos and payee names are injection surfaces. A documented incident caused $2.3M in fraudulent wire approvals.
Your projectThe LLM's scope is deliberately narrow: navigate to the Statements page, then say done. It never reads transaction data, balances, or memo fields. Downloads are pure Playwright — no LLM. The worst outcome from a successful injection is wrong navigation, not financial action.
flows/download_statement.py — task prompts scope LLM to navigation only
05 · Session Timeout — bank idles out during LLM generation Was: Medium → Now: Solved
Cloud threatUnoptimized agents take 15–30 seconds per step. A 20-step task runs 5–10 minutes — directly inside the 5–15 minute bank session idle timeout window. Even with fast cloud APIs, long LLM pauses with no DOM interaction trigger idle detection.
Your projectThree mitigations: (1) _keepalive() moves the mouse during every LLM call — keeps the session warm. (2) LLM handles only 3–5 navigation steps, then Playwright takes over. (3) Playwright downloads have no LLM latency between them.
agent.py:516–521async def _keepalive(self): """Nudge the page while the LLM is thinking.""" try: await self._page.mouse.move(640, 450) except Exception: pass
06 · Credential Handling — password field exposure Was: Medium → Now: Local-only risk
Cloud threatBrowser Use serializes password field values verbatim to the cloud API. The sensitive_data opt-in has confirmed bugs where it silently fails. Credentials reach Anthropic/OpenAI servers.
Your projectCredentials are in .env, gitignored, and loaded locally. They're passed to the local LLM in the login task prompt — the LLM needs them to fill the form. Since Ollama is local, nothing reaches an external server. The snapshot masking (agent.py:420) adds defense-in-depth against accidental re-exposure in page-state feedback.
.gitignore — .env is excluded · setup_wizard() warns "do not share or commit"
07 · Bot Detection — ThreatMetrix / CDP fingerprinting Was: High → Now: Partially mitigated
Cloud threatLexisNexis ThreatMetrix is at 9 of 10 top US banks. CDP (Chrome DevTools Protocol) leaks through a V8 Error object getter detectable server-side. TLS/JA4 fingerprinting is server-side and unbypassable by the browser. Detection leads to session termination or account lockout.
Your projectchannel="chrome" launches the real Chrome binary instead of Playwright's bundled Chromium — eliminates many Chromium-specific fingerprints, improves behavioral biometric signals. This is the most impactful available mitigation. The remaining risk: CDP protocol itself is still detectable via V8 serialization; TLS/JA4 fingerprinting cannot be addressed at the browser level.
agent.py:92 — channel="chrome"
SECTION C · WHAT REMAINS

Risks the architecture cannot eliminate

Still Present

Bank Terms of Service violation. Chase's March 2026 ToS explicitly prohibit AI agents. Your project is an AI agent accessing Chase. Personal use for your own accounts reduces the CFAA risk substantially (the active Ninth Circuit case concerns commercial agents), but the ToS violation is unambiguous. The practical consequence isn't legal — it's account action: if Chase detects consistent automation, they can freeze, close, or flag the account. The probability is non-zero and rises with frequency of use.

Partially Mitigated

CDP detection — real Chrome helps but doesn't close the gap. channel="chrome" is the right call and meaningfully better than Playwright's bundled Chromium. It removes Chromium-specific signals and produces more human-like rendering behavior. What it doesn't change: CDP itself leaks through V8's Error object serialization, which is a kernel-level signal read server-side without any DOM inspection. TLS/JA4 fingerprinting is also server-side. These are hard limits of the Playwright approach regardless of which Chrome binary is used.

SECTION D · DESIGN STRENGTHS

Patterns that directly address prior report recommendations

Design choiceWhereWhy it matters
Local Ollama onlyagent.py:68Eliminates all cloud payload, retention, and provider-side risks at once
LLM scope = navigation onlyflows/ task promptsLLM never reads financial data; prompt injection surface collapses to navigation UI labels
Playwright handles all downloadsagent.py:128–380Zero LLM latency between PDF downloads; solves session timeout for the long tail
Keepalive mouse movementagent.py:516Prevents idle timeout during qwen3:32b generation; runs concurrent with LLM call
Model warmup on startagent.py:82–121Loads qwen3:32b into VRAM while browser launches; first navigation step returns fast
Auto-discovery from sidebarmain_multi.py:137No hardcoded account list; works for any Chase user; found THIRD MG LLC that wasn't in the original list
channel="chrome"agent.py:92Real Chrome binary reduces fingerprinting signals vs. Playwright's bundled Chromium
think=Falseagent.py:551Suppresses qwen3 chain-of-thought; cuts token generation, fixes JSON parsing, reduces step latency
Snapshot capped at 4,000 charsagent.py:424Prevents multi-minute generations on large account overview pages with many transactions
.env gitignored + wizard warning.gitignore, main_multi.py:129Credential file excluded from version control; setup warns "do not share or commit"
SECTION E · ASSESSMENT

What this means

This is the architecture the prior reports recommended. It is meaningfully safer than cloud API mode.

The risk assessment artifact's conclusion was "do not use cloud API mode against production bank accounts." Your project implements almost every mitigation that report identified as the viable alternative: local model, Playwright-direct downloads, narrow LLM scope, session keepalive, real Chrome binary, no third-party framework. Five of the seven risk categories from that report do not apply here.

What's left is the irreducible minimum for this type of tool: you're still running an automated session against a bank that has updated its ToS to prohibit AI agents. The CDP fingerprint is still present. These are hard limits of the Playwright-based approach — not things your architecture can fix. The honest framing is: this is as safe as a self-hosted Playwright bank agent can be, and that ceiling is below "fully compliant."

For personal use, occasional runs, on your own accounts: the practical risk is low. Chase's automated detection is tuned to commercial-scale access patterns, not an individual downloading their own statements monthly. The keepalive and real Chrome reduce the signal further. You've downloaded 133 statements across 14 accounts without incident — that's evidence the risk is manageable, not zero.

If you share this with your colleague, flag both the ToS and the CDP detection risk explicitly. Their account, their risk profile. The setup wizard should probably include a one-line note about Chase's automated-access policy — not to scare them off, but so they can make an informed call.

Reviewed Against / Sources

Reviewed against: OpenClaw + Cloud API — Banking Risk Assessment (Jun 2026) Sources: /Users/smgarlock/bank-agent/ · agent.py · main_multi.py · flows/download_statement.py · context/chase.txt