|
| 1 | +#!/usr/bin/env node |
| 2 | + |
| 3 | +const { text } = require('node:stream/consumers') |
| 4 | + |
| 5 | +const { ArgumentDefaultsHelpFormatter, ArgumentParser, FileType } = require('argparse') |
| 6 | + |
| 7 | +const adblockRust = require('./index.js') |
| 8 | +const adblockRustPackage = require('../package.json') |
| 9 | + |
| 10 | +// These are defined by different content filter projects (AdBlock Plus, |
| 11 | +// uBlockOrigin, AdGuard, etc.). |
| 12 | +// For example, https://github.com/gorhill/uBlock/wiki/Static-filter-syntax |
| 13 | +const filterListRequestTypes = [ |
| 14 | + 'beacon', |
| 15 | + 'csp_report', |
| 16 | + 'document', |
| 17 | + 'font', |
| 18 | + 'image', |
| 19 | + 'media', |
| 20 | + 'object', |
| 21 | + 'ping', |
| 22 | + 'script', |
| 23 | + 'stylesheet', |
| 24 | + 'sub_frame', |
| 25 | + 'websocket', |
| 26 | + 'xhr', |
| 27 | + 'other', |
| 28 | + 'speculative', |
| 29 | + 'web_manifest', |
| 30 | + 'xbl', |
| 31 | + 'xml_dtd', |
| 32 | + 'xslt' |
| 33 | +] |
| 34 | + |
| 35 | +// These values are defined by Blink, in `Resource::ResourceTypeToString`. |
| 36 | +// See third_party/blink/renderer/platform/loader/fetch/resource.h. |
| 37 | +// The OTHER catch all case covers the additional types |
| 38 | +// defined in `blink::Resource::InitiatorTypeNameToString`. |
| 39 | +// |
| 40 | +// See https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/platform/loader/fetch/resource.cc |
| 41 | +/* eslint-disable quote-props */ |
| 42 | +const chromiumRequestTypeMapping = { |
| 43 | + 'Attribution resource': 'other', |
| 44 | + 'Audio': 'media', |
| 45 | + 'CSS resource': 'stylesheet', |
| 46 | + 'CSS stylesheet': 'stylesheet', |
| 47 | + 'Dictionary': 'other', |
| 48 | + 'Document': 'document', |
| 49 | + 'Fetch': 'xhr', |
| 50 | + 'Font': 'font', |
| 51 | + 'Icon': 'other', |
| 52 | + 'Image': 'image', |
| 53 | + 'Internal resource': 'other', |
| 54 | + 'Link element resource': 'other', |
| 55 | + 'Link prefetch resource': 'speculative', |
| 56 | + 'Manifest': 'web_manifest', |
| 57 | + 'Mock': 'other', |
| 58 | + 'Other resource': 'other', |
| 59 | + 'Processing instruction': 'other', |
| 60 | + 'Script': 'script', |
| 61 | + 'SpeculationRule': 'speculative', |
| 62 | + 'SVG document': 'media', |
| 63 | + 'SVG Use element resource': 'media', |
| 64 | + 'Text track': 'other', |
| 65 | + 'Track': 'other', |
| 66 | + 'User Agent CSS resource': 'stylesheet', |
| 67 | + 'Video': 'media', |
| 68 | + 'XML resource': 'document', |
| 69 | + 'XMLHttpRequest': 'xhr', |
| 70 | + 'XSL stylesheet': 'xslt' |
| 71 | +} |
| 72 | +/* eslint-enable quote-props */ |
| 73 | + |
| 74 | +const parser = new ArgumentParser({ |
| 75 | + add_help: true, |
| 76 | + formatter_class: ArgumentDefaultsHelpFormatter, |
| 77 | + description: 'Check whether a URL would be blocked by given filter list rules' |
| 78 | +}) |
| 79 | +parser.add_argument('-v', '--version', { |
| 80 | + action: 'version', |
| 81 | + version: adblockRustPackage.version |
| 82 | +}) |
| 83 | + |
| 84 | +parser.add_argument('--url', { |
| 85 | + required: true, |
| 86 | + type: URL, |
| 87 | + help: 'The full URL to check against the provided filter lists.' |
| 88 | +}) |
| 89 | + |
| 90 | +const requestTypeGroup = parser.add_mutually_exclusive_group(true) |
| 91 | +requestTypeGroup.add_argument('--type', { |
| 92 | + help: 'The type of the request, using the types defined by ' + |
| 93 | + 'filter list projects', |
| 94 | + choices: filterListRequestTypes |
| 95 | +}) |
| 96 | +requestTypeGroup.add_argument('--chromium-type', { |
| 97 | + help: 'The type of the request, using the types defined by chromium', |
| 98 | + choices: Object.keys(chromiumRequestTypeMapping) |
| 99 | +}) |
| 100 | + |
| 101 | +parser.add_argument('--context-url', { |
| 102 | + required: true, |
| 103 | + type: URL, |
| 104 | + help: 'The security context the request occurred in, as a full URL' |
| 105 | +}) |
| 106 | +parser.add_argument('--rule-files', { |
| 107 | + required: true, |
| 108 | + type: FileType('r'), |
| 109 | + nargs: '*', |
| 110 | + help: 'One or more paths to files of filter list rules to check the ' + |
| 111 | + 'request against' |
| 112 | +}) |
| 113 | +parser.add_argument('--verbose', { |
| 114 | + default: false, |
| 115 | + action: 'store_true', |
| 116 | + help: 'Print information about what rule(s) the request matched.' |
| 117 | +}) |
| 118 | + |
| 119 | +;(async () => { |
| 120 | + const args = parser.parse_args() |
| 121 | + |
| 122 | + const filterSet = new adblockRust.FilterSet(true) |
| 123 | + for (const aRuleFile of args.rule_files) { |
| 124 | + const rulesText = await text(aRuleFile) |
| 125 | + filterSet.addFilters(rulesText.split('\n')) |
| 126 | + } |
| 127 | + |
| 128 | + const engine = new adblockRust.Engine(filterSet, true) |
| 129 | + const result = engine.check( |
| 130 | + args.url.toString(), |
| 131 | + args.context_url.toString(), |
| 132 | + args.type || chromiumRequestTypeMapping[args.chromium_type], |
| 133 | + true |
| 134 | + ) |
| 135 | + |
| 136 | + if (args.verbose) { |
| 137 | + console.log(result) |
| 138 | + } |
| 139 | + process.exit(result.matched ? 0 : 1) |
| 140 | +})() |
0 commit comments