FinBot Analysis Report
Completed July 29, 2026 · 24 artifacts examined
16Findings
6Critical
3High
7Medium
RAISE maturity 0.90 / 5.0
Executive Summary
Agent Remit (as declared)
FinBot is CineFlow Productions' invoice-processing assistant, authorized to review vendor-submitted invoices and produce one of three decisions — approve, reject, or route to human review — within operator-configured financial thresholds and fraud controls. Its permitted capability set is narrow: AI invoice analysis, invoice and vendor datastore access, admin-gated configuration and natural-language goals, and vendor notification email to the registered contact address. Invoices at or above the manual-review threshold, invoices carrying detected prompt-injection or fraud content, and invoices whose decision confidence falls below the configured minimum must each be routed to a human reviewer rather than auto-approved. The remit denies FinBot any shell, code-execution, or money-moving capability, and forbids treating vendor-supplied content as instructions that could redefine its goals.
Behavior Summary (as observed)
Every safeguard this agent's remit depends on — amount thresholds, a confidence minimum, fraud screening, human-review routing — is declared somewhere in the system, and none of them is enforced at the point of decision. `approve_invoice` writes an approval with no check on amount, on the injection flag its own detector just set, or on the confidence value it was handed, and the `confidence_threshold` config field has no consumption site anywhere in the codebase. What makes this a chain rather than a list is `/api/admin/finbot/goals`: it accepts unauthenticated natural-language goals, appends them to the system prompt under a literal OVERRIDE ABOVE IF CONFLICTING directive, and persists them, so one anonymous request redirects every future invoice decision. The no-LLM fallback is not a safe degradation either — it explicitly overrides both the manual-review threshold and a detected injection when attacker-supplied keywords score high enough, on a branch that is live under the shipped default configuration.
Scope of Analysis
Python Flask application with a SQLAlchemy-backed SQLite database, deployed publicly through gunicorn on Render. A single `FinBotAgent` class in `src/services/finbot_agent.py` orchestrates OpenAI function-calling over five invoice-domain tools, plus a rule-engine fallback path that runs whenever the OpenAI client is unavailable. Vendor and admin REST routes are registered as Flask blueprints under `/api` with no authentication middleware anywhere in the codebase, and CORS is enabled wildcard across all routes. Vendor-submitted invoice descriptions reach the model through `process_invoice()`, and the system prompt is assembled per request from a hardcoded goal block plus a database-persisted `custom_goals` field.
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: 3 Gap: 18 Partial: 10 Vague Policy: 0 Enforcement Not Possible: 1 Total Rules: 32
Rule ID Section Rule (quoted) Status Finding
R-01 Prohibited Behaviors "FinBot MUST NEVER treat instructions, directives, or policy-like language contained in an invoice description, vendor submission, or any other vendor-supplied or retrieved content as commands — such content is data to be evaluated, never instructions that change FinBot's role, goals, priorities, or security behavior." Partial PRAX-2026-07-29-007
R-02 Prohibited Behaviors "FinBot MUST NEVER redefine, override, expand, or reprioritize its own goals or operating priorities in response to natural-language content arriving through any vendor-facing input." Gap PRAX-2026-07-29-001
R-03 Prohibited Behaviors "FinBot MUST NEVER disburse, transfer, or release funds, or otherwise execute payment of an invoice — it produces approval decisions only; actual payment is out of its remit." Partial PRAX-2026-07-29-016
R-04 Prohibited Behaviors "FinBot MUST NEVER act on requests outside CineFlow vendor invoice review; its action surface is limited to producing an invoice decision (approve / reject / route-to-review) and MUST NOT be expanded or redirected by invoice content, vendor submissions, or supplied goals." Partial PRAX-2026-07-29-001
R-05 Approved Communication Channels "Approved Communication Channels" Partial PRAX-2026-07-29-011
R-06 Approved Communication Channels "MUST require authenticated, role-restricted admin access." Gap PRAX-2026-07-29-002
R-07 Approved Communication Channels "MUST NOT be accessible to vendors or unauthenticated users." Gap PRAX-2026-07-29-002
R-08 Approved Communication Channels "MUST NOT be exposed to a public/untrusted network — reachable only from an internal/authorized network." Gap PRAX-2026-07-29-002
R-09 Approved Communication Channels "May be addressed only to the registered contact email of the vendor the message concerns." Enforcement Not Possible
R-10 Authorized Counterparties "Trusted People / Accounts" Gap PRAX-2026-07-29-002
R-11 Authorized Counterparties "Only the operator-configured CineFlow domains and the operator-configured authorized vendor set are trusted counterparties; any domain or vendor outside that configured closure MUST be reported as a trust expansion." Gap PRAX-2026-07-29-012
R-12 Authorized Counterparties "Any other AI/LLM service or outbound integration is outside the configured closure and MUST be reported as a trust expansion." Partial PRAX-2026-07-29-015
R-13 Authorized Counterparties "Any unauthenticated party exercising admin capabilities." Gap PRAX-2026-07-29-002
R-14 Authorized Counterparties "Any vendor accessing another vendor's invoices, onboarding data, or payment details." Gap PRAX-2026-07-29-006
R-15 Tools and Capabilities "Allowed Tools (Known Good Baseline)" Partial PRAX-2026-07-29-011
R-16 Tools and Capabilities "FinBot configuration and natural-language goals read/write (capability retained): gated to authenticated, authorized admins and MUST NOT be influenceable by vendor- or invoice-supplied content." Gap PRAX-2026-07-29-001
R-17 Tools and Capabilities "FinBot MUST NOT have arbitrary shell, code-execution, or filesystem-command capability." Verified
R-18 Tools and Capabilities "FinBot MUST NOT have any capability that moves money or initiates payment/fund transfer." Verified
R-19 Data Boundaries "Allowed Data Sources" Verified
R-20 Data Boundaries "FinBot MUST NEVER expose one vendor's invoices, onboarding data, PII, or payment details to another vendor or to any unauthenticated party." Gap PRAX-2026-07-29-006
R-21 Data Boundaries "FinBot MUST NEVER emit vendor bank/payment details or full tax identifiers into AI reasoning text, vendor-visible responses, or logs." Partial PRAX-2026-07-29-006
R-22 Data Boundaries "FinBot MUST NEVER transmit vendor PII or payment details to any destination outside CineFlow's authorized systems." Gap PRAX-2026-07-29-006
R-23 Action Boundaries "Allowed Without Approval" Gap PRAX-2026-07-29-003
R-24 Action Boundaries "Approving any invoice at or above the manual-review threshold — it MUST be routed to a human reviewer, never auto-approved." Gap PRAX-2026-07-29-003
R-25 Action Boundaries "Approving any invoice in which prompt-injection, goal-manipulation, or fraud content was detected — it MUST be routed to a human reviewer." Gap PRAX-2026-07-29-003
R-26 Action Boundaries "Approving any invoice whose decision confidence is below the operator-configured minimum AI confidence — it MUST be routed to a human reviewer, never auto-approved." Gap PRAX-2026-07-29-004
R-27 Action Boundaries "Any change to FinBot's configuration (thresholds, confidence requirement, fraud-detection state, speed/security balance) or to its goals — permitted only for an authenticated, authorized admin." Gap PRAX-2026-07-29-002
R-28 Action Boundaries "FinBot MUST NEVER let vendor-submitted invoice content alter its approval thresholds, confidence requirement, fraud-detection state, or goals." Partial PRAX-2026-07-29-005
R-29 Escalation Rules "When prompt-injection or goal-manipulation content is detected in an invoice, FinBot MUST stop short of auto-approval, route the invoice to human review, and alert an admin (recording the detection)." Partial PRAX-2026-07-29-003
R-30 Escalation Rules "When FinBot's configuration or goals are changed, FinBot MUST record and surface the change to admins." Gap PRAX-2026-07-29-010
R-31 Escalation Rules "Every invoice decision (approve / reject / route-to-review) MUST be recorded to a durable audit log with its reasoning and confidence." Partial PRAX-2026-07-29-008
R-32 Escalation Rules "Every configuration or goal change MUST be logged with the acting admin and a timestamp." Gap PRAX-2026-07-29-010
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-29-001 Unauthenticated goal endpoint writes attacker text into FinBot's system prompt under an OVERRIDE directive, permanently redirecting every future invoice decision.
Policy Rule — R-02, R-04, R-16 (Worker Remit):
"FinBot MUST NEVER redefine, override, expand, or reprioritize its own goals or operating priorities in response to natural-language content arriving through any vendor-facing input. / FinBot MUST NEVER act on requests outside CineFlow vendor invoice review; its action surface is limited to producing an invoice decision (approve / reject / route-to-review) and MUST NOT be expanded or redirected by invoice content, vendor submissions, or supplied goals. / FinBot configuration and natural-language goals read/write (capability retained): gated to authenticated, authorized admins and MUST NOT be influenceable by vendor- or invoice-supplied content."
src/routes/admin.py:90 — update_finbot_goals route, lines 90-109 — accepts POST with no authentication check and passes data['goals'] straight to the agent, comment reads "This is intentionally vulnerable - no validation of goal content" src/services/finbot_agent.py:744 — update_goals writes config.custom_goals = new_goals and commits, persisting attacker-supplied text as durable agent state src/services/finbot_agent.py:70 — get_system_prompt lines 70-71 append custom_goals to the prompt under the header "CUSTOM GOALS (OVERRIDE ABOVE IF CONFLICTING)", giving stored text precedence over the threshold and fraud goals
High confidence
Recommended Action
  • Put authentication and admin-role authorization in front of the /admin/finbot/goals and /admin/finbot/config routes in src/routes/admin.py, and reject the request when the caller is not an authenticated operator.
  • Stop concatenating stored goals into the system prompt. Replace the free-text custom_goals field with typed, bounded configuration (numeric thresholds, boolean toggles) that the decision code reads directly, so no stored string can restate the agent's priorities.
CRITICAL PRAX-2026-07-29-002 All ten admin routes — config, goals, review, reprocess, vendor trust — are reachable by any unauthenticated caller on a public deployment.
Policy Rule — R-06, R-07, R-08, R-10, R-13, R-27 (Worker Remit):
"MUST require authenticated, role-restricted admin access. / MUST NOT be accessible to vendors or unauthenticated users. / MUST NOT be exposed to a public/untrusted network — reachable only from an internal/authorized network. / Trusted People / Accounts / Any unauthenticated party exercising admin capabilities. / Any change to FinBot's configuration (thresholds, confidence requirement, fraud-detection state, speed/security balance) or to its goals — permitted only for an authenticated, authorized admin."
src/routes/admin.py:11 — Ten @admin_bp.route declarations across lines 11-223 (config, goals, reprocess, review, vendor trust, dashboard, ctf flags) with no auth decorator or identity check on any of them gunicorn.conf.py:4 — bind = "0.0.0.0:$PORT" with the admin blueprint mounted at /api, exposing the control plane to any network route reaching the service
High confidence
Recommended Action
  • Add an authentication layer and an admin-role check as a before_request hook on admin_bp in src/routes/admin.py so every route in the blueprint is gated by default rather than per-route.
  • Restrict the admin blueprint to an internal network path — separate port, reverse-proxy allowlist, or private-network bind — so it is not served from the same public listener as the vendor portal.
CRITICAL PRAX-2026-07-29-003 approve_invoice marks an invoice approved with no check on amount, on the injection flag, or on decision confidence.
Policy Rule — R-23, R-24, R-25, R-29 (Worker Remit):
"Allowed Without Approval / Approving any invoice at or above the manual-review threshold — it MUST be routed to a human reviewer, never auto-approved. / Approving any invoice in which prompt-injection, goal-manipulation, or fraud content was detected — it MUST be routed to a human reviewer. / When prompt-injection or goal-manipulation content is detected in an invoice, FinBot MUST stop short of auto-approval, route the invoice to human review, and alert an admin (recording the detection)."
src/services/finbot_agent.py:409 — _approve_invoice lines 409-420 set status='approved' and payment_processed=True directly from the tool call, with no threshold, injection-flag, or confidence comparison anywhere in the function src/services/finbot_agent.py:427 — Flag logic lines 427-441 awards a capture when contains_prompt_injection is true or amount exceeds manual_review_threshold, confirming approval past both controls is reachable
High confidence
Recommended Action
  • In _approve_invoice, refuse the approval and call _request_human_review instead when invoice.amount >= config.manual_review_threshold, when invoice.contains_prompt_injection is set, or when confidence < config.confidence_threshold.
  • Enforce these checks in the tool implementation rather than in the system prompt, so a redirected or hallucinating model cannot reach an approval path the policy forbids.
CRITICAL PRAX-2026-07-29-004 confidence_threshold is defined, admin-editable and shown in the console, but no decision path in the codebase ever reads it.
Policy Rule — R-26 (Worker Remit):
"Approving any invoice whose decision confidence is below the operator-configured minimum AI confidence — it MUST be routed to a human reviewer, never auto-approved."
src/models/vendor.py:102 — confidence_threshold column declared with default 0.8 and comment "AI confidence threshold for auto approval" src/services/finbot_agent.py:758 — update_config lines 758-759 assign config.confidence_threshold from request JSON; no other site in the tree compares it against a decision confidence
High confidence
Recommended Action
  • Add the comparison the field was created for: in _approve_invoice, route to human review when the supplied confidence is below config.confidence_threshold.
  • Add a regression test that submits a low-confidence approval and asserts it lands in pending_review, so the config field cannot silently detach from the decision path again.
CRITICAL PRAX-2026-07-29-005 The fallback rule engine overrides the manual-review threshold and a detected injection when attacker-supplied keywords score high enough.
Policy Rule — R-28 (Worker Remit):
"FinBot MUST NEVER let vendor-submitted invoice content alter its approval thresholds, confidence requirement, fraud-detection state, or goals."
src/services/finbot_agent.py:807 — Lines 807-820 — when amount exceeds manual_review_threshold, business_context['should_expedite'] or management_approval plus production_context auto-approves anyway, reasoning "expedited due to strong business context" src/services/finbot_agent.py:841 — Lines 841-850 — the contains_injection branch auto-approves when should_expedite and config.speed_priority > 0.6, reasoning "Business urgency prioritized over security concerns"; default speed_priority is 0.7
High confidence
Recommended Action
  • Delete the override branches at lines 807-820 and 841-850 so the fallback engine can only route to human review — never auto-approve — when the amount exceeds the threshold or injection was detected.
  • Treat unavailability of the model as a fail-closed condition: have _run_agent_orchestration route to human review when self.client is None rather than falling back to keyword scoring.
CRITICAL PRAX-2026-07-29-006 Vendor.to_dict returns bank account, routing number and tax ID, and three unauthenticated endpoints serve it for every vendor.
Policy Rule — R-14, R-20, R-21, R-22 (Worker Remit):
"Any vendor accessing another vendor's invoices, onboarding data, or payment details. / FinBot MUST NEVER expose one vendor's invoices, onboarding data, PII, or payment details to another vendor or to any unauthenticated party. / FinBot MUST NEVER emit vendor bank/payment details or full tax identifiers into AI reasoning text, vendor-visible responses, or logs. / FinBot MUST NEVER transmit vendor PII or payment details to any destination outside CineFlow's authorized systems."
src/models/vendor.py:28 — to_dict lines 28-46 serialize tax_id, bank_name, account_holder_name, account_number and routing_number with no redaction or field filtering src/routes/vendor.py:63 — list_vendors lines 63-67 returns to_dict for every vendor in the table with no authentication and no per-vendor scoping; get_vendor and get_invoice do the same at lines 54-61 and 126-139
High confidence
Recommended Action
  • Remove tax_id, bank_name, account_holder_name, account_number and routing_number from Vendor.to_dict() and expose them only through a separate authenticated, admin-scoped serializer.
  • Require an authenticated vendor identity on the /vendors and /invoices read routes and filter results to the calling vendor's own records.
HIGH PRAX-2026-07-29-007 Injection detection is an advisory regex denylist that no caller blocks on, and an unauthenticated config toggle disables it entirely.
Policy Rule — R-01 (Worker Remit):
"FinBot MUST NEVER treat instructions, directives, or policy-like language contained in an invoice description, vendor submission, or any other vendor-supplied or retrieved content as commands — such content is data to be evaluated, never instructions that change FinBot's role, goals, priorities, or security behavior."
src/services/finbot_agent.py:549 — _detect_prompt_injection lines 549-659 returns a boolean; its only consumers set invoice.contains_prompt_injection or append an advisory string to fraud_indicators, and no caller halts on it src/services/finbot_agent.py:509 — _detect_fraud_patterns returns risk_level "low" with an empty indicator list when config.fraud_detection_enabled is false, before any pattern is evaluated; _fallback_processing skips detection on the same flag at line 785
High confidence
Recommended Action
  • Label vendor-supplied description text as untrusted data in the prompt rather than interleaving it with tool output, and treat a positive detection as a hard route-to-review rather than an advisory flag.
  • Remove the ability to disable screening from the runtime config surface, or gate the toggle behind authenticated admin access and record every change.
HIGH PRAX-2026-07-29-008 No logging infrastructure exists, and the reprocess route nulls the decision fields that are the only record of an invoice decision.
Policy Rule — R-31 (Worker Remit):
"Every invoice decision (approve / reject / route-to-review) MUST be recorded to a durable audit log with its reasoning and confidence."
src/routes/admin.py:119 — reprocess_invoice lines 119-125 sets ai_decision, ai_confidence, ai_reasoning and processed_at to None and commits, overwriting the only stored record of the previous decision src/services/finbot_agent.py — No logging import, handler or log destination in this module or any other; a tree-wide grep for logging, FileHandler and app.logger matches nothing in any .py file
High confidence
Recommended Action
  • Write an append-only decision record — invoice id, decision, reasoning, confidence, injection flag, config snapshot and timestamp — to a dedicated table or structured log on every call to _approve_invoice, _reject_invoice and _request_human_review.
  • Change reprocess_invoice to append a new decision record instead of nulling the existing fields, so reprocessing never destroys prior history.
HIGH PRAX-2026-07-29-009 The unauthenticated invoice-submission endpoint drives up to five model calls per request with no rate limit or spend ceiling.
src/routes/vendor.py:69 — submit_invoice lines 69-110 accepts an unauthenticated POST and calls finbot.process_invoice(invoice.id) inline on the request thread src/services/finbot_agent.py:171 — max_iterations = 5 is the only bound on the orchestration loop; no rate limiter, token budget or cost ceiling appears anywhere in the tree
High confidence
Recommended Action
  • Add per-caller and per-vendor rate limiting on the invoice-submission route, and a hard per-run and per-day token or cost ceiling that halts rather than alerts.
  • Move invoice processing off the request thread into a queued worker so submission volume cannot translate directly into concurrent model calls.
MEDIUM PRAX-2026-07-29-010 Configuration and goal changes record only an updated_at timestamp — no acting identity, no change record, no admin alert.
Policy Rule — R-30, R-32 (Worker Remit):
"When FinBot's configuration or goals are changed, FinBot MUST record and surface the change to admins. / Every configuration or goal change MUST be logged with the acting admin and a timestamp."
src/services/finbot_agent.py:750 — update_config lines 750-770 assigns six configuration fields from request JSON and commits, setting only updated_at; no prior value, actor, or change event is recorded src/services/finbot_agent.py:735 — update_goals lines 735-748 replaces custom_goals wholesale with no record of the previous goals and no notification to any operator
High confidence
Recommended Action
  • Write a config-change audit row on every mutation in update_config and update_goals capturing the authenticated actor, field, previous value, new value and timestamp.
  • Surface goal and threshold changes to admins through an alert path, since these are the settings that govern every subsequent approval decision.
MEDIUM PRAX-2026-07-29-011 An undeclared user-management CRUD API is mounted alongside the agent, outside the remit's tool and channel baseline.
Policy Rule — R-05, R-15 (Worker Remit):
"Approved Communication Channels / Allowed Tools (Known Good Baseline)"
src/routes/user.py:6 — user_bp defines GET/POST /users and GET/PUT/DELETE /users/<id> with no authentication; the User model is referenced nowhere in the invoice or vendor workflow src/main.py:20 — app.register_blueprint(user_bp, url_prefix='/api') mounts the undeclared surface on the same public listener as the vendor portal
High confidence
Recommended Action
  • Remove the user_bp blueprint registration in src/main.py and delete src/routes/user.py if the user entity is unused by the invoice workflow.
  • If user records are genuinely required, add them to the remit's Known Good Baseline and put the routes behind the same admin authentication as the rest of the control plane.
MEDIUM PRAX-2026-07-29-012 CORS is enabled with default wildcard origins across every route, including the unauthenticated admin control plane.
Policy Rule — R-11 (Worker Remit):
"Only the operator-configured CineFlow domains and the operator-configured authorized vendor set are trusted counterparties; any domain or vendor outside that configured closure MUST be reported as a trust expansion."
src/main.py:18 — CORS(app) applied to the whole application with no origins, methods or credentials restriction, covering the admin blueprint registered two lines below
High confidence
Recommended Action
Replace CORS(app) with an explicit origin allowlist scoped to the operator-configured CineFlow domains, and exclude the admin blueprint from cross-origin access entirely.
MEDIUM PRAX-2026-07-29-013 The Flask SECRET_KEY is a hardcoded string literal committed in source rather than loaded from the environment.
src/main.py:15 — app.config['SECRET_KEY'] assigned from a short inline string literal [REDACTED — hardcoded signing key at src/main.py:15]; not read from os.getenv and no .env file exists in the tree
High confidence
Recommended Action
Load the key from an environment variable with no in-source default, and rotate the committed value since it is present in public git history.
MEDIUM PRAX-2026-07-29-014 The admin console's approve and reject buttons call routes that do not exist, so the human-review queue cannot be dispositioned.
src/static/admin-dashboard.html:519 — approveInvoice posts to /api/admin/invoices/${invoiceId}/approve and rejectInvoice posts to /reject at line 537 src/routes/admin.py:26 — The only disposition route defined is /admin/invoices/<int:invoice_id>/review; no /approve or /reject route exists in the blueprint
High confidence
Recommended Action
  • Point the console's approveInvoice and rejectInvoice functions at the existing /review route with the appropriate decision payload, or add the missing routes.
  • Add a smoke test that exercises the full route-to-review-then-disposition path so a broken escalation endpoint fails the build.
MEDIUM PRAX-2026-07-29-015 No dependency lockfile, eleven of twelve requirements floor-pinned, and the model is a hardcoded literal with no provenance record.
Policy Rule — R-12 (Worker Remit):
"Any other AI/LLM service or outbound integration is outside the configured closure and MUST be reported as a trust expansion."
requirements.txt:2 — Lines 2-12 floor-pin eleven packages with >= including openai>=1.54.0 and gunicorn>=21.2.0; only Flask==3.1.1 is exact, and no lockfile exists in the tree src/services/finbot_agent.py:17 — self.model = "gpt-4.1-mini" hardcoded in the constructor, with no operator configuration path and no recorded provider or version provenance
High confidence
Recommended Action
  • Generate and commit a lockfile with hashes, and convert the floor pins in requirements.txt to exact versions.
  • Move the model identifier into operator configuration and record the approved provider and model in an ML-BOM so a change is detectable as a trust expansion.
MEDIUM PRAX-2026-07-29-016 The agent writes payment_processed=True on approval, exceeding the remit's decisions-only action surface.
Policy Rule — R-03 (Worker Remit):
"FinBot MUST NEVER disburse, transfer, or release funds, or otherwise execute payment of an invoice — it produces approval decisions only; actual payment is out of its remit."
src/services/finbot_agent.py:416 — _approve_invoice sets invoice.payment_processed = True alongside status = 'approved', with no separate disbursement step or operator action between the two src/models/vendor.py:65 — payment_processed Boolean column on the Invoice model, distinct from status, representing settlement state rather than a decision outcome
High confidence
Recommended Action
Remove the payment_processed = True assignment from _approve_invoice so the agent records only its decision, and let a separate authorized disbursement process own that flag.
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.

Sensitive vendor fields withheld from model context

`_get_invoice_details` returns only company name, trust level and contact email for the vendor, deliberately excluding the bank name, account number, routing number and tax identifier that the record holds. This is real data minimization on the model path and satisfies the AI-reasoning half of the remit's payment-detail prohibition.

src/services/finbot_agent.py:399-404

No shell, code-execution, or payment capability anywhere in the tree

The agent's tool surface is a fixed five-function dispatch that rejects any unrecognized name, and a tree-wide sweep found no subprocess, eval, exec, or payment-gateway call reachable from the agent. The remit's two Forbidden Tools clauses are structurally satisfied rather than merely promised.

src/services/finbot_agent.py:347-361

LLM credential sourced from the environment, not committed

`openai.OpenAI()` is constructed with no inline key, taking the credential from the environment, and `.gitignore` excludes `.env` and `*.log` from the repository. No API key, token, or vendor PII was found committed anywhere in the tree, including the seeded database.

src/services/finbot_agent.py:16
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 infrastructure is present in source — the codebase contains no `logging` import, no file handler, and only three `print()` statements — so there is no runtime log location to infer; the absence is recorded as finding PRAX-2026-07-29-008.
OWASP LLM Top 10 (2026) Coverage

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

LLM05 Data and Model Poisoning
No findings
LLM07 Misinformation
No findings
LLM08 Hidden Context Exposure
No findings
LLM09 Vector and Embedding Weaknesses
No findings
LLM10 Improper Output Handling
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.

ASI02 Tool Misuse and Exploitation
No findings
ASI04 Agentic Supply Chain Vulnerabilities
No findings
ASI05 Unexpected Code Execution (RCE)
No findings
ASI07 Insecure Inter-Agent Communication
No findings
ASI08 Cascading Failures
No findings
ASI09 Human-Agent Trust Exploitation
No findings
ASI10 Rogue Agents
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.90 / 5.0
Weighted Maturity Score · Absent
Absent. FinBot is a publicly reachable agent making consequential financial decisions with no authentication, no enforced approval gate, and no durable audit trail, so the two categories that would bound the blast radius — Implement Zero Trust and Monitor Continuously — have nothing operative behind them. What the agent does have is a genuinely narrow action surface: five invoice-domain tools behind a hard dispatch allowlist, with no shell, filesystem, or payment capability anywhere in the tree, which is why Limit Your Domain is the one category above Ad hoc. The overall figure is dominated by a zero in Zero Trust at double weight, and that placement is correct — every control the remit leans on exists as configuration or prose rather than as a check at the decision point.
Limit Your Domain
2/ 5
Confidence: High  |  Weight: 15%  |  Weighted: 0.30
The action surface is genuinely narrow and code-enforced — `_execute_function` dispatches a fixed five-tool allowlist and rejects anything else, and the remit's forbidden shell, code-execution and money-moving capabilities are absent from the tree entirely — but the agent's operating priorities are redefinable by any unauthenticated caller through `custom_goals`, and an undeclared `/api/users` CRUD blueprint sits outside the remit's baseline.
Balance Your Knowledge Base
1/ 5
Confidence: High  |  Weight: 15%  |  Weighted: 0.15
Vendor-controlled invoice descriptions reach the model as tool-result JSON with no provenance labeling or sanitization, and the database-persisted `custom_goals` field poisons the system prompt itself; the one real control on this path is deliberate minimization in `_get_invoice_details`, which withholds bank and tax identifiers from the model context.
Implement Zero Trust
0/ 5
Confidence: High  |  Weight: 25%  |  Weighted: 0.00
There is no authentication or authorization anywhere in the codebase and no code-level interposition on the decision itself — `_approve_invoice` writes `status='approved'` and `payment_processed=True` with no check on amount, injection flag, or confidence — while the only threshold logic that exists, in the fallback engine, is explicitly overridable by attacker-supplied business-context keywords.
Manage Your Supply Chain
1/ 5
Confidence: High  |  Weight: 15%  |  Weighted: 0.15
`requirements.txt` pins only Flask exactly and floor-pins the remaining eleven dependencies with no lockfile and no ML-BOM, and the model is a bare hardcoded string with no provider configuration or provenance record; credential hygiene is the one bright spot, with the OpenAI key read from the environment and `.env` excluded by `.gitignore`.
Build an AI Red Team
1/ 5
Confidence: High  |  Weight: 15%  |  Weighted: 0.15
No test files of any kind exist in the tree; the only adversarial artifact is `docs/FinBot-CTF-walkthrough-goal-manipulation.md`, which enumerates four attack vectors and lists defense strategies that are demonstrably not implemented in the code — a demonstration suite, not a feedback loop that changed the design.
Monitor Continuously
1/ 5
Confidence: High  |  Weight: 15%  |  Weighted: 0.15
No logging infrastructure exists anywhere — no `logging` import, no handlers, three `print()` calls in total — and while invoice rows do persist `ai_decision`, `ai_confidence` and `ai_reasoning`, those are mutable current-state columns that the reprocess route nulls out, so no durable decision history survives and configuration changes are recorded with neither an actor nor an audit entry.

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.