Skip to content

Commit

Permalink
Add static network filter option: permissions
Browse files Browse the repository at this point in the history
Related discussion:
uBlockOrigin/uBlock-issues#2714

Reference:
https://adguard.com/kb/general/ad-filtering/create-own-filters/#permissions-modifier

Example:

    ||example.org^$permissions=browsing-topics=()

Difference with AdGuard's syntax: use `|` to separate permissions
policy directives instead of `\,` -- uBO will replace instances
of `|` with `, `:

    *$permissions=oversized-images=()|unsized-media=()

Eventually uBO will support AdGuard's syntax of using escaped
commas, but not for this first iteration.
  • Loading branch information
gorhill committed Jul 9, 2023
1 parent 8d09c56 commit 5ebdbf3
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 13 deletions.
8 changes: 8 additions & 0 deletions src/js/static-filtering-parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ export const NODE_TYPE_NET_OPTION_NAME_MP4 = iota++;
export const NODE_TYPE_NET_OPTION_NAME_NOOP = iota++;
export const NODE_TYPE_NET_OPTION_NAME_OBJECT = iota++;
export const NODE_TYPE_NET_OPTION_NAME_OTHER = iota++;
export const NODE_TYPE_NET_OPTION_NAME_PERMISSIONS = iota++;
export const NODE_TYPE_NET_OPTION_NAME_PING = iota++;
export const NODE_TYPE_NET_OPTION_NAME_POPUNDER = iota++;
export const NODE_TYPE_NET_OPTION_NAME_POPUP = iota++;
Expand Down Expand Up @@ -252,6 +253,7 @@ export const nodeTypeFromOptionName = new Map([
[ 'object', NODE_TYPE_NET_OPTION_NAME_OBJECT ],
/* synonym */ [ 'object-subrequest', NODE_TYPE_NET_OPTION_NAME_OBJECT ],
[ 'other', NODE_TYPE_NET_OPTION_NAME_OTHER ],
[ 'permissions', NODE_TYPE_NET_OPTION_NAME_PERMISSIONS ],
[ 'ping', NODE_TYPE_NET_OPTION_NAME_PING ],
/* synonym */ [ 'beacon', NODE_TYPE_NET_OPTION_NAME_PING ],
[ 'popunder', NODE_TYPE_NET_OPTION_NAME_POPUNDER ],
Expand Down Expand Up @@ -1293,6 +1295,11 @@ export class AstFilterParser {
case NODE_TYPE_NET_OPTION_NAME_MATCHCASE:
realBad = this.isRegexPattern() === false;
break;
case NODE_TYPE_NET_OPTION_NAME_PERMISSIONS:
realBad = modifierType !== 0 || (hasValue || isException) === false;
if ( realBad ) { break; }
modifierType = type;
break;
case NODE_TYPE_NET_OPTION_NAME_PING:
case NODE_TYPE_NET_OPTION_NAME_WEBSOCKET:
realBad = hasValue;
Expand Down Expand Up @@ -1349,6 +1356,7 @@ export class AstFilterParser {
realBad = abstractTypeCount || behaviorTypeCount || requestTypeCount;
break;
case NODE_TYPE_NET_OPTION_NAME_CSP:
case NODE_TYPE_NET_OPTION_NAME_PERMISSIONS:
realBad = abstractTypeCount || behaviorTypeCount || requestTypeCount;
break;
case NODE_TYPE_NET_OPTION_NAME_INLINEFONT:
Expand Down
42 changes: 29 additions & 13 deletions src/js/static-net-filtering.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,19 +182,22 @@ const MODIFIER_TYPE_REDIRECT = 1;
const MODIFIER_TYPE_REDIRECTRULE = 2;
const MODIFIER_TYPE_REMOVEPARAM = 3;
const MODIFIER_TYPE_CSP = 4;
const MODIFIER_TYPE_PERMISSIONS = 5;

const modifierTypeFromName = new Map([
[ 'redirect', MODIFIER_TYPE_REDIRECT ],
[ 'redirect-rule', MODIFIER_TYPE_REDIRECTRULE ],
[ 'removeparam', MODIFIER_TYPE_REMOVEPARAM ],
[ 'csp', MODIFIER_TYPE_CSP ],
[ 'permissions', MODIFIER_TYPE_PERMISSIONS ],
]);

const modifierNameFromType = new Map([
[ MODIFIER_TYPE_REDIRECT, 'redirect' ],
[ MODIFIER_TYPE_REDIRECTRULE, 'redirect-rule' ],
[ MODIFIER_TYPE_REMOVEPARAM, 'removeparam' ],
[ MODIFIER_TYPE_CSP, 'csp' ],
[ MODIFIER_TYPE_PERMISSIONS, 'permissions' ],
]);

//const typeValueFromCatBits = catBits => (catBits >>> TypeBitsOffset) & 0b11111;
Expand Down Expand Up @@ -3169,6 +3172,7 @@ class FilterCompiler {
]);
this.modifierIdToNormalizedId = new Map([
[ sfp.NODE_TYPE_NET_OPTION_NAME_CSP, MODIFIER_TYPE_CSP ],
[ sfp.NODE_TYPE_NET_OPTION_NAME_PERMISSIONS, MODIFIER_TYPE_PERMISSIONS ],
[ sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECT, MODIFIER_TYPE_REDIRECT ],
[ sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE, MODIFIER_TYPE_REDIRECTRULE ],
[ sfp.NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM, MODIFIER_TYPE_REMOVEPARAM ],
Expand Down Expand Up @@ -3438,6 +3442,12 @@ class FilterCompiler {
this.processMethodOption(parser.getNetOptionValue(id));
this.optionUnitBits |= this.METHOD_BIT;
break;
case sfp.NODE_TYPE_NET_OPTION_NAME_PERMISSIONS:
if ( this.processModifierOption(id, parser.getNetOptionValue(id)) === false ) {
return false;
}
this.optionUnitBits |= this.PERMISSIONS_BIT;
break;
case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECT: {
const actualId = this.action === AllowAction
? sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE
Expand Down Expand Up @@ -3554,6 +3564,7 @@ class FilterCompiler {
case sfp.NODE_TYPE_NET_OPTION_NAME_FROM:
case sfp.NODE_TYPE_NET_OPTION_NAME_HEADER:
case sfp.NODE_TYPE_NET_OPTION_NAME_METHOD:
case sfp.NODE_TYPE_NET_OPTION_NAME_PERMISSIONS:
case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECT:
case sfp.NODE_TYPE_NET_OPTION_NAME_REDIRECTRULE:
case sfp.NODE_TYPE_NET_OPTION_NAME_REMOVEPARAM:
Expand Down Expand Up @@ -3622,8 +3633,12 @@ class FilterCompiler {
this.optionUnitBits |= this.NOT_TYPE_BIT;
}

// CSP directives implicitly apply only to document/subdocument.
if ( this.modifyType === MODIFIER_TYPE_CSP ) {
// CSP/permissions options implicitly apply only to
// document/subdocument.
if (
this.modifyType === MODIFIER_TYPE_CSP ||
this.modifyType === MODIFIER_TYPE_PERMISSIONS
) {
if ( this.typeBits === 0 ) {
this.processTypeOption(sfp.NODE_TYPE_NET_OPTION_NAME_DOC, false);
this.processTypeOption(sfp.NODE_TYPE_NET_OPTION_NAME_FRAME, false);
Expand Down Expand Up @@ -4013,17 +4028,18 @@ class FilterCompiler {
}
}

FilterCompiler.prototype.FROM_BIT = 0b00000000001;
FilterCompiler.prototype.TO_BIT = 0b00000000010;
FilterCompiler.prototype.DENYALLOW_BIT = 0b00000000100;
FilterCompiler.prototype.HEADER_BIT = 0b00000001000;
FilterCompiler.prototype.STRICT_PARTY_BIT = 0b00000010000;
FilterCompiler.prototype.CSP_BIT = 0b00000100000;
FilterCompiler.prototype.REMOVEPARAM_BIT = 0b00001000000;
FilterCompiler.prototype.REDIRECT_BIT = 0b00010000000;
FilterCompiler.prototype.NOT_TYPE_BIT = 0b00100000000;
FilterCompiler.prototype.IMPORTANT_BIT = 0b01000000000;
FilterCompiler.prototype.METHOD_BIT = 0b10000000000;
FilterCompiler.prototype.FROM_BIT = 0b000000000001;
FilterCompiler.prototype.TO_BIT = 0b000000000010;
FilterCompiler.prototype.DENYALLOW_BIT = 0b000000000100;
FilterCompiler.prototype.HEADER_BIT = 0b000000001000;
FilterCompiler.prototype.STRICT_PARTY_BIT = 0b000000010000;
FilterCompiler.prototype.CSP_BIT = 0b000000100000;
FilterCompiler.prototype.REMOVEPARAM_BIT = 0b000001000000;
FilterCompiler.prototype.REDIRECT_BIT = 0b000010000000;
FilterCompiler.prototype.NOT_TYPE_BIT = 0b000100000000;
FilterCompiler.prototype.IMPORTANT_BIT = 0b001000000000;
FilterCompiler.prototype.METHOD_BIT = 0b010000000000;
FilterCompiler.prototype.PERMISSIONS_BIT = 0b100000000000;

FilterCompiler.prototype.FILTER_OK = 0;
FilterCompiler.prototype.FILTER_INVALID = 1;
Expand Down
34 changes: 34 additions & 0 deletions src/js/traffic.js
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,10 @@ const onHeadersReceived = function(details) {
modifiedHeaders = true;
}

if ( injectPP(fctxt, pageStore, responseHeaders) === true ) {
modifiedHeaders = true;
}

// https://bugzilla.mozilla.org/show_bug.cgi?id=1376932
// Prevent document from being cached by the browser if we modified it,
// either through HTML filtering and/or modified response headers.
Expand Down Expand Up @@ -1004,6 +1008,36 @@ const injectCSP = function(fctxt, pageStore, responseHeaders) {

/******************************************************************************/

const injectPP = function(fctxt, pageStore, responseHeaders) {
const permissions = [];
const directives = staticNetFilteringEngine.matchAndFetchModifiers(fctxt, 'permissions');
if ( directives !== undefined ) {
for ( const directive of directives ) {
if ( directive.result !== 1 ) { continue; }
permissions.push(directive.value.replace('|', ', '));
}
}

if ( logger.enabled && directives !== undefined ) {
fctxt.setRealm('network')
.pushFilters(directives.map(a => a.logData()))
.toLogger();
}

if ( permissions.length === 0 ) { return; }

µb.updateToolbarIcon(fctxt.tabId, 0x02);

responseHeaders.push({
name: 'permissions-policy',
value: permissions.join(', ')
});

return true;
};

/******************************************************************************/

// https://github.com/gorhill/uBlock/issues/1163
// "Block elements by size".
// https://github.com/gorhill/uBlock/issues/1390#issuecomment-187310719
Expand Down

0 comments on commit 5ebdbf3

Please sign in to comment.