Skip to content

feat: enhance CowSwapWidget with Safe SDK bridge option and improve e…#7460

Open
fairlighteth wants to merge 8 commits intodevelopfrom
fix/deepsec
Open

feat: enhance CowSwapWidget with Safe SDK bridge option and improve e…#7460
fairlighteth wants to merge 8 commits intodevelopfrom
fix/deepsec

Conversation

@fairlighteth
Copy link
Copy Markdown
Contributor

@fairlighteth fairlighteth commented May 6, 2026

Summary

Addresses the HIGH findings from the vulnerability report for apps/cow-fi.

This PR hardens the cow-fi app while preserving expected user-facing behavior:

  • Removes the committed Dune API key from apps/cow-fi/.env and keeps Dune auth runtime-only.
  • Removes broad raw HTML rendering from article markdown.
  • Preserves CMS-authored raw tags by converting only safe image HTML into markdown image nodes before skipHtml drops the rest.
  • Adds route-level framing protections for /widget.
  • Keeps the widget parent bridge intact by using CowSwapWidget instead of a raw iframe.
  • Adds enableSafeSdkBridge as a narrow widget opt-out so cow-fi can disable Safe SDK forwarding without breaking widget params, app data updates, deeplinks, or parent message handling.
  • Makes the React wrapper recreate the widget when enableSafeSdkBridge changes after mount.
  • Adds regression tests for the widget bridge flag behavior and CMS HTML image handling.

Screenshots not applicable; this is security hardening plus behavior preservation.

To Test

  1. Run targeted tests
  • NX_DAEMON=false ./node_modules/.bin/nx run cow-fi:test --runInBand should pass.
  • NX_DAEMON=false ./node_modules/.bin/nx run widget-lib:test --runInBand should pass.
  • NX_DAEMON=false ./node_modules/.bin/nx run widget-react:test --runInBand should pass.
  • NX_DAEMON=false ./node_modules/.bin/nx run cow-fi:lint --verbose should complete with 0 errors.
  1. Verify article markdown behavior
  • Open a cow-fi article containing normal markdown images; images should still render lazily.
  • Open or mock an article body containing a raw HTML image, e.g. <img src="https://..." alt="...">; the image should render through the lazy image path.
  • Raw non-image HTML such as <script>, <iframe>, or inline event handlers should not render.
  • Unsafe image sources such as javascript: should be ignored.
  1. Verify Dune key handling
  • Confirm apps/cow-fi/.env no longer contains a real Dune API key.
  • Provide DUNE_API_KEY through local/deployment secrets and verify Dune-backed data still loads.
  • Without DUNE_API_KEY, the app should fail closed for Dune calls instead of using a committed fallback secret.
  1. Verify /widget
  • Open /widget; the widget UI should render.
  • Widget params such as appCode and standaloneMode should still reach the swap app.
  • Widget parent-message behavior should still work, including deeplink/window-open interception.
  • Response headers for /widget should include Content-Security-Policy: frame-ancestors 'self' and X-Frame-Options: SAMEORIGIN.
  • Safe SDK bridge forwarding should be disabled for the cow-fi widget page.

Background

The vulnerability report flagged cow-fi issues around exposed secrets, unsafe raw markdown/HTML handling, and widget embedding behavior.

The initial safe-looking markdown fix removed rehypeRaw, but that also dropped CMS-authored raw tags that existing article content relies on. This PR keeps the hardening by leaving skipHtml enabled, then explicitly allowlists only raw HTML image
tags and turns them into markdown image nodes before rendering.

For /widget, replacing the widget with a raw iframe would remove the parent-side widget bridge. Instead, this PR keeps the existing widget wrapper and adds a narrow enableSafeSdkBridge flag so only the risky Safe SDK forwarding is disabled while normal widget messaging remains intact.

Summary by CodeRabbit

  • New Features

    • Widget gains optional Safe SDK bridge toggle.
    • Markdown now better renders embedded images.
  • Improvements

    • Added CSP and X-Frame-Options for widget routes.
    • Safer article subtitle date handling.
    • Centralized cache-control header.
  • Tests

    • Expanded widget and markdown image test suites.
  • Chores

    • API key must be supplied via secure env (no hardcoded key).
    • Updated dependency declarations.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cowfi Ready Ready Preview May 8, 2026 7:45am
explorer-dev Ready Ready Preview May 8, 2026 7:45am
storybook Error Error May 8, 2026 7:45am
swap-dev Ready Ready Preview May 8, 2026 7:45am
widget-configurator Ready Ready Preview May 8, 2026 7:45am
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
cosmos Ignored Ignored May 8, 2026 7:45am
sdk-tools Ignored Ignored Preview May 8, 2026 7:45am

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 6, 2026

Review Change Stack

Walkthrough

