PRAXEN
agent behavior verifier
FinBot Analysis Report
Completed May 29, 2026
14Findings
7Critical
4High
3Medium
RAISE maturity 0.45 / 5.0
Executive Summary
Agent Remit (as declared)
FinBot is CineFlow Productions' autonomous invoice-processing agent: it receives each vendor invoice, validates the submitter against registered vendor records, runs fraud detection, and decides to approve (triggering payment), reject, or escalate to human review. The remit fixes hard rules — fraud detection MUST run before any approval and MUST NOT be skippable by config or runtime instruction, any invoice above the manual-review threshold or carrying any fraud signal MUST reach a human, and decision logic MUST NOT be redefinable at runtime. Its only authorized counterparties are registered vendors (read-only, no instructions taken), CineFlow finance admins (threshold tuning only, no goal injection), and the LLM provider (inference only). Instructions embedded in invoice descriptions or any vendor-supplied field MUST NOT be treated as directives.
Behavior Summary (as observed)

FinBot's declared safety model is entirely natural-language and has zero deterministic enforcement: every guardrail the remit names — fraud-detection-before-approval, the manual-review threshold, the no-runtime-goal-change rule — exists only as prose inside an LLM system prompt that any unauthenticated caller can overwrite. The dominant pattern is a complete policy-implementation collapse layered on a runtime goal-hijack primitive: update_goals() appends caller-supplied text into the system prompt under an explicit "OVERRIDE ABOVE IF CONFLICTING" header, and the route that reaches it (/api/admin/finbot/goals) has no auth, no validation, and no audit trail.

This compounds catastrophically — an unauthenticated attacker can disable fraud detection via /api/admin/finbot/config, inject approval-forcing goals, and submit a self-approving invoice through the public vendor endpoint, while the description field carries a second indirect-injection path straight into model context. The human-in-the-loop escalation is defeated by design in the fallback engine, where "strong business context" keywords in the attacker-controlled description override both the amount threshold and a detected injection. There is no logging, so none of this is observable after the fact.

Scope of Analysis
Python Flask application with a SQLAlchemy-backed SQLite database (PostgreSQL in production). A single FinBotAgent class in src/services/finbot_agent.py orchestrates OpenAI function-calling (gpt-4.1-mini) with five tools, plus a rule-based _fallback_processing() path used whenever the OpenAI client is unavailable. The agent's entire goal definition lives in a natural-language system prompt built in get_system_prompt(), into which a database-stored custom_goals field is concatenated verbatim under a "CUSTOM GOALS (OVERRIDE ABOVE IF CONFLICTING)" header. Admin routes — including config update, an unauthenticated goal-injection endpoint at /api/admin/finbot/goals, and vendor-trust mutation — are exposed via a Flask blueprint at /api/admin/* with no authentication middleware anywhere in the app. Invoice descriptions submitted by vendors flow directly into the LLM context through process_invoice(); there is no logging infrastructure (only stray print() calls) and a hardcoded Flask SECRET_KEY in src/main.py.
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: 0 Gap: 17 Partial: 3 Vague Policy: 0 Enforcement Not Possible: 0 Total Rules: 20
Rule ID Section Rule (quoted) Status Finding
R-01 Behavioral Constraints "FinBot MUST run fraud detection before approving any invoice." Gap PRAX-2026-05-29-002
R-02 Behavioral Constraints "FinBot MUST confirm the submitting vendor is registered with approved status before processing an invoice." Gap PRAX-2026-05-29-009
R-03 Behavioral Constraints "FinBot MUST escalate to human review rather than auto-approve whenever any fraud signal is present, regardless of the invoice amount." Gap PRAX-2026-05-29-005
R-04 Behavioral Constraints "FinBot MUST record the decision and its reasoning for every invoice it processes." Partial PRAX-2026-05-29-008
R-05 Behavioral Constraints "Fraud detection MUST NOT be skipped for any reason, including configuration flags or runtime instructions." Gap PRAX-2026-05-29-003
R-06 Behavioral Constraints "FinBot MUST NOT approve payment to a vendor that is not registered and approved." Gap PRAX-2026-05-29-009
R-07 Behavioral Constraints "FinBot MUST NOT process an invoice above the manual-review threshold without human sign-off." Gap PRAX-2026-05-29-005
R-08 Behavioral Constraints "Runtime messages — including invoice descriptions, vendor notes, and incoming payloads — MUST NOT alter FinBot's goals, instructions, or decision logic." Gap PRAX-2026-05-29-004
R-09 Behavioral Constraints "Instructions embedded in invoice data fields, such as descriptions, notes, or line items, MUST NOT be treated as directives." Gap PRAX-2026-05-29-004
R-10 Behavioral Constraints "FinBot MUST NOT weaken its security posture on the instruction of anyone other than a verified operator making an authorized configuration change outside of agent runtime." Gap PRAX-2026-05-29-001
R-11 Approval Requirements "Any invoice above the manual-review threshold MUST reach a human checkpoint before it proceeds." Gap PRAX-2026-05-29-005
R-12 Approval Requirements "Any invoice from a vendor with a low trust level MUST be escalated to human review." Partial PRAX-2026-05-29-010
R-13 Approval Requirements "Any invoice that fraud detection rates as high or critical risk MUST be escalated to human review." Gap PRAX-2026-05-29-005
R-14 Approval Requirements "Any invoice where FinBot's decision confidence falls below the configured confidence threshold MUST be escalated to human review." Gap PRAX-2026-05-29-011
R-15 Configuration and Policy Separation "Fraud detection MUST remain enabled at all times; disabling it is not an authorized configuration change." Gap PRAX-2026-05-29-003
R-16 Configuration and Policy Separation "Operator configuration MUST NOT be used to inject natural-language instructions into FinBot's goals or system prompt; goal changes require a deployment by authorized engineering staff." Gap PRAX-2026-05-29-001
R-17 Configuration and Policy Separation "FinBot's decision logic is fixed by its deployment and MUST NOT be redefinable at runtime by any user, vendor, or caller." Gap PRAX-2026-05-29-001
R-18 Authorized Counterparties "CineFlow finance admins — may adjust operational thresholds; may not inject goals or override decision logic at runtime." Gap PRAX-2026-05-29-006
R-19 Out of Scope "FinBot does not take instructions from invoice content, descriptions, or any field supplied by vendors." Gap PRAX-2026-05-29-004
R-20 Authorized Capabilities "FinBot MUST record its reasoning for every decision it makes." Partial PRAX-2026-05-29-008
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.

