Skip to content

gate-7 Pattern 2 assumes OpenRegister enforces per-object authz — for most of the fleet it is default-OPEN (compounds #365) #372

Description

@rubenvdlinde

Summary

gate-7 clears a #[NoAdminRequired] method that reaches storage through
ObjectService / a *Mapper on the stated reasoning that OpenRegister's
register RBAC enforces per-object authorisation one layer down
(check_no_admin_idor.py Pattern 2, and the runner's own FAIL guidance:
"If the endpoint reaches storage through a service or mapper, check whether the
guard is enforced THERE"
).

For most of the fleet that layer does not exist. OpenRegister's
PermissionHandler::hasGroupPermission() returns true for any schema whose
authorization block is empty or null, and the opt-in that would close it
defaults to false.

openregister/lib/Service/Object/PermissionHandler.php:1207

if (empty($authorization) === true || $publicOptIn === true) {
    if ($this->isDefaultClosedEnforced() === true && ...) { return false; }
    // Default-OPEN behaviour preserved.
    return true;
}

isDefaultClosedEnforced() reads
appConfig->getValueBool('openregister', 'enforce_default_closed', default: false),
and its own docblock states the reason: "the fleet ships ~15 leaf apps whose
*_register.json files do not yet declare authorization blocks. Flipping the
default would brick them overnight."

This is already known and tracked on the product side as
ConductionNL/openregister#1955 and #2011. What is not tracked is that gate-7
encodes the opposite assumption
, and therefore issues a clean verdict over
exactly the endpoints those issues describe as open.

Measured — how many fleet schemas actually declare authorization

Walked every register/schema JSON shipped by each app at origin/development:

app schemas with authorization
shillinq 871 0
scholiq 191 0
openconnector 87 0
nldesign 50 0
doriath 1 0
zaakafhandelapp ships no register config at all 0
pipelinq 164 1
hermiq 69 1
docudesk 72 1
procest 299 6
larpingapp 23 2
openregister 145 15
opencatalogi 74 15
openbuild 39 15
decidesk 172 24
softwarecatalog 27 21

Positive control on the lookup (an empty lookup manufactures the most
alarming possible answer for free): the same walker finds 21/27 in
softwarecatalog and 26 literal "authorization" occurrences in
softwarecatalog/lib/Settings/softwarecatalogus_register.json. The zeros are
measurements, not a bad key.

zaakafhandelapp is the sharpest case: it ships no register config, and its own
exported production register (docs/json/zaken_2026-03-27_102152.json)
carries "authorization": null on the zaken register and on all twelve ZGW
schemas. empty(null) === truereturn true.

Why this matters here and not only on #2011

Combined with #365 (gate-7 accepts a no user → 401 preamble as an
authorisation guard), the two produce a gate that is silent in both directions
at once:

Measured consequence: gate-7 reports 0 in all 18 fleet apps. With #365's
proposed fix applied, the same runs over the same trees report 423 findings.

What I am asking for

Not a Pattern-2 removal — the pattern is correct where an authorization block
exists. Two smaller things:

  1. Make the assumption checkable. Pattern 2 currently applies on the
    namespace (OCA\OpenRegister) or on the presence of an ObjectService
    call
    . It should be conditional on the touched schema actually declaring an
    authorization block, or — if that is too much resolution for a regex gate —
    the runner's FAIL/PASS guidance should say plainly that OR is default-OPEN
    for schemas without an authorization block, so "the guard is enforced
    THERE" stops being the default reading.
  2. Cross-link gate-7 accepts a 401 authentication check as an authorisation guard — 4 real IDORs read as PASS (fleet-wide false negative) #365, this issue, [MEDIUM] Object write path is default-open (anonymous/any-user create & update) openregister#1955 and #2011 so
    the three halves are not closed independently.

⚠️ Sequencing: like #365, #347, #356 and #358, any fix here has the false-RED
shape and will redden repos simultaneously. Filing, not merging.

Note on #2011's audit command

#2011 suggests grep -L '"authorization"' apps-extra/*/registers/*.json. No
fleet app has a registers/ directory — the configs live under
lib/Settings/*_register.json and lib/Settings/register.d/*.json. As written
that command matches nothing and exits reporting nothing to do, which is the
same absence-claim shape this programme keeps paying for.


Confirmed by: canonical ConductionNL/.github @ 112d4c9 (post-#368);
openregister @ origin/development 9628dc5d0; per-app refs listed in
/home/rubenlinde/fleet-board/findings/gate7-fleet-reaudit.md.

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