Adds a remark-based HTML image extractor and integrates it into article rendering (removes rehype_raw), introduces an optional enableSafeSdkBridge flag with widget lifecycle and test refactors, extracts DUNE API key handling to a helper and removes hardcoded env value, and standardizes route cache/CSP headers.

Changes

Safe SDK Bridge Configuration

Layer / File(s) Summary
Type Definition
libs/widget-lib/src/types.ts
Added optional enableSafeSdkBridge boolean flag to CowSwapWidgetProps.
Core Widget Implementation
libs/widget-lib/src/cowSwapWidget.ts
Conditionally constructs IframeSafeSdkBridge based on enableSafeSdkBridge (defaults to true); uses optional chaining on bridge destruction.
Widget Tests
libs/widget-lib/src/cowSwapWidget.spec.ts
Tracked widget handlers for teardown, added cleanup flow and test verifying parent-origin messaging when Safe SDK forwarding is disabled; createWidget helper now accepts enableSafeSdkBridge.
React Integration
libs/widget-react/src/lib/CowSwapWidget.tsx
Centralized widget creation via createWidget, added useDestroyWidgetOnUnmount, tryOrHandleError, WidgetError UI, and recreation when enableSafeSdkBridge or provider changes.
React Tests & Dependencies
libs/widget-react/src/lib/CowSwapWidget.spec.tsx, libs/widget-react/package.json
Added typed Jest mock tests that assert widget recreation when enableSafeSdkBridge toggles; expanded devDependencies for testing.
Usage
apps/cow-fi/app/(main)/widget/page.tsx
Page imports CowSwapWidgetParams as type-only, sets widgetParams.width='100%', declares Page(): ReactNode, and passes enableSafeSdkBridge={false}.

Markdown HTML Image Processing Rewrite

Layer / File(s) Summary
New Utilities
apps/cow-fi/util/markdownHtmlImages.ts
Adds AllowedHtmlImage type, getAllowedHtmlImage(s) helpers, and remarkAllowedHtmlImages plugin that parses sanitized HTML image tags and converts them to Markdown image nodes.
Utility Tests
apps/cow-fi/util/markdownHtmlImages.test.ts
Tests extraction of allowed images, container handling, multi-image blocks, unsafe rejection, and AST conversion behavior.
Article Component Integration
apps/cow-fi/components/ArticlePageComponent.tsx
Replaces useMemo + replaceImageUrls + rehypeRaw path with ReactMarkdown using remarkAllowedHtmlImages; updates ArticleSubtitle to render formattedDate only when valid.
Cleanup
apps/cow-fi/util/lazyLoadImages.ts, apps/cow-fi/package.json
Removed replaceImageUrls helper and removed rehype-raw dependency.

Configuration & Infrastructure

Layer / File(s) Summary
Environment & Secrets
apps/cow-fi/.env
Removed concrete DUNE_API_KEY; added comment to supply it via .env.local or deployment secrets.
API Key Helper
apps/cow-fi/services/dune/index.tsx
Added getDuneApiKey() to read and validate env, updated getFromDune to use it, and explicitly typed getTotalTrades(): Promise<TotalCount>.
Route Headers & Cache
apps/cow-fi/next.config.ts
Introduced DEFAULT_CACHE_CONTROL_HEADER, applied it to routes, and added /widget route headers including CSP frame-ancestors 'self' and X-Frame-Options: SAMEORIGIN.
UI Export
libs/ui/src/index.ts
Re-exported getGlobalFooterNavItems.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • cowprotocol/cowswap#7311: Related prior changes touching widget iframe messaging and bridge/disableExternalNavigation behavior.

Suggested reviewers

  • shoom3301
  • elena-zh
  • limitofzero

Poem

🐰 A bridge can sleep or wake when told,
Images find paths in markdown, tidy and bold.
Keys tucked away, headers stand guard,
Tests hop around, keeping behavior unmarred. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.23% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main feature: adding an enableSafeSdkBridge option to CowSwapWidget and improving error handling, which aligns with the primary changes in the changeset.
Description check ✅ Passed The description comprehensively addresses all template requirements with clear summary of security hardening changes, detailed testing steps with checkboxes, and background context on design decisions.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/deepsec

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 6, 2026

Deploying swap-dev with  Cloudflare Pages  Cloudflare Pages

Latest commit: 755edff
Status:🚫  Build failed.

View logs

Copy link
Copy Markdown
Contributor

