Skip to content

Dynamic filter precedence doesn't actually follow "more specific = higher precedence" #1698

@duxovni

Description

@duxovni

Prerequisites

I tried to reproduce the issue when...

  • uBO is the only extension
  • uBO with default lists/settings
  • using a new, unmodified browser profile

Description

The documentation for dynamic filtering says

The more specific the party, the higher the precedence.
example.com overrides 3rd-party scripts

I'd expect that to mean that example.com * * noop overrides * * 3p-script block. However, in practice, * * 3p-script block wins.

Looking at the code in src/js/dynamic-net-filtering.js#L277, we've got

        // Precedence: from most specific to least specific

        // Specific-destination, any party, any type
        decomposeHostname(desHostname, decomposedDestination);
        for ( const deshn of decomposedDestination ) {
            if ( deshn === '*' ) { break; }
            this.y = deshn;
            if ( this.evaluateCellZ(srcHostname, deshn, '*') !== 0 ) {
                return this.r;
            }
        }

        const thirdParty = is3rdParty(srcHostname, desHostname);

        // Any destination
        this.y = '*';

        // Specific party
        // TODO: equate `object` as `sub_frame`
        if ( thirdParty ) {
            // 3rd-party, specific type
            if ( type === 'script' ) {
                if ( this.evaluateCellZ(srcHostname, '*', '3p-script') !== 0 ) {
                    return this.r;
                }
            } else if ( type === 'sub_frame' ) {
                if ( this.evaluateCellZ(srcHostname, '*', '3p-frame') !== 0 ) {
                    return this.r;
                }
            }
            // 3rd-party, any type
            if ( this.evaluateCellZ(srcHostname, '*', '3p') !== 0 ) {
                return this.r;
            }
        } else if ( type === 'script' ) {
            // 1st party, specific type
            if ( this.evaluateCellZ(srcHostname, '*', '1p-script') !== 0 ) {
                return this.r;
            }
        }

        // Any destination, any party, specific type
        if ( supportedDynamicTypes[type] !== undefined ) {
            if ( this.evaluateCellZ(srcHostname, '*', type) !== 0 ) {
                return this.r;
            }
        }

        // Any destination, any party, any type
        if ( this.evaluateCellZ(srcHostname, '*', '*') !== 0 ) {
            return this.r;
        }

Since evaluateCellZ checks every decomposition of the source hostname, if example.com makes a 3rd-party JS request to example.net, we check rules in the following order:

[line 281, first loop]

  • example.com example.net *
  • com example.net *
  • * example.net *

[line 281, second loop]

  • example.com net *
  • com net *
  • * net *

[line 299]

  • example.com * 3p-script
  • com * 3p-script
  • * * 3p-script

[line 308]

  • example.com * 3p
  • com * 3p
  • * * 3p

[line 326]

  • example.com * *
  • com * *
  • * * *

So, we have * * 3p-script > example.com * 3p > * * 3p > example.com * *. This doesn't match my intuition of what "more specific" means at all. It seems like what we need to do is move the decomposition of the source hostname to an outer loop around the entire rule evaluation, rather than performing it in an inner loop for each rule type; or if this is the intended behavior, make that a lot clearer in the wiki.

My proposed change would be approximately to rewrite evaluateCellZ to not decompose the source hostname, and instead wrap lines 279-328 in a loop over the decompositions of the source hostname. That would give the following evaluation order:

  • example.com example.net *
  • example.com net *
  • example.com * 3p-script
  • example.com * 3p
  • example.com * *
  • com example.net *
  • com net *
  • com * 3p-script
  • com * 3p
  • com * *
  • * example.net *
  • * net *
  • * * 3p-script
  • * * 3p
  • * * *
    Which seems much more intuitive to me. I'm happy to draft a pull request.

A specific URL where the issue occurs

https://github.com

Steps to Reproduce

  1. Enable dynamic filtering
  2. Set up rules to block 3rd-party scripts by default: * * 3p-script block
  3. Set up rules to noop all requests from a specific site: github.com * * noop

Expected behavior

All requests on github.com should be allowed unless they're blocked by a static filter rule.

Actual behavior

3rd-party script requests from github.com to githubassets.com are blocked by dynamic filtering.

uBlock Origin version

1.37.2

Browser name and version

Firefox 90.0.2

Operating System and version

Fedora 33

Metadata

Metadata

Assignees

No one assigned

    Labels

    invalidnot a uBlock issuewikirelated to wiki

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions