Skip to content

security(spp_change_request_v2): writable selected_field_name can steer dynamic-approval routing [Severity: Medium] #344

Description

@gonzalesedwin1123

Follow-up from PR #343 (writable selected_field bypasses CR conflict checks). Same root cause, different sink; deliberately out of #343's scope (which is conflict/duplicate detection).

Finding

selected_field_name on spp.change.request is only view/model-readonly (not server-side write protection), and group_cr_user has write on their own change requests. Besides conflict detection (fixed in #343), the field is also read by dynamic-approval routing:

  • _resolve_dynamic_approval (change_request.py:905) reads self.selected_field_name and feeds self to the CEL evaluator; candidate approval definitions' CEL conditions match on record.selected_field_name (see README/USAGE examples). Called from _get_approval_definition during the approval flow.

So in draft, a user can write({'selected_field_name': <field>}) to make a different candidate CEL condition match — steering the CR to a weaker or no-approval definition — then submit. #343 (conflict) does not touch routing; #264 (cr-dynamic-approval-field) freezes selected_field_name only after the CR leaves draft/revision, so neither closes this draft-time routing manipulation.

Severity: Medium

Requires an authenticated CR user, but lets them bypass the intended approval workflow for their own change request (approve via an easier/catch-all definition). Confidentiality/integrity impact depends on the CR type's field-conditioned approval config.

Suggested fix

Harden selected_field_name so it can only hold the server-derived value:

Best coordinated with #264 (which already adds a write() guard on these fields for the post-submit freeze) to avoid two overlapping write overrides. Needs a design decision on A vs B.

Source: observed at change_request.py:905 / :888 while working internal/plans/sec-cr-selected-field-conflict-bypass.md. Related: #343, #264.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions