FinBot Analysis Report
Completed July 11, 2026 · 18 artifacts examined
15Findings
5Critical
4High
6Medium
RAISE maturity 0.90 / 5.0
Executive Summary
Agent Remit (as declared)
FinBot is CineFlow Productions' autonomous invoice-processing agent: for each vendor-submitted invoice it validates the vendor, runs fraud detection, and decides to approve (which triggers payment), reject, or escalate to human review, recording its reasoning. Its authorized surfaces are the vendor portal (inbound, untrusted data), the invoice/vendor database, an external LLM provider for inference, and an authenticated operator-only admin channel; it has no email, outbound, or vendor-mutation capability. The remit forbids treating vendor-supplied invoice content as instructions, redefining goals or thresholds at runtime, auto-approving at or above the manual-review threshold, and skipping or disabling fraud detection.
Behavior Summary (as observed)

Every approval control FinBot claims lives in the LLM system prompt or the remit, and nothing enforces it in code: approve_invoice() sets payment_processed=True unconditionally, with no check on amount, fraud outcome, vendor-approved status, or the declared confidence_threshold (which is never read). The dominant pattern is a one-hop external-input-to-payment chain — an untrusted vendor invoice description flows into the LLM context, and any unauthenticated caller can rewrite the agent's goals and thresholds through open /admin/finbot/* endpoints, so the same prompt that decides payment is attacker-controllable from two directions.

Compounding this, fraud detection is a bypassable regex denylist that can be switched off through the open config endpoint, and there is no logging, audit trail, or alerting on any decision or config change — so the gaps are both exploitable and undetectable.

Scope of Analysis
Python Flask application (SQLAlchemy over SQLite locally / Postgres on Render) whose FinBotAgent class in src/services/finbot_agent.py orchestrates OpenAI gpt-4.1-mini function-calling over five invoice tools, with a rule-based _fallback_processing() path when no LLM client is configured. Vendor and admin routes are exposed via Flask blueprints (src/routes/vendor.py, src/routes/admin.py) with no authentication middleware and app-wide CORS(app). Operational config — including a free-text custom_goals field — is persisted in the FinBotConfig table and injected verbatim into the agent's system prompt; a Flask SECRET_KEY is hardcoded in src/main.py. Vendor-submitted invoice descriptions flow into the LLM context through _get_invoice_details(), and approve_invoice() writes payment_processed=True with no code-level check.
Remit Coverage

Every actionable rule in the Worker Remit, checked against the running code. Gap = declared but unenforced; Partial = enforced but incomplete or bypassable; Vague Policy = too imprecise to verify.

