Skip to content

Commit 2e4525f

Browse files
committed
Add new static network filter option: urltransform
The `urltransform` option allows to redirect a non-blocked network request to another URL. There are restrictions on its usage: - require a trusted source -- thus uBO-maintained lists or user filters - the `urltransform` value must start with a `/` If at least one of these conditions is not fulfilled, the filter will be invalid and rejected. The requirement to start with `/` is to enforce that only the path part of a URL can be modified, thus ensuring the network request is redirected to the same scheme and authority (as defined at https://en.wikipedia.org/wiki/Uniform_Resource_Identifier#Syntax). Usage example (redirect requests for CSS resources to a non-existing resource, for demonstration purpose): ||iana.org^$css,urltransform=/notfound.css Name of this option is inspired from DNR API: https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/declarativeNetRequest/URLTransform This commit required to bring the concept of "trusted source" to the static network filtering engine.
1 parent bee64eb commit 2e4525f

14 files changed

+110
-31
lines changed

platform/mv3/make-scriptlets.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ export function compile(details) {
8888
const scriptletToken = details.args[0];
8989
const resourceEntry = resourceDetails.get(scriptletToken);
9090
if ( resourceEntry === undefined ) { return; }
91-
if ( resourceEntry.requiresTrust && details.isTrusted !== true ) {
91+
if ( resourceEntry.requiresTrust && details.trustedSource !== true ) {
9292
console.log(`Rejecting ${scriptletToken}: source is not trusted`);
9393
return;
9494
}

src/js/1p-filters.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ const cmEditor = new CodeMirror(qs$('#userFilters'), {
4848
styleActiveLine: {
4949
nonEmpty: true,
5050
},
51+
trustedSource: true,
5152
});
5253

5354
uBlockDashboard.patchCodeMirrorEditor(cmEditor);

src/js/asset-viewer.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ import './codemirror/ubo-static-filtering.js';
8282
what : 'getAssetContent',
8383
url: assetKey,
8484
});
85+
cmEditor.setOption('trustedSource', details.trustedSource === true);
8586
cmEditor.setValue(details && details.content || '');
8687

8788
if ( subscribeElem !== null ) {

src/js/benchmarks.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,8 @@ const loadBenchmarkDataset = (( ) => {
181181
if ( r === 1 ) { blockCount += 1; }
182182
else if ( r === 2 ) { allowCount += 1; }
183183
if ( r !== 1 ) {
184-
if ( staticNetFilteringEngine.hasQuery(fctxt) ) {
184+
staticNetFilteringEngine.transformRequest(fctxt);
185+
if ( fctxt.redirectURL !== undefined && staticNetFilteringEngine.hasQuery(fctxt) ) {
185186
staticNetFilteringEngine.filterQuery(fctxt, 'removeparam');
186187
}
187188
if ( fctxt.type === 'main_frame' || fctxt.type === 'sub_frame' ) {

src/js/codemirror/ubo-static-filtering.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,21 @@ const preparseDirectiveHints = [];
3737
const originHints = [];
3838
let hintHelperRegistered = false;
3939

40+
/******************************************************************************/
41+
42+
let trustedSource = false;
43+
44+
CodeMirror.defineOption('trustedSource', false, (cm, state) => {
45+
trustedSource = state;
46+
self.dispatchEvent(new Event('trustedSource'));
47+
});
4048

4149
/******************************************************************************/
4250

4351
CodeMirror.defineMode('ubo-static-filtering', function() {
4452
const astParser = new sfp.AstFilterParser({
4553
interactive: true,
54+
trustedSource,
4655
nativeCssHas: vAPI.webextFlavor.env.includes('native_css_has'),
4756
});
4857
const astWalker = astParser.getWalker();
@@ -205,7 +214,11 @@ CodeMirror.defineMode('ubo-static-filtering', function() {
205214
return '+';
206215
};
207216

208-
return {
217+
self.addEventListener('trustedSource', ( ) => {
218+
astParser.options.trustedSource = trustedSource;
219+
});
220+
221+
return {
209222
lineComment: '!',
210223
token: function(stream) {
211224
if ( stream.sol() ) {
@@ -977,6 +990,10 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => {
977990
}
978991
};
979992

993+
self.addEventListener('trustedSource', ( ) => {
994+
astParser.options.trustedSource = trustedSource;
995+
});
996+
980997
CodeMirror.defineInitHook(cm => {
981998
cm.on('changes', onChanges);
982999
cm.on('beforeChange', onBeforeChanges);

src/js/messaging.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ const onMessage = function(request, sender, callback) {
108108
dontCache: true,
109109
needSourceURL: true,
110110
}).then(result => {
111+
result.trustedSource = µb.isTrustedList(result.assetKey);
111112
callback(result);
112113
});
113114
return;

src/js/pagestore.js

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -860,7 +860,7 @@ const PageStore = class {
860860
if ( (fctxt.itype & fctxt.INLINE_ANY) === 0 ) {
861861
if ( result === 1 ) {
862862
this.redirectBlockedRequest(fctxt);
863-
} else if ( snfe.hasQuery(fctxt) ) {
863+
} else {
864864
this.redirectNonBlockedRequest(fctxt);
865865
}
866866
}
@@ -922,25 +922,31 @@ const PageStore = class {
922922
}
923923

924924
redirectBlockedRequest(fctxt) {
925-
const directives = staticNetFilteringEngine.redirectRequest(
926-
redirectEngine,
927-
fctxt
928-
);
925+
const directives = staticNetFilteringEngine.redirectRequest(redirectEngine, fctxt);
929926
if ( directives === undefined ) { return; }
930927
if ( logger.enabled !== true ) { return; }
931928
fctxt.pushFilters(directives.map(a => a.logData()));
932929
if ( fctxt.redirectURL === undefined ) { return; }
933930
fctxt.pushFilter({
934931
source: 'redirect',
935-
raw: redirectEngine.resourceNameRegister
932+
raw: directives[directives.length-1].value
936933
});
937934
}
938935

939936
redirectNonBlockedRequest(fctxt) {
940-
const directives = staticNetFilteringEngine.filterQuery(fctxt);
941-
if ( directives === undefined ) { return; }
937+
const transformDirectives = staticNetFilteringEngine.transformRequest(fctxt);
938+
const pruneDirectives = fctxt.redirectURL === undefined &&
939+
staticNetFilteringEngine.hasQuery(fctxt) &&
940+
staticNetFilteringEngine.filterQuery(fctxt) ||
941+
undefined;
942+
if ( transformDirectives === undefined && pruneDirectives === undefined ) { return; }
942943
if ( logger.enabled !== true ) { return; }
943-
fctxt.pushFilters(directives.map(a => a.logData()));
944+
if ( transformDirectives !== undefined ) {
945+
fctxt.pushFilters(transformDirectives.map(a => a.logData()));
946+
}
947+
if ( pruneDirectives !== undefined ) {
948+
fctxt.pushFilters(pruneDirectives.map(a => a.logData()));
949+
}
944950
if ( fctxt.redirectURL === undefined ) { return; }
945951
fctxt.pushFilter({
946952
source: 'redirect',

src/js/redirect-engine.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,6 @@ class RedirectEngine {
167167
this.resources = new Map();
168168
this.reset();
169169
this.modifyTime = Date.now();
170-
this.resourceNameRegister = '';
171170
}
172171

173172
reset() {
@@ -183,7 +182,6 @@ class RedirectEngine {
183182
) {
184183
const entry = this.resources.get(this.aliases.get(token) || token);
185184
if ( entry === undefined ) { return; }
186-
this.resourceNameRegister = token;
187185
return entry.toURL(fctxt, asDataURI);
188186
}
189187

src/js/reverselookup.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ const fromNetFilter = async function(rawFilter) {
131131
const writer = new CompiledListWriter();
132132
const parser = new sfp.AstFilterParser({
133133
expertMode: true,
134+
trustedSource: true,
134135
maxTokenLength: staticNetFilteringEngine.MAX_TOKEN_LENGTH,
135136
nativeCssHas: vAPI.webextFlavor.env.includes('native_css_has'),
136137
});
@@ -169,6 +170,7 @@ const fromExtendedFilter = async function(details) {
169170

170171
const parser = new sfp.AstFilterParser({
171172
expertMode: true,
173+
trustedSource: true,
172174
nativeCssHas: vAPI.webextFlavor.env.includes('native_css_has'),
173175
});
174176
parser.parse(details.rawFilter);

src/js/scriptlet-filtering.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ scriptletFilteringEngine.compile = function(parser, writer) {
306306

307307
// Only exception filters are allowed to be global.
308308
const isException = parser.isException();
309-
const normalized = normalizeRawFilter(parser, writer.properties.get('isTrusted'));
309+
const normalized = normalizeRawFilter(parser, writer.properties.get('trustedSource'));
310310

311311
// Can fail if there is a mismatch with trust requirement
312312
if ( normalized === undefined ) { return; }

0 commit comments

Comments
 (0)