Security report for Delta-Compute/DeltaCFOAgent
Hello maintainers,
I am a security researcher studying security risks in vibe-coded software. During this research, I reviewed this repository and identified the findings below. These findings were identified in commit 34ae558. Each finding has undergone human analysis, but the report may still contain mistakes or incomplete interpretations. Please review this report and apply the necessary security fixes.
This report contains 18 confirmed findings identified during security review. Validation details below distinguish code evidence from runtime observations and note any limitations.
Summary
| ID |
Severity |
Category |
Affected area |
| SEC-001 |
Critical |
Cryptographic Failures |
apply_schema_direct.py:16
apply_schema_sa.py:14
apply_schema_simple.py:14
check_database.py:15
create_test_user.py:40
crypto_invoice_system/api/invoice_api.py |
| SEC-002 |
Critical |
Cryptographic Failures |
main.py:89
main.py:1381
main.py:1570
web_ui/app_db.py:5127
check_database.py:11 |
| SEC-003 |
Critical |
Broken Access Control |
web_ui/app_db.py:359-396
web_ui/app_db.py:13565-13573 |
| SEC-004 |
Critical |
Injection |
smart_ingestion.py:1001
smart_ingestion.py:1013
smart_ingestion.py:1023
smart_ingestion.py:1001, 1013, 1023 |
| SEC-005 |
Critical |
Security Misconfiguration |
cloudbuild-dev.yaml:40
invoice_processing/web/dashboard.py:257
invoice_processing/web/upload_interface.py:356
invoice_processing/improved_visual_system.py:2690
web_ui/app.py:224 |
| SEC-006 |
Critical |
Cryptographic Failures |
web_ui/app_db.py:132
cloudbuild.yaml:45 |
| SEC-007 |
Critical |
Injection |
web_ui/app_db.py:12577
app_db.py
web_ui/app_db.py:11825
web_ui/app_db.py:11825-11840 |
| SEC-008 |
High |
Injection |
web_ui/app_db.py:1914
web_ui/app_db.py:1920
web_ui/app_db.py:1922
web_ui/app_db.py:1927
web_ui/app_db.py:1929
web_ui/app_db.py:1934
web_ui/app_db.py:1942
web_ui/app_db.py:1963
web_ui/app_db.py:2001
web_ui/app_db.py:2013
web_ui/app_db.py:5858
web_ui/app_db.py:2470-2471
web_ui/ai_tools.py
analyze_db_schema.py:34
analyze_db_schema.py:40
migrate_data_to_postgresql.py:209
migrate_data_to_postgresql.py:214
migrate_data_to_postgresql.py:240
check_database.py
web_ui/app_db.py:1914, 1920, 1922, 1927, 1929, 1934, 1942, 1963, 2001, 2013
analyze_db_schema.py:34, 40
migrate_data_to_postgresql.py:209, 214, 240
web_ui/app_db.py:5858, 2470-2471 |
| SEC-009 |
High |
Security Misconfiguration |
B2Bee/bumblebee-backend/app/main.py:13-19
B2Bee/bumblebee-backend/app/main.py:15 |
| SEC-010 |
High |
Broken Access Control |
web_ui/app_db.py:13164-13180 |
| SEC-011 |
High |
Broken Access Control |
web_ui/app_db.py:13565-13573 |
| SEC-012 |
High |
Broken Access Control |
frontend/middleware.ts
frontend/src/app/(dashboard)/layout.tsx:19-45
frontend/src/app/(super-admin)/layout.tsx:28-33 |
| SEC-013 |
High |
Broken Access Control |
frontend/src/context/auth-context.tsx:207-222
frontend/src/context/tenant-context.tsx:92-108
frontend/src/app/(dashboard)/dashboard/page.tsx:531
frontend/src/app/(dashboard)/dashboard/page.tsx:698
frontend/src/lib/api.ts
middleware/auth_middleware.py
tenant-context.tsx:92-108
dashboard/page.tsx:531, 698
frontend/src/context/*.tsx
web_ui/app_db.py |
| SEC-014 |
High |
Injection |
frontend/src/app/(dashboard)/invoices/[id]/page.tsx:654
frontend/src/app/(dashboard)/invoices/[id]/page.tsx:781
invoice_processing/improved_visual_system.py
frontend/src/app/(dashboard)/invoices/[id]/page.tsx:654, 781
add_tenant_switcher_to_all_templates.py |
| SEC-015 |
High |
Broken Access Control |
frontend/src/lib/api.ts:40
frontend/src/context/tenant-context.tsx:53
frontend/src/lib/api.ts:40-48 |
| SEC-016 |
Medium |
Cryptographic Failures |
main.py:1972
main.py:2269
web_ui/app_db.py:2298
web_ui/app_db.py:2608
web_ui/app_db.py:4688
web_ui/app_db.py:11314
web_ui/app_db.py:11329
web_ui/services/file_storage_service.py:109
invoice_processing/core/email_monitor.py:123
main.py:1972, 2269
web_ui/app_db.py:2298, 2608, 4688, 11314, 11329 |
| SEC-017 |
Medium |
Cryptographic Failures |
web_ui/historical_currency_converter.py:308 |
| SEC-018 |
Low |
Injection |
web_ui/reporting_api.py:3446
web_ui/app.py:141
web_ui/app.py:144
web_ui/app.py:141, 144 |
1. Remove hardcoded credentials from source code and scripts
ID: SEC-001
Severity: Critical
Category: Cryptographic Failures
Affected code: apply_schema_direct.py:16, apply_schema_sa.py:14, apply_schema_simple.py:14, check_database.py:15, create_test_user.py:40, crypto_invoice_system/api/invoice_api.py
Impact
Repository readers, including former readers, can extract plaintext database and exchange credentials, potentially enabling unauthorized access to production services.
Technical details
- Git-tracked files contain literal database passwords, including
DB_PASSWORD = "..." in apply_schema_direct.py:16, apply_schema_sa.py:14, and related scripts.
crypto_invoice_system/run_server.sh:8 and crypto_invoice_system/api/invoice_api.py contain plaintext MEXC API credentials and fallback literals.
- The affected values are used as source-code or shell-script defaults rather than being required from the runtime environment.
Relevant code
apply_schema_direct.py:10-22
# Configurações
DB_HOST = "34.27.251.47"
DB_PORT = 5432
DB_NAME = "delta_cfo"
DB_USER = "postgres"
DB_PASSWORD = [REDACTED_SECRET]
def wait_for_connection(max_retries=10):
"""Aguardar instância estar acessível"""
for i in range(max_retries):
try:
conn = psycopg2.connect(
Validation
Code evidence
-
apply_schema_direct.py:10-22 defines DB_HOST, DB_USER, and a literal DB_PASSWORD.
-
crypto_invoice_system/run_server.sh:8 and invoice_api.py contain MEXC key and secret fallback values.
Limitations
-
No credential-validity or live-service access test was supplied.
Recommended remediation
Rotate every exposed credential, purge secrets from repository history, and load them from required environment or secret-manager variables that fail startup when unset.
2. Remove committed .env.production file containing live secrets
ID: SEC-002
Severity: Critical
Category: Cryptographic Failures
Affected code: main.py:89, main.py:1381, main.py:1570, web_ui/app_db.py:5127, check_database.py:11
Impact
A tracked production environment file exposes infrastructure identifiers and a Flask secret value to repository readers; duplicated database defaults increase exposure and configuration drift.
Technical details
.env.production is tracked and contains DB_HOST=34.39.143.82, a Cloud SQL connection name, and a Flask secret key in a comment.
main.py:89,1381,1570, web_ui/app_db.py:5127, and check_database.py:11 use the same production database host as a fallback.
- The supplied record states that
.gitignore has no .env.production rule.
Relevant code
main.py:83-95
# Try to load from database first (SaaS architecture)
try:
import psycopg2
import os as os_module
# Get database credentials from environment
db_host = os_module.environ.get('DB_HOST', '34.39.143.82')
db_port = os_module.environ.get('DB_PORT', '5432')
db_name = os_module.environ.get('DB_NAME', 'delta_cfo')
db_user = os_module.environ.get('DB_USER', 'delta_user')
db_password = [REDACTED_SECRET] 'nWr0Y8bU51ypLjMIfx8bTe+V/1iOV59r90T8wJEsSGo=')
# Use tenant_id from constructor
Validation
Code evidence
-
The tracked .env.production contains production connection values and a commented Flask key.
-
main.py uses os.environ.get('DB_HOST', '34.39.143.82') at the reported locations.
Limitations
-
No live authentication or database-access test was supplied.
Recommended remediation
Rotate exposed values, remove .env.production and its secrets from Git history, add it to .gitignore, and require deployment-time secret injection without production fallbacks.
3. Enforce authentication on all sensitive Flask routes
ID: SEC-003
Severity: Critical
Category: Broken Access Control
Affected code: web_ui/app_db.py:359-396, web_ui/app_db.py:13565-13573
Impact
Unauthenticated callers may read financial data, perform mutations or uploads, export records, and enumerate or delete Firebase users.
Technical details
- The repository contains approximately 235 Flask routes, while only 16-18 reportedly use an authentication decorator.
GET /api/transactions and GET /api/stats lack authentication; the admin Firebase routes at web_ui/app_db.py:359-396 also lack decorators.
- Upload and mutation routes, including
/api/invoices/upload, /api/invoices/upload-batch, and attachment uploads, are listed without required authentication and tenant/role checks.
Relevant code
web_ui/app_db.py:1-30
#!/usr/bin/env python3
"""
Delta CFO Agent - Database-Backed Web Dashboard
Advanced web interface for financial transaction management with Claude AI integration
"""
import os
import sys
import json
import sqlite3 # Kept for backward compatibility - main DB uses database.py manager
import pandas as pd
import time
import threading
import traceback
from datetime import datetime, timedelta
from flask import Flask, render_template, request, jsonify, send_file, session, Response
from concurrent.futures import ThreadPoolExecutor, as_completed
import random
import anthropic
from typing import List, Dict, Any, Optional
from werkzeug.utils import secure_filename
import subprocess
import shutil
import hashlib
import uuid
import base64
import zipfile
import re
import logging
from dotenv import load_dotenv
Validation
Code evidence
-
web_ui/app_db.py defines sensitive routes under @app.route without consistent @require_auth enforcement.
-
The reported admin routes can return or modify Firebase user records without a decorator.
Runtime evidence
-
Unauthenticated GET https://deltacfoagent.vercel.app/api/transactions reportedly returned HTTP 200.
-
Unauthenticated GET https://deltacfoagent.vercel.app/api/stats reportedly returned HTTP 200 with financial KPIs.
Limitations
-
The supplied runtime results cover read endpoints, not every listed mutation or upload route.
Recommended remediation
Require authentication on every sensitive route and enforce authorization, role, and tenant ownership checks server-side before reads, writes, exports, uploads, or administrative actions.
4. Replace eval() calls in smart_ingestion.py with a safe expression evaluator
ID: SEC-004
Severity: Critical
Category: Injection
Affected code: smart_ingestion.py:1001, smart_ingestion.py:1013, smart_ingestion.py:1023, smart_ingestion.py:1001, 1013, 1023
Impact
Expression evaluation can provide a code-execution path if an attacker can influence the mapping expressions or the AI-generated upload-processing input.
Technical details
smart_ingestion.py:1001, 1013, and 1023 call eval() for condition_expr, origin_expr, and destination_expr.
- The calls pass
{"__builtins__": {}} as globals but expose pd and str in local_vars; this is not a complete Python sandbox.
- The reported path begins at the upload-processing functionality, where expressions are generated or influenced during CSV processing.
Relevant code
smart_ingestion.py:995-1007
for row_idx, row in df.iterrows():
try:
# Create local namespace with row data
local_vars = {'row': row, 'pd': pd, 'str': str}
# Evaluate condition
if eval(condition_expr, {"__builtins__": {}}, local_vars):
matches.append(row_idx)
except Exception as e:
# Skip rows that fail evaluation
continue
if matches:
Validation
Code evidence
-
smart_ingestion.py:995-1007 executes eval(condition_expr, {"__builtins__": {}}, local_vars).
-
The two corresponding origin and destination evaluations are reported at lines 1013 and 1023.
Limitations
-
The supplied Vercel probe returned 404 for /api/upload-with-progress; runtime exploitation was not demonstrated.
-
The record identifies Cloud Run as the primary exposure target, but does not provide a Cloud Run URL.
Recommended remediation
Replace eval() with a strict allowlisted expression grammar or safe evaluator that supports only required operators, fields, and functions; reject unsupported expressions.
5. Disable Flask debug mode in all deployment and source configurations
ID: SEC-005
Severity: Critical
Category: Security Misconfiguration
Affected code: cloudbuild-dev.yaml:40, invoice_processing/web/dashboard.py:257, invoice_processing/web/upload_interface.py:356, invoice_processing/improved_visual_system.py:2690, web_ui/app.py:224
Impact
If the reported Cloud Run configuration is deployed, Werkzeug's interactive debugger may expose a Python console and turn application errors into code execution.
Technical details
cloudbuild-dev.yaml:40 sets FLASK_DEBUG=true while also configuring an unauthenticated Cloud Run service.
web_ui/app.py:224 and invoice_processing/web/dashboard.py:257 reportedly call app.run(debug=True); additional hardcoded debug settings are listed in related modules.
- The deployment manifest supplies the debug flag through environment variables, so source-level defaults are not the only activation path.
Relevant code
cloudbuild-dev.yaml:34-46
- '--cpu-throttling'
- '--execution-environment'
- 'gen2'
- '--max-instances'
- '10'
- '--set-env-vars'
- 'DB_TYPE=postgresql,DB_SOCKET_PATH=/cloudsql/$PROJECT_ID:southamerica-east1:delta-cfo-db,DB_NAME=delta_cfo,DB_USER=delta_user,FLASK_ENV=development,PORT=8080,PYTHONUNBUFFERED=1,FLASK_DEBUG=true,GCS_BUCKET_NAME=deltacfo-uploads-dev,GOOGLE_CLOUD_PROJECT=$PROJECT_ID'
- '--set-cloudsql-instances'
- '$PROJECT_ID:southamerica-east1:delta-cfo-db'
- '--set-secrets'
- 'DB_PASSWORD=db_password_sa:latest,ANTHROPIC_API_KEY=ANTHROPIC_API_KEY:latest'
images:
Validation
Code evidence
-
cloudbuild-dev.yaml:34-46 includes FLASK_DEBUG=true in --set-env-vars.
-
web_ui/app.py:224 and invoice_processing/web/dashboard.py:257 are reported to use debug=True.
Runtime evidence
-
The Vercel probe reportedly returned 404 for /__debugger__/ and /console.
Limitations
-
The supplied runtime result does not validate the Cloud Run deployment described by cloudbuild-dev.yaml.
Recommended remediation
Remove debug flags from deployment manifests and production entry points. Permit debug mode only through an explicitly controlled development configuration that cannot be enabled in production.
6. Remove insecure fallback Flask secret key and require it at startup
ID: SEC-006
Severity: Critical
Category: Cryptographic Failures
Affected code: web_ui/app_db.py:132, cloudbuild.yaml:45
Impact
Anyone who knows the fallback Flask key, or obtains the committed production key, may forge Flask session cookies and impersonate users or alter session state.
Technical details
web_ui/app_db.py:132 assigns app.secret_key from FLASK_SECRET_KEY with the public fallback dev-secret-key-delta-cfo-agent-2024.
cloudbuild.yaml:45 reportedly injects database, Anthropic, and Firebase secrets but not FLASK_SECRET_KEY.
- The tracked
.env.production contains a production Flask key in a comment.
Relevant code
web_ui/app_db.py:126-138
app.config['TEMPLATES_AUTO_RELOAD'] = True
app.jinja_env.auto_reload = True
# Configure Flask secret key for sessions
# Use a fixed key in development for session persistence across restarts
# In production, set FLASK_SECRET_KEY environment variable
app.secret_key = os.getenv('FLASK_SECRET_KEY', 'dev-secret-key-delta-cfo-agent-2024')
# Lazy blueprint registration function to avoid circular imports
def register_auth_blueprints():
"""
Register authentication blueprints using lazy loading.
This function is called after app initialization to avoid circular imports.
Validation
Code evidence
-
web_ui/app_db.py:126-138 uses os.getenv('FLASK_SECRET_KEY', 'dev-secret-key-delta-cfo-agent-2024').
-
cloudbuild.yaml:45 lacks FLASK_SECRET_KEY in the reported --set-secrets configuration.
Runtime evidence
-
The deployed URL reportedly served Flask responses.
Limitations
-
No session-cookie forgery or authenticated-privilege test was supplied.
Recommended remediation
Rotate all exposed Flask keys, remove the literal fallback, require FLASK_SECRET_KEY at startup, and inject it through the deployment secret mechanism.
7. Eliminate user-controlled input in subprocess Python code string execution
ID: SEC-007
Severity: Critical
Category: Injection
Affected code: web_ui/app_db.py:12577, app_db.py, web_ui/app_db.py:11825, web_ui/app_db.py:11825-11840
Impact
If an attacker controls the interpolated filename, the subprocess Python command may execute injected Python code. A weak Flask session key and reported unauthenticated upload behavior increase the potential attack path.
Technical details
- The duplicate-processing pipeline builds a
python -c string containing filename_safe and invokes it with subprocess.run.
- The reported sanitization replaces backslashes but does not escape single quotes or newlines before interpolation.
- The route source at
web_ui/app_db.py:12571 includes @require_auth, but the supplied runtime note states the deployed endpoint did not return an authentication challenge.
Relevant code
web_ui/app_db.py:12571-12583
@app.route('/api/process-duplicates', methods=['POST'])
@require_auth
def process_duplicates():
"""Process a file that was already uploaded with specific duplicate handling - requires authentication"""
try:
duplicate_handling = request.form.get('duplicateHandling', 'overwrite')
filename = request.form.get('filename', '')
if not filename:
return jsonify({'error': 'No filename provided'}), 400
print(f"[PROCESS] Processing duplicates for {filename} with mode: {duplicate_handling}")
Validation
Code evidence
-
The reported subprocess construction occurs at web_ui/app_db.py:11825-11840 and 12577/12620.
-
filename_safe = filename.replace(chr(92), '/') is insufficient for embedding untrusted data in Python source.
Runtime evidence
-
A POST to /api/process-duplicates without credentials reportedly returned HTTP 400 File not found, not 401.
-
The supplied note also reports /api/upload accepted unauthenticated access.
Limitations
-
No command-execution result was supplied.
Recommended remediation
Do not generate Python source from request data. Pass values as argv or environment variables to a fixed worker, validate filenames against an upload directory, and enforce authentication consistently.
8. Eliminate dynamic SQL identifier interpolation to prevent SQL injection
ID: SEC-008
Severity: High
Category: Injection
Affected code: web_ui/app_db.py:1914, web_ui/app_db.py:1920, web_ui/app_db.py:1922, web_ui/app_db.py:1927, web_ui/app_db.py:1929, web_ui/app_db.py:1934, web_ui/app_db.py:1942, web_ui/app_db.py:1963, web_ui/app_db.py:2001, web_ui/app_db.py:2013, web_ui/app_db.py:5858, web_ui/app_db.py:2470-2471, web_ui/ai_tools.py, analyze_db_schema.py:34, analyze_db_schema.py:40, migrate_data_to_postgresql.py:209, migrate_data_to_postgresql.py:214, migrate_data_to_postgresql.py:240, check_database.py, web_ui/app_db.py:1914, 1920, 1922, 1927, 1929, 1934, 1942, 1963, 2001, 2013, analyze_db_schema.py:34, 40, migrate_data_to_postgresql.py:209, 214, 240, web_ui/app_db.py:5858, 2470-2471
Impact
An attacker may inject an SQL identifier into transaction updates; the unauthenticated endpoint may also permit modification of tenant-related columns and undermine tenant isolation.
Technical details
api_update_transaction obtains field = data.get('field') and passes unknown values to update_transaction_field without an allowlist.
- The fallback query uses
UPDATE transactions SET {field} = %s WHERE tenant_id = %s AND transaction_id = %s at web_ui/app_db.py:2470-2471.
- The reported endpoint at
web_ui/app_db.py:5852 has no authentication decorator.
Relevant code
web_ui/app_db.py:1908-1920
placeholder = "%s" if is_postgresql else "?"
# Build WHERE clause from filters
where_clause, params = build_filter_where_clause(filters, tenant_id, is_postgresql)
# Total transactions with filters
cursor.execute(f"SELECT COUNT(*) as total FROM transactions WHERE {where_clause}", params)
result = cursor.fetchone()
total_transactions = result['total'] if is_postgresql else result[0]
# Revenue and expenses with filters
if is_postgresql:
cursor.execute(f"SELECT COALESCE(SUM(amount), 0) as revenue FROM transactions WHERE {where_clause} AND amount > 0 AND amount::text != 'NaN'", params)
Validation
Code evidence
-
The reported fallback directly interpolates {field} into an SQL statement; parameter binding protects values but not identifiers.
-
Other reported query construction sites include web_ui/app_db.py:1914-2013, analyze_db_schema.py:34,40, and migration scripts.
Runtime evidence
-
A POST to /api/update_transaction with field=confidence reportedly reached the endpoint and returned HTTP 500 rather than an authentication challenge.
Limitations
-
The supplied probe did not demonstrate successful SQL manipulation or data alteration.
Recommended remediation
Allow only a fixed set of writable column names before query construction, reject unknown fields, keep values parameterized, and require authentication plus tenant authorization.
9. Restrict wildcard CORS combined with credentials in FastAPI subservice
ID: SEC-009
Severity: High
Category: Security Misconfiguration
Affected code: B2Bee/bumblebee-backend/app/main.py:13-19, B2Bee/bumblebee-backend/app/main.py:15
Impact
If the B2Bee service is reachable, arbitrary origins may be able to issue credentialed cross-origin requests, depending on the Starlette version and response behavior.
Technical details
B2Bee/bumblebee-backend/app/main.py:13-19 configures CORSMiddleware with allow_origins=["*"] and allow_credentials=True.
- The same configuration permits all methods and headers with
allow_methods=["*"] and allow_headers=["*"].
- This policy does not restrict browser callers to a trusted origin set.
Relevant code
B2Bee/bumblebee-backend/app/main.py:7-25
app = FastAPI(
title="BumbleBee API",
description="AI Assistant Backend",
version="0.1.0",
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/health")
async def health_check():
return {"status": "healthy"}
Validation
Code evidence
-
B2Bee/bumblebee-backend/app/main.py:7-25 contains the reported wildcard CORS configuration.
Runtime evidence
-
The B2Bee API was reportedly not reachable at the supplied Vercel URL; API paths returned 404.
Limitations
-
No deployed B2Bee endpoint or credentialed cross-origin request result was supplied.
Recommended remediation
Replace the wildcard origin with an explicit allowlist of trusted origins and restrict methods and headers to those required by the service.
10. Fix path traversal in unauthenticated invoice PDF download endpoint
ID: SEC-010
Severity: High
Category: Broken Access Control
Affected code: web_ui/app_db.py:13164-13180
Impact
An unauthenticated caller may request a path containing traversal segments and cause the application to serve files outside the invoice directory.
Technical details
/api/invoices/pdf/<path:filename> accepts slashes through Flask's path converter and has no reported authentication decorator.
- The handler joins
filename directly with base_dir/invoices/issued using os.path.join.
- A filename such as
../../.env resolves outside the intended directory before send_file is called.
Relevant code
web_ui/app_db.py:13158-13186
except Exception as e:
print(f"Error creating invoice: {e}")
import traceback
traceback.print_exc()
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/invoices/pdf/<path:filename>')
def api_download_invoice_pdf(filename):
"""API endpoint to download invoice PDF"""
try:
from flask import send_file
import os
# Construct the full path (use absolute path)
base_dir = os.path.dirname(os.path.abspath(__file__))
pdf_path = os.path.join(base_dir, 'invoices', 'issued', filename)
# Check if file exists
if not os.path.exists(pdf_path):
return jsonify({'success': False, 'error': 'PDF file not found'}), 404
# Send the file
return send_file(pdf_path, mimetype='application/pdf', as_attachment=True, download_name=filename)
except Exception as e:
print(f"Error downloading invoice PDF: {e}")
return jsonify({'success': False, 'error': str(e)}), 500
@app.route('/api/invoices')
Validation
Code evidence
-
web_ui/app_db.py:13158-13186 constructs pdf_path = os.path.join(base_dir, 'invoices', 'issued', filename) and sends it when it exists.
-
The route declaration at line 13164 uses <path:filename>.
Runtime evidence
-
The supplied Vercel probe for /api/invoices/pdf/test.pdf returned HTML 404.
Limitations
-
The current Vercel routing did not expose the route, so file disclosure was not demonstrated there.
Recommended remediation
Require authentication and use send_from_directory or safe_join with a fixed base directory. Reject traversal and verify the canonical resolved path remains within the invoice directory.
11. Validate archive member paths before extraction to prevent Zip Slip
ID: SEC-011
Severity: High
Category: Broken Access Control
Affected code: web_ui/app_db.py:13565-13573
Impact
A crafted archive may write files outside the intended extraction directory, potentially overwriting application or configuration files if the endpoint is deployed and writable.
Technical details
- The ZIP branch calls
zip_ref.extractall(extract_dir) at web_ui/app_db.py:13567 without validating archive member paths.
- Archive entries containing traversal components such as
../../../../... can resolve outside extract_dir.
- The reported batch upload route lacks
@require_auth.
Relevant code
web_ui/app_db.py:13559-13579
file_ext = os.path.splitext(file_path)[1].lower()
os.makedirs(extract_dir, exist_ok=True)
# Extract archive based on file type
if file_ext == '.zip':
with zipfile.ZipFile(file_path, 'r') as zip_ref:
zip_ref.extractall(extract_dir)
elif file_ext == '.7z':
if not PY7ZR_AVAILABLE:
return {'error': '7z support not available - py7zr package required'}
with py7zr.SevenZipFile(file_path, mode='r') as archive:
archive.extractall(path=extract_dir)
elif file_ext == '.rar':
return {'error': 'RAR format requires additional setup. Please use ZIP or 7Z format.'}
else:
return {'error': f'Unsupported archive format: {file_ext}'}
Validation
Code evidence
-
web_ui/app_db.py:13559-13579 extracts ZIP contents directly with extractall(extract_dir).
-
The route is reported as /api/invoices/upload-batch at line 13668 without an authentication decorator.
Runtime evidence
-
The supplied Vercel probe returned 404 for /api/invoices/upload-batch.
Limitations
-
No file-write or application-overwrite result was demonstrated in a live deployment.
Recommended remediation
Require authentication and validate every archive member's canonical destination against the extraction root before writing. Reject absolute paths, traversal components, and links.
12. Add server-side authentication enforcement to Next.js middleware and layouts
ID: SEC-012
Severity: High
Category: Broken Access Control
Affected code: frontend/middleware.ts, frontend/src/app/(dashboard)/layout.tsx:19-45, frontend/src/app/(super-admin)/layout.tsx:28-33
Impact
Unauthenticated users may receive dashboard HTML, and client-side-only role checks do not provide server-side protection for super-admin content or operations.
Technical details
frontend/middleware.ts only invokes next-intl routing and performs no session validation.
- The dashboard layout's
if (!isAuthenticated) return null guard is reportedly commented out.
- The super-admin role check is performed in
useEffect, after the server has delivered the page.
Relevant code
frontend/middleware.ts:1-21
import createMiddleware from "next-intl/middleware";
import { locales, defaultLocale } from "./src/i18n/config";
export default createMiddleware({
// A list of all locales that are supported
locales,
// Used when no locale matches
defaultLocale,
// Don't redirect to locale prefix for default locale
localePrefix: "as-needed",
});
export const config = {
// Match all pathnames except for:
// - API routes
// - Static files
// - Next.js internals
matcher: ["/((?!api|_next|.*\\..*).*)"],
};
Validation
Code evidence
-
frontend/middleware.ts:1-21 contains locale middleware without an authentication check.
-
frontend/src/app/(dashboard)/layout.tsx:41-45 reportedly comments out the unauthenticated guard.
Runtime evidence
-
Unauthenticated GET https://deltacfoagent.vercel.app/dashboard reportedly returned HTTP 200 with full dashboard HTML.
Limitations
-
The supplied result does not establish whether protected backend APIs independently enforce authorization.
Recommended remediation
Validate the session and required role in server-side middleware or layouts before rendering protected routes, and enforce the same authorization on every backend operation.
13. Implement CSRF protection on all cookie-authenticated state-changing requests
ID: SEC-013
Severity: High
Category: Broken Access Control
Affected code: frontend/src/context/auth-context.tsx:207-222, frontend/src/context/tenant-context.tsx:92-108, frontend/src/app/(dashboard)/dashboard/page.tsx:531, frontend/src/app/(dashboard)/dashboard/page.tsx:698, frontend/src/lib/api.ts, middleware/auth_middleware.py, tenant-context.tsx:92-108, dashboard/page.tsx:531, 698, frontend/src/context/*.tsx, web_ui/app_db.py
Impact
Cookie-authenticated state-changing requests may be triggered cross-site, allowing unauthorized tenant switches or mutations when a victim is logged in.
Technical details
requirements.txt:11 includes Flask-WTF, but the supplied repository review found no CSRFProtect initialization or flask_wtf imports.
- No
SESSION_COOKIE_SAMESITE setting is reported.
frontend/src/context/auth-context.tsx:209-211 sends a credentialed POST to /api/auth/switch-tenant/<tenantId> without an Authorization header.
- The supplied record states that session cookies are accepted as an authentication fallback.
Relevant code
frontend/src/context/auth-context.tsx:201-228
const firebaseUser = firebaseAuth.currentUser;
if (firebaseUser) {
await syncWithBackend(firebaseUser);
}
}
async function switchTenant(tenantId: string) {
try {
const response = await fetch(`/api/auth/switch-tenant/${tenantId}`, {
method: "POST",
credentials: "include",
});
if (response.ok) {
const data = await response.json();
if (data.success) {
setCurrentTenant(data.tenant);
}
}
} catch (error) {
console.error("Switch tenant error:", error);
}
}
return (
<AuthContext.Provider
value={{
user,
Validation
Code evidence
-
auth-context.tsx:201-228 uses credentials: "include" for a state-changing POST without a CSRF token.
-
web_ui/app_db.py:9442 reportedly defines bulk_update_transactions without @require_auth.
Runtime evidence
-
The supplied probe for /api/bulk_update_transactions returned 404 on Vercel.
Limitations
-
No cross-site request or state-change result was demonstrated.
Recommended remediation
Initialize global CSRF protection for cookie-authenticated state changes, require and validate CSRF tokens, configure an appropriate SameSite cookie policy, and enforce authentication on every mutation.
14. Prevent XSS from unvalidated URLs and innerHTML assignments
ID: SEC-014
Severity: High
Category: Injection
Affected code: frontend/src/app/(dashboard)/invoices/[id]/page.tsx:654, frontend/src/app/(dashboard)/invoices/[id]/page.tsx:781, invoice_processing/improved_visual_system.py, frontend/src/app/(dashboard)/invoices/[id]/page.tsx:654, 781, add_tenant_switcher_to_all_templates.py
Impact
Attacker-controlled tenant or invoice content may execute JavaScript in users' browsers through unescaped HTML; unvalidated attachment URLs may also permit dangerous URI schemes in the frontend.
Technical details
- The supplied deployed-template evidence reports
tenant.company_name and tenant.role inserted into innerHTML without escaping in business_overview.html:726 and invoices.html:2841.
- The Next.js invoice page uses
<a href={attachment.url}> at lines 654 and 781 without an explicit HTTP/HTTPS scheme allowlist.
- The record states the Next.js frontend is not the application currently served at the supplied Vercel URL.
Relevant code
frontend/src/app/(dashboard)/invoices/[id]/page.tsx:648-660
</p>
</div>
</div>
<div className="flex items-center gap-2">
<Button variant="ghost" size="sm" asChild>
<a
href={attachment.url}
target="_blank"
rel="noopener noreferrer"
>
<Download className="h-4 w-4" />
</a>
</Button>
Validation
Code evidence
-
Reported template code assigns interpolated tenant values to tenantItem.innerHTML.
-
frontend/src/app/(dashboard)/invoices/[id]/page.tsx:648-660 renders href={attachment.url}.
Runtime evidence
-
GET https://deltacfoagent.vercel.app/ reportedly returned HTTP 200 and serves the Flask template.
-
The reported /api/auth/me probe returned 404.
Limitations
-
No live XSS payload execution was supplied.
-
The Next.js URL sink was not confirmed in the deployed Vercel application.
Recommended remediation
Render untrusted values as text or through context-appropriate escaping, avoid innerHTML, and allow only http and https attachment schemes before rendering links.
15. Derive tenant identity from authenticated session rather than localStorage
ID: SEC-015
Severity: High
Category: Broken Access Control
Affected code: frontend/src/lib/api.ts:40, frontend/src/context/tenant-context.tsx:53, frontend/src/lib/api.ts:40-48
Impact
Client-controlled tenant identifiers can route API requests to another tenant, risking cross-tenant data access when backend authorization relies on the supplied header.
Technical details
frontend/src/lib/api.ts:40-48 falls back to localStorage.getItem("tenantId") when the in-memory tenant ID is absent.
- The value is reportedly sent as the
X-Tenant-ID header on API calls.
frontend/src/context/tenant-context.tsx:53 uses the hardcoded fallback "delta" when tenant values are missing.
Relevant code
frontend/src/lib/api.ts:34-46
currentTenantId = tenantId;
}
/**
* Get the current tenant ID
* Falls back to localStorage if not set in memory
*/
export function getApiTenantId(): string | null {
if (currentTenantId) return currentTenantId;
// Fallback to localStorage for persistence across page refreshes
if (typeof window !== "undefined") {
return localStorage.getItem("tenantId");
}
Validation
Code evidence
-
getApiTenantId() returns a browser-controlled localStorage value after the in-memory value is unavailable.
-
The tenant context expression config.tenant_id || response.tenant_id || "delta" supplies a fixed tenant fallback.
Runtime evidence
-
The deployed application reportedly returned HTTP 200; the supplied record states both code paths are active in the production bundle.
Limitations
-
No cross-tenant request or unauthorized data response was supplied.
Recommended remediation
Derive tenant identity exclusively from the authenticated server-side session or token, ignore client-supplied tenant headers for authorization, and fail closed when tenant context is absent.
16. Replace MD5 usage in security-relevant contexts with strong hash algorithms
ID: SEC-016
Severity: Medium
Category: Cryptographic Failures
Affected code: main.py:1972, main.py:2269, web_ui/app_db.py:2298, web_ui/app_db.py:2608, web_ui/app_db.py:4688, web_ui/app_db.py:11314, web_ui/app_db.py:11329, web_ui/services/file_storage_service.py:109, invoice_processing/core/email_monitor.py:123, main.py:1972, 2269, web_ui/app_db.py:2298, 2608, 4688, 11314, 11329
Impact
MD5 collisions can cause incorrect deduplication or integrity decisions, potentially suppressing distinct financial or email records.
Technical details
hashlib.md5(...) is reported in main.py:1972,2269, web_ui/app_db.py:2298,2608,4688,11314,11329, web_ui/services/file_storage_service.py:109, and invoice_processing/core/email_monitor.py:123.
main.py:1972 derives a 12-character transaction ID from date, description, and amount using MD5.
file_storage_service.py:109 reportedly uses MD5 for file integrity checking.
Relevant code
main.py:1966-1978
if 'transaction_id' not in df.columns or pd.isna(row.get('transaction_id')):
# Generate transaction_id from date + description + amount
date_str = str(row[date_col]) if date_col in df.columns else ''
desc_str = str(row[desc_col]) if desc_col in df.columns else ''
amount_str = str(row[amount_col]) if amount_col in df.columns else ''
identifier = f"{date_str}{desc_str}{amount_str}"
transaction_id = hashlib.md5(identifier.encode()).hexdigest()[:12]
df.at[idx, 'transaction_id'] = transaction_id
# Run enhanced processing pipeline if requested
if enhance:
print("\n Running enhanced processing pipeline...")
Validation
Code evidence
-
main.py:1966-1978 constructs identifier and assigns hashlib.md5(identifier.encode()).hexdigest()[:12] as transaction_id.
Runtime evidence
-
The deployed application root reportedly returned HTTP 200.
Limitations
-
No collision-based record suppression or integrity bypass was demonstrated at runtime.
Recommended remediation
Use SHA-256 or stronger for non-password integrity and identifiers. For password hashing, use a password-specific algorithm such as Argon2 or bcrypt; review whether truncated identifiers require collision-resistant redesign.
17. Transmit Fixer.io API key over HTTPS instead of plaintext HTTP
ID: SEC-017
Severity: Medium
Category: Cryptographic Failures
Affected code: web_ui/historical_currency_converter.py:308
Impact
The Fixer.io API key is sent over plaintext HTTP and may be observed or modified by network intermediaries, proxies, or logs.
Technical details
web_ui/historical_currency_converter.py:308 builds http://data.fixer.io/api/{date_str}.
- The request passes
self.backup_apis['fixer'] as the access_key query parameter.
- The credential-bearing request therefore lacks transport encryption.
Relevant code
web_ui/historical_currency_converter.py:302-314
to_currency: str,
rate_date: datetime
) -> Optional[Dict]:
"""Fetch from fixer.io (backup API)"""
try:
date_str = rate_date.strftime('%Y-%m-%d')
url = f"http://data.fixer.io/api/{date_str}"
params = {
'access_key': self.backup_apis['fixer'],
'base': from_currency,
'symbols': to_currency
}
Validation
Code evidence
-
web_ui/historical_currency_converter.py:302-314 shows the HTTP URL and query parameter containing access_key.
Runtime evidence
-
The deployed application root reportedly returned HTTP 200.
Limitations
-
No outbound request capture was supplied to confirm invocation in the deployed environment.
Recommended remediation
Use the Fixer.io HTTPS endpoint and verify certificate validation. Do not place long-lived credentials in URLs where possible; use an HTTPS-only alternative if the service plan does not support TLS.
18. Reject non-finite float values (NaN/Inf) from query parameters
ID: SEC-018
Severity: Low
Category: Injection
Affected code: web_ui/reporting_api.py:3446, web_ui/app.py:141, web_ui/app.py:144, web_ui/app.py:141, 144
Impact
Special floating-point values can silently alter financial report filtering and produce incorrect dashboard results.
Technical details
web_ui/reporting_api.py:3446 converts the min_amount query parameter directly with float(...).
- The value is reportedly passed to SQL
HAVING SUM(amount) >= %s comparisons without a finiteness check.
- The deprecated
web_ui/app.py:141,144 contains the same parsing pattern.
Relevant code
web_ui/reporting_api.py:3440-3452
try:
start_time = datetime.now()
# Parse parameters
start_date_str = request.args.get('start_date')
end_date_str = request.args.get('end_date')
min_amount = float(request.args.get('min_amount', 1000))
max_categories = int(request.args.get('max_categories', 8))
# NEW: Parse filter parameters
keyword = request.args.get('keyword', '').strip()
entity = request.args.get('entity', '').strip()
accounting_category = request.args.get('accounting_category', '').strip()
Validation
Code evidence
-
web_ui/reporting_api.py:3440-3452 parses min_amount using float(request.args.get('min_amount', 1000)) with no math.isfinite() validation.
Runtime evidence
-
The supplied request to /api/reports/sankey-flow?min_amount=nan returned HTTP 404 on Vercel.
Limitations
-
The deployed route was unavailable, so incorrect query results were not observed at runtime.
Recommended remediation
Parse numeric inputs and reject values for which math.isfinite() is false. Return a validation error before constructing or executing the report query.
Security report for Delta-Compute/DeltaCFOAgent
Hello maintainers,
I am a security researcher studying security risks in vibe-coded software. During this research, I reviewed this repository and identified the findings below. These findings were identified in commit
34ae558. Each finding has undergone human analysis, but the report may still contain mistakes or incomplete interpretations. Please review this report and apply the necessary security fixes.This report contains 18 confirmed findings identified during security review. Validation details below distinguish code evidence from runtime observations and note any limitations.
Summary
apply_schema_direct.py:16apply_schema_sa.py:14apply_schema_simple.py:14check_database.py:15create_test_user.py:40crypto_invoice_system/api/invoice_api.pymain.py:89main.py:1381main.py:1570web_ui/app_db.py:5127check_database.py:11web_ui/app_db.py:359-396web_ui/app_db.py:13565-13573smart_ingestion.py:1001smart_ingestion.py:1013smart_ingestion.py:1023smart_ingestion.py:1001, 1013, 1023cloudbuild-dev.yaml:40invoice_processing/web/dashboard.py:257invoice_processing/web/upload_interface.py:356invoice_processing/improved_visual_system.py:2690web_ui/app.py:224web_ui/app_db.py:132cloudbuild.yaml:45web_ui/app_db.py:12577app_db.pyweb_ui/app_db.py:11825web_ui/app_db.py:11825-11840web_ui/app_db.py:1914web_ui/app_db.py:1920web_ui/app_db.py:1922web_ui/app_db.py:1927web_ui/app_db.py:1929web_ui/app_db.py:1934web_ui/app_db.py:1942web_ui/app_db.py:1963web_ui/app_db.py:2001web_ui/app_db.py:2013web_ui/app_db.py:5858web_ui/app_db.py:2470-2471web_ui/ai_tools.pyanalyze_db_schema.py:34analyze_db_schema.py:40migrate_data_to_postgresql.py:209migrate_data_to_postgresql.py:214migrate_data_to_postgresql.py:240check_database.pyweb_ui/app_db.py:1914, 1920, 1922, 1927, 1929, 1934, 1942, 1963, 2001, 2013analyze_db_schema.py:34, 40migrate_data_to_postgresql.py:209, 214, 240web_ui/app_db.py:5858, 2470-2471B2Bee/bumblebee-backend/app/main.py:13-19B2Bee/bumblebee-backend/app/main.py:15web_ui/app_db.py:13164-13180web_ui/app_db.py:13565-13573frontend/middleware.tsfrontend/src/app/(dashboard)/layout.tsx:19-45frontend/src/app/(super-admin)/layout.tsx:28-33frontend/src/context/auth-context.tsx:207-222frontend/src/context/tenant-context.tsx:92-108frontend/src/app/(dashboard)/dashboard/page.tsx:531frontend/src/app/(dashboard)/dashboard/page.tsx:698frontend/src/lib/api.tsmiddleware/auth_middleware.pytenant-context.tsx:92-108dashboard/page.tsx:531, 698frontend/src/context/*.tsxweb_ui/app_db.pyfrontend/src/app/(dashboard)/invoices/[id]/page.tsx:654frontend/src/app/(dashboard)/invoices/[id]/page.tsx:781invoice_processing/improved_visual_system.pyfrontend/src/app/(dashboard)/invoices/[id]/page.tsx:654, 781add_tenant_switcher_to_all_templates.pyfrontend/src/lib/api.ts:40frontend/src/context/tenant-context.tsx:53frontend/src/lib/api.ts:40-48main.py:1972main.py:2269web_ui/app_db.py:2298web_ui/app_db.py:2608web_ui/app_db.py:4688web_ui/app_db.py:11314web_ui/app_db.py:11329web_ui/services/file_storage_service.py:109invoice_processing/core/email_monitor.py:123main.py:1972, 2269web_ui/app_db.py:2298, 2608, 4688, 11314, 11329web_ui/historical_currency_converter.py:308web_ui/reporting_api.py:3446web_ui/app.py:141web_ui/app.py:144web_ui/app.py:141, 1441. Remove hardcoded credentials from source code and scripts
ID:
SEC-001Severity: Critical
Category: Cryptographic Failures
Affected code:
apply_schema_direct.py:16,apply_schema_sa.py:14,apply_schema_simple.py:14,check_database.py:15,create_test_user.py:40,crypto_invoice_system/api/invoice_api.pyImpact
Repository readers, including former readers, can extract plaintext database and exchange credentials, potentially enabling unauthorized access to production services.
Technical details
DB_PASSWORD = "..."inapply_schema_direct.py:16,apply_schema_sa.py:14, and related scripts.crypto_invoice_system/run_server.sh:8andcrypto_invoice_system/api/invoice_api.pycontain plaintext MEXC API credentials and fallback literals.Relevant code
apply_schema_direct.py:10-22Validation
Code evidence
apply_schema_direct.py:10-22definesDB_HOST,DB_USER, and a literalDB_PASSWORD.crypto_invoice_system/run_server.sh:8andinvoice_api.pycontain MEXC key and secret fallback values.Limitations
No credential-validity or live-service access test was supplied.
Recommended remediation
Rotate every exposed credential, purge secrets from repository history, and load them from required environment or secret-manager variables that fail startup when unset.
2. Remove committed .env.production file containing live secrets
ID:
SEC-002Severity: Critical
Category: Cryptographic Failures
Affected code:
main.py:89,main.py:1381,main.py:1570,web_ui/app_db.py:5127,check_database.py:11Impact
A tracked production environment file exposes infrastructure identifiers and a Flask secret value to repository readers; duplicated database defaults increase exposure and configuration drift.
Technical details
.env.productionis tracked and containsDB_HOST=34.39.143.82, a Cloud SQL connection name, and a Flask secret key in a comment.main.py:89,1381,1570,web_ui/app_db.py:5127, andcheck_database.py:11use the same production database host as a fallback..gitignorehas no.env.productionrule.Relevant code
main.py:83-95Validation
Code evidence
The tracked
.env.productioncontains production connection values and a commented Flask key.main.pyusesos.environ.get('DB_HOST', '34.39.143.82')at the reported locations.Limitations
No live authentication or database-access test was supplied.
Recommended remediation
Rotate exposed values, remove
.env.productionand its secrets from Git history, add it to.gitignore, and require deployment-time secret injection without production fallbacks.3. Enforce authentication on all sensitive Flask routes
ID:
SEC-003Severity: Critical
Category: Broken Access Control
Affected code:
web_ui/app_db.py:359-396,web_ui/app_db.py:13565-13573Impact
Unauthenticated callers may read financial data, perform mutations or uploads, export records, and enumerate or delete Firebase users.
Technical details
GET /api/transactionsandGET /api/statslack authentication; the admin Firebase routes atweb_ui/app_db.py:359-396also lack decorators./api/invoices/upload,/api/invoices/upload-batch, and attachment uploads, are listed without required authentication and tenant/role checks.Relevant code
web_ui/app_db.py:1-30Validation
Code evidence
web_ui/app_db.pydefines sensitive routes under@app.routewithout consistent@require_authenforcement.The reported admin routes can return or modify Firebase user records without a decorator.
Runtime evidence
Unauthenticated
GET https://deltacfoagent.vercel.app/api/transactionsreportedly returned HTTP 200.Unauthenticated
GET https://deltacfoagent.vercel.app/api/statsreportedly returned HTTP 200 with financial KPIs.Limitations
The supplied runtime results cover read endpoints, not every listed mutation or upload route.
Recommended remediation
Require authentication on every sensitive route and enforce authorization, role, and tenant ownership checks server-side before reads, writes, exports, uploads, or administrative actions.
4. Replace eval() calls in smart_ingestion.py with a safe expression evaluator
ID:
SEC-004Severity: Critical
Category: Injection
Affected code:
smart_ingestion.py:1001,smart_ingestion.py:1013,smart_ingestion.py:1023,smart_ingestion.py:1001, 1013, 1023Impact
Expression evaluation can provide a code-execution path if an attacker can influence the mapping expressions or the AI-generated upload-processing input.
Technical details
smart_ingestion.py:1001,1013, and1023calleval()forcondition_expr,origin_expr, anddestination_expr.{"__builtins__": {}}as globals but exposepdandstrinlocal_vars; this is not a complete Python sandbox.Relevant code
smart_ingestion.py:995-1007Validation
Code evidence
smart_ingestion.py:995-1007executeseval(condition_expr, {"__builtins__": {}}, local_vars).The two corresponding origin and destination evaluations are reported at lines 1013 and 1023.
Limitations
The supplied Vercel probe returned 404 for
/api/upload-with-progress; runtime exploitation was not demonstrated.The record identifies Cloud Run as the primary exposure target, but does not provide a Cloud Run URL.
Recommended remediation
Replace
eval()with a strict allowlisted expression grammar or safe evaluator that supports only required operators, fields, and functions; reject unsupported expressions.5. Disable Flask debug mode in all deployment and source configurations
ID:
SEC-005Severity: Critical
Category: Security Misconfiguration
Affected code:
cloudbuild-dev.yaml:40,invoice_processing/web/dashboard.py:257,invoice_processing/web/upload_interface.py:356,invoice_processing/improved_visual_system.py:2690,web_ui/app.py:224Impact
If the reported Cloud Run configuration is deployed, Werkzeug's interactive debugger may expose a Python console and turn application errors into code execution.
Technical details
cloudbuild-dev.yaml:40setsFLASK_DEBUG=truewhile also configuring an unauthenticated Cloud Run service.web_ui/app.py:224andinvoice_processing/web/dashboard.py:257reportedly callapp.run(debug=True); additional hardcoded debug settings are listed in related modules.Relevant code
cloudbuild-dev.yaml:34-46Validation
Code evidence
cloudbuild-dev.yaml:34-46includesFLASK_DEBUG=truein--set-env-vars.web_ui/app.py:224andinvoice_processing/web/dashboard.py:257are reported to usedebug=True.Runtime evidence
The Vercel probe reportedly returned 404 for
/__debugger__/and/console.Limitations
The supplied runtime result does not validate the Cloud Run deployment described by
cloudbuild-dev.yaml.Recommended remediation
Remove debug flags from deployment manifests and production entry points. Permit debug mode only through an explicitly controlled development configuration that cannot be enabled in production.
6. Remove insecure fallback Flask secret key and require it at startup
ID:
SEC-006Severity: Critical
Category: Cryptographic Failures
Affected code:
web_ui/app_db.py:132,cloudbuild.yaml:45Impact
Anyone who knows the fallback Flask key, or obtains the committed production key, may forge Flask session cookies and impersonate users or alter session state.
Technical details
web_ui/app_db.py:132assignsapp.secret_keyfromFLASK_SECRET_KEYwith the public fallbackdev-secret-key-delta-cfo-agent-2024.cloudbuild.yaml:45reportedly injects database, Anthropic, and Firebase secrets but notFLASK_SECRET_KEY..env.productioncontains a production Flask key in a comment.Relevant code
web_ui/app_db.py:126-138Validation
Code evidence
web_ui/app_db.py:126-138usesos.getenv('FLASK_SECRET_KEY', 'dev-secret-key-delta-cfo-agent-2024').cloudbuild.yaml:45lacksFLASK_SECRET_KEYin the reported--set-secretsconfiguration.Runtime evidence
The deployed URL reportedly served Flask responses.
Limitations
No session-cookie forgery or authenticated-privilege test was supplied.
Recommended remediation
Rotate all exposed Flask keys, remove the literal fallback, require
FLASK_SECRET_KEYat startup, and inject it through the deployment secret mechanism.7. Eliminate user-controlled input in subprocess Python code string execution
ID:
SEC-007Severity: Critical
Category: Injection
Affected code:
web_ui/app_db.py:12577,app_db.py,web_ui/app_db.py:11825,web_ui/app_db.py:11825-11840Impact
If an attacker controls the interpolated filename, the subprocess Python command may execute injected Python code. A weak Flask session key and reported unauthenticated upload behavior increase the potential attack path.
Technical details
python -cstring containingfilename_safeand invokes it withsubprocess.run.web_ui/app_db.py:12571includes@require_auth, but the supplied runtime note states the deployed endpoint did not return an authentication challenge.Relevant code
web_ui/app_db.py:12571-12583Validation
Code evidence
The reported subprocess construction occurs at
web_ui/app_db.py:11825-11840and12577/12620.filename_safe = filename.replace(chr(92), '/')is insufficient for embedding untrusted data in Python source.Runtime evidence
A POST to
/api/process-duplicateswithout credentials reportedly returned HTTP 400File not found, not 401.The supplied note also reports
/api/uploadaccepted unauthenticated access.Limitations
No command-execution result was supplied.
Recommended remediation
Do not generate Python source from request data. Pass values as argv or environment variables to a fixed worker, validate filenames against an upload directory, and enforce authentication consistently.
8. Eliminate dynamic SQL identifier interpolation to prevent SQL injection
ID:
SEC-008Severity: High
Category: Injection
Affected code:
web_ui/app_db.py:1914,web_ui/app_db.py:1920,web_ui/app_db.py:1922,web_ui/app_db.py:1927,web_ui/app_db.py:1929,web_ui/app_db.py:1934,web_ui/app_db.py:1942,web_ui/app_db.py:1963,web_ui/app_db.py:2001,web_ui/app_db.py:2013,web_ui/app_db.py:5858,web_ui/app_db.py:2470-2471,web_ui/ai_tools.py,analyze_db_schema.py:34,analyze_db_schema.py:40,migrate_data_to_postgresql.py:209,migrate_data_to_postgresql.py:214,migrate_data_to_postgresql.py:240,check_database.py,web_ui/app_db.py:1914, 1920, 1922, 1927, 1929, 1934, 1942, 1963, 2001, 2013,analyze_db_schema.py:34, 40,migrate_data_to_postgresql.py:209, 214, 240,web_ui/app_db.py:5858, 2470-2471Impact
An attacker may inject an SQL identifier into transaction updates; the unauthenticated endpoint may also permit modification of tenant-related columns and undermine tenant isolation.
Technical details
api_update_transactionobtainsfield = data.get('field')and passes unknown values toupdate_transaction_fieldwithout an allowlist.UPDATE transactions SET {field} = %s WHERE tenant_id = %s AND transaction_id = %satweb_ui/app_db.py:2470-2471.web_ui/app_db.py:5852has no authentication decorator.Relevant code
web_ui/app_db.py:1908-1920Validation
Code evidence
The reported fallback directly interpolates
{field}into an SQL statement; parameter binding protects values but not identifiers.Other reported query construction sites include
web_ui/app_db.py:1914-2013,analyze_db_schema.py:34,40, and migration scripts.Runtime evidence
A POST to
/api/update_transactionwithfield=confidencereportedly reached the endpoint and returned HTTP 500 rather than an authentication challenge.Limitations
The supplied probe did not demonstrate successful SQL manipulation or data alteration.
Recommended remediation
Allow only a fixed set of writable column names before query construction, reject unknown fields, keep values parameterized, and require authentication plus tenant authorization.
9. Restrict wildcard CORS combined with credentials in FastAPI subservice
ID:
SEC-009Severity: High
Category: Security Misconfiguration
Affected code:
B2Bee/bumblebee-backend/app/main.py:13-19,B2Bee/bumblebee-backend/app/main.py:15Impact
If the B2Bee service is reachable, arbitrary origins may be able to issue credentialed cross-origin requests, depending on the Starlette version and response behavior.
Technical details
B2Bee/bumblebee-backend/app/main.py:13-19configuresCORSMiddlewarewithallow_origins=["*"]andallow_credentials=True.allow_methods=["*"]andallow_headers=["*"].Relevant code
B2Bee/bumblebee-backend/app/main.py:7-25Validation
Code evidence
B2Bee/bumblebee-backend/app/main.py:7-25contains the reported wildcard CORS configuration.Runtime evidence
The B2Bee API was reportedly not reachable at the supplied Vercel URL; API paths returned 404.
Limitations
No deployed B2Bee endpoint or credentialed cross-origin request result was supplied.
Recommended remediation
Replace the wildcard origin with an explicit allowlist of trusted origins and restrict methods and headers to those required by the service.
10. Fix path traversal in unauthenticated invoice PDF download endpoint
ID:
SEC-010Severity: High
Category: Broken Access Control
Affected code:
web_ui/app_db.py:13164-13180Impact
An unauthenticated caller may request a path containing traversal segments and cause the application to serve files outside the invoice directory.
Technical details
/api/invoices/pdf/<path:filename>accepts slashes through Flask'spathconverter and has no reported authentication decorator.filenamedirectly withbase_dir/invoices/issuedusingos.path.join.../../.envresolves outside the intended directory beforesend_fileis called.Relevant code
web_ui/app_db.py:13158-13186Validation
Code evidence
web_ui/app_db.py:13158-13186constructspdf_path = os.path.join(base_dir, 'invoices', 'issued', filename)and sends it when it exists.The route declaration at line 13164 uses
<path:filename>.Runtime evidence
The supplied Vercel probe for
/api/invoices/pdf/test.pdfreturned HTML 404.Limitations
The current Vercel routing did not expose the route, so file disclosure was not demonstrated there.
Recommended remediation
Require authentication and use
send_from_directoryorsafe_joinwith a fixed base directory. Reject traversal and verify the canonical resolved path remains within the invoice directory.11. Validate archive member paths before extraction to prevent Zip Slip
ID:
SEC-011Severity: High
Category: Broken Access Control
Affected code:
web_ui/app_db.py:13565-13573Impact
A crafted archive may write files outside the intended extraction directory, potentially overwriting application or configuration files if the endpoint is deployed and writable.
Technical details
zip_ref.extractall(extract_dir)atweb_ui/app_db.py:13567without validating archive member paths.../../../../...can resolve outsideextract_dir.@require_auth.Relevant code
web_ui/app_db.py:13559-13579Validation
Code evidence
web_ui/app_db.py:13559-13579extracts ZIP contents directly withextractall(extract_dir).The route is reported as
/api/invoices/upload-batchat line 13668 without an authentication decorator.Runtime evidence
The supplied Vercel probe returned 404 for
/api/invoices/upload-batch.Limitations
No file-write or application-overwrite result was demonstrated in a live deployment.
Recommended remediation
Require authentication and validate every archive member's canonical destination against the extraction root before writing. Reject absolute paths, traversal components, and links.
12. Add server-side authentication enforcement to Next.js middleware and layouts
ID:
SEC-012Severity: High
Category: Broken Access Control
Affected code:
frontend/middleware.ts,frontend/src/app/(dashboard)/layout.tsx:19-45,frontend/src/app/(super-admin)/layout.tsx:28-33Impact
Unauthenticated users may receive dashboard HTML, and client-side-only role checks do not provide server-side protection for super-admin content or operations.
Technical details
frontend/middleware.tsonly invokesnext-intlrouting and performs no session validation.if (!isAuthenticated) return nullguard is reportedly commented out.useEffect, after the server has delivered the page.Relevant code
frontend/middleware.ts:1-21Validation
Code evidence
frontend/middleware.ts:1-21contains locale middleware without an authentication check.frontend/src/app/(dashboard)/layout.tsx:41-45reportedly comments out the unauthenticated guard.Runtime evidence
Unauthenticated
GET https://deltacfoagent.vercel.app/dashboardreportedly returned HTTP 200 with full dashboard HTML.Limitations
The supplied result does not establish whether protected backend APIs independently enforce authorization.
Recommended remediation
Validate the session and required role in server-side middleware or layouts before rendering protected routes, and enforce the same authorization on every backend operation.
13. Implement CSRF protection on all cookie-authenticated state-changing requests
ID:
SEC-013Severity: High
Category: Broken Access Control
Affected code:
frontend/src/context/auth-context.tsx:207-222,frontend/src/context/tenant-context.tsx:92-108,frontend/src/app/(dashboard)/dashboard/page.tsx:531,frontend/src/app/(dashboard)/dashboard/page.tsx:698,frontend/src/lib/api.ts,middleware/auth_middleware.py,tenant-context.tsx:92-108,dashboard/page.tsx:531, 698,frontend/src/context/*.tsx,web_ui/app_db.pyImpact
Cookie-authenticated state-changing requests may be triggered cross-site, allowing unauthorized tenant switches or mutations when a victim is logged in.
Technical details
requirements.txt:11includes Flask-WTF, but the supplied repository review found noCSRFProtectinitialization orflask_wtfimports.SESSION_COOKIE_SAMESITEsetting is reported.frontend/src/context/auth-context.tsx:209-211sends a credentialed POST to/api/auth/switch-tenant/<tenantId>without an Authorization header.Relevant code
frontend/src/context/auth-context.tsx:201-228Validation
Code evidence
auth-context.tsx:201-228usescredentials: "include"for a state-changing POST without a CSRF token.web_ui/app_db.py:9442reportedly definesbulk_update_transactionswithout@require_auth.Runtime evidence
The supplied probe for
/api/bulk_update_transactionsreturned 404 on Vercel.Limitations
No cross-site request or state-change result was demonstrated.
Recommended remediation
Initialize global CSRF protection for cookie-authenticated state changes, require and validate CSRF tokens, configure an appropriate SameSite cookie policy, and enforce authentication on every mutation.
14. Prevent XSS from unvalidated URLs and innerHTML assignments
ID:
SEC-014Severity: High
Category: Injection
Affected code:
frontend/src/app/(dashboard)/invoices/[id]/page.tsx:654,frontend/src/app/(dashboard)/invoices/[id]/page.tsx:781,invoice_processing/improved_visual_system.py,frontend/src/app/(dashboard)/invoices/[id]/page.tsx:654, 781,add_tenant_switcher_to_all_templates.pyImpact
Attacker-controlled tenant or invoice content may execute JavaScript in users' browsers through unescaped HTML; unvalidated attachment URLs may also permit dangerous URI schemes in the frontend.
Technical details
tenant.company_nameandtenant.roleinserted intoinnerHTMLwithout escaping inbusiness_overview.html:726andinvoices.html:2841.<a href={attachment.url}>at lines 654 and 781 without an explicit HTTP/HTTPS scheme allowlist.Relevant code
frontend/src/app/(dashboard)/invoices/[id]/page.tsx:648-660Validation
Code evidence
Reported template code assigns interpolated tenant values to
tenantItem.innerHTML.frontend/src/app/(dashboard)/invoices/[id]/page.tsx:648-660rendershref={attachment.url}.Runtime evidence
GET https://deltacfoagent.vercel.app/reportedly returned HTTP 200 and serves the Flask template.The reported
/api/auth/meprobe returned 404.Limitations
No live XSS payload execution was supplied.
The Next.js URL sink was not confirmed in the deployed Vercel application.
Recommended remediation
Render untrusted values as text or through context-appropriate escaping, avoid
innerHTML, and allow onlyhttpandhttpsattachment schemes before rendering links.15. Derive tenant identity from authenticated session rather than localStorage
ID:
SEC-015Severity: High
Category: Broken Access Control
Affected code:
frontend/src/lib/api.ts:40,frontend/src/context/tenant-context.tsx:53,frontend/src/lib/api.ts:40-48Impact
Client-controlled tenant identifiers can route API requests to another tenant, risking cross-tenant data access when backend authorization relies on the supplied header.
Technical details
frontend/src/lib/api.ts:40-48falls back tolocalStorage.getItem("tenantId")when the in-memory tenant ID is absent.X-Tenant-IDheader on API calls.frontend/src/context/tenant-context.tsx:53uses the hardcoded fallback"delta"when tenant values are missing.Relevant code
frontend/src/lib/api.ts:34-46Validation
Code evidence
getApiTenantId()returns a browser-controlled localStorage value after the in-memory value is unavailable.The tenant context expression
config.tenant_id || response.tenant_id || "delta"supplies a fixed tenant fallback.Runtime evidence
The deployed application reportedly returned HTTP 200; the supplied record states both code paths are active in the production bundle.
Limitations
No cross-tenant request or unauthorized data response was supplied.
Recommended remediation
Derive tenant identity exclusively from the authenticated server-side session or token, ignore client-supplied tenant headers for authorization, and fail closed when tenant context is absent.
16. Replace MD5 usage in security-relevant contexts with strong hash algorithms
ID:
SEC-016Severity: Medium
Category: Cryptographic Failures
Affected code:
main.py:1972,main.py:2269,web_ui/app_db.py:2298,web_ui/app_db.py:2608,web_ui/app_db.py:4688,web_ui/app_db.py:11314,web_ui/app_db.py:11329,web_ui/services/file_storage_service.py:109,invoice_processing/core/email_monitor.py:123,main.py:1972, 2269,web_ui/app_db.py:2298, 2608, 4688, 11314, 11329Impact
MD5 collisions can cause incorrect deduplication or integrity decisions, potentially suppressing distinct financial or email records.
Technical details
hashlib.md5(...)is reported inmain.py:1972,2269,web_ui/app_db.py:2298,2608,4688,11314,11329,web_ui/services/file_storage_service.py:109, andinvoice_processing/core/email_monitor.py:123.main.py:1972derives a 12-character transaction ID from date, description, and amount using MD5.file_storage_service.py:109reportedly uses MD5 for file integrity checking.Relevant code
main.py:1966-1978Validation
Code evidence
main.py:1966-1978constructsidentifierand assignshashlib.md5(identifier.encode()).hexdigest()[:12]astransaction_id.Runtime evidence
The deployed application root reportedly returned HTTP 200.
Limitations
No collision-based record suppression or integrity bypass was demonstrated at runtime.
Recommended remediation
Use SHA-256 or stronger for non-password integrity and identifiers. For password hashing, use a password-specific algorithm such as Argon2 or bcrypt; review whether truncated identifiers require collision-resistant redesign.
17. Transmit Fixer.io API key over HTTPS instead of plaintext HTTP
ID:
SEC-017Severity: Medium
Category: Cryptographic Failures
Affected code:
web_ui/historical_currency_converter.py:308Impact
The Fixer.io API key is sent over plaintext HTTP and may be observed or modified by network intermediaries, proxies, or logs.
Technical details
web_ui/historical_currency_converter.py:308buildshttp://data.fixer.io/api/{date_str}.self.backup_apis['fixer']as theaccess_keyquery parameter.Relevant code
web_ui/historical_currency_converter.py:302-314Validation
Code evidence
web_ui/historical_currency_converter.py:302-314shows the HTTP URL and query parameter containingaccess_key.Runtime evidence
The deployed application root reportedly returned HTTP 200.
Limitations
No outbound request capture was supplied to confirm invocation in the deployed environment.
Recommended remediation
Use the Fixer.io HTTPS endpoint and verify certificate validation. Do not place long-lived credentials in URLs where possible; use an HTTPS-only alternative if the service plan does not support TLS.
18. Reject non-finite float values (NaN/Inf) from query parameters
ID:
SEC-018Severity: Low
Category: Injection
Affected code:
web_ui/reporting_api.py:3446,web_ui/app.py:141,web_ui/app.py:144,web_ui/app.py:141, 144Impact
Special floating-point values can silently alter financial report filtering and produce incorrect dashboard results.
Technical details
web_ui/reporting_api.py:3446converts themin_amountquery parameter directly withfloat(...).HAVING SUM(amount) >= %scomparisons without a finiteness check.web_ui/app.py:141,144contains the same parsing pattern.Relevant code
web_ui/reporting_api.py:3440-3452Validation
Code evidence
web_ui/reporting_api.py:3440-3452parsesmin_amountusingfloat(request.args.get('min_amount', 1000))with nomath.isfinite()validation.Runtime evidence
The supplied request to
/api/reports/sankey-flow?min_amount=nanreturned HTTP 404 on Vercel.Limitations
The deployed route was unavailable, so incorrect query results were not observed at runtime.
Recommended remediation
Parse numeric inputs and reject values for which
math.isfinite()is false. Return a validation error before constructing or executing the report query.