Skip to content

Type casting improvements#2381

Draft
jonathanKingston wants to merge 8 commits intomainfrom
jkt/auto/type-casting-improvements-3f3a
Draft

Type casting improvements#2381
jonathanKingston wants to merge 8 commits intomainfrom
jkt/auto/type-casting-improvements-3f3a

Conversation

@jonathanKingston
Copy link
Copy Markdown
Contributor

@jonathanKingston jonathanKingston commented Feb 28, 2026

Asana Task/Github Issue: N/A

Description

This PR improves type safety by eliminating unnecessary any casts and introducing better type narrowing in config-feature.js and element-hiding.js.

Specifically:

  • config-feature.js: Removed an any cast in matchConditionalFeatureSetting's filter callback, which led to fixing two type gaps:
    • rule.domain could be undefined (now explicitly checked).
    • condition could be undefined when passed to _matchConditionalBlockOrArray (parameter type widened to handle "no condition = always matches" semantic).
  • element-hiding.js: Introduced ElementHidingRuleWithSelector type and a hasSelector type guard to remove 6 inline casts related to .selector access in injectStyleTag, hideAdNodes, and overrideRules.

These are type-only changes that preserve existing runtime behavior.

Testing Steps

  • npx tsc --noEmit
  • npm run tsc-strict-core
  • npm run lint-fix
  • npm run test-unit (all 104 specs pass)
  • npx playwright test --grep "Element Hiding" --reporter list (integration test passes)
  • npm run test-unit -- config-feature (93 specs pass)

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

Open in Web Open in Cursor 


Note

Low Risk
Primarily type-narrowing/refactor changes with minimal logic adjustment; the main behavioral impact is that config entries with no condition/domain now explicitly match all pages.

Overview
Improves type safety in conditional config and element-hiding rule handling by removing any casts and introducing explicit type narrowing.

ConfigFeature.matchConditionalFeatureSetting now explicitly handles missing condition/domain (treating no condition as an unconditional match) instead of relying on property-in checks. ElementHiding introduces a hasSelector type guard and ElementHidingRuleWithSelector to only read .selector where valid, hardening style-tag injection and override filtering against rules like disable-default and non-array domains[].rules.

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

cursoragent and others added 2 commits February 28, 2026 10:11
…_matchConditionalBlockOrArray to accept undefined

- Annotate conditionalChanges as ConditionalSettingEntry[] instead of relying on any
- Replace 'domain' in rule check with rule.domain !== undefined for proper narrowing
- Make _matchConditionalBlockOrArray explicitly accept undefined (no-condition = always match)
- Preserves existing runtime behavior while eliminating the any cast

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
- Add ElementHidingRuleWithSelector type alias for rules with .selector
- Add hasSelector type guard to narrow ElementHidingRule to rules with selector
- Use type predicate filter for overrideRules to properly narrow to ElementHidingRuleHide
- Type injectStyleTag param as ElementHidingRuleWithSelector[] removing casts for .selector access
- Type extractTimeoutRules arrays explicitly so injectStyleTag receives correct types
- Use hasSelector guard in hideAdNodes and override comparison instead of inline casts
- Replace ConditionalSettingEntry item.rules cast with typed intermediate variable

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
@cursor
Copy link
Copy Markdown
Contributor

cursor Bot commented Feb 28, 2026

Cursor Agent can help with this pull request. Just @cursor in comments and I'll start working on changes in this branch.
Learn more about Cursor Agents

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 28, 2026

[Beta] Generated file diff

Time updated: Mon, 09 Mar 2026 22:43:39 GMT

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

File has changed

Apple
    - apple/contentScope.js
  • apple/contentScopeIsolated.js
  • apple/duckAiChatHistory.js
  • apple/duckAiDataClearing.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

@github-actions
Copy link
Copy Markdown
Contributor

⚠️ Cursor assessed this PR as Medium Risk (only Low Risk is auto-approved).

This PR requires a manual review and approval from a member of one of the following teams:

  • @duckduckgo/content-scope-scripts-owners
  • @duckduckgo/apple-devs
  • @duckduckgo/android-devs
  • @duckduckgo/team-windows-development
  • @duckduckgo/extension-owners
  • @duckduckgo/config-aor
  • @duckduckgo/breakage-aor
  • @duckduckgo/breakage

…andle undefined at call site

Move the undefined-condition guard into matchConditionalFeatureSetting's
filter callback instead of widening _matchConditionalBlockOrArray's
parameter type. This keeps the internal method's contract strict and
avoids codifying the 'no condition = always match' behavior as part of
a shared API surface.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 28, 2026

Build Branch