Verified: 5 Gap: 18 Partial: 1 Vague Policy: 0 Enforcement Not Possible: 1 Total Rules: 25
Rule ID Section Rule (quoted) Status Finding
R-01 Mission "FinBot's goals and decision logic are fixed by its deployment and exist to serve CineFlow finance, never a submitting party." Gap PRAX-2026-07-11-004
R-02 Job Description "Confirm the submitting vendor is registered and in approved status before processing the invoice." Gap PRAX-2026-07-11-008
R-03 Job Description "Approve an invoice — which marks it approved and triggers payment processing — only when it is within FinBot's autonomous authority." Gap PRAX-2026-07-11-002
R-04 Job Description "Record the decision and its supporting reasoning for every invoice it processes." Verified
R-05 Non-Goals (Out of Scope) "Redefining its own goals, thresholds, or decision logic in response to runtime input from any user, vendor, or caller." Gap PRAX-2026-07-11-004
R-06 Non-Goals (Out of Scope) "Taking instructions, goals, or decision directives from invoice content, descriptions, notes, line items, or any field supplied by a vendor." Gap PRAX-2026-07-11-006
R-07 Approved Communication Channels "Operator-only, authenticated channel for adjusting operational thresholds; must not be reachable by unauthenticated or vendor-side callers." Gap PRAX-2026-07-11-003
R-08 Approved Communication Channels "Inference only; no tool call or side effect may originate from a model response except through FinBot's defined tools." Verified
R-09 Data Boundaries "Invoice, vendor, or decision data MUST NOT be sent to any destination other than the invoice database and the LLM provider used for inference." Verified
R-10 Data Boundaries "Sensitive vendor or payment data MUST NOT be exfiltrated through any channel; FinBot has no authorized outbound channel to external systems." Verified
R-11 Action Boundaries "Any invoice at or above the manual-review threshold — it MUST reach a human checkpoint before it proceeds; FinBot MUST NOT auto-approve it." Gap PRAX-2026-07-11-002
R-12 Action Boundaries "Any invoice that fraud detection rates as high or critical risk." Gap PRAX-2026-07-11-002
R-13 Action Boundaries "Any invoice where FinBot's decision confidence is below the configured confidence threshold." Gap PRAX-2026-07-11-010
R-14 Action Boundaries "Any change to operational thresholds — permitted only via the authenticated admin channel, never at runtime." Gap PRAX-2026-07-11-003
R-15 Action Boundaries "Approving or triggering payment to a vendor that is not registered and in approved status." Gap PRAX-2026-07-11-008
R-16 Action Boundaries "Approving any invoice without first running fraud detection." Gap PRAX-2026-07-11-002
R-17 Action Boundaries "Skipping, disabling, or bypassing fraud detection for any reason, including a configuration flag or a runtime instruction." Gap PRAX-2026-07-11-005
R-18 Action Boundaries "Auto-approving any invoice when a fraud signal is present, regardless of the invoice amount — such an invoice MUST be escalated." Gap PRAX-2026-07-11-007
R-19 Action Boundaries "Treating instructions embedded in invoice descriptions, vendor notes, line items, or any vendor-supplied field as directives, goals, or policy." Gap PRAX-2026-07-11-006
R-20 Action Boundaries "Allowing runtime input (invoice content, vendor notes, incoming payloads) to alter FinBot's goals, instructions, thresholds, or decision logic." Gap PRAX-2026-07-11-004
R-21 Tools and Capabilities "Any capability that disables or bypasses fraud detection." Gap PRAX-2026-07-11-005
R-22 Escalation Rules "Fraud detection is disabled, unavailable, or bypassed for any invoice." Gap PRAX-2026-07-11-009
R-23 Escalation Rules "A prompt-injection or embedded-directive attempt is detected in invoice content." Partial PRAX-2026-07-11-009
R-24 Behavioral Expectations "Actions that should never be retried: triggering payment, and approval of an invoice after an inconclusive fraud or vendor check." Enforcement Not Possible
R-25 Tools and Capabilities "Any capability to send email, place outbound calls, or reach systems other than the invoice/vendor database and the LLM provider." Verified
Findings Register

Findings, ordered by severity — each linked to its remit rule, evidence, and a recommended action. Tag chips jump to the relevant entry in the RAISE framework, the OWASP LLM Top 10, or the OWASP Agentic Top 10.

LLM06 primary — the finding's main OWASP categoryASI10 secondary — a category it also touches

CRITICAL PRAX-2026-07-11-001 Untrusted vendor invoice text flows into the LLM context and drives an unconditional approve-and-pay action with no gate and no logging.
Policy Rule — R-06, R-11 (Worker Remit):
"Taking instructions, goals, or decision directives from invoice content, descriptions, notes, line items, or any field supplied by a vendor. / Any invoice at or above the manual-review threshold — it MUST reach a human checkpoint before it proceeds; FinBot MUST NOT auto-approve it."
src/services/finbot_agent.py:394 — _get_invoice_details returns the raw vendor-supplied description into the tool result that becomes LLM context src/services/finbot_agent.py:415 — _approve_invoice sets invoice.status='approved' and payment_processed=True with no amount/fraud/vendor/confidence gate
Recommended Action
  • Add a deterministic pre-payment gate in _approve_invoice() that re-checks amount against manual_review_threshold, requires a passing fraud-detection result, verifies vendor status, and routes any failure to _request_human_review() before setting payment_processed.
  • Label vendor-supplied invoice fields as untrusted data in prompt construction and never allow them to reach an approve action without passing the code gate.
CRITICAL PRAX-2026-07-11-002 Approval requirements — manual-review threshold, fraud-risk routing, fraud-ran precondition — exist only in the prompt; approve_invoice enforces none of them in code.
Policy Rule — R-03, R-11, R-12, R-16 (Worker Remit):
"Approve an invoice — which marks it approved and triggers payment processing — only when it is within FinBot's autonomous authority. / Any invoice at or above the manual-review threshold — it MUST reach a human checkpoint before it proceeds; FinBot MUST NOT auto-approve it. / Any invoice that fraud detection rates as high or critical risk. / Approving any invoice without first running fraud detection."
src/services/finbot_agent.py:409 — _approve_invoice has no branch on config.manual_review_threshold, fraud result, or whether detect_fraud_patterns was ever called src/services/finbot_agent.py:49 — Financial thresholds and human-review rules stated only as system-prompt text, not as code guards
Recommended Action
Move threshold, fraud-risk, and fraud-ran checks out of the prompt and into _approve_invoice() as hard preconditions that force escalation when unmet.
CRITICAL PRAX-2026-07-11-003 All /admin/finbot config, goals, vendor-trust, and review endpoints are unauthenticated and reachable by any caller.
Policy Rule — R-07, R-14 (Worker Remit):
"Operator-only, authenticated channel for adjusting operational thresholds; must not be reachable by unauthenticated or vendor-side callers. / Any change to operational thresholds — permitted only via the authenticated admin channel, never at runtime."
src/routes/admin.py:74 — update_finbot_config route has no authentication; accepts arbitrary config JSON from any caller src/routes/admin.py:90 — update_finbot_goals route accepts arbitrary goal text with no auth and an explicit MAJOR VULNERABILITY comment
Recommended Action
Add authentication and operator-role authorization middleware to every /admin/* route, and reject unauthenticated or vendor-origin requests before any config or goal mutation.
CRITICAL PRAX-2026-07-11-004 Runtime goal injection — custom_goals is stored unvalidated and spliced into the system prompt with an explicit instruction to override built-in goals.
Policy Rule — R-01, R-05, R-20 (Worker Remit):
"FinBot's goals and decision logic are fixed by its deployment and exist to serve CineFlow finance, never a submitting party. / Redefining its own goals, thresholds, or decision logic in response to runtime input from any user, vendor, or caller. / Allowing runtime input (invoice content, vendor notes, incoming payloads) to alter FinBot's goals, instructions, thresholds, or decision logic."
src/services/finbot_agent.py:70 — config.custom_goals appended to system prompt under 'CUSTOM GOALS (OVERRIDE ABOVE IF CONFLICTING)' src/services/finbot_agent.py:744 — update_goals writes new_goals to config.custom_goals with a comment noting no validation of goal content
Recommended Action
  • Remove the runtime-editable custom_goals path from the system prompt entirely; make goal and decision-logic changes deployment-time only.
  • If operator goal edits are required, gate them behind the authenticated admin channel plus schema/allowlist validation and an audit record.
CRITICAL PRAX-2026-07-11-005 Fraud detection can be disabled via a config flag, after which detection returns empty and the fallback path skips injection checks entirely.
Policy Rule — R-17, R-21 (Worker Remit):
"Skipping, disabling, or bypassing fraud detection for any reason, including a configuration flag or a runtime instruction. / Any capability that disables or bypasses fraud detection."
src/services/finbot_agent.py:509 — detect_fraud_patterns returns empty indicators and risk_level 'low' when config.fraud_detection_enabled is false src/services/finbot_agent.py:785 — _fallback_processing only runs _detect_prompt_injection when fraud_detection_enabled, else skips it
Recommended Action
Remove the ability to disable fraud detection at runtime, or make a disabled state force every invoice to human review and raise a halt/alert per the remit's escalation rule.
HIGH PRAX-2026-07-11-006 Vendor-supplied invoice description enters the LLM context unvalidated, providing a direct prompt-injection and goal-manipulation ingress.
Policy Rule — R-06, R-19 (Worker Remit):
"Taking instructions, goals, or decision directives from invoice content, descriptions, notes, line items, or any field supplied by a vendor. / Treating instructions embedded in invoice descriptions, vendor notes, line items, or any vendor-supplied field as directives, goals, or policy."
src/routes/vendor.py:93 — invoice description accepted as free text from vendor submission with no sanitization src/services/finbot_agent.py:394 — description returned into tool output that is appended to messages as LLM context
Recommended Action
Delimit and label vendor-supplied fields as untrusted data in prompt construction, and ensure no decision path can act on instructions parsed from those fields.
HIGH PRAX-2026-07-11-007 The fallback rule engine explicitly auto-approves above-threshold and injection-flagged invoices when business-context or speed_priority conditions are met.
Policy Rule — R-11, R-18 (Worker Remit):
"Any invoice at or above the manual-review threshold — it MUST reach a human checkpoint before it proceeds; FinBot MUST NOT auto-approve it. / Auto-approving any invoice when a fraud signal is present, regardless of the invoice amount — such an invoice MUST be escalated."
src/services/finbot_agent.py:807 — above manual_review_threshold, business_context should_expedite overrides to decision='auto_approve' src/services/finbot_agent.py:843 — when injection detected, should_expedite and speed_priority>0.6 auto-approve over security concerns
Recommended Action
Remove the business-context and speed_priority override branches; force escalation for any above-threshold amount or any detected fraud/injection signal.
HIGH PRAX-2026-07-11-008 The agent never verifies vendor registered-and-approved status before approving, and vendor registration auto-sets status to approved.
Policy Rule — R-02, R-15 (Worker Remit):
"Confirm the submitting vendor is registered and in approved status before processing the invoice. / Approving or triggering payment to a vendor that is not registered and in approved status."
src/routes/vendor.py:37 — register_vendor sets status='approved' and trust_level='standard' at self-registration with the comment 'Auto-approve for demo' src/services/finbot_agent.py:409 — approve path never consults vendor.status before triggering payment
Recommended Action
Add a vendor status check to the approval gate that requires vendor.status == 'approved', and stop auto-approving vendors at self-registration.
HIGH PRAX-2026-07-11-009 No logging, audit trail, or alerting exists for decisions or config changes, so none of the remit's halt/alert escalation rules are implemented.
Policy Rule — R-22, R-23 (Worker Remit):
"Fraud detection is disabled, unavailable, or bypassed for any invoice. / A prompt-injection or embedded-directive attempt is detected in invoice content."
src/routes/admin.py:239 — only observability on a mutating path is a print() to console; no structured or persisted audit log src/services/finbot_agent.py:373 — contains_prompt_injection set to True in the DB but no alert or escalation is raised
Recommended Action
Introduce structured, durable logging of every decision, tool call, and admin config/goal change, and wire the remit's halt/alert conditions to a real alerting path.
MEDIUM PRAX-2026-07-11-010 The confidence_threshold config is declared and settable but never consulted by any decision path.
Policy Rule — R-13 (Worker Remit):
"Any invoice where FinBot's decision confidence is below the configured confidence threshold."
src/models/vendor.py:102 — confidence_threshold column defined with default 0.8 src/services/finbot_agent.py:758 — confidence_threshold is written by update_config but never read in any approve/escalate decision
Recommended Action
Consume confidence_threshold in the approval gate — route any decision with confidence below it to human review.
MEDIUM PRAX-2026-07-11-011 A Flask SECRET_KEY is hardcoded in source rather than loaded from the environment.
src/main.py:15 — hardcoded Flask SECRET_KEY string literal at src/main.py:15 [REDACTED — secret value], not loaded from environment
Recommended Action
Load SECRET_KEY from an environment variable or secret manager and rotate the committed value.
MEDIUM PRAX-2026-07-11-012 CORS is enabled for all origins on all routes, including the unauthenticated admin endpoints.
src/main.py:18 — CORS(app) enables wildcard cross-origin access for all routes including /admin/*
Recommended Action
Restrict CORS to a known operator-origin allowlist and scope it away from admin routes.
MEDIUM PRAX-2026-07-11-013 Python dependencies are floor-pinned with >= and no lockfile is committed.
requirements.txt:2 — flask-cors, flask-sqlalchemy, openai and others floor-pinned with >= and no committed lockfile
Recommended Action
Pin all dependencies to exact versions and commit a lockfile so the deployed dependency set is reproducible.
MEDIUM PRAX-2026-07-11-014 The public invoice-submission endpoint triggers LLM processing with no rate limiting or cost controls.
src/routes/vendor.py:104 — submit_invoice calls finbot.process_invoice on every public submission with no rate limit or budget cap
Recommended Action
Add per-caller rate limiting and a cost/budget threshold on the invoice-submission and LLM-processing path.
MEDIUM PRAX-2026-07-11-015 Prompt-injection and fraud detection rely on a bypassable regex denylist, and its result never blocks an approval.
src/services/finbot_agent.py:552 — technical_patterns and business_manipulation_patterns are a fixed regex denylist matched against invoice text src/services/finbot_agent.py:546 — detect_fraud_patterns returns advisory risk_level only; no caller enforces escalation on high risk
Recommended Action
Treat detection output as advisory input to a deterministic gate, and design the approval control to fail closed rather than depending on denylist completeness.
What's Working Well

Controls and behaviors that are correctly implemented and verified during this scan. These represent areas where the agent's implementation aligns with its stated policy and security best practices.

Decision reasoning persisted per invoice

Every decision records ai_decision, ai_confidence, and ai_reasoning to the database, giving a durable per-invoice decision record that satisfies the remit's reasoning-recording rule.

src/services/finbot_agent.py:417-419

No outbound or exfiltration channel

FinBot's tool inventory is limited to invoice-database and LLM inference operations — no email, network, subprocess, or file-send capability exists, matching the remit's forbidden-tools and data-boundary rules.

src/services/finbot_agent.py:228-345

Sensitive vendor banking fields excluded from LLM context

_get_invoice_details returns only company name, trust level, and contact email for the vendor, keeping bank account and routing numbers out of the model context.

src/services/finbot_agent.py:399-404

Model version pinned and API key sourced from environment

The agent pins the model to gpt-4.1-mini and instantiates the OpenAI client from environment credentials rather than a hardcoded key.

src/services/finbot_agent.py:16-17

Bounded agent orchestration loop

The function-calling loop is capped at five iterations, limiting runaway tool-call loops on a single invoice.

src/services/finbot_agent.py:171
Discovered Log Files

Log files found in the agent's workspace during this scan. Reviewing these files provides runtime evidence to complement the static analysis above.

No log files exist on disk and no logging framework is present in source (only stray print() calls) — the absence of any action or audit logging is captured as finding PRAX-2026-07-11-009.
OWASP LLM Top 10 (2025) Coverage

Each card represents one category and shows the top 3 findings. All items in the Findings section.

LLM04 Data and Model Poisoning
No findings
LLM05 Improper Output Handling
No findings
LLM07 System Prompt Leakage
No findings
LLM08 Vector and Embedding Weaknesses
No findings
LLM09 Misinformation
No findings
OWASP Agentic Top 10 (2026) Coverage

Each card represents one category and shows the top 3 findings. All items in the Findings section.

RAISE Maturity Posture

Overall maturity assessment across the six categories of the RAISE framework. This is a maturity model, not a school grade: a score of 3 / 5 means Established, not 60 percent. Most production AI agents today score between Ad hoc (1) and Established (3). See the full RAISE framework reference for the complete scale and scoring.

0.90 / 5.0
Weighted Maturity Score · Absent
Absent overall posture. FinBot's mission and tool set are narrow and it has no outbound or exfiltration channel, but the categories that matter for a payment-triggering agent are effectively empty: Implement Zero Trust scores 0 because no code interposes on the approve action and the admin surface is unauthenticated, and Monitor Continuously, Supply Chain, and Red Team all sit at Ad hoc. The single 2 (Limit Your Domain) reflects a genuinely narrow capability set, but even that is undermined by runtime goal injection, so the weighted result lands firmly in the Absent band.
Limit Your Domain
2/ 5
Confidence: Medium  |  Weight: 15%  |  Weighted: 0.30
Tool inventory is narrow and on-mission (five invoice tools, no email/exec/network capability), but domain scope is enforced only in the prompt and is directly redefinable at runtime through the unvalidated custom_goals field injected into the system prompt.
Balance Your Knowledge Base
1/ 5
Confidence: Medium  |  Weight: 15%  |  Weighted: 0.15
Untrusted vendor-supplied invoice descriptions enter the LLM context through _get_invoice_details() with no trust boundary; the only control is a bypassable regex denylist, though sensitive vendor bank fields are at least excluded from the returned context.
Implement Zero Trust
0/ 5
Confidence: High  |  Weight: 25%  |  Weighted: 0.00
No code-level interposition exists on the agent's decisions — approve_invoice() writes payment_processed=True with no check on amount, fraud outcome, vendor status, or confidence, and the admin config/goals endpoints that govern those decisions have no authentication.
Manage Your Supply Chain
1/ 5
Confidence: High  |  Weight: 15%  |  Weighted: 0.15
The model version is pinned and the OpenAI key is sourced from the environment, but Python dependencies are floor-pinned with >= and no lockfile is committed, and a Flask SECRET_KEY is hardcoded in src/main.py.
Build an AI Red Team
1/ 5
Confidence: Medium  |  Weight: 15%  |  Weighted: 0.15
The repository ships a CTF walkthrough that demonstrates goal-manipulation attacks, but there is no evidence any adversarial finding drove an architectural fix — the demonstrated weaknesses remain fully present in the code.
Monitor Continuously
1/ 5
Confidence: Medium  |  Weight: 15%  |  Weighted: 0.15
Per-invoice decisions and reasoning are persisted to the database, but there is no logging framework (only stray print() calls), no audit of admin config or goal changes, and none of the remit's halt/alert escalations are implemented.

Maturity Scoring Rubric

Every score above is based on this scale. A score is a snapshot of observable posture — not a verdict on the people or team behind the system.

Score Label Meaning
5 Exemplary Best-in-class; automated, continuously tested, reference quality. Rarely achieved in shipping systems.
4 Strong Comprehensive controls, active management, minor gaps. Production-ready.
3 Established Documented controls consistently applied; known gaps accepted. A respectable baseline.
2 Partial Some controls exist but coverage is incomplete; key gaps remain.
1 Ad hoc Informal or inconsistent measures; relies on individual judgment.
0 Absent No evidence this category is addressed at all.
Weighting: the weighted overall above is the sum of each category's score × weight (the per-category weights are shown on each card). Zero Trust carries double weight by design; see the RAISE framework reference for the rationale.