Skip to content

Conversation

@laghee
Copy link
Contributor

@laghee laghee commented Oct 21, 2025

Asana Task/Github Issue: https://app.asana.com/1/137249556945/project/72649045549333/task/1208581002806545?focus=true

Description

Adding an override for our client hints brands in the JS API to match those we send in the Sec-CH-UA header and resolve breakage caused by current inconsistency between the two.

Testing Steps

Use privacy test page (duckduckgo/privacy-test-pages#356) on WebView2 build from this branch of C-S-S
OR
Manually compare Sec-Ch-Ua header brands with output of navigator.userAgentData.brands and navigator.userAgentData.getHighEntropyValues(['brands'])

Checklist

Please tick all that apply:

  • I have tested this change locally
  • I have tested this change locally in all supported browsers
  • This change will be visible to users
  • I have added automated tests that cover this change
  • I have ensured the change is gated by config
  • This change was covered by a ship review
  • This change was covered by a tech design
  • Any dependent config has been merged

Note

Implements a Windows-only uaChBrands feature to override navigator.userAgentData.brands with configured values (preserving GREASE) and adds integration tests and pages to verify behavior.

  • Feature: uaChBrands
    • Implements UaChBrands to override navigator.userAgentData.brands with configured settings.brands and mirror into getHighEntropyValues(['brands']).
    • Preserves existing GREASE brand (and position) from original brands; no-op if brands not configured.
  • Wiring
    • Registers uaChBrands in injected/src/features.js and enables it for the windows platform.
  • Tests
    • Adds Playwright spec integration-test/ua-ch-brands.spec.js and includes it in Windows playwright.config.js.
    • Adds test pages and configs under integration-test/test-pages/ua-ch-brands/ to verify brand override and early-return when brands are missing.

Written by Cursor Bugbot for commit 152f3d5. This will update automatically on new commits. Configure here.


@netlify
Copy link

netlify bot commented Oct 21, 2025

Deploy Preview for content-scope-scripts ready!

Name Link
🔨 Latest commit 152f3d5
🔍 Latest deploy log https://app.netlify.com/projects/content-scope-scripts/deploys/6920cd0235534a000857d2ab
😎 Deploy Preview https://deploy-preview-2028--content-scope-scripts.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions
Copy link

github-actions bot commented Oct 21, 2025

Temporary Branch Update

The temporary branch has been updated with the latest changes. Below are the details:

Please use the above install command to update to the latest version.

@github-actions
Copy link

github-actions bot commented Oct 21, 2025

[Beta] Generated file diff

Time updated: Fri, 21 Nov 2025 21:37:00 GMT

Android
    - android/adsjsContentScope.js
  • android/autofillImport.js
  • android/brokerProtection.js
  • android/contentScope.js

File has changed

Apple
    - apple/contentScope.js
  • apple/contentScopeIsolated.js

File has changed

Chrome-mv3
    - chrome-mv3/inject.js

File has changed

Firefox
    - firefox/inject.js

File has changed

Integration
    - integration/contentScope.js

File has changed

Windows
    - windows/contentScope.js

File has changed

cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

result.brands = newBrands;
}
return result;
});
Copy link

Choose a reason for hiding this comment

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

Bug: Direct modification of frozen result object

The getHighEntropyValues wrapper directly modifies the result object returned by the browser (result.brands = newBrands), which could fail if the browser returns a frozen or sealed object. The safer pattern, as demonstrated in harmful-apis.js lines 126-165, creates a new object and copies properties rather than modifying the original. This prevents potential TypeErrors in strict mode and avoids unintended side effects if the browser caches result objects.

Fix in Cursor Fix in Web

@laghee laghee changed the title [DRAFT] Add Windows client hint brands override to match header we override natively Add Windows client hint brands override to match header we override natively Nov 21, 2025
@laghee laghee marked this pull request as ready for review November 21, 2025 21:36
@laghee laghee requested review from a team as code owners November 21, 2025 21:36
this.log.info('shimUserAgentDataBrands - captured original brands:', this.originalBrands.map(b => `"${b.brand}" v${b.version}`).join(', '));
}

if (this.cachedBrands) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't even think we need this check do we? (and the cachedBrands prop).

shimUserAgentDataBrands is called once in init which is also called once.

return this.originalBrands.find((brand) => {
const name = brand.brand;
// Check if it starts with "Not" or " Not" or contains special chars
return name.trim().startsWith('Not') || /[^\w\s.]/.test(name);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the special chars thing finds the GREASE brand as per: https://wicg.github.io/ua-client-hints/#create-arbitrary-brands-section but it might also pick up other values. Should we add an exclusion list from the config in case we match genuine values?

I think we can remove the "not" part, it's not part of the spec and could change.

What does Windows do here with the header?

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.

2 participants