Skip to content

Comments

fix: prevent link cloaking and add malicious URL detection in workflows#26292

Merged
pedroccastro merged 7 commits intomainfrom
devin/prevent-link-cloaking-workflows-1767043625
Dec 30, 2025
Merged

fix: prevent link cloaking and add malicious URL detection in workflows#26292
pedroccastro merged 7 commits intomainfrom
devin/prevent-link-cloaking-workflows-1767043625

Conversation

@PeerRich
Copy link
Member

@PeerRich PeerRich commented Dec 29, 2025

What does this PR do?

This PR adds two security features for workflow emails and SMS:

1. Link Cloaking Prevention - Prevents users from hiding URLs behind custom text (e.g., "Click here" linking to a phishing URL). The full URL is always displayed to recipients.

2. Malicious URL Detection - Integrates Cloudflare Radar URL Scanner to detect malicious URLs in workflow content and event type redirect URLs. When malicious URLs are detected, the user's account is locked.

Link Cloaking Prevention Changes:

  • Excludes the "link" toolbar item from the Editor component for all workflow actions
  • Adds replaceCloakedLinksInHtml utility that transforms <a href="url">text</a><a href="url">url</a>
  • Applies sanitization to email content before sending in all workflow email paths
  • Includes HTML escaping to prevent XSS when href is inserted as text content

Malicious URL Detection Changes:

  • Adds urlScanner.ts utility for Cloudflare Radar URL Scanner API integration
  • Creates scanWorkflowUrls async task with submit → poll pattern (15s intervals, max 10 attempts)
  • Integrates URL scanning into scanWorkflowBody task (runs after Iffy spam scan)
  • Adds URL scanning for event type redirect URLs (successRedirectUrl)
  • Adds MALICIOUS_URL_IN_WORKFLOW lock reason
  • Respects whitelistWorkflows flag - whitelisted users won't be auto-locked

Environment Variables Required

For malicious URL detection:

  • CLOUDFLARE_URL_SCANNER_API_TOKEN - API token for Cloudflare Radar
  • CLOUDFLARE_ACCOUNT_ID - Cloudflare account ID

URL scanning is automatically disabled if these are not set.

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. N/A - no docs changes needed.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

Link Cloaking Prevention:

  1. Create a new workflow with an email action
  2. Verify the "link" button is no longer available in the email body editor toolbar
  3. For existing workflows with cloaked links, trigger the workflow and verify the sent email displays the full URL

Malicious URL Detection:

  1. Set CLOUDFLARE_URL_SCANNER_API_TOKEN and CLOUDFLARE_ACCOUNT_ID environment variables
  2. Create a workflow with a known malicious URL in the email body
  3. Verify the tasker creates a scanWorkflowUrls task
  4. Verify the user is locked if the URL is flagged as malicious
  5. Test event type redirect URL scanning by setting a redirect URL in Advanced settings

Checklist

  • My code follows the style guidelines of this project
  • I have commented my code, particularly in hard-to-understand areas
  • I have checked if my changes generate no new warnings

Human Review Checklist

  • Verify Cloudflare API integration (endpoint: /client/v4/accounts/{account_id}/urlscanner/v2/scan)
  • Verify polling logic handles pending (404), completed (200), and error states correctly
  • Verify URL extraction regex captures href attributes and bare URLs properly
  • Verify fail-open behavior is acceptable (marks as verified if API errors occur)
  • Verify whitelistWorkflows flag correctly prevents auto-locking
  • Review the 25 unit tests for link cloaking sanitization
  • Note: No unit tests for URL scanning code - relies on integration testing

Important Review Notes

  1. Fail-open behavior: If Cloudflare API submissions fail or max poll attempts reached, the workflow step is marked as verified (not blocked). This prevents legitimate workflows from being blocked due to API issues.

  2. Async scanning: URL scanning is async via the tasker system. Workflow steps have verifiedAt set to null until scanning completes. Steps with null verifiedAt should not be sent.

  3. Pre-existing fixes: This PR also fixes two pre-existing lint issues:

    • parseInt(..., 0)parseInt(..., 10) in constants.ts
    • secondaryEmail && secondaryEmail.emailVerifiedsecondaryEmail?.emailVerified in update.handler.ts

Link to Devin run: https://app.devin.ai/sessions/c245edc8038c4ac99ae88d65cbe65b71
Requested by: peer@cal.com (@PeerRich)

- Exclude 'link' toolbar item from Editor for all workflow actions (not just SMS)
- Add replaceCloakedLinksInHtml utility to sanitize HTML and replace cloaked links with visible URLs
- Apply sanitization to email content before sending in both event and form workflows

This ensures recipients can see the actual destination of URLs, helping them identify potentially malicious links.

Co-Authored-By: peer@cal.com <peer@cal.com>
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

- Changed regex from [^<]* to [\s\S]*? to capture nested HTML content
- Added HTML tag stripping before comparing link text with href
- Added comprehensive unit tests covering:
  - Basic cloaked links
  - Nested HTML tags (<b>, <strong>, <span>)
  - Already-visible URLs (should remain unchanged)
  - Empty link text
  - Multiple links in content
  - Edge cases (multiline, single quotes, etc.)

Co-Authored-By: peer@cal.com <peer@cal.com>
@pull-request-size pull-request-size bot added size/L and removed size/M labels Dec 29, 2025
@vercel
Copy link

vercel bot commented Dec 29, 2025

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

3 Skipped Deployments
Project Deployment Review Updated (UTC)
cal Ignored Ignored Dec 30, 2025 3:25am
cal-companion Ignored Ignored Preview Dec 30, 2025 3:25am
cal-eu Ignored Ignored Dec 30, 2025 3:25am

Co-Authored-By: peer@cal.com <peer@cal.com>
@pedroccastro pedroccastro marked this pull request as ready for review December 30, 2025 00:44
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 6 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="packages/features/ee/workflows/lib/reminders/utils.ts">

<violation number="1" location="packages/features/ee/workflows/lib/reminders/utils.ts:146">
P1: XSS vulnerability: The `href` value is inserted directly into HTML text content without escaping. If a malicious href contains `&lt;/a&gt;&lt;script&gt;...&lt;/script&gt;`, it would close the anchor tag and execute arbitrary JavaScript. The `href` should be HTML-escaped before being inserted into text content.</violation>
</file>

Reply to cubic to teach it or ask questions. Tag @cubic-dev-ai to re-run a review.

@pedroccastro pedroccastro changed the title fix: prevent link cloaking in workflow emails and SMS fix: display full URLs in workflow links Dec 30, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Dec 30, 2025

E2E results are ready!

@pedroccastro pedroccastro merged commit ec90c1a into main Dec 30, 2025
117 of 124 checks passed
@pedroccastro pedroccastro deleted the devin/prevent-link-cloaking-workflows-1767043625 branch December 30, 2025 04:53
@devin-ai-integration devin-ai-integration bot changed the title fix: display full URLs in workflow links fix: prevent link cloaking and add malicious URL detection in workflows Jan 2, 2026
@chopsol
Copy link

chopsol commented Jan 5, 2026

Unfortunately, this PR is a modest idea.
When implementing HTML messages, it should be possible to offer links behind ‘click here’ texts.
In its current form, you have destroyed all my work on designing appealing HTML emails. And I can't even change it easily, as the function for editing hyperlinks has been completely removed without replacement.

Users should be free to activate or deactivate this option. Especially with a self-hosted instance, this is a form of paternalism that I would very much like to reverse.

Anshumancanrock pushed a commit to Anshumancanrock/cal.com that referenced this pull request Jan 12, 2026
- Exclude 'link' toolbar item from Editor for all workflow actions (not just SMS)
- Add replaceCloakedLinksInHtml utility to sanitize HTML and replace cloaked links with visible URLs
- Apply sanitization to email content before sending in all workflow email paths

This ensures recipients can see the actual destination of URLs, helping them identify potentially malicious links.

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@PeerRich
Copy link
Member Author

Unfortunately, this PR is a modest idea. When implementing HTML messages, it should be possible to offer links behind ‘click here’ texts. In its current form, you have destroyed all my work on designing appealing HTML emails. And I can't even change it easily, as the function for editing hyperlinks has been completely removed without replacement.

Users should be free to activate or deactivate this option. Especially with a self-hosted instance, this is a form of paternalism that I would very much like to reverse.

i agree. we are reverting this change

devin-ai-integration bot added a commit that referenced this pull request Jan 27, 2026
This reverts the changes from PR #26292 and PR #26528 to restore
link cloaking functionality for all users.

The plan is to fight malicious links with Cloudflare URL Scanner
instead of restricting link cloaking to Organization accounts only.

Changes:
- Remove replaceCloakedLinksInHtml function and its usage
- Restore link toolbar item for all users in workflow editor
- Remove isOrganization checks from email workflow processing
- Delete the replaceCloakedLinksInHtml test file

Co-Authored-By: peer@cal.com <peer@cal.com>
PeerRich added a commit that referenced this pull request Jan 27, 2026
…#27289)

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants