Skip to content

Commit

Permalink
support referrerpolicy modifier by ModifierValidator. #98 AG-24994
Browse files Browse the repository at this point in the history
Squashed commit of the following:

commit bf758af
Author: Slava Leleka <[email protected]>
Date:   Tue Nov 7 14:10:51 2023 +0200

    fix linter errors

commit 81654b0
Author: Slava Leleka <[email protected]>
Date:   Tue Nov 7 14:09:22 2023 +0200

    support referrerpolicy modifier by ModifierValidator
  • Loading branch information
slavaleleka committed Nov 7, 2023
1 parent 31fb2b2 commit 3359288
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 11 deletions.
7 changes: 7 additions & 0 deletions packages/agtree/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog][keepachangelog], and this project adheres to [Semantic Versioning][semver].


## 1.1.7 - 2023-11-07

### Added

- Support of `referrerpolicy` modifier [#98](https://github.com/AdguardTeam/tsurlfilter/issues/98)


## 1.1.6 - 2023-09-22

### Changed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ The value format describes the format of the modifier value. It can be one of th
- `pipe_separated_stealth_options` validates value for `$stealth` modifier
- `csp_value` validates value for `$csp` modifier
- `permissions_value` validates value for `$permissions` modifier
- `referrerpolicy_value` validates value for `$referrerpolicy` modifier
- `url` validates that the value is a valid URL.
- `regexp` validates that the value is a valid regular expression.
> :warning: **This is not the same as when you assign a regular expression to value_format!**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
adg_os_any:
name: referrerpolicy
description: This modifier allows overriding of a page's referrer policy.
docs: https://adguard.app/kb/general/ad-filtering/create-own-filters/#referrerpolicy-modifier
conflicts:
- document
- subdocument
inverse_conflicts: true
assignable: true
negatable: false
value_optional: true
value_format: referrerpolicy_value
2 changes: 2 additions & 0 deletions packages/agtree/src/compatibility-tables/raw-modifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import popunder from './modifiers/popunder.yml';
import popup from './modifiers/popup.yml';
import redirectRule from './modifiers/redirect-rule.yml';
import redirect from './modifiers/redirect.yml';
import referrerpolicy from './modifiers/referrerpolicy.yml';
import removeheader from './modifiers/removeheader.yml';
import removeparam from './modifiers/removeparam.yml';
import replace from './modifiers/replace.yml';
Expand Down Expand Up @@ -108,6 +109,7 @@ export const rawModifiersData: RawModifierData = {
popup,
redirectRule,
redirect,
referrerpolicy,
removeheader,
removeparam,
replace,
Expand Down
19 changes: 18 additions & 1 deletion packages/agtree/src/validator/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export const ALLOWED_CSP_DIRECTIVES = new Set([
]);

/**
* Allowed stealth options for $permissions modifier.
* Allowed directives for $permissions modifier.
*
* @see {@link https://adguard.app/kb/general/ad-filtering/create-own-filters/#permissions-modifier}
*/
Expand Down Expand Up @@ -153,6 +153,22 @@ export const PERMISSIONS_TOKEN_SELF = 'self';
*/
export const EMPTY_PERMISSIONS_ALLOWLIST = `${OPEN_PARENTHESIS}${CLOSE_PARENTHESIS}`;

/**
* Allowed directives for $referrerpolicy modifier.
*
* @see {@link https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy}
*/
export const REFERRER_POLICY_DIRECTIVES = new Set([
'no-referrer',
'no-referrer-when-downgrade',
'origin',
'origin-when-cross-origin',
'same-origin',
'strict-origin',
'strict-origin-when-cross-origin',
'unsafe-url',
]);

/**
* Prefixes for error messages used in modifier validation.
*/
Expand All @@ -165,6 +181,7 @@ export const VALIDATION_ERROR_PREFIX = {
INVALID_PERMISSION_DIRECTIVE: 'Invalid Permissions-Policy directive for the modifier',
INVALID_PERMISSION_ORIGINS: 'Origins in the value is invalid for the modifier and the directive',
INVALID_PERMISSION_ORIGIN_QUOTES: 'Double quotes should be used for origins in the value of the modifier',
INVALID_REFERRER_POLICY_DIRECTIVE: 'Invalid Referrer-Policy directive for the modifier',
MIXED_NEGATIONS: 'Simultaneous usage of negated and not negated values is forbidden for the modifier',
NO_CSP_VALUE: 'No CSP value for the modifier and the directive',
NO_CSP_DIRECTIVE_QUOTE: 'CSP directives should no be quoted for the modifier',
Expand Down
27 changes: 27 additions & 0 deletions packages/agtree/src/validator/value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
APP_NAME_ALLOWED_CHARS,
EMPTY_PERMISSIONS_ALLOWLIST,
PERMISSIONS_TOKEN_SELF,
REFERRER_POLICY_DIRECTIVES,
SOURCE_DATA_ERROR_PREFIX,
VALIDATION_ERROR_PREFIX,
} from './constants';
Expand All @@ -57,6 +58,7 @@ const enum CustomValueFormatValidatorName {
Domain = 'pipe_separated_domains',
Method = 'pipe_separated_methods',
Permissions = 'permissions_value',
ReferrerPolicy = 'referrerpolicy_value',
StealthOption = 'pipe_separated_stealth_options',
}

Expand Down Expand Up @@ -635,6 +637,30 @@ const validatePermissions = (modifier: Modifier): ValidationResult => {
return { valid: true };
};

/**
* Validates `referrerpolicy_value` custom value format.
* Used for $referrerpolicy modifier.
*
* @param modifier Modifier AST node.
*
* @returns Validation result.
*/
const validateReferrerPolicy = (modifier: Modifier): ValidationResult => {
if (!modifier.value?.value) {
return getValueRequiredValidationResult(modifier.modifier.value);
}

const modifierName = modifier.modifier.value;
const modifierValue = modifier.value.value;

if (!REFERRER_POLICY_DIRECTIVES.has(modifierValue)) {
// eslint-disable-next-line max-len
return getInvalidValidationResult(`${VALIDATION_ERROR_PREFIX.INVALID_REFERRER_POLICY_DIRECTIVE}: '${modifierName}': '${modifierValue}'`);
}

return { valid: true };
};

/**
* Map of all available pre-defined validators for modifiers with custom `value_format`.
*/
Expand All @@ -645,6 +671,7 @@ const CUSTOM_VALUE_FORMAT_MAP = {
[CustomValueFormatValidatorName.Domain]: validatePipeSeparatedDomains,
[CustomValueFormatValidatorName.Method]: validatePipeSeparatedMethods,
[CustomValueFormatValidatorName.Permissions]: validatePermissions,
[CustomValueFormatValidatorName.ReferrerPolicy]: validateReferrerPolicy,
[CustomValueFormatValidatorName.StealthOption]: validatePipeSeparatedStealthOptions,
};

Expand Down
64 changes: 54 additions & 10 deletions packages/agtree/test/validator/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ describe('ModifierValidator', () => {
'popup',
'redirect-rule',
'redirect',
'referrerpolicy',
'rewrite',
'removeheader',
'removeparam',
Expand Down Expand Up @@ -150,7 +151,7 @@ describe('ModifierValidator', () => {
},
];
test.each(supportedModifiers)('%s', (modifierName) => {
expect(modifierValidator.exists(modifierName)).toBeTruthy();
expect(modifierValidator.exists(modifierName as Modifier)).toBeTruthy();
});

const unsupportedModifiers = [
Expand All @@ -164,7 +165,7 @@ describe('ModifierValidator', () => {
},
];
test.each(unsupportedModifiers)('$modifier.value', (modifierName) => {
expect(modifierValidator.exists(modifierName)).toBeFalsy();
expect(modifierValidator.exists(modifierName as Modifier)).toBeFalsy();
});
});
});
Expand Down Expand Up @@ -367,14 +368,6 @@ describe('ModifierValidator', () => {
"jsonprune=\\$.*.*[?(key-eq 'Some key' 'Some value')]",
'jsonprune=\\$.elements[?(has "\\$.a.b.c")]',
'jsonprune=\\$.elements[?(key-eq "\\$.a.b.c" "abc")]',
'method=get',
'method=get|head|put',
'method=~post',
'method=~post|~put',
'permissions=autoplay=()',
'permissions=storage-access=()\\,camera=()',
'permissions=storage-access=()\\, camera=()',
'permissions=storage-access=()\\, camera=()',
'redirect=noopjs',
'redirect=noopmp4-1s',
'redirect=googletagmanager-gtm',
Expand Down Expand Up @@ -932,6 +925,57 @@ describe('ModifierValidator', () => {
});
});
});

