Gate parse-time reads on the user the page is parsed for - #1346
Open
alistair3149 wants to merge 1 commit into
Open
Gate parse-time reads on the user the page is parsed for#1346alistair3149 wants to merge 1 commit into
alistair3149 wants to merge 1 commit into
Conversation
alistair3149
force-pushed
the
1059-parse-time-gates
branch
from
September 2, 2026 15:08
580868e to
ceb4b6c
Compare
alistair3149
added a commit
that referenced
this pull request
Sep 2, 2026
For #1059 Follows-up to #1346 With the parse-time gates in place, the parser cache still served whatever the last parse produced to every reader: a sysop's parse cached restricted values for anonymous readers, and an anonymous parse cached gaps for sysops. Such output is now keyed by the parsing user's access class. * A lazy parser option, `neowikiAccessClass`, registered through `ParserOptionsRegister` and marked cache-varying. Its value is a hash of the parsing user's effective groups plus the wiki-level `read` and `neowiki-query` decisions, and null for the anonymous user, so save-time and job-queue parses keep the canonical key. * Every gated read records the option as used: the two raw query functions, `{{#neowiki_value}}`, and the Lua library. Only pages with such reads carry the option in their cache key; every other page, `{{#view}}` included, keeps one entry. * ADR 27's open decision on parse-time semantics resolves to this rule. The alternatives weighed (a fixed anonymous authority, post-cache trimming, a cache-off operating rule) are recorded under Alternatives Considered. The class is a proxy for the permission hooks: exact wherever page access follows group membership, wrong for hooks that grant per user, which is why such wikis must run with the parser cache off. Existing installs need one parser-cache purge, noted in the upgrading docs: entries saved before this change carry no access-class key. ## Manual Browser Check 1. With the parser cache enabled, restrict a page's read permission for anonymous users (for example with the Lockdown extension) and give it a Subject with a text property. 2. On a second page, save `{{#neowiki_value: <property> | page=<restricted page>}}` as a sysop and view it: the value shows. 3. View that page logged out, without purging: the value is absent. Log back in and view it again: the value shows. Each access class keeps its own cached copy. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YDxvurgMTmC6JieEVYRzq8
alistair3149
force-pushed
the
1059-parse-time-gates
branch
from
September 2, 2026 20:08
ceb4b6c to
815a282
Compare
alistair3149
added a commit
that referenced
this pull request
Sep 2, 2026
For #1059 Follows-up to #1346 With the parse-time gates in place, the parser cache still served whatever the last parse produced to every reader: a sysop's parse cached restricted values for anonymous readers, and an anonymous parse cached gaps for sysops. Such output is now keyed by the parsing user's access class. * A lazy parser option, `neowikiAccessClass`, registered through `ParserOptionsRegister` and marked cache-varying. Its value is a hash of the parsing user's effective groups plus the wiki-level `read` and `neowiki-query` decisions, and null for the anonymous user, so save-time and job-queue parses keep the canonical key. * Every gated read records the option as used: the two raw query functions, `{{#neowiki_value}}`, and the Lua library. Only pages with such reads carry the option in their cache key; every other page, `{{#view}}` included, keeps one entry. * ADR 27's open decision on parse-time semantics resolves to this rule. The alternatives weighed (a fixed anonymous authority, post-cache trimming, a cache-off operating rule) are recorded under Alternatives Considered. The class is a proxy for the permission hooks: exact wherever page access follows group membership, wrong for hooks that grant per user, which is why such wikis must run with the parser cache off. Existing installs need one parser-cache purge, noted in the upgrading docs: entries saved before this change carry no access-class key. ## Manual Browser Check 1. With the parser cache enabled, restrict a page's read permission for anonymous users (for example with the Lockdown extension) and give it a Subject with a text property. 2. On a second page, save `{{#neowiki_value: <property> | page=<restricted page>}}` as a sysop and view it: the value shows. 3. View that page logged out, without purging: the value is absent. Log back in and view it again: the value shows. Each access class keeps its own cached copy. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YDxvurgMTmC6JieEVYRzq8
alistair3149
added a commit
that referenced
this pull request
Sep 3, 2026
For #1059 Follows-up to #1346 With the parse-time gates in place, the parser cache still served whatever the last parse produced to every reader: a sysop's parse cached restricted values for anonymous readers, and an anonymous parse cached gaps for sysops. Such output is now keyed by the parsing user's access class. * A lazy parser option, `neowikiAccessClass`, registered through `ParserOptionsRegister` and marked cache-varying. Its value is the parsing user's effective groups plus the wiki-level `read` and `neowiki-query` decisions, as a readable string such as `*,autoconfirmed,user;read;query`, and null for the anonymous user, so save-time and job-queue parses keep the canonical key. * Obtaining the parsing authority (`ParserAuthority::of`) reads that option, which marks it used, so every gated read keys its page by access class without a call site having to remember to. Only pages with such reads carry the option in their cache key. `{{#view}}` now resolves its page's Main Subject through the public `PageSubjectsLookup` instead of a parsing authority, so it keeps one entry as before. * ADR 27's open decision on parse-time semantics resolves to this rule. The alternatives weighed (a fixed anonymous authority, post-cache trimming, a cache-off operating rule) are recorded under Alternatives Considered. The class is a proxy for the permission hooks: exact wherever page access follows group membership, wrong for hooks that grant per user, which is why such wikis must run with the parser cache off; the installation docs now say so next to the rights. Existing installs need one parser-cache purge and, with restricted content, a `refreshLinks` run, noted in the upgrading docs. Not solved here, and now stated in ADR 27's consequences: data derived from the canonical parse (categories, page properties, links tables, the Page node's categories in graph projections) is computed as the anonymous user, so on a wiki where anonymous users cannot read, a category derived from a parse-time read is never set. A designated reader for canonical parses would lift that; it needs a decision. ## Manual Browser Check 1. With the parser cache enabled, restrict a page's read permission for anonymous users (for example with the Lockdown extension) and give it a Subject with a text property. 2. On a second page, save `{{#neowiki_value: <property> | page=<restricted page>}}` as a sysop and view it: the value shows. 3. View that page logged out, without purging: the value is absent. Log back in and view it again: the value shows. Each access class keeps its own cached copy. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YDxvurgMTmC6JieEVYRzq8
alistair3149
added a commit
that referenced
this pull request
Sep 3, 2026
For #1059 Follows-up to #1346 With the parse-time gates in place, the parser cache still served whatever the last parse produced to every reader: a sysop's parse cached restricted values for anonymous readers, and an anonymous parse cached gaps for sysops. Such output is now keyed by the parsing user's access class. * A cache-varying parser option, `neowikiAccessClass`, registered through `ParserOptionsRegister`. It carries no value; the class enters the key through the `PageRenderingHash` hook, and only for a page whose parse recorded the option. A lazily valued option would instead be loaded for every logged-in edit of every page by core's cache-key comparison, pushing all of them onto the deferred parser-cache path. * The class itself, from `UserAccessClass`, is the parsing user's effective groups plus the wiki-level `read` and `neowiki-query` decisions, as a readable string such as `*,autoconfirmed,user;read;query`. The anonymous user has none, so save-time and job-queue parses keep the canonical key. * Obtaining the parsing authority (`ParserAuthority::of`) records the option, so every gated read keys its page by access class without a call site having to remember to. `{{#view}}` now resolves its page's Main Subject through the public `PageSubjectsLookup` instead of a parsing authority, so it keeps one entry as before. * ADR 27's open decision on parse-time semantics resolves to this rule. The alternatives weighed (a fixed anonymous authority, post-cache trimming, a cache-off operating rule) are recorded under Alternatives Considered. The class is a proxy for the permission hooks: exact wherever page access follows group membership, wrong for hooks that grant per user, which is why such wikis must run with the parser cache off; the installation docs now say so next to the rights. Existing installs need one parser-cache purge and, with restricted content, a `refreshLinks` run, noted in the upgrading docs. Not solved here, and now stated in ADR 27's consequences: data derived from the canonical parse (categories, page properties, links tables, the Page node's categories in graph projections) is computed as the anonymous user, so on a wiki where anonymous users cannot read, a category derived from a parse-time read is never set. A designated reader for canonical parses would lift that; it needs a decision. ## Manual Browser Check 1. With the parser cache enabled, restrict a page's read permission for anonymous users (for example with the Lockdown extension) and give it a Subject with a text property. 2. On a second page, save `{{#neowiki_value: <property> | page=<restricted page>}}` as a sysop and view it: the value shows. 3. View that page logged out, without purging: the value is absent. Log back in and view it again: the value shows. Each access class keeps its own cached copy. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YDxvurgMTmC6JieEVYRzq8
alistair3149
added a commit
that referenced
this pull request
Sep 3, 2026
For #1059 Follows-up to #1346 With the parse-time gates in place, the parser cache still served whatever the last parse produced to every reader: a sysop's parse cached restricted values for anonymous readers, and an anonymous parse cached gaps for sysops. Such output is now keyed by the parsing user's access class. * A cache-varying parser option, `neowikiAccessClass`, registered through `ParserOptionsRegister`. It carries no value; the class enters the key through the `PageRenderingHash` hook, and only for a page whose parse recorded the option. A lazily valued option would instead be loaded for every logged-in edit of every page by core's cache-key comparison, pushing all of them onto the deferred parser-cache path. * The class itself, from `UserAccessClass`, is the parsing user's effective groups plus the wiki-level `read` and `neowiki-query` decisions, as a readable string such as `*,autoconfirmed,user;read;query`. Group names are encoded, since they reach the class from hooks and the database and could otherwise be named to describe like another set of groups. Every reader gets a class, the anonymous one included, so a page that reads Subjects never reuses an entry cached before this change and no upgrade purge is needed. * Obtaining the parsing authority (`ParserAuthority::of`) records the option, so every gated read keys its page by access class without a call site having to remember to. `{{#view}}` now resolves its page's Main Subject through the public `PageSubjectsLookup` instead of a parsing authority, so it keeps one entry as before. * ADR 27's open decision on parse-time semantics resolves to this rule. The alternatives weighed (a fixed anonymous authority, post-cache trimming, a cache-off operating rule) are recorded under Alternatives Considered. The class is a proxy for the permission hooks: exact wherever page access follows group membership, wrong for hooks that grant per user, which is why such wikis must run without a parser cache. The installation docs now say so next to the rights, name both settings that takes (`$wgParserCacheType` alone leaves the old-revision cache on), and warn that the site notice and the cached sidebar hold wikitext parsed for whoever viewed first, so NeoWiki functions do not belong in them. Installs with restricted content need one `refreshLinks` run, noted in the upgrading docs. Not solved here, and now stated in ADR 27's consequences: data derived from the canonical parse (categories, page properties, links tables, the Page node's categories in graph projections) is computed as the anonymous user, so on a wiki where anonymous users cannot read, a category derived from a parse-time read is never set. A designated reader for canonical parses would lift that; it needs a decision. ## Manual Browser Check 1. With the parser cache enabled, restrict a page's read permission for anonymous users (for example with the Lockdown extension) and give it a Subject with a text property. 2. On a second page, save `{{#neowiki_value: <property> | page=<restricted page>}}` as a sysop and view it: the value shows. 3. View that page logged out, without purging: the value is absent. Log back in and view it again: the value shows. Each access class keeps its own cached copy. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YDxvurgMTmC6JieEVYRzq8
alistair3149
marked this pull request as ready for review
September 3, 2026 01:40
alistair3149
force-pushed
the
1059-parse-time-gates
branch
from
September 3, 2026 01:44
815a282 to
3e0b5eb
Compare
alistair3149
added a commit
that referenced
this pull request
Sep 3, 2026
For #1059 Follows-up to #1346 With the parse-time gates in place, the parser cache still served whatever the last parse produced to every reader: a sysop's parse cached restricted values for anonymous readers, and an anonymous parse cached gaps for sysops. Such output is now keyed by the parsing user's access class. * A cache-varying parser option, `neowikiAccessClass`, registered through `ParserOptionsRegister`. It carries no value; the class enters the key through the `PageRenderingHash` hook, and only for a page whose parse recorded the option. A lazily valued option would instead be loaded for every logged-in edit of every page by core's cache-key comparison, pushing all of them onto the deferred parser-cache path. * The class itself, from `UserAccessClass`, is the parsing user's effective groups plus the wiki-level `read` and `neowiki-query` decisions, as a readable string such as `*,autoconfirmed,user;read;query`. Group names are encoded, since they reach the class from hooks and the database and could otherwise be named to describe like another set of groups. Every reader gets a class, the anonymous one included, so a page that reads Subjects never reuses an entry cached before this change and no upgrade purge is needed. * Obtaining the parsing authority (`ParserAuthority::of`) records the option, so every gated read keys its page by access class without a call site having to remember to. `{{#view}}` now resolves its page's Main Subject through the public `PageSubjectsLookup` instead of a parsing authority, so it keeps one entry as before. * ADR 27's open decision on parse-time semantics resolves to this rule. The alternatives weighed (a fixed anonymous authority, post-cache trimming, a cache-off operating rule) are recorded under Alternatives Considered. The class is a proxy for the permission hooks: exact wherever page access follows group membership, wrong for hooks that grant per user, which is why such wikis must run without a parser cache. The installation docs now say so next to the rights, name both settings that takes (`$wgParserCacheType` alone leaves the old-revision cache on), and warn that the site notice and the cached sidebar hold wikitext parsed for whoever viewed first, so NeoWiki functions do not belong in them. Installs with restricted content need one `refreshLinks` run, noted in the upgrading docs. Not solved here, and now stated in ADR 27's consequences: data derived from the canonical parse (categories, page properties, links tables, the Page node's categories in graph projections) is computed as the anonymous user, so on a wiki where anonymous users cannot read, a category derived from a parse-time read is never set. A designated reader for canonical parses would lift that; it needs a decision. ## Manual Browser Check 1. With the parser cache enabled, restrict a page's read permission for anonymous users (for example with the Lockdown extension) and give it a Subject with a text property. 2. On a second page, save `{{#neowiki_value: <property> | page=<restricted page>}}` as a sysop and view it: the value shows. 3. View that page logged out, without purging: the value is absent. Log back in and view it again: the value shows. Each access class keeps its own cached copy. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YDxvurgMTmC6JieEVYRzq8
For #1059 Every parse-time read now runs as the user the page is parsed for, taken from the parser rather than the request context, and is gated the way the REST endpoints are: * `{{#cypher_raw}}`, `{{#sparql_raw}}`, `nw.query` and `nw.sparqlQuery` require the `neowiki-query` right. The check lives in the two query services, so every raw-query surface shares it, the REST endpoints included, which no longer check it themselves. Denial renders the function's error box, or raises a `LuaError`. * `{{#neowiki_value}}` and the `nw` data functions check the `read` permission of the page hosting the Subject, through the existing `PageReadAuthorizer`. Denial is indistinguishable from absent data; a relation whose target sits on an unreadable page shows the target's Subject id instead of a label. * `nw.getSchema` reads through a lookup built for the user the page is parsed for instead of the request-wide one. * Parse-time query limits are pinned to the `default` tier: the output is parser-cached, so the tier must not vary by who parsed. The request context's user was the wrong identity: it is the saver during the canonical parse of an edit and the job runner otherwise, neither of which matches the identity the parser cache files the output under. **Do not deploy without #1347.** The parser cache still keeps one copy per page. On a wiki where anonymous users cannot read, the save-time parse runs as anonymous, so every edit caches a copy with all parse-time values blank and serves it to every logged-in reader until the next purge; on a public wiki with per-page restrictions the same happens for the restricted values. #1347 keys such output by the reader's permissions. The per-wiki switch to disable the parse-time functions is deferred; running with the parser cache off covers the residual case. ## Manual Browser Check 1. Restrict a page's read permission for anonymous users (for example with the Lockdown extension), and give it a Subject with a text property. 2. On another page, add `{{#neowiki_value: <property> | page=<restricted page>}}` and preview while logged in: the value shows. Log out, purge, and view the page: the value is absent. 3. Set `$wgGroupPermissions['*']['neowiki-query'] = false;`, purge a page holding `{{#cypher_raw: RETURN 1 AS n}}`, and view it logged out: "You do not have permission to run Cypher queries." renders in place of the result. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YDxvurgMTmC6JieEVYRzq8
alistair3149
force-pushed
the
1059-parse-time-gates
branch
from
September 3, 2026 01:46
3e0b5eb to
98c98e5
Compare
alistair3149
added a commit
that referenced
this pull request
Sep 3, 2026
For #1059 Follows-up to #1346 With the parse-time gates in place, the parser cache still served whatever the last parse produced to every reader: a sysop's parse cached restricted values for anonymous readers, and an anonymous parse cached gaps for sysops. Such output is now keyed by the parsing user's access class. * A cache-varying parser option, `neowikiAccessClass`, registered through `ParserOptionsRegister`. It carries no value; the class enters the key through the `PageRenderingHash` hook, and only for a page whose parse recorded the option. A lazily valued option would instead be loaded for every logged-in edit of every page by core's cache-key comparison, pushing all of them onto the deferred parser-cache path. * The class itself, from `UserAccessClass`, is the parsing user's effective groups plus the wiki-level `read` and `neowiki-query` decisions, as a readable string such as `*,autoconfirmed,user;read;query`. Group names are encoded, since they reach the class from hooks and the database and could otherwise be named to describe like another set of groups. Every reader gets a class, the anonymous one included, so a page that reads Subjects never reuses an entry cached before this change and no upgrade purge is needed. * Obtaining the parsing authority (`ParserAuthority::of`) records the option, so every gated read keys its page by access class without a call site having to remember to. `{{#view}}` now resolves its page's Main Subject through the public `PageSubjectsLookup` instead of a parsing authority, so it keeps one entry as before. * ADR 27's open decision on parse-time semantics resolves to this rule. The alternatives weighed (a fixed anonymous authority, post-cache trimming, a cache-off operating rule) are recorded under Alternatives Considered. The class is a proxy for the permission hooks: exact wherever page access follows group membership, wrong for hooks that grant per user, which is why such wikis must run without a parser cache. The installation docs now say so next to the rights, name both settings that takes (`$wgParserCacheType` alone leaves the old-revision cache on), and warn that the site notice and the cached sidebar hold wikitext parsed for whoever viewed first, so NeoWiki functions do not belong in them. Installs with restricted content need one `refreshLinks` run, noted in the upgrading docs. Not solved here, and now stated in ADR 27's consequences: data derived from the canonical parse (categories, page properties, links tables, the Page node's categories in graph projections) is computed as the anonymous user, so on a wiki where anonymous users cannot read, a category derived from a parse-time read is never set. A designated reader for canonical parses would lift that; it needs a decision. ## Manual Browser Check 1. With the parser cache enabled, restrict a page's read permission for anonymous users (for example with the Lockdown extension) and give it a Subject with a text property. 2. On a second page, save `{{#neowiki_value: <property> | page=<restricted page>}}` as a sysop and view it: the value shows. 3. View that page logged out, without purging: the value is absent. Log back in and view it again: the value shows. Each access class keeps its own cached copy. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YDxvurgMTmC6JieEVYRzq8
alistair3149
added a commit
that referenced
this pull request
Sep 3, 2026
For #1059 Follows-up to #1346 With the parse-time gates in place, the parser cache still served whatever the last parse produced to every reader: a sysop's parse cached restricted values for anonymous readers, and an anonymous parse cached gaps for sysops. Such output is now keyed by the parsing user's access class. * A cache-varying parser option, `neowikiAccessClass`, registered through `ParserOptionsRegister`. It carries no value; the class enters the key through the `PageRenderingHash` hook, and only for a page whose parse recorded the option. A lazily valued option would instead be loaded for every logged-in edit of every page by core's cache-key comparison, pushing all of them onto the deferred parser-cache path. * The class itself, from `UserAccessClass`, is the parsing user's effective groups plus the wiki-level `read` and `neowiki-query` decisions, as a readable string such as `*,autoconfirmed,user;read;query`. Group names are encoded, since they reach the class from hooks and the database and could otherwise be named to describe like another set of groups. Every reader gets a class, the anonymous one included, so a page that reads Subjects never reuses an entry cached before this change and no upgrade purge is needed. * Obtaining the parsing authority (`ParserAuthority::of`) records the option, so every gated read keys its page by access class without a call site having to remember to. `{{#view}}` now resolves its page's Main Subject through the public `PageSubjectsLookup` instead of a parsing authority, so it keeps one entry as before. * ADR 27's open decision on parse-time semantics resolves to this rule. The alternatives weighed (a fixed anonymous authority, post-cache trimming, a cache-off operating rule) are recorded under Alternatives Considered. The class is a proxy for the permission hooks: exact wherever page access follows group membership, wrong for hooks that grant per user, which is why such wikis must run with the parser cache off. The installation docs now say so next to the rights. Installs with restricted content need one `refreshLinks` run, noted in the upgrading docs, because MediaWiki rewrites categories and page properties only on an edit. Not solved here, and now stated in ADR 27's consequences: data derived from the canonical parse (categories, page properties, links tables, the Page node's categories in graph projections) is computed as the anonymous user, so on a wiki where anonymous users cannot read, a category derived from a parse-time read is never set. A designated reader for canonical parses would lift that; it needs a decision. ## Manual Browser Check 1. With the parser cache enabled, restrict a page's read permission for anonymous users (for example with the Lockdown extension) and give it a Subject with a text property. 2. On a second page, save `{{#neowiki_value: <property> | page=<restricted page>}}` as a sysop and view it: the value shows. 3. View that page logged out, without purging: the value is absent. Log back in and view it again: the value shows. Each access class keeps its own cached copy. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YDxvurgMTmC6JieEVYRzq8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
For #1059
Every parse-time read now runs as the user the page is parsed for, taken from the parser rather than the request context, and is gated the way the REST endpoints are:
{{#cypher_raw}},{{#sparql_raw}},nw.queryandnw.sparqlQueryrequire theneowiki-queryright. The check lives in the two query services, so every raw-query surface shares it, the REST endpoints included, which no longer check it themselves. Denial renders the function's error box, or raises aLuaError.{{#neowiki_value}}and thenwdata functions check thereadpermission of the page hosting the Subject, through the existingPageReadAuthorizer. Denial is indistinguishable from absent data; a relation whose target sits on an unreadable page shows the target's Subject id instead of a label.nw.getSchemareads through a lookup built for the user the page is parsed for instead of the request-wide one.defaulttier: the output is parser-cached, so the tier must not vary by who parsed.The request context's user was the wrong identity: it is the saver during the canonical parse of an edit and the job runner otherwise, neither of which matches the identity the parser cache files the output under.
Do not deploy without #1347. The parser cache still keeps one copy per page. On a wiki where anonymous users cannot read, the save-time parse runs as anonymous, so every edit caches a copy with all parse-time values blank and serves it to every logged-in reader until the next purge; on a public wiki with per-page restrictions the same happens for the restricted values. #1347 keys such output by the reader's permissions. The per-wiki switch to disable the parse-time functions is deferred; running with the parser cache off covers the residual case.
Manual Browser Check
{{#neowiki_value: <property> | page=<restricted page>}}and preview while logged in: the value shows. Log out, purge, and view the page: the value is absent.$wgGroupPermissions['*']['neowiki-query'] = false;, purge a page holding{{#cypher_raw: RETURN 1 AS n}}, and view it logged out: "You do not have permission to run Cypher queries." renders in place of the result.