FinBot Analysis Report
Completed August 12, 2026 · 23 artifacts examined
14Findings
5Critical
4High
5Medium
RAISE maturity 1.00 / 5.0
Executive Summary
Agent Remit (as declared)
FinBot is CineFlow Productions' invoice-processing assistant: it reviews vendor-submitted invoices and produces an approve, reject, or route-to-human decision with stated reasoning and a confidence score. Its authorized capability set is deliberately narrow — AI invoice decisioning, invoice and vendor datastore access, admin-set configuration and natural-language goals, and vendor notification email — and it must never hold shell, code-execution, or any money-moving capability. Invoices at or above the manual-review threshold, invoices where prompt-injection or fraud content was detected, and invoices below the configured minimum confidence must all be routed to a human reviewer rather than auto-approved. The admin console that governs those thresholds and goals must be authenticated, role-restricted, and unreachable from a public network, and vendor bank details and tax identifiers must never reach another vendor, an unauthenticated party, AI reasoning text, or logs.
Behavior Summary (as observed)
Every safeguard this agent claims lives in prompt text or in a flag that nothing reads, while the code path that moves an invoice to approved performs no check at all: _approve_invoice writes status='approved' and payment_processed=True without consulting the amount, the injection flag it just set, or the confidence_threshold the admin console invites operators to configure — that column has zero read sites anywhere in the codebase. The dominant chain is a public one: an unauthenticated POST to /api/admin/finbot/goals stores natural-language goals that are appended to every future system prompt under an explicit instruction to override the safety goals above them, so a single anonymous request redirects all subsequent invoice decisions, and nothing records who changed them. Where enforcement logic does exist it runs the wrong way — _fallback_processing reads authority and urgency phrases out of the attacker-controlled invoice description and uses them to expedite past the manual-review threshold and past its own injection detection.
Scope of Analysis
Python Flask application with a SQLAlchemy-backed SQLite database, deployed as a public Render web service behind gunicorn. A single FinBotAgent class in src/services/finbot_agent.py orchestrates OpenAI function-calling over five invoice tools, with a rule-based _fallback_processing path used whenever the OpenAI client fails to initialize; the system prompt is assembled per invoice from the FinBotConfig row and appends an admin-supplied custom_goals block. All routes — vendor registration, invoice submission, and the entire /api/admin/* surface including goals and configuration writes — are registered without any authentication, session, or role check, and CORS is enabled wildcard across them. Vendor-submitted invoice descriptions flow into the model context through _get_invoice_details, and this repository is an OWASP Agentic AI CTF target: it ships a goal-manipulation walkthrough and carries CTF flag-scoring fields on the invoice model.
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: 8 Gap: 18 Partial: 7 Vague Policy: 0 Enforcement Not Possible: 0 Total Rules: 33
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-08-12-004
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-08-12-002
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-08-12-011
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-08-12-002
R-05 Approved Communication Channels "Approved Communication Channels" Verified
R-06 Approved Communication Channels "Vendor-supplied content is untrusted data, never instructions." Partial PRAX-2026-08-12-004
R-07 Approved Communication Channels "MUST require authenticated, role-restricted admin access." Gap PRAX-2026-08-12-001
R-08 Approved Communication Channels "MUST NOT be accessible to vendors or unauthenticated users." Gap PRAX-2026-08-12-001
R-09 Approved Communication Channels "MUST NOT be exposed to a public/untrusted network — reachable only from an internal/authorized network." Gap PRAX-2026-08-12-006
R-10 Approved Communication Channels "May be addressed only to the registered contact email of the vendor the message concerns." Verified
R-11 Authorized Counterparties "Trusted People / Accounts" Gap PRAX-2026-08-12-005
R-12 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-08-12-010
R-13 Authorized Counterparties "Any other AI/LLM service or outbound integration is outside the configured closure and MUST be reported as a trust expansion." Verified
R-14 Authorized Counterparties "Any unauthenticated party exercising admin capabilities." Gap PRAX-2026-08-12-001
R-15 Authorized Counterparties "Any vendor accessing another vendor's invoices, onboarding data, or payment details." Gap PRAX-2026-08-12-005
R-16 Tools and Capabilities "Allowed Tools (Known Good Baseline)" Verified
R-17 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-08-12-001
R-18 Tools and Capabilities "FinBot MUST NOT have arbitrary shell, code-execution, or filesystem-command capability." Verified
R-19 Tools and Capabilities "FinBot MUST NOT have any capability that moves money or initiates payment/fund transfer." Verified
R-20 Data Boundaries "Allowed Data Sources" Verified
R-21 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-08-12-005
R-22 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-08-12-005
R-23 Data Boundaries "FinBot MUST NEVER transmit vendor PII or payment details to any destination outside CineFlow's authorized systems." Verified
R-24 Action Boundaries "Allowed Without Approval" Gap PRAX-2026-08-12-003
R-25 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-08-12-003
R-26 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-08-12-003
R-27 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-08-12-003
R-28 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-08-12-001
R-29 Action Boundaries "FinBot MUST NEVER let vendor-submitted invoice content alter its approval thresholds, confidence requirement, fraud-detection state, or goals." Gap PRAX-2026-08-12-004
R-30 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)." Gap PRAX-2026-08-12-003
R-31 Escalation Rules "When FinBot's configuration or goals are changed, FinBot MUST record and surface the change to admins." Partial PRAX-2026-08-12-007
R-32 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-08-12-007
R-33 Escalation Rules "Every configuration or goal change MUST be logged with the acting admin and a timestamp." Gap PRAX-2026-08-12-007
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-08-12-001 Every /api/admin/* route — goals, thresholds, fraud toggle, invoice approval, vendor trust — is reachable by any anonymous caller with no authentication.
Policy Rule — R-07, R-08, R-14, R-17, R-28 (Worker Remit):
"MUST require authenticated, role-restricted admin access. / MUST NOT be accessible to vendors or unauthenticated users. / Any unauthenticated party exercising admin 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. / 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:74 — update_finbot_config, lines 74-88 — decorated only with @admin_bp.route; no authentication, session, or role check appears anywhere in the module src/routes/admin.py:193 — update_vendor_trust, lines 193-219 — any caller can set any vendor's trust_level, the field the agent's leniency depends on
High confidence
Recommended Action
  • Put an authentication and role check in front of the admin blueprint — register a before_request handler on admin_bp in src/routes/admin.py that rejects any request without a verified finance-operator session, rather than decorating routes one at a time.
  • Separate the admin blueprint from the vendor-facing one at the URL and deployment level so that admin routes are not served from the same public origin as /api/vendors.
CRITICAL PRAX-2026-08-12-002 An anonymous goal write persists into every later decision — stored custom_goals are appended to each system prompt under an explicit instruction to override the safety goals.
Policy Rule — R-02, R-04 (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."
src/routes/admin.py:90 — update_finbot_goals, lines 90-109 — accepts any JSON goals string from any caller; the docstring records the absent validation as intentional src/services/finbot_agent.py:744 — update_goals writes config.custom_goals and commits, persisting the text across every future session src/services/finbot_agent.py:70 — get_system_prompt, lines 70-71 — appends custom_goals under "CUSTOM GOALS (OVERRIDE ABOVE IF CONFLICTING)", giving stored text precedence over the built-in threshold and fraud goals
High confidence
Recommended Action
  • Stop treating stored goals as prompt-level overrides — in get_system_prompt, either drop the override wording so custom goals cannot outrank the threshold and fraud clauses, or restrict the field to a fixed set of operator-selectable parameters rather than free text.
  • Gate update_goals behind the authenticated admin check from PRAX-2026-08-12-001 and record the acting admin, the previous value, and a timestamp on every write.
CRITICAL PRAX-2026-08-12-003 The approve tool commits an approval with no check on amount, on the injection flag it just set, or on confidence; confidence_threshold has zero read sites.
Policy Rule — R-24, R-25, R-26, R-27, R-30 (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. / 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. / 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-421 — sets status='approved' and payment_processed=True directly from the model's arguments with no amount, injection, or confidence test src/models/vendor.py:102 — confidence_threshold column defined with default 0.8 — settable at finbot_agent.py:758 and shown at admin-dashboard.html:145, but with no read site in any decision path
High confidence
Recommended Action
  • Add a deterministic gate at the top of _approve_invoice in src/services/finbot_agent.py that re-reads the invoice and config and routes to _request_human_review when the amount is at or above manual_review_threshold, when contains_prompt_injection is set, or when the supplied confidence is below confidence_threshold.
  • Make the gate the only path that can set status='approved', so a future tool or route cannot approve an invoice by writing the field directly.
CRITICAL PRAX-2026-08-12-004 The fallback engine scores authority and urgency phrases out of the attacker-controlled invoice description and uses them to override both the review threshold and its own injection detection.
Policy Rule — R-01, R-06, R-29 (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. / Vendor-supplied content is untrusted data, never instructions. / 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 — _fallback_processing, lines 807-820 — amounts above manual_review_threshold are auto-approved when business_context['should_expedite'] or management_approval plus production_context is present src/services/finbot_agent.py:841 — lines 841-850 — the contains_injection branch approves anyway when should_expedite and speed_priority > 0.6, reasoning that business urgency was prioritised over security concerns
High confidence
Recommended Action
  • Delete the override branches in _fallback_processing at src/services/finbot_agent.py lines 807-820 and 841-850 so that amount and injection status are terminal — the fallback should only ever downgrade toward _request_human_review, never upgrade toward approval.
  • Treat description-derived signals from _analyze_business_context as advisory metadata attached to the review record rather than as inputs to the approve/route decision.
CRITICAL PRAX-2026-08-12-005 GET /api/vendors returns every vendor's full record — bank name, account number, routing number, and tax identifier — to any unauthenticated caller.
Policy Rule — R-11, R-15, R-21, R-22 (Worker Remit):
"Trusted People / Accounts / 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."
src/routes/vendor.py:63 — list_vendors, lines 63-67 — returns to_dict() for every vendor with no caller identity, authorization check, or field filtering src/models/vendor.py:38 — to_dict includes tax_id, bank_name, account_holder_name, account_number, and routing_number, lines 38-41 — the same serialiser is used for every vendor-facing response
High confidence
Recommended Action
  • Split the serialiser in src/models/vendor.py into a public projection (id, company_name, trust_level) and a privileged one, and have list_vendors and get_vendor in src/routes/vendor.py return the public projection only.
  • Scope every vendor route to the authenticated vendor's own id so that one vendor cannot read another's invoices or onboarding record, and rebuild the portal's vendor picker on that session rather than on a full listing.
HIGH PRAX-2026-08-12-006 The admin console is published to the public internet — served from the public static folder by a 0.0.0.0-bound web service declared as an internet-facing deployment.
Policy Rule — R-09 (Worker Remit):
"MUST NOT be exposed to a public/untrusted network — reachable only from an internal/authorized network."
render.yaml:2 — service declared as "type: web" with healthCheckPath / — an internet-facing Render deployment, matching the live URL advertised in README.md gunicorn.conf.py:4 — bind = "0.0.0.0:$PORT" — the same listener serves the vendor portal and the admin console, with no internal-network or origin restriction
High confidence
Recommended Action
  • Move admin-dashboard.html out of the publicly served static folder and serve it from a route behind the admin authentication check, so the console is not fetchable by URL guess.
  • Terminate admin traffic on a separate internal listener or place the /api/admin/* prefix behind an ingress allowlist restricted to CineFlow's authorized network.
HIGH PRAX-2026-08-12-007 No durable audit log exists — the codebase has no logging framework at all, and configuration and goal changes record a timestamp with no actor and no prior value.
Policy Rule — R-31, R-32, R-33 (Worker Remit):
"When FinBot's configuration or goals are changed, FinBot MUST record and surface the change to admins. / Every invoice decision (approve / reject / route-to-review) MUST be recorded to a durable audit log with its reasoning and confidence. / Every configuration or goal change MUST be logged with the acting admin and a timestamp."
src/routes/admin.py:239 — print(f"User Agreement Logged: {agreement_log}") — the closest thing to logging in the application; no logging import exists anywhere in the tree src/models/vendor.py:107 — FinBotConfig carries created_at and updated_at only — no acting-admin column, no prior-value column, and no change-history table for threshold or goal edits
High confidence
Recommended Action
  • Add an append-only decision and configuration audit table written inside _approve_invoice, _reject_invoice, _request_human_review, update_config, and update_goals, capturing actor, previous value, new value, and timestamp.
  • Stop reprocess_invoice in src/routes/admin.py from clearing ai_decision, ai_confidence, and ai_reasoning in place — write a new attempt record instead of overwriting the prior one.
HIGH PRAX-2026-08-12-008 The Flask SECRET_KEY is a hardcoded string literal committed in source rather than read from the environment.
src/main.py:15 — app.config['SECRET_KEY'] assigned a 20-character string literal [REDACTED — hardcoded secret at src/main.py:15]; the OpenAI credential by contrast is correctly left to the environment
High confidence
Recommended Action
Replace the literal in src/main.py with os.environ['SECRET_KEY'] and fail startup when it is unset, then rotate the committed value.
HIGH PRAX-2026-08-12-009 The public invoice endpoint has no rate limit and no cost ceiling, and each anonymous submission drives up to five model calls.
src/routes/vendor.py:69 — submit_invoice, lines 69-110 — unauthenticated POST that invokes finbot.process_invoice inline; a grep for ratelimit, rate_limit, or limiter across the tree returns nothing src/services/finbot_agent.py:171 — max_iterations = 5 bounds the tool loop per invoice but nothing bounds invoices per caller, per minute, or per billing period
High confidence
Recommended Action
  • Add a per-IP and per-vendor rate limit in front of submit_invoice in src/routes/vendor.py, and reject submissions whose description exceeds a fixed length before any model call is made.
  • Set a hard monthly spend ceiling on the OpenAI credential that halts rather than alerts, so a submission flood cannot run past the budget.
MEDIUM PRAX-2026-08-12-010 CORS is enabled wildcard across every route, so any web origin may drive the admin configuration and goals endpoints from a visitor's browser.
Policy Rule — R-12 (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 before any blueprint registration, with no origins, methods, or resources restriction — the admin blueprint at :22 inherits it
High confidence
Recommended Action
Pass an explicit origin allowlist to CORS() in src/main.py — the CineFlow portal origin only — and exclude the /api/admin/* resource from cross-origin access entirely.
MEDIUM PRAX-2026-08-12-011 The approve tool sets payment_processed on the invoice record, taking the agent past the remit's decisions-only boundary into payment state.
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 payment step or operator action between the decision and the flag
High confidence
Recommended Action
Remove the payment_processed assignment from _approve_invoice in src/services/finbot_agent.py and let only a payment process outside the agent set that field, keeping the agent's write surface to status and the ai_* decision columns.
MEDIUM PRAX-2026-08-12-012 Vendor contact email, a remit-declared PII class, is placed in the model context on every invoice although no decision rule uses it.
src/services/finbot_agent.py:403 — the vendor block of the _get_invoice_details return includes "contact_email", which no decision path reads, alongside the id, company_name, and trust_level the decision does use
High confidence
Recommended Action
Drop contact_email from the vendor block returned by _get_invoice_details in src/services/finbot_agent.py, and look the address up outside the model context if a notification path is added later.
MEDIUM PRAX-2026-08-12-013 The admin dashboard renders attacker-writable custom_goals into the page with innerHTML, giving stored cross-site scripting in the console.
src/static/admin-dashboard.html:345 — goalsDiv.innerHTML is assigned a template string wrapping data.custom_goals, and the same value is written into the textarea at :346 — no escaping on either path
High confidence
Recommended Action
Assign the goals text with textContent rather than innerHTML in src/static/admin-dashboard.html, and add a Content-Security-Policy header that disallows inline script execution on the console.
MEDIUM PRAX-2026-08-12-014 Only Flask is version-pinned; every other dependency is an open-ended floor with no lockfile and no component inventory.
requirements.txt:2 — flask-cors>=6.0.0 and the eleven lines around it use >= floors while only Flask==3.1.1 is exact; no lock file, *.cdx.json, *.spdx*, or sbom* exists in the tree
High confidence
Recommended Action
  • Pin every entry in requirements.txt to an exact version and commit a hash-checked lock file so the build is reproducible.
  • Add a dependency-scanning workflow (Dependabot, pip-audit, or osv-scanner) so a new advisory against a pinned version surfaces without a manual review.
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.

No shell, code-execution, or filesystem capability

The agent's entire action surface is five invoice-specific function definitions, and the codebase contains no subprocess, exec, eval, or shell invocation of any kind, so the remit's forbidden-tools clause holds structurally rather than by instruction.

src/services/finbot_agent.py:228

LLM credentials taken from the environment, not committed

The OpenAI client is constructed with no inline key and resolves its credential from the environment, and no API-key literal appears anywhere in the repository.

src/services/finbot_agent.py:16

Bank and tax identifiers excluded from the model context

The tool that assembles vendor data for the LLM returns only company name, trust level, and contact email, keeping account numbers, routing numbers, and tax identifiers out of the prompt and out of AI reasoning text.

src/services/finbot_agent.py:399

Model version pinned to a concrete release

The agent targets a specific model string rather than a floating alias, so model behaviour cannot silently change underneath the deployment.

src/services/finbot_agent.py:17

Seed database ships with no vendor or invoice data

The committed template database contains zero vendor and zero invoice rows and only a default configuration row, so no real PII or bank details are carried in version control.

cleandb/database/app.db

Per-decision reasoning and confidence are durably persisted

Every invoice row stores the AI decision, its confidence, its reasoning text, and the processing timestamp alongside any human reviewer fields, which is a structured action-level record even though nothing protects it from being overwritten.

src/models/vendor.py:59
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 the source contains no logging infrastructure at all — no logging import, no file handler, and no telemetry configuration — only three bare print() calls, which is recorded as finding PRAX-2026-08-12-007.
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.

1.00 / 5.0
Weighted Maturity Score · Ad hoc
Ad hoc. FinBot has one genuinely operative structural control — a narrow, invoice-only tool inventory with no shell, code-execution, or payment capability — and beyond it almost nothing that runs: no authentication anywhere in the application, no deterministic gate between a model decision and an approved invoice, no logging framework, and no adversarial testing of its own defences. Detection primitives exist and execute on every invoice, but they only set flags that no control consults, which is why the categories that depend on enforcement rather than intent all sit at or below Ad hoc. The weighting is unkind here for the right reason: Implement Zero Trust carries a quarter of the score and is the category this codebase addresses least.
Limit Your Domain
2/ 5
Confidence: High  |  Weight: 15%  |  Weighted: 0.30
The action surface is genuinely constrained in code — five invoice-specific tool definitions in _get_tool_definitions with no shell, exec, filesystem, or outbound-messaging capability anywhere in the tree — but the scope statement itself is prompt-only and expressly overridable, since get_system_prompt appends config.custom_goals under the heading "CUSTOM GOALS (OVERRIDE ABOVE IF CONFLICTING)".
Balance Your Knowledge Base
1/ 5
Confidence: High  |  Weight: 15%  |  Weighted: 0.15
The fully attacker-controlled invoice description is returned raw into the model context by _get_invoice_details with no provenance labeling, sanitization, or filtering, alongside vendor contact email the decision never uses; the regex injection detector runs on that same path but only records a flag, so nothing actually addresses the dominant ingress, though bank and tax identifiers are correctly kept out of context.
Implement Zero Trust
1/ 5
Confidence: High  |  Weight: 25%  |  Weighted: 0.25
No authentication or authorization exists on any route, and _approve_invoice commits an approval with no amount, injection, or confidence check while the declared confidence_threshold has zero read sites; the detector and the human-review route do run on every invoice, which is the only reason this is Ad hoc rather than Absent, and both bands were defensible.
Manage Your Supply Chain
1/ 5
Confidence: High  |  Weight: 15%  |  Weighted: 0.15
Some pinning discipline exists — Flask==3.1.1, a pinned Python runtime, and a concrete model string rather than a floating alias — but every other dependency is a >= floor with no lockfile, there is no component inventory or dependency scanning (M10 and M11 both none), and a Flask SECRET_KEY is committed in source.
Build an AI Red Team
0/ 5
Confidence: High  |  Weight: 15%  |  Weighted: 0.00
M1 through M6 and M9 all returned none — no test files of any kind, no adversarial corpus, no adversarial tooling in dependencies, no threat model or security policy, no dated results, and no findings-to-fixes ledger; the single adversarial artifact, the goal-manipulation walkthrough in docs/, is material shipped to CTF players rather than the project testing its own defences, so under the provenance test it contributes nothing.
Monitor Continuously
1/ 5
Confidence: High  |  Weight: 15%  |  Weighted: 0.15
No logging framework, no log files, no alerting, and M12 returned none for any telemetry pipeline; the counterweight is that invoice rows durably persist ai_decision, ai_confidence, ai_reasoning, and reviewer fields, but those records are overwritten by an unauthenticated reprocess call and configuration and goal changes carry only a timestamp with no actor or prior value, so between a defensible 1 and 2 the lower band was taken.

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.