Skip to content

spp_programs base_manager: safe_eval context exposes a live user recordset (ORM/cursor reachable) — same class as #459 #468

Description

@gonzalesedwin1123

Split out of the review of #459 (spp_change_request_v2 field-mapping transforms), where the same class of safe_eval sandbox escape was the headline finding. This one is in spp_programs and is out of that PR's scope, but it is the same defect.

The hole

spp_programs/models/managers/base_manager.py _get_eval_context() returns:

return {
    "datetime": safe_eval.datetime,
    "dateutil": safe_eval.dateutil,
    "time": safe_eval.time,
    "uid": self.env.uid,
    "user": self.env.user,   # <-- live recordset
}

user is a live res.users recordset. Odoo 19's safe_eval applies no attribute allowlist — only dunder-name blocking and the fixed _UNSAFE_ATTRIBUTES list (frames/code/mro/tracebacks). Recordsets pass check_values. So a mode="eval" expression evaluated in this context can reach the full ORM and the DB cursor via the recordset's own attributes:

user.env['res.users'].sudo().search([]).mapped('login')
user.sudo()
user._cr.execute("...")          # raw SQL, ACL/record-rule/audit bypass
user.env['ir.config_parameter'].sudo().get_param('database.secret')

The nosemgrep justification on the safe_eval call claims "the evaluation context only exposes datetime/dateutil/time and the current user" — which is the same blind spot: handing in user hands in user.env.

This was proven at runtime for the identical pattern in #459 (two independent reviewers executed the escapes against Odoo 19's real safe_eval in-container). The mechanism is not module-specific.

What to check when fixing

  • Who authors the expressions and who triggers evaluation. _safe_eval here backs eligibility / entitlement / payment domain and condition expressions. Determine the trust boundary: which roles can write these expressions (config-time), and whether any evaluation runs under sudo/superuser or in a low-privileged user's session. That sets the severity (trusted-admin config vs privilege escalation).
  • Enumerate every caller of _safe_eval / _get_eval_context in spp_programs and downstream managers.

Fix direction (mirror #459's resolution)

  • Don't put a live recordset in the context. If expressions need user attributes, pass a handle-free snapshot (e.g. SimpleNamespace(id=..., login=..., ...)) or just the specific scalars (uid is already there). uid alone is safe; user is the problem.
  • If author-side restriction is the intended boundary, enforce it with field-level groups=/ACLs rather than help-text ("only admins should…" is not enforcement), and add a test that pins it.
  • Correct the nosemgrep justification once the recordset is out.

Context: #459 (review — findings C1/C2 and suggestion S3), spp_change_request_v2/strategies/field_mapping.py for the resolved pattern.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions