Add sandbox attribute to EmbeddedDemo iframe to prevent blocked navigation#85125
Add sandbox attribute to EmbeddedDemo iframe to prevent blocked navigation#85125stitesExpensify merged 3 commits intomainfrom
Conversation
…navigation The Storylane test drive tour iframe navigates to www.storylane.io when the Expensify logo is clicked, but that page blocks framing via X-Frame-Options and CSP headers, showing "This content is blocked." Adding the sandbox attribute without allow-top-navigation prevents the iframe from navigating to non-embeddable URLs while keeping the tour functional. Co-authored-by: Shridhar Goel <ShridharGoel@users.noreply.github.com>
|
@MelvinBot It would be better to open the Expensify home page on clicking the logo |
|
Thanks for the suggestion! Unfortunately, the Expensify logo and its click behavior are part of Storylane's embedded tour content — we don't control what happens when a user interacts with elements inside the iframe. Since the iframe loads third-party Storylane URLs (e.g., If we want the logo to open the Expensify home page, that would need to be configured on Storylane's side (their demo settings/branding options). This PR's scope is limited to preventing the broken "This content is blocked" error page that currently appears when the logo is clicked, which is the reported bug in #82517. Would you like me to open a follow-up issue to track the Storylane configuration change, or is this something that can be handled outside of this PR? |
|
@MelvinBot Instead of directly changing EmbeddedDemo, pass the sandbox values using iframeProps via TestDriveDemo. |
…rops Co-authored-by: Shridhar Goel <ShridharGoel@users.noreply.github.com>
|
Done! Moved the |
|
@MelvinBot Mark this as ready for review |
|
Done — PR has been marked as ready for review. |
|
@ShridharGoel Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
|
@trjExpensify This is the experience. It opens the storylane website on clicking the logo which we can't control via the App code. Screen.Recording.2026-03-14.at.12.09.04.AM.mov |
| <EmbeddedDemo | ||
| url={getTestDriveURL(shouldUseNarrowLayout, introSelected, isCurrentUserPolicyAdmin)} | ||
| iframeTitle={testDrive.EMBEDDED_DEMO_IFRAME_TITLE} | ||
| iframeProps={{sandbox: 'allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox'}} |
There was a problem hiding this comment.
❌ CONSISTENCY-2 (docs)
The sandbox attribute value 'allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox' is a hardcoded magic string with security implications. Other iframe configuration values for the embedded demo (e.g., EMBEDDED_DEMO_WHITELIST, EMBEDDED_DEMO_IFRAME_TITLE) are already defined as named constants. This value should follow the same pattern for consistency, discoverability, and ease of future changes.
Define this as a named constant alongside the existing embedded demo constants, for example in src/libs/actions/Welcome/OnboardingFlow.ts:
EMBEDDED_DEMO_SANDBOX: 'allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox',Then reference it in the component:
iframeProps={{sandbox: testDrive.EMBEDDED_DEMO_SANDBOX}}Please rate this suggestion with 👍 or 👎 to help us improve! Reactions are used to monitor reviewer efficiency.
There was a problem hiding this comment.
@MelvinBot Do the above, and use a proper name for the constant
Co-authored-by: Shridhar Goel <ShridharGoel@users.noreply.github.com>
|
Extracted the inline sandbox string into |
|
@stitesExpensify Please copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1237374714
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| <EmbeddedDemo | ||
| url={getTestDriveURL(shouldUseNarrowLayout, introSelected, isCurrentUserPolicyAdmin)} | ||
| iframeTitle={testDrive.EMBEDDED_DEMO_IFRAME_TITLE} | ||
| iframeProps={{sandbox: 'allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox'}} |
There was a problem hiding this comment.
Block iframe self-navigation explicitly
This sandbox value does not prevent the embedded page from navigating its own iframe context; it only restricts ancestor/top-level navigation when allow-top-navigation is omitted. If the Storylane logo uses _self (which is consistent with the current blocked-page symptom inside the iframe), the iframe can still navigate to www.storylane.io, so this change will not reliably prevent the "This content is blocked" flow.
Useful? React with 👍 / 👎.
|
The failing check is unrelated to this PR. Failing test: Analysis: This PR only modifies Evidence: The same |
|
The failing check Failing test: Why it's unrelated:
The other failing check ( Recommendation: Re-run the failed workflow to clear the flaky test failure. |
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppAndroid: mWeb ChromeiOS: HybridAppiOS: mWeb SafariMacOS: Chrome / SafariScreen.Recording.2026-03-14.at.12.09.04.AM.mov |
stitesExpensify
left a comment
There was a problem hiding this comment.
Code looks fine to me, tests are failing though
|
I re-ran the test and it passed. We're good to go here! |
|
🚧 @stitesExpensify has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚀 Deployed to staging by https://github.com/stitesExpensify in version: 9.3.39-0 🚀
|
|
@ShridharGoel PR is failing with original issue ID #82517 Recording.3676.mp4 |
|
@MelvinBot It was working fine till this comment: #85125 (comment) What could have caused the issue to happen again? |
|
Good question. I investigated the code and it looks correct — the The likely root cause is that the Why it may have appeared to work initially: During local testing, if the Storylane tour hadn't fully loaded or the logo link wasn't yet active, the navigation wouldn't trigger — giving the false impression the sandbox fix prevented it. To actually fix this, a different approach is needed since sandbox can't prevent in-iframe navigation. Some options:
|
|
@MelvinBot Let's try option 2 in a new PR |
|
🚀 Deployed to production by https://github.com/cristipaval in version: 9.3.39-3 🚀
|
Explanation of Change
When a new user clicks "Take a 2-minute test drive," a Storylane interactive tour opens in an iframe. If the user clicks the Expensify logo inside the tour, the iframe navigates to
www.storylane.io, which returns anti-framing headers (X-Frame-Options: SAMEORIGINand CSPframe-ancestors 'self'), causing the browser to display "This content is blocked."This PR adds the
sandboxattribute to the<iframe>inEmbeddedDemowithallow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox. By omittingallow-top-navigation, the browser blocks the iframe from navigating itself to the non-embeddable URL, while the Storylane tour continues to function normally (scripts, same-origin access, and popups still work).Fixed Issues
$ #82517
PROPOSAL: #82517 (comment)
Tests
Offline tests
N/A — the embedded demo requires an internet connection to load the Storylane tour. The sandbox attribute is a static HTML attribute that does not depend on network state.
QA Steps
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
N/A — change is web-only (iframe sandbox attribute)
Android: mWeb Chrome
N/A — change is web-only (iframe sandbox attribute)
iOS: Native
N/A — change is web-only (iframe sandbox attribute)
iOS: mWeb Safari
N/A — change is web-only (iframe sandbox attribute)
MacOS: Chrome / Safari