@elena-zh elena-zh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @fairlighteth , thank you, but could you please provide a bit more instructions on how to test these steps:

  • Open or mock an article body containing a raw HTML image, e.g. <img src="https://..." alt="...">; the image should render through the lazy image path.

  • Raw non-image HTML such as <script>, <iframe>, or inline event handlers should not render.

  • Unsafe image sources such as javascript: should be ignored.

  • Confirm apps/cow-fi/.env no longer contains a real Dune API key.

  • Provide DUNE_API_KEY through local/deployment secrets and verify Dune-backed data still loads.

  • Without DUNE_API_KEY, the app should fail closed for Dune calls instead of using a committed fallback secret.

  • Response headers for /widget should include Content-Security-Policy: frame-ancestors 'self' and X-Frame-Options: SAMEORIGIN.

  • Safe SDK bridge forwarding should be disabled for the cow-fi widget page.

As for the rest steps, I:

@fairlighteth
Copy link
Copy Markdown
Contributor Author

Hey @elena-zh, thanks for checking.

Some of these are easier to verify from the implementation side, so no need to spend too much QA time on the lower-level/security ones. I can cover those and report back with the exact evidence.

For QA, the most useful checks would be:

  • Articles page:

    • Open an article that contains an image in the body.
    • Confirm the image loads normally.
    • Confirm no raw HTML text like <img ...> is visible to the user.
    • Please also keep the blinking/shaking issue noted. I’ll check whether that is related to this PR or a separate regression.
  • Widget:

    • Confirm the widget page loads.
    • Confirm a trade can still be placed from it, as you already did.

I’ll validate the rest myself (and front-end):

  • unsafe/non-image HTML is sanitized
  • javascript: image sources are ignored
  • .env no longer contains a real Dune API key
  • Dune calls fail closed when DUNE_API_KEY is missing
  • /widget response headers include the expected CSP / X-Frame-Options values
  • Safe SDK bridge forwarding is disabled for the cow-fi widget page

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
libs/widget-lib/src/types.ts (1)

65-65: ⚡ Quick win

Add JSDoc for the new public enableSafeSdkBridge prop.

This is a public widget API addition. Surrounding props are documented with JSDoc that explains intent, defaults, and security/behavior tradeoffs. Without docs, consumers won't know:

  • the default is true,
  • what it actually disables (Safe SDK forwarding while preserving widget params/app data/deeplinks/parent message handling),
  • when they should set it to false.
📝 Proposed documentation
   onReady?(): void
-  enableSafeSdkBridge?: boolean
+  /**
+   * Controls whether the widget creates an `IframeSafeSdkBridge` to forward Safe SDK
+   * messages between the host page and the iframe.
+   *
+   * Set to `false` to disable Safe SDK forwarding while keeping widget params,
+   * app-data updates, deeplinks, and parent message handling intact.
+   *
+   * Defaults to `true`.
+   */
+  enableSafeSdkBridge?: boolean
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/widget-lib/src/types.ts` at line 65, Add JSDoc for the new public prop
enableSafeSdkBridge in libs/widget-lib/src/types.ts: document that its default
is true, describe that setting it to false disables Safe SDK forwarding while
still preserving widget params, app data, deeplinks, and parent message
handling, and explain recommended use cases and security/behavior tradeoffs
(when to set false). Place the JSDoc immediately above the enableSafeSdkBridge?
boolean declaration and follow the same style/annotations as adjacent props so
consumers see defaults and implications.
apps/cow-fi/util/markdownHtmlImages.test.ts (1)

12-93: 💤 Low value

Solid coverage of the safe-list contract.

The mix of positive-extraction, container-wrapping, multi-image, scheme-injection (including the &#x0a; obfuscation), disallowed-parent rejection, and AST round-trip with mixed allowed/disallowed siblings exercises the most important invariants of markdownHtmlImages.ts.

If you want to lock down the contract a bit more, consider adding cases for: data-src preference over src, HTML-entity-encoded alt round-tripping (e.g., &amp;&), comment stripping (<!-- ... --><img ...>), and nested HTML inside a list item or blockquote so the recursive transformAllowedHtmlImages is exercised at depth.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cow-fi/util/markdownHtmlImages.test.ts` around lines 12 - 93, Add tests
covering data-src fallback/preference, HTML-entity-encoded alt decoding,
comment-stripping, and nested-container recursion: specifically add cases
asserting getAllowedHtmlImage prefers data-src over src (e.g., <img
data-src="/uploads/x.png" src="/bad.png">), that alt values with entities (e.g.,
"&amp;") decode to "&", that leading HTML comments before an image
(<!--...--><img ...>) still yield a valid image, and that
remarkAllowedHtmlImages/transformAllowedHtmlImages correctly transform images
when they appear nested inside list items or blockquotes to exercise depth
recursion; reference getAllowedHtmlImage, getAllowedHtmlImages, and
remarkAllowedHtmlImages in the new tests.
apps/cow-fi/util/markdownHtmlImages.ts (1)

175-191: 💤 Low value

Consider broader HTML entity coverage for alt text.

decodeHtmlAttribute only decodes amp/apos/gt/lt/quot plus numeric references. Common CMS-authored alt-text entities like &copy;, &nbsp;, &trade;, &mdash;, &hellip; will be rendered/announced literally.

Two pragmatic options:

  • Extend the local HTML_ENTITIES map with the handful of entities your CMS actually emits (lowest effort, recommended for this utility's scope).
  • Use he library (most robust, but adds 121.2 KB; overkill if CMS output is limited).

Either way, keep the order: decode → safety-check, exactly as today.

This is purely a cosmetic/accessibility quality issue for alt text — src is unaffected because URLs are percent-encoded by authors anyway.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/cow-fi/util/markdownHtmlImages.ts` around lines 175 - 191, The
decodeHtmlAttribute function only covers a few named entities; extend its
decoding to handle common CMS entities by adding entries to the HTML_ENTITIES
map (e.g., copy, nbsp, trade, mdash, hellip, etc.) or alternatively replace the
local decoder with a call to a robust library like he in decodeHtmlAttribute
while preserving the current decode → safety-check flow; update references in
decodeHtmlAttribute and ensure the normalizedEntity lookup still uses
HTML_ENTITIES[normalizedEntity] so existing fallback numeric decoding and
String.fromCodePoint behavior remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/cow-fi/util/markdownHtmlImages.test.ts`:
- Around line 12-93: Add tests covering data-src fallback/preference,
HTML-entity-encoded alt decoding, comment-stripping, and nested-container
recursion: specifically add cases asserting getAllowedHtmlImage prefers data-src
over src (e.g., <img data-src="/uploads/x.png" src="/bad.png">), that alt values
with entities (e.g., "&amp;") decode to "&", that leading HTML comments before
an image (<!--...--><img ...>) still yield a valid image, and that
remarkAllowedHtmlImages/transformAllowedHtmlImages correctly transform images
when they appear nested inside list items or blockquotes to exercise depth
recursion; reference getAllowedHtmlImage, getAllowedHtmlImages, and
remarkAllowedHtmlImages in the new tests.

In `@apps/cow-fi/util/markdownHtmlImages.ts`:
- Around line 175-191: The decodeHtmlAttribute function only covers a few named
entities; extend its decoding to handle common CMS entities by adding entries to
the HTML_ENTITIES map (e.g., copy, nbsp, trade, mdash, hellip, etc.) or
alternatively replace the local decoder with a call to a robust library like he
in decodeHtmlAttribute while preserving the current decode → safety-check flow;
update references in decodeHtmlAttribute and ensure the normalizedEntity lookup
still uses HTML_ENTITIES[normalizedEntity] so existing fallback numeric decoding
and String.fromCodePoint behavior remain unchanged.

In `@libs/widget-lib/src/types.ts`:
- Line 65: Add JSDoc for the new public prop enableSafeSdkBridge in
libs/widget-lib/src/types.ts: document that its default is true, describe that
setting it to false disables Safe SDK forwarding while still preserving widget
params, app data, deeplinks, and parent message handling, and explain
recommended use cases and security/behavior tradeoffs (when to set false). Place
the JSDoc immediately above the enableSafeSdkBridge? boolean declaration and
follow the same style/annotations as adjacent props so consumers see defaults
and implications.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 50eb3a86-b5da-4307-aab8-52a604404473

📥 Commits

Reviewing files that changed from the base of the PR and between 716164f and 55801d2.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (16)
  • apps/cow-fi/.env
  • apps/cow-fi/app/(main)/widget/page.tsx
  • apps/cow-fi/components/ArticlePageComponent.tsx
  • apps/cow-fi/next.config.ts
  • apps/cow-fi/package.json
  • apps/cow-fi/services/dune/index.tsx
  • apps/cow-fi/util/lazyLoadImages.ts
  • apps/cow-fi/util/markdownHtmlImages.test.ts
  • apps/cow-fi/util/markdownHtmlImages.ts
  • libs/ui/src/index.ts
  • libs/widget-lib/src/cowSwapWidget.spec.ts
  • libs/widget-lib/src/cowSwapWidget.ts
  • libs/widget-lib/src/types.ts
  • libs/widget-react/package.json
  • libs/widget-react/src/lib/CowSwapWidget.spec.tsx
  • libs/widget-react/src/lib/CowSwapWidget.tsx
💤 Files with no reviewable changes (2)
  • apps/cow-fi/util/lazyLoadImages.ts
  • apps/cow-fi/package.json

@elena-zh
Copy link
Copy Markdown
Contributor

elena-zh commented May 8, 2026

Hey @fairlighteth , thank you for the clarification. Everything works great, however, I still can see the page blinking when an article is being loaded.

Also, this article cannot be loaded for me https://cowfi-git-fix-deepsec-cowswap.vercel.app/learn/what-you-need-to-know-about-crypto-linked-orders-in-2026, however, it workd fine on Prod. Could you please check why?
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants