Bubble cacheability of resolved props values and access results + `PropSourceBase::evaluate()` does not return cacheability at all
>>> [!note] Migrated issue <!-- Drupal.org comment --> <!-- Migrated from issue #3554184. --> Reported by: [f.mazeikis](https://www.drupal.org/user/3513671) Related to !361 !360 !340 >>> <h3 id="overview">Overview</h3> <p>When a prop source references an entity, we evaluate it's value but ignore all of it's cache metadata. This leads to situation when "referenced entity" can be updated, but that does not invalidate relevant render cache. This in turn results in stale data being shown to end user.</p> <p>Steps to reproduce:</p> <ol> <li>Add an entity reference field to content type A to refer to other entities of content type B.</li> <li>Create an entity of type B to use as reference target.</li> <li>Create an entity of type A and use reference field to target entity of type B</li> <li>Map a text component to the title of the referred entity type B in the content template of type A</li> <li>Check that correct title value of referenced entity type B is displayed when viewing entity of type A outside of Canvas UI</li> <li>Update the title of referred entity (type B)</li> <li>Observe the stale version of referred entity type B still being displayed when viewing entity type A outside of Canvas UI</li> </ol> <p><strong>Worse, there's a security implication too!</strong> Without correct cacheability metadata associated with rendered component instances, granting or disallowing access to an entity or a field would also not be bubbled.</p> <p>IOW: a component instance rendering e.g. the label of an entity referenced (say, Product 5) by the host entity (say, Node 3) would forever continue to display the Product 5 label, even if access to it was revoked for the user, until Render cache, Dynamic Page Cache, Page Cache (if anonymous user) or the external reverse proxy (Varnish, a CDN &hellip; &mdash; again, if anonymous user) would be invalidated.</p> <p>Bubbling of cacheability is essential for ensuring entity/field access (which <span class="drupalorg-gitlab-issue-link drupalorg-gitlab-link-wrapper"><a href="https://git.drupalcode.org/project/canvas/-/work_items/3546996" class="drupalorg-gitlab-link">https://git.drupalcode.org/project/canvas/-/work_items/3546996</a></span> fixed when evaluating prop expressions) is <em>actually</em> respected.</p> <p><strong>Worse, this applies to all prop sources, but not all have security implications:</strong></p> <ol> <li><code>DynamicPropSource</code>: see above; this is what prompted the original bug report</li> <li><code>StaticPropSource</code>s can point to another entity (e.g. an image media item) and when that referenced entity changes, the rendered component tree needs to be invalidated &rarr; here, the security implications <em>do</em> also apply</li> <li><code>HostEntityUrlPropSource</code> can generate absolute URLs, which vary by the <code>url.site</code> cache context (and in principle path processing could bubble additional metadata: <code>\Drupal\Core\PathProcessor\OutboundPathProcessorInterface::processOutbound()</code>), but currently that is not passed/bubbled &rarr; no security implications here</li> </ol> <h3 id="proposed-resolution">Proposed resolution</h3> <p>We should refactor <code>Drupal\canvas\PropSource\PropSourceBase::evaluate()</code> and <code>Drupal\canvas\PropExpressions\StructuredData\Evaluator</code> to correctly bubble cacheability.</p> <p>Phase 1:</p> <ol> <li>&#9989; Introduce <code>EvaluationResult</code> immutable value object: it carries an evaluation result + cacheability. </li><li>&#9989; Update <code>Evaluator</code> to always return a <code>EvaluationResult</code> object. </li></ol> <p>Phase 2:</p> <ol> <li>&#9989; Update <code>PropSourceTest::test(Static|Dynamic)PropSource()</code> expectations to expect the appropriate cacheability </li><li>&#9989; Make <code>PropSourceTest</code> actually pass </li></ol> <p>Phase 3:</p> <ol> <li>&#9989; Update <code>\Drupal\canvas\Plugin\Canvas\ComponentSource\GeneratedFieldExplicitInputUxComponentSourceBase::(hydrate|render)Component()</code> to actually surface this! </li><li>&#9989; Functional test coverage to prove it works correctly: update <code>CanvasPageVariantTest</code> &mdash; <strong>see #23 for crucial findings!</strong> </li></ol> > Related issue: [Issue #3559209](https://www.drupal.org/node/3559209)
issue