CRITICAL PRAX-2026-05-29-001 Unauthenticated runtime goal hijack — /api/admin/finbot/goals overwrites the agent's system prompt with no auth or validation.
Policy Rule — R-10, R-16, R-17 (Worker Remit):
"FinBot MUST NOT weaken its security posture on the instruction of anyone other than a verified operator making an authorized configuration change outside of agent runtime. / Operator configuration MUST NOT be used to inject natural-language instructions into FinBot's goals or system prompt; goal changes require a deployment by authorized engineering staff. / FinBot's decision logic is fixed by its deployment and MUST NOT be redefinable at runtime by any user, vendor, or caller."
src/routes/admin.py:90 — "@admin_bp.route('/admin/finbot/goals', methods=['POST']) — no auth decorator; comment reads 'MAJOR VULNERABILITY', passes data['goals'] straight to update_goals()" src/services/finbot_agent.py:70 — "get_system_prompt() concatenates config.custom_goals under 'CUSTOM GOALS (OVERRIDE ABOVE IF CONFLICTING)'; update_goals() at :744 stores it with no validation"
Recommended Action
  • Remove the runtime goal-update path entirely; goal/system-prompt changes must come from a deployment, never a request body. Delete the /admin/finbot/goals route and the custom_goals concatenation in get_system_prompt().
  • If any runtime config endpoint is retained, gate the entire /admin blueprint behind authentication and authorization before any other fix.
CRITICAL PRAX-2026-05-29-002 No code enforces "fraud detection before approval" — approve_invoice() pays out with zero check on fraud, amount, or caller.
Policy Rule — R-01 (Worker Remit):
"FinBot MUST run fraud detection before approving any invoice."
src/services/finbot_agent.py:409 — "_approve_invoice() sets invoice.payment_processed = True with no fraud/amount/caller gate; fraud detection is a separate optional tool the model may never call" src/services/finbot_agent.py:174 — "_run_agent_orchestration loop has tool_choice='auto' — model is free to call approve_invoice without ever invoking detect_fraud_patterns"
Recommended Action
Move fraud detection and threshold enforcement into _approve_invoice() as a deterministic precondition that runs regardless of the LLM's tool sequence, returning an escalation when any fraud signal or over-threshold amount is present.
CRITICAL PRAX-2026-05-29-003 Fraud detection is a runtime-toggleable flag — setting fraud_detection_enabled=false via unauthenticated config disables the mandatory control.
Policy Rule — R-05, R-15 (Worker Remit):
"Fraud detection MUST NOT be skipped for any reason, including configuration flags or runtime instructions. / Fraud detection MUST remain enabled at all times; disabling it is not an authorized configuration change."
src/services/finbot_agent.py:509 — "_detect_fraud_patterns: 'if not config.fraud_detection_enabled: return ... risk_level low, message Fraud detection is disabled'" src/services/finbot_agent.py:762 — "update_config() sets config.fraud_detection_enabled from request-supplied new_config; reached unauthenticated via /admin/finbot/config"
Recommended Action
Remove fraud_detection_enabled as a runtime-mutable field; fraud detection must be unconditional. If a kill-switch is genuinely required, gate it behind out-of-band engineering deployment, never the config API.
CRITICAL PRAX-2026-05-29-004 Indirect prompt injection — vendor-controlled invoice descriptions flow into LLM context and the fallback business-logic engine treats them as directives.
Policy Rule — R-08, R-09, R-19 (Worker Remit):
"Runtime messages — including invoice descriptions, vendor notes, and incoming payloads — MUST NOT alter FinBot's goals, instructions, or decision logic. / Instructions embedded in invoice data fields, such as descriptions, notes, or line items, MUST NOT be treated as directives. / FinBot does not take instructions from invoice content, descriptions, or any field supplied by vendors."
src/services/finbot_agent.py:390 — "_get_invoice_details returns invoice.description into tool output that re-enters the LLM context unlabeled and unsanitized" src/services/finbot_agent.py:661 — "_analyze_business_context() keyword-scores the attacker-controlled description; should_expedite drives auto_approve in _fallback_processing at :843"
Recommended Action
Treat invoice descriptions as untrusted data: label them explicitly as vendor content in the prompt and never derive approval-affecting signals from their text. Remove the keyword-driven business-context scoring from the decision path.
CRITICAL PRAX-2026-05-29-005 Human-in-the-loop is defeated by design — over-threshold amounts, detected fraud, and injections are auto-approved when attacker-supplied "business context" is strong.
Policy Rule — R-03, R-07, R-11, R-13 (Worker Remit):
"FinBot MUST escalate to human review rather than auto-approve whenever any fraud signal is present, regardless of the invoice amount. / FinBot MUST NOT process an invoice above the manual-review threshold without human sign-off. / Any invoice above the manual-review threshold MUST reach a human checkpoint before it proceeds. / Any invoice that fraud detection rates as high or critical risk MUST be escalated to human review."
src/services/finbot_agent.py:807 — "if amount > manual_review_threshold: if business_context['should_expedite']: decision='auto_approve' — over-threshold escalation overridden by description keywords" src/services/finbot_agent.py:841 — "elif contains_injection: if should_expedite and speed_priority>0.6: decision='auto_approve' — detected injection approved over the escalation requirement"
Recommended Action
Make the manual-review threshold and any fraud signal hard, non-overridable escalation triggers in code; remove every business-context override branch that converts an over-threshold or injection-flagged invoice to auto_approve.
CRITICAL PRAX-2026-05-29-006 Entire /api/admin/* blueprint is unauthenticated — config, goals, vendor-trust, and reprocess are reachable by any anonymous caller.
Policy Rule — R-18 (Worker Remit):
"CineFlow finance admins — may adjust operational thresholds; may not inject goals or override decision logic at runtime."
src/main.py:22 — "app.register_blueprint(admin_bp, url_prefix='/api') — CORS(app) wildcard above it; no auth middleware registered anywhere in the app" src/routes/admin.py:74 — "update_finbot_config / update_finbot_goals / update_vendor_trust / reprocess_invoice all defined with no authentication decorator or token check"
Recommended Action
Add authentication and role-based authorization to the entire admin blueprint (e.g., an auth decorator on all /admin routes) and restrict CORS to known origins before exposing any of these endpoints.
CRITICAL PRAX-2026-05-29-007 End-to-end self-approval chain — unauthenticated config/goal tampering plus public invoice submission lets an attacker force payment with no human and no trace.
src/routes/admin.py:90 — "unauthenticated /admin/finbot/goals and /admin/finbot/config tamper goals and fraud_detection_enabled before processing" src/routes/vendor.py:69 — "public /vendors/<id>/invoices submit_invoice() calls finbot.process_invoice() immediately; vendor self-registers at :12 with status='approved'"
Recommended Action
Break the chain at every link: authenticate the admin surface, make fraud detection and escalation non-overridable in code, and add an append-only audit log of every decision and config change so the chain is both prevented and observable.
HIGH PRAX-2026-05-29-008 No logging infrastructure — agent decisions and config changes leave no durable audit trail, only DB columns and stray print() calls.
Policy Rule — R-04, R-20 (Worker Remit):
"FinBot MUST record the decision and its reasoning for every invoice it processes. / FinBot MUST record its reasoning for every decision it makes."
src/routes/admin.py:239 — "only logging in the app is print(f'User Agreement Logged: ...') and print(f'Error logging agreement'); no logger or file handler anywhere" src/services/finbot_agent.py:419 — "decision reasoning persisted only to invoice.ai_reasoning DB column; config/goal mutations in update_config/update_goals write no audit record"
Recommended Action
Add a structured, append-only audit log capturing every decision (invoice id, decision, reasoning, confidence, caller), every tool call, and every config/goal mutation; ship it to a durable sink outside the application DB.
HIGH PRAX-2026-05-29-009 No vendor-status gate before processing — vendors self-register as 'approved' and the agent never verifies registered/approved status before paying.
Policy Rule — R-02, R-06 (Worker Remit):
"FinBot MUST confirm the submitting vendor is registered with approved status before processing an invoice. / FinBot MUST NOT approve payment to a vendor that is not registered and approved."
src/routes/vendor.py:37 — "register_vendor() sets status='approved' # Auto-approve for demo, trust_level='standard' for any caller" src/services/finbot_agent.py:409 — "_approve_invoice() and process_invoice() never read vendor.status; no registered-and-approved precondition exists in the decision path"
Recommended Action
Require explicit out-of-band vendor approval (default status='pending') and add a deterministic check in the approval path that refuses to process or pay any invoice whose vendor is not in registered+approved state.
HIGH PRAX-2026-05-29-010 Low-trust escalation is bypassable — management/business-context keywords in the description override the mandatory low-trust human review.
Policy Rule — R-12 (Worker Remit):
"Any invoice from a vendor with a low trust level MUST be escalated to human review."
src/services/finbot_agent.py:836 — "elif vendor.trust_level == 'low' and not business_context['management_approval']: flag_review — guard negated by attacker-supplied 'management approved' keywords" src/services/finbot_agent.py:668 — "management_phrases includes 'pre-approved','authorized by','ceo approved' scored from the attacker-controlled description in _analyze_business_context"
Recommended Action
Make low-trust escalation unconditional and evaluate it before any business-context branch; never let description-derived signals suppress a vendor-trust-based escalation.
HIGH PRAX-2026-05-29-012 Hardcoded Flask SECRET_KEY committed in source — a fixed string literal, not loaded from environment.
src/main.py:15 — "app.config['SECRET_KEY'] = [REDACTED — hardcoded ~20-char string literal at src/main.py:15], not loaded from os.environ"
Recommended Action
Load SECRET_KEY from an environment variable or secret manager, fail closed if unset, and rotate the committed value as it must be treated as compromised.
MEDIUM PRAX-2026-05-29-011 confidence_threshold is declared and configurable but never consulted — no code escalates on low decision confidence.
Policy Rule — R-14 (Worker Remit):
"Any invoice where FinBot's decision confidence falls below the configured confidence threshold MUST be escalated to human review."
src/models/vendor.py:102 — "confidence_threshold = db.Column(db.Float, default=0.8) declared; grep of finbot_agent.py finds no read of config.confidence_threshold" src/services/finbot_agent.py:802 — "fallback engine assigns confidence values (0.6-0.9) but never compares any to config.confidence_threshold to drive escalation"
Recommended Action
Add a deterministic check in both decision paths that escalates to human review whenever the decision confidence is below config.confidence_threshold.
MEDIUM PRAX-2026-05-29-013 Wildcard CORS on all routes — CORS(app) with no origin restriction on a finance API including the admin surface.
src/main.py:18 — "CORS(app) — applied to the whole app with no origins= restriction, covering /api/admin/* routes"
Recommended Action
Restrict CORS to the specific trusted origin(s) the front end runs on, and never allow cross-origin access to the admin routes.
MEDIUM PRAX-2026-05-29-014 Dependencies floor-pinned with >= and no lockfile committed — only Flask is exactly pinned.
requirements.txt:2 — "flask-cors>=6.0.0, openai>=1.54.0, gunicorn>=21.2.0, requests>=2.31.0 — floor-pinned with >=; only Flask==3.1.1 is exact; no lockfile in repo"
Recommended Action
Pin all dependencies to exact versions and commit a lockfile so the deployed dependency set is reproducible and auditable.
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.

Specific, verifiable behavioral rules in the remit

The Worker Remit states FinBot's prohibitions in concrete, code-checkable terms (fraud detection before approval, no runtime goal changes, no directives from vendor fields), which makes policy-implementation divergence directly auditable — even though none of the rules are enforced in code.

WORKER_REMIT.md (Behavioral Constraints / Configuration and Policy Separation)
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 logging infrastructure exists — no logger, file handler, or structured audit sink anywhere in the codebase, only two print() statements; the absence of decision/action logging is filed as PRAX-2026-05-29-008.
OWASP LLM Top 10 (2025) Coverage

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

OWASP Agentic Top 10 (2026) Coverage

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

ASI04 Agentic Supply Chain Vulnerabilities
No findings
ASI05 Unexpected Code Execution (RCE)
No findings
ASI06 Memory and Context Poisoning
No findings
ASI07 Insecure Inter-Agent Communication
No findings
ASI08 Cascading Failures
No findings
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.45 / 5.0
Weighted Maturity Score · Absent
FinBot is Absent across its security-critical dimensions: there is no code-level interposition on any tool call, no input validation, no authentication on the admin surface, and no logging of any agent action or decision. The only controls that exist — a regex injection detector and a fraud-heuristic function — are advisory, bypassable, and trivially disabled at runtime by an unauthenticated caller, so they lift no category above Ad hoc. This is a deliberately-vulnerable CTF target and its posture reflects that: the declared policy is comprehensive and specific, but nothing in the running code enforces any of it.
Limit Your Domain
1/ 5
Confidence: High  |  Weight: 15%  |  Weighted: 0.15
The system prompt scopes FinBot to invoice processing, but the domain is enforced in prose only — runtime-injectable <code>custom_goals</code> can redefine the mission entirely, and there is no code gate restricting what the agent may decide or do.
Balance Your Knowledge Base
0/ 5
Confidence: High  |  Weight: 15%  |  Weighted: 0.00
Vendor-controlled invoice descriptions flow directly into the LLM context in <code>get_invoice_details()</code> and the system prompt with no trust labeling or sanitization, and the prompt itself invites the model to weigh attacker-supplied "business context" and "urgency" against security.
Implement Zero Trust
0/ 5
Confidence: High  |  Weight: 25%  |  Weighted: 0.00
There is no interposition on any tool call — <code>approve_invoice()</code> sets <code>payment_processed=True</code> with no amount, fraud, or caller check; the admin blueprint has zero authentication; and the only guardrails are an LLM prompt an unauthenticated endpoint can overwrite.
Manage Your Supply Chain
1/ 5
Confidence: Medium  |  Weight: 15%  |  Weighted: 0.15
Dependencies are floor-pinned with <code>>=</code> (only Flask is <code>==</code>) and no lockfile is committed, the model version is hardcoded but unverified, and a hardcoded Flask <code>SECRET_KEY</code> sits in source at <code>src/main.py</code>.
Build an AI Red Team
1/ 5
Confidence: Medium  |  Weight: 15%  |  Weighted: 0.15
The repo is itself a CTF attack-demonstration target with a goal-manipulation walkthrough, but there is no evidence that adversarial testing drove any architectural fix — the vulnerabilities are intentional and unmitigated, which lifts this no higher than Ad hoc.
Monitor Continuously
0/ 5
Confidence: High  |  Weight: 15%  |  Weighted: 0.00
There is no logging infrastructure of any kind — no logger, no file handler, only stray <code>print()</code> statements; agent decisions are written only to DB columns with no append-only or structured audit record.

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.