Why Security Auditing Matters for Odoo
Odoo modules handle sensitive business data: customer information, financial records, employee details, and trade secrets. Yet many custom modules are deployed with security gaps: missing access control lists, sudo() calls that bypass security, SQL queries vulnerable to injection, controllers that don't verify authentication, and XSS vulnerabilities in rendered HTML. AI security auditing agents scan your module code and identify these vulnerabilities before they reach production.
What AI Security Agents Check
1. Missing Access Control
# AI scans for models without proper access rules:
"Security Audit: Access Control
CRITICAL — Models without ACL rules:
├── custom.payment.token (stores payment data!)
│ No ir.model.access.csv entry found
│ Risk: Any authenticated user can read/write all records
│ Fix: Add access rules for appropriate groups
│
└── custom.api.log (stores API keys in log entries)
No ir.model.access.csv entry found
Risk: Sensitive data exposed to all users
WARNING — Models with overly permissive rules:
├── custom.customer.note
│ All internal users have read/write/create/delete
│ Should this be limited to sales team?
│
└── custom.financial.report
All internal users can read
Should financial reports be restricted to accounting group?
Missing record rules:
├── custom.support.ticket — no multi-company rule
│ Users in Company A can see Company B tickets
└── custom.employee.review — no manager-only rule
All HR users can see all reviews"2. SQL Injection Vulnerabilities
# AI detects unsafe SQL construction:
"CRITICAL — SQL Injection Vulnerabilities:
File: models/custom_report.py, line 45
Code: cr.execute('SELECT * FROM res_partner WHERE name = \'%s\'' % name)
Risk: Direct string interpolation in SQL query
Fix: cr.execute('SELECT * FROM res_partner WHERE name = %s', (name,))
File: controllers/api.py, line 128
Code: domain = [('name', 'ilike', request.params.get('search'))]
Risk: Unvalidated user input in ORM domain (lower risk but sanitize)
Fix: Validate and sanitize search parameter before use
File: models/custom_export.py, line 67
Code: cr.execute(f'DELETE FROM {table_name} WHERE id = {record_id}')
Risk: Table name and record ID from variable — potential injection
Fix: Use ORM unlink() or parameterized query with whitelisted table"3. Unsafe sudo() Usage
# AI identifies sudo() calls that bypass security:
"WARNING — sudo() Usage Review:
File: controllers/portal.py, line 34
Code: records = request.env['account.move'].sudo().search(domain)
Risk: Bypasses record rules — portal user might access all invoices
Context: Domain uses partner_id filter, but still risky
Fix: Use request.env['account.move'].search(domain) with proper
record rules, or verify domain cannot be manipulated
File: models/custom_workflow.py, line 89
Code: order.sudo().write({'state': 'approved'})
Risk: Current user might not have approval permission
Context: Called from button action — check group on button
Fix: Add groups= attribute to button in view, or check
self.env.user.has_group() before sudo() write
Acceptable sudo() usage found (5 instances):
├── Mail template rendering (standard pattern) ✓
├── Sequence number generation (standard pattern) ✓
└── System parameter read (standard pattern) ✓"4. XSS Vulnerabilities
# AI checks for cross-site scripting risks:
"WARNING — Potential XSS:
File: models/custom_product.py, line 23
Field: description = fields.Html(sanitize=False)
Risk: Unsanitized HTML stored and rendered
Fix: Use sanitize=True (default) or sanitize_tags=True
File: controllers/website.py, line 56
Code: return request.render('module.template', {'user_input': raw_input})
Risk: If template uses t-raw on user_input, XSS is possible
Check: Verify template uses t-esc (escaped) not t-raw (unescaped)
File: static/src/js/widget.js, line 42
Code: this.el.innerHTML = this.props.value;
Risk: Direct innerHTML assignment with user-controlled value
Fix: Use this.el.textContent or sanitize before innerHTML"5. Authentication & Authorization
# AI checks controller endpoints:
"Security Audit: Controllers
CRITICAL — Unauthenticated endpoints:
├── /api/customer/data (type='json', auth='none')
│ Returns customer data without any authentication
│ Fix: Change to auth='user' or auth='public' with API key check
│
└── /api/export/csv (type='http', auth='none')
Allows CSV export of any model without authentication
Fix: Require authentication and verify model access
WARNING — Missing CSRF protection:
├── /custom/webhook (type='json', auth='public', csrf=False)
│ CSRF disabled — verify this is intentionally for webhooks
│ If so: validate webhook signature/secret
│
└── /custom/form/submit (type='http', auth='public', csrf=False)
Form submission without CSRF — vulnerable to forged requests
Fix: Enable CSRF or add custom token validation"Security Score
| Category | Issues | Severity | Auto-fixable |
|---|---|---|---|
| Access Control | 4 | 2 Critical, 2 Warning | Yes (CSV generation) |
| SQL Injection | 3 | 3 Critical | Yes (parameterize) |
| sudo() Misuse | 2 | 2 Warning | Needs review |
| XSS | 3 | 1 Critical, 2 Warning | Partial |
| Authentication | 4 | 2 Critical, 2 Warning | Partial |
| Total | 16 | 7 Critical, 9 Warning |
OWASP Top 10 Coverage
- A01 Broken Access Control: ACL and record rule verification
- A02 Cryptographic Failures: check for plaintext password storage
- A03 Injection: SQL injection and ORM domain injection checks
- A04 Insecure Design: architecture-level security review
- A05 Security Misconfiguration: debug mode, default passwords
- A06 Vulnerable Components: outdated Python package dependencies
- A07 Authentication Failures: session handling, password policies
- A08 Data Integrity: check for unsigned data in critical flows
- A09 Logging Failures: sensitive data in logs, missing audit trail
- A10 SSRF: check for user-controlled URLs in server-side requests
Automated Fix Generation
# AI generates fix patches:
# Fix 1: SQL injection — parameterize query
# File: models/custom_report.py
- cr.execute('SELECT * FROM res_partner WHERE name = \'%s\'' % name)
+ cr.execute('SELECT * FROM res_partner WHERE name = %s', (name,))
# Fix 2: Missing ACL — generate access rules
# File: security/ir.model.access.csv (append)
+ access_custom_payment_token_user,...,model_custom_payment_token,account.group_account_user,1,0,0,0
+ access_custom_payment_token_manager,...,model_custom_payment_token,account.group_account_manager,1,1,1,1
# Fix 3: Unsafe HTML field
# File: models/custom_product.py
- description = fields.Html(sanitize=False)
+ description = fields.Html(sanitize=True)DeployMonkey AI Security Auditor
DeployMonkey's AI security agent scans your custom Odoo modules before deployment. It checks access controls, SQL injection, XSS, authentication, and sudo usage. Get a security score with prioritized fixes before any code reaches production.