LEFT | RIGHT |
1 /* | 1 /* |
2 * This file is part of Adblock Plus <https://adblockplus.org/>, | 2 * This file is part of Adblock Plus <https://adblockplus.org/>, |
3 * Copyright (C) 2006-present eyeo GmbH | 3 * Copyright (C) 2006-present eyeo GmbH |
4 * | 4 * |
5 * Adblock Plus is free software: you can redistribute it and/or modify | 5 * Adblock Plus is free software: you can redistribute it and/or modify |
6 * it under the terms of the GNU General Public License version 3 as | 6 * it under the terms of the GNU General Public License version 3 as |
7 * published by the Free Software Foundation. | 7 * published by the Free Software Foundation. |
8 * | 8 * |
9 * Adblock Plus is distributed in the hope that it will be useful, | 9 * Adblock Plus is distributed in the hope that it will be useful, |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
(...skipping 686 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
697 * Defines whether the filter should distinguish between lower and upper case | 697 * Defines whether the filter should distinguish between lower and upper case |
698 * letters | 698 * letters |
699 * @param {string} [domains] | 699 * @param {string} [domains] |
700 * Domains that the filter is restricted to, e.g. "foo.com|bar.com|~baz.com" | 700 * Domains that the filter is restricted to, e.g. "foo.com|bar.com|~baz.com" |
701 * @param {boolean} [thirdParty] | 701 * @param {boolean} [thirdParty] |
702 * Defines whether the filter should apply to third-party or first-party | 702 * Defines whether the filter should apply to third-party or first-party |
703 * content only | 703 * content only |
704 * @param {string} [sitekeys] | 704 * @param {string} [sitekeys] |
705 * Public keys of websites that this filter should apply to | 705 * Public keys of websites that this filter should apply to |
706 * @param {?string} [rewrite] | 706 * @param {?string} [rewrite] |
707 * The (optional) rule specifying how to rewrite the URL. See | |
708 * RegExpFilter.prototype.rewrite. | |
709 * @param {?string} [resourceName] | |
710 * The name of the internal resource to which to rewrite the | 707 * The name of the internal resource to which to rewrite the |
711 * URL. e.g. if the value of the <code>rewrite</code> parameter is | 708 * URL. e.g. if the value of the <code>rewrite</code> parameter is |
712 * <code>abp-resource:blank-html</code>, this should be | 709 * <code>abp-resource:blank-html</code>, this should be |
713 * <code>blank-html</code>. | 710 * <code>blank-html</code>. |
714 * @constructor | 711 * @constructor |
715 * @augments ActiveFilter | 712 * @augments ActiveFilter |
716 */ | 713 */ |
717 function RegExpFilter(text, regexpSource, contentType, matchCase, domains, | 714 function RegExpFilter(text, regexpSource, contentType, matchCase, domains, |
718 thirdParty, sitekeys, rewrite, resourceName) | 715 thirdParty, sitekeys, rewrite) |
719 { | 716 { |
720 ActiveFilter.call(this, text, domains); | 717 ActiveFilter.call(this, text, domains); |
721 | 718 |
722 if (contentType != null) | 719 if (contentType != null) |
723 this.contentType = contentType; | 720 this.contentType = contentType; |
724 if (matchCase) | 721 if (matchCase) |
725 this.matchCase = matchCase; | 722 this.matchCase = matchCase; |
726 if (thirdParty != null) | 723 if (thirdParty != null) |
727 this.thirdParty = thirdParty; | 724 this.thirdParty = thirdParty; |
728 if (sitekeys != null) | 725 if (sitekeys != null) |
729 this.sitekeySource = sitekeys; | 726 this.sitekeySource = sitekeys; |
730 if (rewrite != null) | 727 if (rewrite != null) |
731 this.rewrite = rewrite; | 728 this.rewrite = rewrite; |
732 if (resourceName) | |
733 this.resourceName = resourceName; | |
734 | 729 |
735 if (regexpSource.length >= 2 && | 730 if (regexpSource.length >= 2 && |
736 regexpSource[0] == "/" && | 731 regexpSource[0] == "/" && |
737 regexpSource[regexpSource.length - 1] == "/") | 732 regexpSource[regexpSource.length - 1] == "/") |
738 { | 733 { |
739 // The filter is a regular expression - convert it immediately to | 734 // The filter is a regular expression - convert it immediately to |
740 // catch syntax errors | 735 // catch syntax errors |
741 let regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), | 736 let regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), |
742 this.matchCase ? "" : "i"); | 737 this.matchCase ? "" : "i"); |
743 Object.defineProperty(this, "regexp", {value: regexp}); | 738 Object.defineProperty(this, "regexp", {value: regexp}); |
744 } | 739 } |
745 else | 740 else |
746 { | 741 { |
747 // Patterns like /foo/bar/* exist so that they are not treated as regular | 742 // Patterns like /foo/bar/* exist so that they are not treated as regular |
748 // expressions. We drop any superfluous wildcards here so our optimizations | 743 // expressions. We drop any superfluous wildcards here so our optimizations |
749 // can kick in. | 744 // can kick in. |
750 if (this.rewrite == null || this.resourceName) | 745 regexpSource = regexpSource.replace(/^\*+/, "").replace(/\*+$/, ""); |
751 regexpSource = regexpSource.replace(/^\*+/, "").replace(/\*+$/, ""); | |
752 | 746 |
753 if (!this.matchCase && isLiteralPattern(regexpSource)) | 747 if (!this.matchCase && isLiteralPattern(regexpSource)) |
754 regexpSource = regexpSource.toLowerCase(); | 748 regexpSource = regexpSource.toLowerCase(); |
755 | 749 |
756 // No need to convert this filter to regular expression yet, do it on demand | 750 // No need to convert this filter to regular expression yet, do it on demand |
757 this.pattern = regexpSource; | 751 this.pattern = regexpSource; |
758 } | 752 } |
759 } | 753 } |
760 exports.RegExpFilter = RegExpFilter; | 754 exports.RegExpFilter = RegExpFilter; |
761 | 755 |
(...skipping 18 matching lines...) Expand all Loading... |
780 */ | 774 */ |
781 pattern: null, | 775 pattern: null, |
782 /** | 776 /** |
783 * Regular expression to be used when testing against this filter | 777 * Regular expression to be used when testing against this filter |
784 * @type {RegExp} | 778 * @type {RegExp} |
785 */ | 779 */ |
786 get regexp() | 780 get regexp() |
787 { | 781 { |
788 let value = null; | 782 let value = null; |
789 | 783 |
790 let {pattern, rewrite, resourceName} = this; | 784 let {pattern} = this; |
791 if ((rewrite != null && !resourceName) || !isLiteralPattern(pattern)) | 785 if (!isLiteralPattern(pattern)) |
792 { | 786 value = new RegExp(filterToRegExp(pattern), this.matchCase ? "" : "i"); |
793 value = new RegExp(filterToRegExp(pattern, rewrite != null), | |
794 this.matchCase ? "" : "i"); | |
795 } | |
796 | 787 |
797 Object.defineProperty(this, "regexp", {value}); | 788 Object.defineProperty(this, "regexp", {value}); |
798 return value; | 789 return value; |
799 }, | 790 }, |
800 /** | 791 /** |
801 * Content types the filter applies to, combination of values from | 792 * Content types the filter applies to, combination of values from |
802 * RegExpFilter.typeMap | 793 * RegExpFilter.typeMap |
803 * @type {number} | 794 * @type {number} |
804 */ | 795 */ |
805 contentType: 0x7FFFFFFF, | 796 contentType: 0x7FFFFFFF, |
(...skipping 29 matching lines...) Expand all Loading... |
835 this.sitekeySource = null; | 826 this.sitekeySource = null; |
836 } | 827 } |
837 | 828 |
838 Object.defineProperty( | 829 Object.defineProperty( |
839 this, "sitekeys", {value: sitekeys, enumerable: true} | 830 this, "sitekeys", {value: sitekeys, enumerable: true} |
840 ); | 831 ); |
841 return this.sitekeys; | 832 return this.sitekeys; |
842 }, | 833 }, |
843 | 834 |
844 /** | 835 /** |
845 * The rule specifying how to rewrite the URL. | |
846 * The syntax is similar to the one of String.prototype.replace(). | |
847 * @type {?string} | |
848 */ | |
849 rewrite: null, | |
850 | |
851 /** | |
852 * The name of the internal resource to which to rewrite the | 836 * The name of the internal resource to which to rewrite the |
853 * URL. e.g. if the value of the <code>rewrite</code> property is | 837 * URL. e.g. if the value of the <code>$rewrite</code> option is |
854 * <code>abp-resource:blank-html</code>, this should be | 838 * <code>abp-resource:blank-html</code>, this should be |
855 * <code>blank-html</code>. | 839 * <code>blank-html</code>. |
856 * @type {?string} | 840 * @type {?string} |
857 */ | 841 */ |
858 resourceName: null, | 842 rewrite: null, |
859 | 843 |
860 /** | 844 /** |
861 * Tests whether the URL matches this filter | 845 * Tests whether the URL matches this filter |
862 * @param {string} location URL to be tested | 846 * @param {string} location URL to be tested |
863 * @param {number} typeMask bitmask of content / request types to match | 847 * @param {number} typeMask bitmask of content / request types to match |
864 * @param {string} [docDomain] domain name of the document that loads the URL | 848 * @param {string} [docDomain] domain name of the document that loads the URL |
865 * @param {boolean} [thirdParty] should be true if the URL is a third-party | 849 * @param {boolean} [thirdParty] should be true if the URL is a third-party |
866 * request | 850 * request |
867 * @param {string} [sitekey] public key provided by the document | 851 * @param {string} [sitekey] public key provided by the document |
868 * @return {boolean} true in case of a match | 852 * @return {boolean} true in case of a match |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
987 } | 971 } |
988 | 972 |
989 let contentType = null; | 973 let contentType = null; |
990 let matchCase = null; | 974 let matchCase = null; |
991 let domains = null; | 975 let domains = null; |
992 let sitekeys = null; | 976 let sitekeys = null; |
993 let thirdParty = null; | 977 let thirdParty = null; |
994 let collapse = null; | 978 let collapse = null; |
995 let csp = null; | 979 let csp = null; |
996 let rewrite = null; | 980 let rewrite = null; |
997 let resourceName = null; | |
998 let options; | 981 let options; |
999 let match = text.includes("$") ? Filter.optionsRegExp.exec(text) : null; | 982 let match = text.includes("$") ? Filter.optionsRegExp.exec(text) : null; |
1000 if (match) | 983 if (match) |
1001 { | 984 { |
1002 options = match[1].split(","); | 985 options = match[1].split(","); |
1003 text = match.input.substr(0, match.index); | 986 text = match.input.substr(0, match.index); |
1004 for (let option of options) | 987 for (let option of options) |
1005 { | 988 { |
1006 let value = null; | 989 let value = null; |
1007 let separatorIndex = option.indexOf("="); | 990 let separatorIndex = option.indexOf("="); |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1053 break; | 1036 break; |
1054 case "collapse": | 1037 case "collapse": |
1055 collapse = !inverse; | 1038 collapse = !inverse; |
1056 break; | 1039 break; |
1057 case "sitekey": | 1040 case "sitekey": |
1058 if (!value) | 1041 if (!value) |
1059 return new InvalidFilter(origText, "filter_unknown_option"); | 1042 return new InvalidFilter(origText, "filter_unknown_option"); |
1060 sitekeys = value.toUpperCase(); | 1043 sitekeys = value.toUpperCase(); |
1061 break; | 1044 break; |
1062 case "rewrite": | 1045 case "rewrite": |
| 1046 if (value == null) |
| 1047 return new InvalidFilter(origText, "filter_unknown_option"); |
1063 if (!value.startsWith("abp-resource:")) | 1048 if (!value.startsWith("abp-resource:")) |
1064 return new InvalidFilter(origText, "filter_invalid_rewrite"); | 1049 return new InvalidFilter(origText, "filter_invalid_rewrite"); |
1065 rewrite = value; | 1050 rewrite = value.substr("abp-resource:".length); |
1066 resourceName = value.substr("abp-resource:".length); | |
1067 break; | 1051 break; |
1068 default: | 1052 default: |
1069 return new InvalidFilter(origText, "filter_unknown_option"); | 1053 return new InvalidFilter(origText, "filter_unknown_option"); |
1070 } | 1054 } |
1071 } | 1055 } |
1072 } | 1056 } |
1073 } | 1057 } |
1074 | 1058 |
1075 // For security reasons, never match $rewrite filters | |
1076 // against requests that might load any code to be executed. | |
1077 // Unless it is to an internal resource. | |
1078 if (rewrite != null && !resourceName) | |
1079 { | |
1080 if (contentType == null) | |
1081 ({contentType} = RegExpFilter.prototype); | |
1082 contentType &= ~(RegExpFilter.typeMap.SCRIPT | | |
1083 RegExpFilter.typeMap.SUBDOCUMENT | | |
1084 RegExpFilter.typeMap.OBJECT | | |
1085 RegExpFilter.typeMap.OBJECT_SUBREQUEST); | |
1086 } | |
1087 | |
1088 try | 1059 try |
1089 { | 1060 { |
1090 if (blocking) | 1061 if (blocking) |
1091 { | 1062 { |
1092 if (csp && Filter.invalidCSPRegExp.test(csp)) | 1063 if (csp && Filter.invalidCSPRegExp.test(csp)) |
1093 return new InvalidFilter(origText, "filter_invalid_csp"); | 1064 return new InvalidFilter(origText, "filter_invalid_csp"); |
1094 | 1065 |
1095 if (resourceName) | 1066 if (rewrite) |
1096 { | 1067 { |
1097 if (text[0] == "|" && text[1] == "|") | 1068 if (text[0] == "|" && text[1] == "|") |
1098 { | 1069 { |
1099 if (!domains && thirdParty != false) | 1070 if (!domains && thirdParty != false) |
1100 return new InvalidFilter(origText, "filter_invalid_rewrite"); | 1071 return new InvalidFilter(origText, "filter_invalid_rewrite"); |
1101 } | 1072 } |
1102 else if (text[0] == "*") | 1073 else if (text[0] == "*") |
1103 { | 1074 { |
1104 if (!domains) | 1075 if (!domains) |
1105 return new InvalidFilter(origText, "filter_invalid_rewrite"); | 1076 return new InvalidFilter(origText, "filter_invalid_rewrite"); |
1106 } | 1077 } |
1107 else | 1078 else |
1108 { | 1079 { |
1109 return new InvalidFilter(origText, "filter_invalid_rewrite"); | 1080 return new InvalidFilter(origText, "filter_invalid_rewrite"); |
1110 } | 1081 } |
1111 } | 1082 } |
1112 | 1083 |
1113 return new BlockingFilter(origText, text, contentType, matchCase, domains, | 1084 return new BlockingFilter(origText, text, contentType, matchCase, domains, |
1114 thirdParty, sitekeys, rewrite, resourceName, | 1085 thirdParty, sitekeys, rewrite, |
1115 collapse, csp); | 1086 collapse, csp); |
1116 } | 1087 } |
1117 return new WhitelistFilter(origText, text, contentType, matchCase, domains, | 1088 return new WhitelistFilter(origText, text, contentType, matchCase, domains, |
1118 thirdParty, sitekeys); | 1089 thirdParty, sitekeys); |
1119 } | 1090 } |
1120 catch (e) | 1091 catch (e) |
1121 { | 1092 { |
1122 return new InvalidFilter(origText, "filter_invalid_regexp"); | 1093 return new InvalidFilter(origText, "filter_invalid_regexp"); |
1123 } | 1094 } |
1124 }; | 1095 }; |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1165 /** | 1136 /** |
1166 * Class for blocking filters | 1137 * Class for blocking filters |
1167 * @param {string} text see {@link Filter Filter()} | 1138 * @param {string} text see {@link Filter Filter()} |
1168 * @param {string} regexpSource see {@link RegExpFilter RegExpFilter()} | 1139 * @param {string} regexpSource see {@link RegExpFilter RegExpFilter()} |
1169 * @param {number} [contentType] see {@link RegExpFilter RegExpFilter()} | 1140 * @param {number} [contentType] see {@link RegExpFilter RegExpFilter()} |
1170 * @param {boolean} [matchCase] see {@link RegExpFilter RegExpFilter()} | 1141 * @param {boolean} [matchCase] see {@link RegExpFilter RegExpFilter()} |
1171 * @param {string} [domains] see {@link RegExpFilter RegExpFilter()} | 1142 * @param {string} [domains] see {@link RegExpFilter RegExpFilter()} |
1172 * @param {boolean} [thirdParty] see {@link RegExpFilter RegExpFilter()} | 1143 * @param {boolean} [thirdParty] see {@link RegExpFilter RegExpFilter()} |
1173 * @param {string} [sitekeys] see {@link RegExpFilter RegExpFilter()} | 1144 * @param {string} [sitekeys] see {@link RegExpFilter RegExpFilter()} |
1174 * @param {?string} [rewrite] | 1145 * @param {?string} [rewrite] |
1175 * The (optional) rule specifying how to rewrite the URL. See | |
1176 * RegExpFilter.prototype.rewrite. | |
1177 * @param {?string} [resourceName] | |
1178 * The name of the internal resource to which to rewrite the | 1146 * The name of the internal resource to which to rewrite the |
1179 * URL. e.g. if the value of the <code>rewrite</code> parameter is | 1147 * URL. e.g. if the value of the <code>rewrite</code> parameter is |
1180 * <code>abp-resource:blank-html</code>, this should be | 1148 * <code>abp-resource:blank-html</code>, this should be |
1181 * <code>blank-html</code>. | 1149 * <code>blank-html</code>. |
1182 * @param {boolean} [collapse] | 1150 * @param {boolean} [collapse] |
1183 * defines whether the filter should collapse blocked content, can be null | 1151 * defines whether the filter should collapse blocked content, can be null |
1184 * @param {string} [csp] | 1152 * @param {string} [csp] |
1185 * Content Security Policy to inject when the filter matches | 1153 * Content Security Policy to inject when the filter matches |
1186 * @constructor | 1154 * @constructor |
1187 * @augments RegExpFilter | 1155 * @augments RegExpFilter |
1188 */ | 1156 */ |
1189 function BlockingFilter(text, regexpSource, contentType, matchCase, domains, | 1157 function BlockingFilter(text, regexpSource, contentType, matchCase, domains, |
1190 thirdParty, sitekeys, rewrite, resourceName, | 1158 thirdParty, sitekeys, rewrite, |
1191 collapse, csp) | 1159 collapse, csp) |
1192 { | 1160 { |
1193 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, | 1161 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, |
1194 thirdParty, sitekeys, rewrite, resourceName); | 1162 thirdParty, sitekeys, rewrite); |
1195 | 1163 |
1196 if (collapse != null) | 1164 if (collapse != null) |
1197 this.collapse = collapse; | 1165 this.collapse = collapse; |
1198 | 1166 |
1199 if (csp != null) | 1167 if (csp != null) |
1200 this.csp = csp; | 1168 this.csp = csp; |
1201 } | 1169 } |
1202 exports.BlockingFilter = BlockingFilter; | 1170 exports.BlockingFilter = BlockingFilter; |
1203 | 1171 |
1204 BlockingFilter.prototype = extend(RegExpFilter, { | 1172 BlockingFilter.prototype = extend(RegExpFilter, { |
(...skipping 12 matching lines...) Expand all Loading... |
1217 */ | 1185 */ |
1218 csp: null, | 1186 csp: null, |
1219 | 1187 |
1220 /** | 1188 /** |
1221 * Rewrites an URL. | 1189 * Rewrites an URL. |
1222 * @param {string} url the URL to rewrite | 1190 * @param {string} url the URL to rewrite |
1223 * @return {string} the rewritten URL, or the original in case of failure | 1191 * @return {string} the rewritten URL, or the original in case of failure |
1224 */ | 1192 */ |
1225 rewriteUrl(url) | 1193 rewriteUrl(url) |
1226 { | 1194 { |
1227 if (this.resourceName) | 1195 return resourceMap.get(this.rewrite) || url; |
1228 return resourceMap.get(this.resourceName) || url; | |
1229 | |
1230 return url; | |
1231 } | 1196 } |
1232 }); | 1197 }); |
1233 | 1198 |
1234 /** | 1199 /** |
1235 * Class for whitelist filters | 1200 * Class for whitelist filters |
1236 * @param {string} text see {@link Filter Filter()} | 1201 * @param {string} text see {@link Filter Filter()} |
1237 * @param {string} regexpSource see {@link RegExpFilter RegExpFilter()} | 1202 * @param {string} regexpSource see {@link RegExpFilter RegExpFilter()} |
1238 * @param {number} [contentType] see {@link RegExpFilter RegExpFilter()} | 1203 * @param {number} [contentType] see {@link RegExpFilter RegExpFilter()} |
1239 * @param {boolean} [matchCase] see {@link RegExpFilter RegExpFilter()} | 1204 * @param {boolean} [matchCase] see {@link RegExpFilter RegExpFilter()} |
1240 * @param {string} [domains] see {@link RegExpFilter RegExpFilter()} | 1205 * @param {string} [domains] see {@link RegExpFilter RegExpFilter()} |
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1435 | 1400 |
1436 /** | 1401 /** |
1437 * Script that should be executed | 1402 * Script that should be executed |
1438 * @type {string} | 1403 * @type {string} |
1439 */ | 1404 */ |
1440 get script() | 1405 get script() |
1441 { | 1406 { |
1442 return this.body; | 1407 return this.body; |
1443 } | 1408 } |
1444 }); | 1409 }); |
LEFT | RIGHT |