Protecting Gmail users from XS-Search with Cross-Origin Opener Policy (COOP)

What’s happening

Gmail is enhancing user security by enabling the Cross-Origin Opener Policy (COOP). As a result, developers of websites and browser extensions opening or manipulating the Gmail page may have to update their code to ensure continued functionality when enforcement begins on January 20, 2026. There is no action needed from Workspace admins or end users.

COOP background

Cross-Site Search (XS-Search) is a type of Cross-Site Leaks (XS-Leaks) attack that targets query-based search systems, like Gmail. Attackers exploit this vulnerability by gaining control of a Gmail window, either by opening a new popup or accessing an existing one via its window handle. Once they have this access, they can gather information via a side channel to determine if specific search results exist by repeatedly loading different search terms, thereby leaking sensitive user data.

COOP is a web security feature designed to isolate the web applications from untrusted origins. This measure will prevent attackers from accessing Gmail's window handle, thereby protecting users from various Cross-Site Search (XS-Search) attacks that rely on window handles for collecting side-channel information, such as frame counting. This also significantly hinders attacks like cache probing, which rely on timing and other observations for resources that Gmail loads for search results. While these attacks don't directly collect side-channel information through the window handles themselves, COOP prevents repeated searches and thereby increases difficulty and reduces effectiveness, making them far less of a threat.

Who’s impacted

Websites or browser extensions that open Gmail in a pop-up window and interact with that window by accessing its properties (closed, location, length, focus) or invoking its functions (close, postMessage). Also, browser extensions that are injected into Gmail page and access the opener handle which is a reference to the window that opened the current Gmail page.

Additional details

To enforce COOP, the Cross-Origin-Opener-Policy header will be present in the response:

Cross-Origin-Opener-Policy: same-origin-allow-popups; report-to="gmail-web-coop-coep"
Report-To:{"group":"gmail-web-coop-coep","endpoints":[{"url":"https://csp.withgoogle.com/csp/report-to/gmail-web"}]}

Getting started

  • Developers:
    • For websites and browser extensions opening Gmail, refactor the offending code to avoid accessing the window properties or functions through the window handle and instead, utilize alternative APIs to achieve the desired functionality (e.g., chrome.tabs, Messaging).
    • For browser extensions injected into the Gmail page, instead of trying to communicate with or access the opener, the browser extension should be updated so it doesn't need to interact with it at all and the extension's logic should be revised to work independently. If that is not possible, browser extensions can use existing APIs (e.g., chrome.tabs) to implement their logic.
  • Admins: There is no admin control for this feature.
  • End users: There is no end user setting for this feature. 

Rollout pace

  • Enforcement will begin on January 20, 2026. Rollout will be extended (potentially longer than 15 days for feature visibility).

Resources


Gemini in Google Sheets can now analyze data across multiple tables

 What’s changing

Starting today, Gemini in Sheets can understand and analyze multiple tables within a single tab of a spreadsheet.  This new functionality allows you to ask questions and perform analysis that spans multiple data sources, dramatically increasing the power and accuracy of your queries. You can select or deselect tables to focus Gemini’s attention, and you can also directly make a selection within a table to be used as input for your query.

You can use this update while:

  1. Generating formulas: Generate complex formulas that reference and perform calculations across several tables in one go. For example, you can now have Gemini write XLOOKUP functions to look up values from one table to use in another, or aggregate data from multiple tables into a single summary calculation. 
  2. Analyzing and generating charts: Ask Gemini to analyze data scattered across multiple tables to uncover insights and create data visualizations. You can get a holistic view of your data, whether you’re identifying top performers by combining sales data from different tables, or creating a single chart that compares sales performance data from separate tables.
  3. Editing your data: Apply changes to multiple tables at once with a single request. For example, you can now use Gemini to apply consistent conditional formatting across all your tables in a spreadsheet or build a single pivot table that summarizes data from multiple tables. 
  4. Targeting analysis with selections: You can now make selections within your tables to focus Gemini’s analysis. Ask for summaries of specific data, find outliers in a selected range, or generate formulas based on the data you’ve highlighted. For example, you can select a column and ask, "What are the trends in the selected column?"
Gemini in Google Sheets

Prompt: “Generate a formula that fills the column next to due date that looks up the assigned teammate based on task id” 

Generate one chart of trends over time which contain the data from all three teams

Prompt: “Generate a formula that fills the column next to due date that looks up the assigned teammate based on task id”
 


Getting started

Rollout pace

Availability

Available for Google Workspace:

  • Business Standard and Plus 
  • Enterprise Standard and Plus 
  • Customers with the Gemini Education or Gemini Education Premium add-on 
  • Google AI Pro and Ultra

Anyone who previously purchased these add-ons will also receive this feature: 

  • Gemini Business* 
  • Gemini Enterprise* 
*As of January 15, 2025, we’re no longer offering the Gemini Business and Gemini Enterprise add-ons for sale. Please refer to this announcement for more details. 

Resources


Dynamic App Links: Elevating your Android deep linking

Posted by Ran Mor - Product Manager
   
We're excited to announce the availability of Dynamic App Links, a significant leap forward for Android App Links that brings them on par with, and in many ways surpasses, industry standards for deep linking. For too long, Android App Links have been limited in their functionality, but with this launch, we're introducing powerful new features that provide unparalleled control and flexibility for developers.

Since Android 6, App Links has been crucial for delivering a seamless web-to-app user experience. By directing users directly to relevant content within your app, rather than a web browser or mobile-web page, you enhance engagement, boost conversions, and foster greater customer loyalty. Now Dynamic App Links, available on Android 15 and later,  makes achieving this even easier and more effective.

What's New: Functionalities Enabled by Dynamic App Links

The core of these enhancements lies in the Digital Asset Links JSON file. Previously, this file was primarily used for basic verification. Now, it's a powerful configuration tool that allows you to specify paths, query parameters, fragments, and exclusions, providing a dynamic and robust deep linking solution.

Here what’s new in Dynamic App Links:

Exclusions support

You can now specify certain paths or sections of a URL that should not open your app, even if they would otherwise match your App Link configuration. This is incredibly useful for:

  • Unsupported Content: Directing users to web content that isn't yet supported within your app.

  • Legacy Content: Managing old URLs that you no longer want to route to your app.

  • Specific Campaigns: Temporarily excluding certain links during promotions or tests.

This granular control ensures users always land in the most appropriate experience.

Query parameters support

With the new Query parameters functionality you can define specific parameters that, if present in a URL, will prevent your app from opening. This opens up exciting possibilities for:

  • Dynamic Exclusions: Quickly turning off app linking for specific scenarios without requiring an app update.

  • A/B Testing: Directing users to different experiences (app vs. web) based on test parameters.

  • Controlled Rollouts: Gradually enabling app linking for certain user segments.

Dynamic updates

Make easier updates to your App Links configuration without needing to update your app. You can now specify the URL paths that your app will handle directly within the Digital Asset Links JSON file that is hosted on your server. 

This means you can:

  • Respond quickly to changes: Adapt your deep linking strategy in real-time without the overhead of a new app release.

  • Reduce development cycles: Implement and test App Link changes much more efficiently.

  • Maintain agility: Keep your app's deep linking configuration current with your evolving content and features.

Why Dynamic App Links?

Android Dynamic App Links are the preferred way to link to content within your app because they offer:

  • Seamless User Experience: Direct users instantly to the exact content they're looking for, bypassing browser redirects.

  • Improved Engagement: Keep users within your app, leading to higher engagement and longer session times.

  • Increased Conversions: Guide users effortlessly through your app's flows, improving the likelihood of desired actions.

  • Enhanced Customer Loyalty: Deliver a polished and efficient experience that keeps users coming back.

With Dynamic App Links, you now have the tools to build even more powerful and flexible deep linking experiences, ensuring your users always find the content they need, right where they expect it.

We're excited to see what you'll build with Dynamic App Links. Visit our documentation to start exploring these new features today and elevate your app's deep linking strategy!


Simplify Your Code: Functional Core, Imperative Shell

This article was adapted from a Google Tech on the Toilet (TotT) episode. You can download a printer-friendly version of this TotT episode and post it in your office.

By Arham Jain

Is your code a tangled mess of business logic and side effects? Mixing database calls, network requests, and other external interactions directly with your core logic can lead to code that’s difficult to test, reuse, and understand. Instead, consider writing a functional core that’s called from an imperativ​​e shell.

Diagram of functional core, imperative shell

Separating your code into functional cores and imperative shells makes it more testable, maintainable, and adaptable. The core logic can be tested in isolation, and the imperati​​ve shell can be swapped out or modified as needed. Here’s some messy example code that mixes logic and side effects to send expiration notification emails to users:

// Bad: Logic and side effects are mixed

function sendUserExpiryEmail(): void {

  for (const user of db.getUsers()) {

    if (user.subscriptionEndDate > Date.now()) continue;

    if (user.isFreeTrial) continue;

    email.send(user.email, "Your account has expired " + user.name + “.”);

  }

}

A functional core should contain pure, testable business logic, which is free of side effects (such as I/O or external state mutation). It operates only on the data it is given.

An imperative shell is responsible for side effects, like database calls and sending emails. It uses the functions in your functional core to perform the business logic.

Rewriting the above code to follow the functional core / imperative shell pattern might look like:

Functional core

function getExpiredUsers(users: User[], cutoff: Date): User[] {

  return users.filter(user => user.subscriptionEndDate <= cutoff && !user.isFreeTrial);

}

function generateExpiryEmails(users: User[]): Array<[string, string]> {

  return users.map(user => 

    ([user.email, “Your account has expired “ + user.name + “.”])

  );

}

Imperative shell

email.bulkSend(generateExpiryEmails(getExpiredUsers(db.getUsers(), Date.now())));

Now that the code is following this pattern, adding a feature to send a new type of email is as simple as writing a new pure function and reusing getExpiredUsers:

// Sending a reminder email to users

function generateReminderEmails(users: User[], cutoff: Date): Array<[string, string]> {...}

const fiveDaysFromNow = ...

email.bulkSend(generateReminderEmails(getExpiredUsers(db.getUsers(), fiveDaysFromNow)));


Learn more in Gary Bernhardt’s original talk about functional core, imperative shell.