describe('required value - validate by referrerpolicy_value', () => {
describe('referrerpolicy_value valid', () => {
test.each([
'referrerpolicy=no-referrer',
'referrerpolicy=no-referrer-when-downgrade',
'referrerpolicy=origin',
'referrerpolicy=origin-when-cross-origin',
'referrerpolicy=same-origin',
'referrerpolicy=strict-origin',
'referrerpolicy=strict-origin-when-cross-origin',
'referrerpolicy=unsafe-url',
// value may be empty in unblocking rules
'referrerpolicy',
])('%s', (rawModifier) => {
const modifier = getModifier(rawModifier);
const validationResult = modifierValidator.validate(AdblockSyntax.Adg, modifier, true);
expect(validationResult.valid).toBeTruthy();
});
});

describe('referrerpolicy_value invalid', () => {
test.each([
{
actual: 'referrerpolicy=autoplay=self',
// eslint-disable-next-line max-len
expected: `${VALIDATION_ERROR_PREFIX.INVALID_REFERRER_POLICY_DIRECTIVE}: 'referrerpolicy': 'autoplay=self'`,
},
{
actual: 'referrerpolicy=no-origin',
// eslint-disable-next-line max-len
expected: `${VALIDATION_ERROR_PREFIX.INVALID_REFERRER_POLICY_DIRECTIVE}: 'referrerpolicy': 'no-origin'`,
},
{
// non-latin "o" in "origin"
actual: 'referrerpolicy=оrigin',
// eslint-disable-next-line max-len
expected: `${VALIDATION_ERROR_PREFIX.INVALID_REFERRER_POLICY_DIRECTIVE}: 'referrerpolicy': 'оrigin'`,
},
{
actual: '~referrerpolicy=same-origin',
expected: `${VALIDATION_ERROR_PREFIX.NOT_NEGATABLE_MODIFIER}: 'referrerpolicy'`,
},
])('$actual', ({ actual, expected }) => {
const modifier = getModifier(actual);
const validationResult = modifierValidator.validate(AdblockSyntax.Adg, modifier);
expect(validationResult.valid).toBeFalsy();
expect(validationResult.error).toEqual(expected);
});
});
});
});
});

Expand Down

0 comments on commit 3359288

Please sign in to comment.