Branch pr-releases/jkt/auto/type-casting-improvements-3f3a
Commit 1875896e64
Updated March 9, 2026 at 10:43:12 PM UTC

Static preview entry points

QR codes (mobile preview)
Entry point QR code
Docs QR for docs preview
Static pages QR for static pages preview
Integration pages QR for integration pages preview

Integration commands

npm (Android / Extension):

npm i github:duckduckgo/content-scope-scripts#pr-releases/jkt/auto/type-casting-improvements-3f3a

Swift Package Manager (Apple):

.package(url: "https://github.com/duckduckgo/content-scope-scripts.git", branch: "pr-releases/jkt/auto/type-casting-improvements-3f3a")

git submodule (Windows):

git -C submodules/content-scope-scripts fetch origin pr-releases/jkt/auto/type-casting-improvements-3f3a
git -C submodules/content-scope-scripts checkout origin/pr-releases/jkt/auto/type-casting-improvements-3f3a
Pin to exact commit

npm (Android / Extension):

npm i github:duckduckgo/content-scope-scripts#1875896e64a5d1865412f329b4673ff05c740a87

Swift Package Manager (Apple):

.package(url: "https://github.com/duckduckgo/content-scope-scripts.git", revision: "1875896e64a5d1865412f329b4673ff05c740a87")

git submodule (Windows):

git -C submodules/content-scope-scripts fetch origin pr-releases/jkt/auto/type-casting-improvements-3f3a
git -C submodules/content-scope-scripts checkout 1875896e64a5d1865412f329b4673ff05c740a87

cursoragent and others added 2 commits February 28, 2026 11:52
The variable-level @type annotation on domainEntries was still a cast.
Replace with Array.isArray guard on item.rules before the narrowing
cast to ElementHidingRule[]. The cast at the Record<string, unknown>
boundary is unavoidable, but is now guarded by a runtime check.

Co-authored-by: Jonathan Kingston <jonathanKingston@users.noreply.github.com>
daxtheduck
daxtheduck previously approved these changes Mar 1, 2026
@github-actions github-actions Bot added the patch Increment the patch version when merged label Mar 4, 2026
@daxtheduck daxtheduck dismissed their stale review March 4, 2026 14:16

Dismissing stale approval — new commits pushed, awaiting Cursor re-review.

daxtheduck
daxtheduck previously approved these changes Mar 4, 2026
@daxtheduck daxtheduck dismissed their stale review March 5, 2026 01:52

Dismissing stale approval — new commits pushed, awaiting Cursor re-review.

@github-actions github-actions Bot added the semver-patch Bug fix / internal — no release needed label Mar 5, 2026
daxtheduck
daxtheduck previously approved these changes Mar 5, 2026
@daxtheduck daxtheduck dismissed their stale review March 9, 2026 22:42

Dismissing stale approval — new commits pushed, awaiting Cursor re-review.

Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Web Compatibility Assessment

  • injected/src/config-feature.js
    Range: L139-L143
    Severity: warning
    The new if (condition === undefined) return true path changes runtime semantics for malformed conditional entries from "non-match/error" to "apply everywhere". In ConfigFeature, this affects all features using matchConditionalFeatureSetting(), so an accidentally incomplete config entry in domains/conditionalChanges can now broaden scope globally and create cross-site breakage instead of failing closed.

  • injected/src/features/element-hiding.js
    Range: L399-L400
    Severity: info
    Array.isArray(item.rules) is a good compatibility guard: it prevents non-array config payloads from flowing into selector processing and avoids downstream querySelectorAll/rule-shape failures.

Security Assessment

  • injected/src/features/element-hiding.js
    Range: L74-L80
    Severity: warning
    hasSelector() uses 'selector' in rule, which walks the prototype chain. In a hostile page, Object.prototype.selector pollution can make non-selector rules pass the guard and route inherited values/accessors into selector handling. Prefer an own-property check (hasOwnProperty.call(rule, 'selector')) using captured globals to avoid prototype-chain influence.

Risk Level

Medium Risk — mostly type-hardening, but one behavior change in core config matching (ConfigFeature) can alter rule application scope across many features if config entries are incomplete.

Recommendations

  1. Tighten matchConditionalFeatureSetting() fail-closed behavior for malformed entries: treat missing both condition and domain as non-match (or gate "always match" to an explicitly supported key/path).
  2. Harden selector type guard to own-property checks with captured globals (hasOwnProperty.call(...)) rather than 'in'.
  3. Add regression tests in config-feature unit tests for entries missing both condition and domain, and in element-hiding tests for prototype-polluted objects to ensure guard behavior is stable.

Open in Web View Automation 

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

Labels

patch Increment the patch version when merged semver-patch Bug fix / internal — no release needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants