FE-1162: Add Petrinaut optimization UI#9051
Conversation
| const postToHost = (message: IframeToHostMessage) => { | ||
| // The sandboxed iframe has an opaque origin. This still targets only its | ||
| // parent window; the host independently verifies `event.source`. | ||
| window.parent.postMessage(message, "*"); |
There was a problem hiding this comment.
Semgrep identified an issue in your code:
The target origin of the window.postMessage() API is set to "*". This could allow for information disclosure due to the possibility of any origin allowed to receive the message.
To resolve this comment:
🔧 No guidance has been designated for this issue. Fix according to your organization's approved methods.
💬 Ignore this finding
Reply with Semgrep commands to ignore this finding.
/fp <comment>for false positive/ar <comment>for acceptable risk/other <comment>for all other reasons
Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by wildcard-postmessage-configuration.
You can view more details about this finding in the Semgrep AppSec Platform.
| window.addEventListener("message", (event) => { | ||
| if (event.source !== window.parent) { | ||
| return; | ||
| } | ||
|
|
||
| const data = event.data as unknown; | ||
| if ( | ||
| typeof data !== "object" || | ||
| data === null || | ||
| typeof (data as { kind?: unknown }).kind !== "string" | ||
| ) { | ||
| return; | ||
| } | ||
|
|
||
| const message = data as HostToIframeMessage; | ||
| if ( | ||
| message.kind !== "optimizationResponseStart" && | ||
| message.kind !== "optimizationChunk" && | ||
| message.kind !== "optimizationEnd" && | ||
| message.kind !== "optimizationError" | ||
| ) { | ||
| return; | ||
| } | ||
|
|
||
| const pending = pendingRequests.get(message.requestId); | ||
| if (!pending) { | ||
| return; | ||
| } | ||
|
|
||
| switch (message.kind) { | ||
| case "optimizationResponseStart": { | ||
| if (pending.responded) { | ||
| rejectPendingRequest( | ||
| message.requestId, | ||
| new Error("The optimizer sent more than one response header"), | ||
| ); | ||
| return; | ||
| } | ||
| pending.responded = true; | ||
| pending.clearResponseStartTimeout(); | ||
| pending.resolveResponse( | ||
| new Response(pending.stream, { | ||
| headers: { "content-type": "application/x-ndjson" }, | ||
| status: message.status, | ||
| statusText: message.statusText, | ||
| }), | ||
| ); | ||
| break; | ||
| } | ||
| case "optimizationChunk": { | ||
| try { | ||
| pending.controller.enqueue(message.bytes); | ||
| } catch { | ||
| // The consumer may already have cancelled the stream. | ||
| } | ||
| break; | ||
| } | ||
| case "optimizationEnd": { | ||
| if (!pending.responded) { | ||
| rejectPendingRequest( | ||
| message.requestId, | ||
| new Error("The optimizer ended before sending a response"), | ||
| ); | ||
| return; | ||
| } | ||
| try { | ||
| pending.controller.close(); | ||
| } catch { | ||
| // The stream is already settled. | ||
| } | ||
| pending.cleanup(); | ||
| pendingRequests.delete(message.requestId); | ||
| break; | ||
| } | ||
| case "optimizationError": | ||
| rejectPendingRequest(message.requestId, new Error(message.message)); | ||
| break; | ||
| } | ||
| }); |
There was a problem hiding this comment.
Semgrep identified an issue in your code:
No validation of origin is done by the addEventListener API. It may be possible to exploit this flaw to perform Cross Origin attacks such as Cross-Site Scripting(XSS).
To resolve this comment:
🔧 No guidance has been designated for this issue. Fix according to your organization's approved methods.
💬 Ignore this finding
Reply with Semgrep commands to ignore this finding.
/fp <comment>for false positive/ar <comment>for acceptable risk/other <comment>for all other reasons
Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by insufficient-postmessage-origin-validation.
You can view more details about this finding in the Semgrep AppSec Platform.
| const waitForOptimizer = async () => { | ||
| for (let attempt = 0; attempt < 60; attempt += 1) { | ||
| try { | ||
| const response = await fetch(`${optimizerOrigin}/status`); |
There was a problem hiding this comment.
Semgrep identified an issue in your code:
Unencrypted request over HTTP detected.
To resolve this comment:
🔧 No guidance has been designated for this issue. Fix according to your organization's approved methods.
💬 Ignore this finding
Reply with Semgrep commands to ignore this finding.
/fp <comment>for false positive/ar <comment>for acceptable risk/other <comment>for all other reasons
Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by react-insecure-request.
You can view more details about this finding in the Semgrep AppSec Platform.
| const waitForOptimizer = async () => { | ||
| for (let attempt = 0; attempt < 60; attempt += 1) { | ||
| try { | ||
| const response = await fetch(`${optimizerOrigin}/status`); |
| const postToHost = (message: IframeToHostMessage) => { | ||
| // The sandboxed iframe has an opaque origin. This still targets only its | ||
| // parent window; the host independently verifies `event.source`. | ||
| window.parent.postMessage(message, "*"); |
| window.addEventListener("message", (event) => { | ||
| if (event.source !== window.parent) { | ||
| return; | ||
| } | ||
|
|
||
| const data = event.data as unknown; | ||
| if ( | ||
| typeof data !== "object" || | ||
| data === null || | ||
| typeof (data as { kind?: unknown }).kind !== "string" | ||
| ) { | ||
| return; | ||
| } | ||
|
|
||
| const message = data as HostToIframeMessage; | ||
| if ( | ||
| message.kind !== "optimizationResponseStart" && | ||
| message.kind !== "optimizationChunk" && | ||
| message.kind !== "optimizationEnd" && | ||
| message.kind !== "optimizationError" | ||
| ) { | ||
| return; | ||
| } | ||
|
|
||
| const pending = pendingRequests.get(message.requestId); | ||
| if (!pending) { | ||
| return; | ||
| } | ||
|
|
||
| switch (message.kind) { | ||
| case "optimizationResponseStart": { | ||
| if (pending.responded) { | ||
| rejectPendingRequest( | ||
| message.requestId, | ||
| new Error("The optimizer sent more than one response header"), | ||
| ); | ||
| return; | ||
| } | ||
| pending.responded = true; | ||
| pending.clearResponseStartTimeout(); | ||
| pending.resolveResponse( | ||
| new Response(pending.stream, { | ||
| headers: { "content-type": "application/x-ndjson" }, | ||
| status: message.status, | ||
| statusText: message.statusText, | ||
| }), | ||
| ); | ||
| break; | ||
| } | ||
| case "optimizationChunk": { | ||
| try { | ||
| pending.controller.enqueue(message.bytes); | ||
| } catch { | ||
| // The consumer may already have cancelled the stream. | ||
| } | ||
| break; | ||
| } | ||
| case "optimizationEnd": { | ||
| if (!pending.responded) { | ||
| rejectPendingRequest( | ||
| message.requestId, | ||
| new Error("The optimizer ended before sending a response"), | ||
| ); | ||
| return; | ||
| } | ||
| try { | ||
| pending.controller.close(); | ||
| } catch { | ||
| // The stream is already settled. | ||
| } | ||
| pending.cleanup(); | ||
| pendingRequests.delete(message.requestId); | ||
| break; | ||
| } | ||
| case "optimizationError": | ||
| rejectPendingRequest(message.requestId, new Error(message.message)); | ||
| break; | ||
| } | ||
| }); |
Benchmark results
|
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 2002 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 1002 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: high, policies: 3314 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 1527 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 2078 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 1033 | Flame Graph |
policy_resolution_medium
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 102 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 52 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: high, policies: 269 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 108 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 133 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 63 | Flame Graph |
policy_resolution_none
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 2 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 2 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 8 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 3 | Flame Graph |
policy_resolution_small
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| resolve_policies_for_actor | user: empty, selectivity: high, policies: 52 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: empty, selectivity: medium, policies: 26 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: high, policies: 94 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: seeded, selectivity: medium, policies: 27 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: high, policies: 66 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: low, policies: 1 | Flame Graph | |
| resolve_policies_for_actor | user: system, selectivity: medium, policies: 29 | Flame Graph |
read_scaling_complete
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_id;one_depth | 1 entities | Flame Graph | |
| entity_by_id;one_depth | 10 entities | Flame Graph | |
| entity_by_id;one_depth | 25 entities | Flame Graph | |
| entity_by_id;one_depth | 5 entities | Flame Graph | |
| entity_by_id;one_depth | 50 entities | Flame Graph | |
| entity_by_id;two_depth | 1 entities | Flame Graph | |
| entity_by_id;two_depth | 10 entities | Flame Graph | |
| entity_by_id;two_depth | 25 entities | Flame Graph | |
| entity_by_id;two_depth | 5 entities | Flame Graph | |
| entity_by_id;two_depth | 50 entities | Flame Graph | |
| entity_by_id;zero_depth | 1 entities | Flame Graph | |
| entity_by_id;zero_depth | 10 entities | Flame Graph | |
| entity_by_id;zero_depth | 25 entities | Flame Graph | |
| entity_by_id;zero_depth | 5 entities | Flame Graph | |
| entity_by_id;zero_depth | 50 entities | Flame Graph |
read_scaling_linkless
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_id | 1 entities | Flame Graph | |
| entity_by_id | 10 entities | Flame Graph | |
| entity_by_id | 100 entities | Flame Graph | |
| entity_by_id | 1000 entities | Flame Graph | |
| entity_by_id | 10000 entities | Flame Graph |
representative_read_entity
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/block/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/book/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/building/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/organization/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/page/v/2
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/person/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/playlist/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/song/v/1
|
Flame Graph | |
| entity_by_id | entity type ID: https://blockprotocol.org/@alice/types/entity-type/uk-address/v/1
|
Flame Graph |
representative_read_entity_type
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| get_entity_type_by_id | Account ID: bf5a9ef5-dc3b-43cf-a291-6210c0321eba
|
Flame Graph |
representative_read_multiple_entities
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| entity_by_property | traversal_paths=0 | 0 | |
| entity_by_property | traversal_paths=255 | 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true | |
| entity_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true | |
| link_by_source_by_property | traversal_paths=0 | 0 | |
| link_by_source_by_property | traversal_paths=255 | 1,resolve_depths=inherit:1;values:255;properties:255;links:127;link_dests:126;type:true | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:0;link_dests:0;type:false | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:0;links:1;link_dests:0;type:true | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:0;properties:2;links:1;link_dests:0;type:true | |
| link_by_source_by_property | traversal_paths=2 | 1,resolve_depths=inherit:0;values:2;properties:2;links:1;link_dests:0;type:true |
scenarios
| Function | Value | Mean | Flame graphs |
|---|---|---|---|
| full_test | query-limited | Flame Graph | |
| full_test | query-unlimited | Flame Graph | |
| linked_queries | query-limited | Flame Graph | |
| linked_queries | query-unlimited | Flame Graph |
444bc2d to
bf55e30
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9051 +/- ##
==========================================
- Coverage 59.22% 59.18% -0.04%
==========================================
Files 1391 1393 +2
Lines 135140 135340 +200
Branches 6223 6268 +45
==========================================
+ Hits 80032 80103 +71
- Misses 54152 54266 +114
- Partials 956 971 +15
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
🌟 What is the purpose of this PR?
Add the Petrinaut optimization UI and its HASH integration on top of the backend introduced in #9040.
🔍 What does this change?
yarn dev:petrinaut-optimizationfor isolated development.Pre-Merge Checklist 🚀
🚢 Has this modified a publishable library?
This PR:
📜 Does this require a change to the docs?
The changes in this PR:
🕸️ Does this require a change to the Turbo Graph?
The changes in this PR:
🛡 What tests cover this?
❓ How to test this?
yarn dev:petrinaut-optimizationfor the isolated demo integration, or start HASH with the backend from FE-1162: Add Petrinaut optimization backend #9040 configured.