Skip to content

OD402 public-write-access

An access row gives public or portal users write, create or unlink, through their group or no group.

Category Severity Series Needs the index
Security Warning all no

What it reports

A row of an ir.model.access CSV file Odoo loads, whether ir.model.access.csv or a name such as ir.model.access-hook.csv, that gives perm_write, perm_create or perm_unlink to base.group_public, to base.group_portal or, with an empty group_id or no such column, to every user. A permission reads as set when it is 1, true or yes, in any case.

An ir.access CSV file starts at saas~19.4, where ir.access replaces ir.model.access: a row of it for one of those two groups whose operation contains c, u or d reports, and a row of an ir.model.access file does not, since Odoo no longer has the model to load it into.

Why it matters

A portal user is a customer or supplier with a login. Like any user, they can call the ORM methods of every model over JSON-RPC, not only through the pages the module wrote for them, so the access rows and record rules are all that stand between them and the table. With write access, they can change any field of the records the rules let through, including fields the portal form never shows. The public user stands for every anonymous visitor, and its rights apply in every auth="public" route.

The usual pattern is read-only access, narrowed by a record rule to the user's own records, and a controller that checks what the user may change and then writes with sudo(). A record rule that confines the row may make it safe; the finding then needs that rule reviewed with it.

An ir.model.access row with no group grants every user, so the public and portal users too. An ir.access row with no group restricts every user instead, and is not reported. Neither is a row with no group that only reads, nor one whose id the scan sees written twice, since the last write may set a group, nor one in a file the manifest does not load, unless the module's own Python loads that file. Before saas~13.2 Odoo does not check the rows of a TransientModel, so none of them is reported, whatever its group.

A row whose active cell reads as false (0, false, no in any case, or empty) grants nothing, so it is not reported, with or without a group. A file with no active column leaves its rows active.

Only CSV rows are checked; an access record written in XML is not, and neither is a row that names its group by database id in a group_id/.id column.

Example

security/ir.model.access.csv
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_library_loan_user,library.loan user,model_library_loan,base.group_user,1,1,1,1
access_library_loan_portal,library.loan portal,model_library_loan,base.group_portal,1,1,1,0

Any portal user can create loans and rewrite the due date or borrower of every loan they can see.

security/ir.model.access.csv
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_library_loan_user,library.loan user,model_library_loan,base.group_user,1,1,1,1
access_library_loan_portal,library.loan portal,model_library_loan,base.group_portal,1,0,0,0

Let the portal controller extend a loan after checking that it belongs to the user.

A row with no group goes further:

security/ir.model.access.csv
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_library_loan_all,library.loan all,model_library_loan,,1,1,0,0

Every user, logged in or not, can rewrite the loans the record rules let through. Name a group.

security/ir.model.access.csv
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_library_loan_all,library.loan all,model_library_loan,base.group_user,1,1,0,0

Suppressing

A comment would break the CSV file, so pass --ignore OD402 or add it to ignore in the configuration file. To keep one reviewed row without turning the rule off, record it in a baseline.

See suppressing a finding.