├── .github └── FUNDING.yml ├── .gitignore ├── .gitmodules ├── .reuse ├── dep5 └── templates │ └── fsf-js.commented.jinja2 ├── .tx └── config ├── LICENSE ├── LICENSES ├── CC-BY-SA-3.0.txt ├── CC0-1.0.txt ├── GPL-3.0-or-later.txt └── MIT.txt ├── README.md ├── TLD ├── generate.pl └── generate.sh ├── build.sh ├── html5_events ├── archive_gen.sh ├── auto_archive.txt ├── historical │ ├── aurora_events.txt │ ├── beta_events.txt │ ├── esr52_events.txt │ ├── esr60_events.txt │ └── release_events.txt ├── html5_events.pl ├── html5_events.re └── html5_events_archive.txt ├── manifest.js ├── manifest.sh ├── nscl_gitsync.sh ├── reuse.sh └── src ├── _locales ├── bn │ └── messages.json ├── br │ └── messages.json ├── ca │ └── messages.json ├── da │ └── messages.json ├── de │ └── messages.json ├── el │ └── messages.json ├── en │ └── messages.json ├── es │ └── messages.json ├── fa │ └── messages.json ├── fi │ └── messages.json ├── fr │ └── messages.json ├── he │ └── messages.json ├── is │ └── messages.json ├── it │ └── messages.json ├── ja │ └── messages.json ├── lt │ └── messages.json ├── mk │ └── messages.json ├── ms │ └── messages.json ├── nb │ └── messages.json ├── nb_NO │ └── messages.json ├── nl │ └── messages.json ├── pl │ └── messages.json ├── pt_BR │ └── messages.json ├── pt_PT │ └── messages.json ├── ro │ └── messages.json ├── ru │ └── messages.json ├── sq │ └── messages.json ├── sv_SE │ └── messages.json ├── tr │ └── messages.json ├── uk │ └── messages.json ├── zh_CN │ └── messages.json └── zh_TW │ └── messages.json ├── bg ├── DNRPolicy.js ├── Defaults.js ├── LifeCycle.js ├── ReportingCSP.js ├── RequestGuard.js ├── Settings.js ├── TabGuard.js ├── main.js └── popupHandler.js ├── common ├── Entities.js ├── themes.css ├── themes.js └── themesDOM.js ├── content ├── content.css ├── content.js ├── dirindex.js ├── embeddingDocument.js ├── eventsHook.js ├── eventsHook.main.js ├── experiments.js ├── onScriptDisabled.js ├── staticNS.js └── syncFetchPolicy.js ├── img ├── error64.png ├── icon256.png ├── icon48.png ├── icon96.png ├── logo.svg ├── noscript-options.png ├── stop.png ├── ui-black64.png ├── ui-clock64.png ├── ui-close64.png ├── ui-custom64.png ├── ui-global-no64.png ├── ui-global-sub-64.png ├── ui-global64.png ├── ui-http64.png ├── ui-https64.png ├── ui-maybe64.png ├── ui-no64.png ├── ui-part64.png ├── ui-reload64.png ├── ui-revoke-temp64.png ├── ui-sub64.png ├── ui-tab-no64.png ├── ui-tab64.png ├── ui-temp-all64.png ├── ui-temp64.png ├── ui-yes64.png ├── vintage │ ├── error64.png │ ├── icon256.png │ ├── icon48.png │ ├── icon96.png │ ├── logo.svg │ ├── noscript-options.png │ ├── stop.png │ ├── ui-black64.png │ ├── ui-clock64.png │ ├── ui-close64.png │ ├── ui-custom64.png │ ├── ui-global-no64.png │ ├── ui-global-sub64.png │ ├── ui-global64.png │ ├── ui-http64.png │ ├── ui-https64.png │ ├── ui-maybe64.png │ ├── ui-no64.png │ ├── ui-part64.png │ ├── ui-reload64.png │ ├── ui-revoke-temp64.png │ ├── ui-sub64.png │ ├── ui-tab-no64.png │ ├── ui-tab64.png │ ├── ui-temp-all64.png │ ├── ui-temp64.png │ ├── ui-yes64.png │ └── warning64.png └── warning64.png ├── lib ├── flextabs.css ├── flextabs.css.license ├── flextabs.js ├── flextabs.js.license ├── he.js └── he.js.license ├── manifest.json ├── sw.js ├── test ├── Policy_test.js ├── Storage_test.js ├── XSS_test.js ├── run.js └── test ├── ui ├── Prompts.js ├── common.css ├── options.css ├── options.html ├── options.js ├── popup.css ├── popup.html ├── popup.js ├── prompt.css ├── prompt.html ├── prompt.js ├── resize_hack.js ├── siteInfo.html ├── siteInfo.js ├── slider.css ├── toolbar.js ├── ui-hc.css ├── ui.css ├── ui.js └── whirlpool.css └── xss ├── ASPIdiocy.js ├── Exceptions.js ├── InjectionCheckWorker.js ├── InjectionChecker.js ├── XSS.js └── sanitizeName.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | liberapay: NoScript 2 | custom: ["https://noscript.net/donate", "https://flattr.com/@ma1/domain/noscript.net"] 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # files (especially tests) to be ignored until a subsequent release for security 2 | **/embargoed/** 3 | # build artifacts 4 | build 5 | unpacked 6 | chromium 7 | xpi 8 | *.xpi 9 | *.zip 10 | *.crx 11 | *.bak 12 | # dev env options 13 | .eslintrc.json 14 | # auto-generated files 15 | TLD/tld.js 16 | TLD/tld_template.js 17 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "nscl"] 2 | path = src/nscl 3 | url = ../nscl.git 4 | -------------------------------------------------------------------------------- /.reuse/dep5: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: NoScript 3 | Upstream-Contact: Giorgio Maone 4 | Source: https://noscript.net 5 | 6 | # Files not worth to be copyrighted 7 | Files: .gitignore .gitmodules .github/* .tx/* TLD/*.dat html5_events/*.txt html5_events/*.re html5_events/historical/* 8 | Copyright: No copyright 9 | License: CC0-1.0 10 | 11 | # Images (icons, mostly) 12 | Files: src/img/* 13 | Copyright: 2005-2024 Giorgio Maone 14 | License: CC-BY-SA-3.0 15 | 16 | # JSON (uncommentable) files 17 | Files: *.json */*.json 18 | Copyright: 2019-2024 Giorgio Maone 19 | License: GPL-3.0-or-later 20 | 21 | # Localization files 22 | Files: _locales/* 23 | Copyright: 2005-2024 The NoScript translators community 24 | License: GPL-3.0-or-later -------------------------------------------------------------------------------- /.reuse/templates/fsf-js.commented.jinja2: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | {% for copyright_line in copyright_lines %} 5 | * {{ copyright_line }} 6 | {% endfor %} 7 | {% if copyright_lines and spdx_expressions %} 8 | * 9 | {% endif %} 10 | {% for expression in spdx_expressions %} 11 | * SPDX-License-Identifier: {{ expression }} 12 | {% endfor %} 13 | {% if "GPL-3.0-or-later" in spdx_expressions %} 14 | * 15 | * This program is free software: you can redistribute it and/or modify it under 16 | * the terms of the GNU General Public License as published by the Free Software 17 | * Foundation, either version 3 of the License, or (at your option) any later 18 | * version. 19 | * 20 | * This program is distributed in the hope that it will be useful, but WITHOUT 21 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 22 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 23 | * 24 | * You should have received a copy of the GNU General Public License along with 25 | * this program. If not, see . 26 | {% endif %} 27 | */ 28 | -------------------------------------------------------------------------------- /.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://www.transifex.com 3 | 4 | [o:otf:p:noscript:r:messagesjson] 5 | file_filter = src/_locales//messages.json 6 | source_file = src/_locales/en/messages.json 7 | source_lang = en_US 8 | type = CHROME 9 | minimum_perc = 90 10 | 11 | -------------------------------------------------------------------------------- /LICENSES/CC0-1.0.txt: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 6 | 7 |
8 | 9 |
10 | 11 | [![Badge License]][License]    12 | [![Badge Release]][Releases] 13 | 14 |
15 |
16 | 17 | [][Website] 21 | 22 | # NoScript Security Suite 23 | 24 | *Free Open Source Software providing*
25 | *extra protection for various browsers.* 26 | 27 |
28 |
29 | 30 | [![Button Website]][Website] 31 | 32 |
33 |
34 | 35 | ## Supported 36 | 37 |
38 | 39 | ![Badge Firefox]    40 | ![Badge Chromium]    41 | 42 | ![Badge Firefox Mobile]    43 | ![Badge Tor Browser] 44 | 45 |
46 |
47 | 48 | ## Security Reports 49 | 50 | We strive to fix security sensitive issues in the shortest
51 | time possible - hours ideally - while protecting users. 52 | 53 | Please report privately to **[security@noscript.net]** 54 | 55 | To ensure confidentiality and protect users,
56 | please encrypt your report with this **PGP key**. 57 | 58 |
59 | 60 | ``` 61 | 3359 0391 70A3 CD9B 25CF 5A46 231A 83AF DA9C 2434 62 | ``` 63 | 64 |
65 | 66 |
67 | 68 | 69 | 70 | 71 | [security@noscript.net]: mailto:security@noscript.net 72 | 73 | [Releases]: https://github.com/hackademix/noscript/releases 74 | [Website]: https://noscript.net 75 | 76 | [License]: LICENSE 77 | 78 | 79 | 80 | 81 | [Badge Release]: https://img.shields.io/github/v/release/hackademix/noscript?style=for-the-badge&labelColor=569A31&color=407225&logoColor=white&logo=GitLFS 82 | [Badge License]: https://img.shields.io/badge/License-GPL3+-015d93.svg?style=for-the-badge&labelColor=blue&logoColor=white&logo=GNU 83 | 84 | [Badge Firefox]: https://img.shields.io/badge/Firefox-e86434.svg?style=for-the-badge&logoColor=white&logo=FirefoxBrowser 85 | [Badge Firefox Mobile]: https://img.shields.io/badge/Firefox_Mobile-2b9a5b.svg?style=for-the-badge&logoColor=white&logo=Android 86 | [Badge Chromium]: https://img.shields.io/badge/Chromium-4285F4.svg?style=for-the-badge&logoColor=white&logo=GoogleChrome 87 | [Badge Tor Browser]: https://img.shields.io/badge/Tor_Browser-7D4698?style=for-the-badge&logo=Tor-Browser&logoColor=white 88 | 89 | 90 | 91 | 92 | [Button Website]: https://img.shields.io/badge/Website-d12027?style=for-the-badge&logoColor=white&logo=ONLYOFFICE 93 | -------------------------------------------------------------------------------- /TLD/generate.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -w 2 | 3 | # Copyright (C) 2005-2024 Giorgio Maone 4 | # 5 | # SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | # use strict; 8 | use utf8; 9 | use open ':utf8'; 10 | use Regexp::Assemble; 11 | $dat="public_suffix_list.dat"; 12 | die(".dat file $dat not found!") unless -f "$dat"; 13 | 14 | sub generate { 15 | my $src = "./tld_template.js"; 16 | my $dst = "./tld.js"; 17 | my (@rx, @ex, $rx, $ex); 18 | open(DAT, $dat) || die("Cannot open $dat"); 19 | while() { 20 | s/\./\\\./g; 21 | s/\s+utf.*//; 22 | s/[\r\n]//g; 23 | if(/^!/) { 24 | s/^!//; 25 | push(@ex, lc($_)); 26 | } elsif (!/^(\/\/|[ \n\r]|$)/) { 27 | s/\*\\\./[^\\.]+\\./; 28 | push(@rx, lc($_)); 29 | } 30 | } 31 | close(DAT); 32 | 33 | #$o = Regexp::Optimizer->new; 34 | #$o = Regexp::List->new; 35 | $o = Regexp::Assemble->new; 36 | $_ = $o->add(@rx)->as_string(); 37 | s/\(\?-xism:(.*)\)/$1/; 38 | $rx = $_; 39 | @rx = NULL; 40 | 41 | $o = Regexp::Assemble->new; 42 | $_ = $o->add(@ex)->as_string(); 43 | s/\(\?-xism:(.*)\)/$1/; 44 | $ex = $_; 45 | @ex = NULL; 46 | 47 | open(SRC, $src) || die("Cannot open $src"); 48 | open(DST, ">$dst") || die("Cannot open $dst"); 49 | while() { 50 | s/(_tldRx:\s*\/\(.*?\)).*?(?=\$\/)/$1$rx/s; 51 | s/(_tldEx:\s*\/\(.*?\)).*?(?=\$\/)/$1$ex/s; 52 | print DST; 53 | } 54 | close(SRC); 55 | close(DST); 56 | } 57 | generate(); 58 | -------------------------------------------------------------------------------- /TLD/generate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (C) 2005-2024 Giorgio Maone 4 | # 5 | # SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | BASE=$(dirname "$0") 8 | TEMPLATE=tld_template.js 9 | if [ -f "$1" ]; then 10 | cp "$1" "$BASE/$TEMPLATE" 11 | fi 12 | pushd "$BASE" >/dev/null 13 | fname=public_suffix_list.dat 14 | nflag="" 15 | if [ -f "$fname" ]; then 16 | nflag="-z $fname" 17 | cp "$fname" "$fname.bak" 18 | fi 19 | echo 'Updating TLDs...' 20 | URL=https://publicsuffix.org/list/$fname 21 | curl -sO $nflag "$URL" 22 | 23 | if ! grep 'com' "$fname" >/dev/null; then 24 | echo >&2 "$fname empty or corrupt!" 25 | exit 1 26 | fi 27 | 28 | ./generate.pl 29 | [ -f "$fname.bak" ] && diff "$fname" "$fname.bak" && echo 'No new data from pubblic suffix list.' 30 | popd >/dev/null 31 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | # Copyright (C) 2005-2024 Giorgio Maone 4 | # 5 | # SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | BASE="$PWD" 8 | SRC="$BASE/src" 9 | BUILD="$BASE/build" 10 | MANIFEST_IN="$SRC/manifest.json" 11 | MANIFEST_OUT="$BUILD/manifest.json" 12 | 13 | if [ "$1" == "watch" ]; then 14 | while :; do 15 | $0 -u debug 16 | inotifywait -e 'create,modify,move,delete' -r "$SRC" 17 | done 18 | fi 19 | 20 | UNPACKED_ONLY= 21 | if [ "$1" == '-u' ]; then 22 | UNPACKED_ONLY=1 23 | shift 24 | fi 25 | 26 | strip_rc_ver() { 27 | MANIFEST="$1" 28 | if [[ "$2" == "rel" ]]; then 29 | # release: truncate alpha/beta/rc suffixes (or *.9xx with increment) 30 | replace='s/("version":.*)[a-z]+\d+/$1/, s/("version":.*)\b(\d+)\.9\d{2}/{ $1 . ($2 + 1) }/e' 31 | else 32 | # turn alpha/beta/rc format into *.9xx with decrement 33 | replace='s/("version":.*?)\b(\d+)(?:\.0)*[a-z]+(\d+)/{ $1 . ($2 - 1) . "." . (900 + $3) }/e' 34 | fi 35 | perl -pi.bak -e "$replace" "$MANIFEST" && rm -f "$MANIFEST".bak 36 | } 37 | 38 | VER=$(grep '"version":' "$MANIFEST_IN" | sed -re 's/.*": "(.*?)".*/\1/') 39 | if [ "$1" == "tag" ]; then 40 | # ensure nscl is up-to-date git-wise 41 | ./nscl_gitsync.sh 42 | OPTS="" 43 | if [ "$2" != "quiet" ]; then 44 | OPTS="-e" 45 | fi 46 | echo "Tagging at $VER" 47 | git tag -a "$VER" $OPTS -m"$(gitcl 2>/dev/null)" && git push && git push origin "$VER" 48 | exit 0 49 | fi 50 | if [[ "$1" =~ ^r(el(ease)?)?$ ]]; then 51 | strip_rc_ver "$MANIFEST_IN" rel 52 | "$0" && "$0" bump 53 | exit 54 | fi 55 | 56 | if [[ "$1" == "bump" ]]; then 57 | if [[ "$2" ]]; then 58 | NEW_VER="$2" 59 | if [[ "$2" == *.* ]]; then # full dotted version number 60 | pattern='"\d+.*?' 61 | NEW_VER='"'"$2" 62 | elif [[ "$2" == *rc* ]]; then # new RC after release 63 | if [[ "$2" == rc* ]]; then 64 | if [[ ! "$VER" == *rc* ]]; then 65 | echo >&2 "Please specify next release version (like 12rc1). Current is $VER" 66 | exit 1 67 | else 68 | pattern='rc\d+' 69 | fi 70 | else 71 | pattern='\b(?:\d+rc)?\d+' 72 | fi 73 | else # incremental version 74 | pattern='\b\d+' 75 | fi 76 | REPLACE_EXPR='s/(?"version":.*)'"$pattern"'"/$+{PREAMBLE}'"$NEW_VER"'"/' 77 | perl -pi.bak -e $REPLACE_EXPR "$MANIFEST_IN" && "$0" bump 78 | rm -f "$MANIFEST_IN".bak 79 | exit 80 | fi 81 | # try to add first manifest.json hunk, tentatively containing "version": ... 82 | INTERACTIVE= 83 | git diff --cached "$MANIFEST_IN" | grep '^[+-] *"version":' \ 84 | || echo -e "s\ns\ny\nq" | git add -p "$MANIFEST_IN" >/dev/null 2>&1 85 | # check whether the commit would contain more than just the version bump 86 | while git diff --cached "$MANIFEST_IN" | grep '^[+-] ' | grep -v '"version":'; do 87 | echo "Cannot commit the bump to $VER, please cleanup $MANIFEST_IN first." 88 | git restore --staged "$MANIFEST_IN" 89 | [[ $INTERACTIVE ]] && exit 1 90 | echo "Please try to isolate the version bump interactively:" 91 | INTERACTIVE=1 92 | git add -p "$MANIFEST_IN" 93 | done 94 | echo "Bumping to $VER" 95 | git commit -m "Version bump: $VER." || exit 96 | if ! ([[ $VER == *rc* ]] || [[ $VER =~ \.9[0-9][0-9]$ ]]); then 97 | # it's a stable release: let's lock nscl and tag 98 | git submodule update 99 | "$0" tag 100 | fi 101 | exit 102 | fi 103 | XPI_DIR="$BASE/xpi" 104 | XPI="$XPI_DIR/noscript-$VER" 105 | LIB="$SRC/lib" 106 | 107 | NSCL="$SRC/nscl" 108 | 109 | rm -rf "$BUILD" "$XPI" 110 | cp -pR "$SRC" "$BUILD" 111 | cp -p LICENSE "$BUILD"/ 112 | 113 | BUILD_CMD="web-ext" 114 | BUILD_OPTS="build --overwrite-dest" 115 | 116 | # save Chromium build settings from Mozilla signing overwrite 117 | CHROMIUM_BUILD_CMD="$BUILD_CMD" 118 | CHROMIUM_BUILD_OPTS="$BUILD_OPTS" 119 | 120 | if [[ "$1" =~ ^sign(ed)?$ ]]; then 121 | BUILD_CMD="$BASE/../../we-sign" 122 | BUILD_OPTS="" 123 | fi 124 | 125 | if [ "$1" != "debug" ]; then 126 | DBG="" 127 | for file in "$BUILD"/**/*.js "$BUILD"/nscl/**/*.js; do 128 | if grep -P '\/\/\s(REL|DEV)_ONLY' "$file" >/dev/null; then 129 | sed -i -r -e 's/\s*\/\/\s*(\S.*)\s*\/\/\s*REL_ONLY.*/\1/' -e 's/.*\/\/\s*DEV_ONLY.*//' "$file" 130 | fi 131 | done 132 | else 133 | DBG="-dbg" 134 | fi 135 | 136 | UNPACKED_BASE="$BASE/unpacked" 137 | mkdir -p "$UNPACKED_BASE" 138 | 139 | if ! [ "$UNPACKED_ONLY" ]; then 140 | echo "Creating $XPI.xpi..." 141 | mkdir -p "$XPI_DIR" 142 | fi 143 | 144 | CYGPATH=$(which cypath) 145 | COMMON_BUILD_OPTS="--ignore-files='test/**' 'embargoed/**' content/experiments.js" 146 | 147 | fix_manifest() { 148 | node manifest.js "$1" "$MANIFEST_IN" "$MANIFEST_OUT" 149 | } 150 | 151 | build() { 152 | if [[ $1 == "zip" ]]; then 153 | shift 154 | elif ! [[ $BUILD_CMD == *we-sign ]]; then 155 | build zip "$1" | \ 156 | grep 'ready: .*\.zip' | sed -re 's/.* ready: //' 157 | return 158 | fi 159 | UNPACKED_DIR="$UNPACKED_BASE/${1:-out}" 160 | rm -rf "$UNPACKED_DIR" 161 | cp -rp "$BUILD" "$UNPACKED_DIR" && echo >&2 "Copied $BUILD to $UNPACKED_DIR" 162 | # include only the actually used nscl dependencies 163 | rm -rf "$UNPACKED_DIR/nscl" 164 | "$BUILD/nscl/include.sh" "$UNPACKED_DIR" 165 | 166 | if [ "$UNPACKED_ONLY" ]; then 167 | return 168 | fi 169 | 170 | if [ "$CYGPATH" ]; then 171 | WEBEXT_IN="$(cygpath -w "$UNPACKED_DIR")" 172 | WEBEXT_OUT="$(cygpath -w "$XPI_DIR")" 173 | else 174 | WEBEXT_IN="$UNPACKED_DIR" 175 | WEBEXT_OUT="$XPI_DIR" 176 | fi 177 | 178 | "$BUILD_CMD" $BUILD_OPTS \ 179 | --source-dir="$WEBEXT_IN" \ 180 | --artifacts-dir="$WEBEXT_OUT" \ 181 | $COMMON_BUILD_OPTS 182 | } 183 | 184 | fix_manifest mv2firefox 185 | build firefox 186 | 187 | SIGNED="$XPI_DIR/noscript_security_suite-$VER-an+fx.xpi" 188 | if [ -f "$SIGNED" ]; then 189 | mv "$SIGNED" "$XPI.xpi" 190 | elif [ -f "$XPI.zip" ]; then 191 | if unzip -l "$XPI.xpi" | grep "META-INF/mozilla.rsa" >/dev/null 2>&1; then 192 | echo "A signed $XPI.xpi already exists, not overwriting." 193 | else 194 | unset SIGNED 195 | [[ "$VER" == *rc* ]] && xpicmd="mv" || xpicmd="cp" 196 | $xpicmd "$XPI.zip" "$XPI$DBG.xpi" 197 | echo "Created $XPI$DBG.xpi" 198 | fi 199 | fi 200 | if [ -f "$XPI.xpi" ]; then 201 | ln -fs "$XPI.xpi" "$BASE/latest.xpi" 202 | elif ! [ "$UNPACKED_ONLY" ]; then 203 | echo >&2 "ERROR: Could not create $XPI$DBG.xpi!" 204 | exit 3 205 | fi 206 | 207 | # create Chromium pre-release 208 | 209 | BUILD_CMD="$CHROMIUM_BUILD_CMD" 210 | BUILD_OPTS="$CHROMIUM_BUILD_OPTS" 211 | 212 | fix_manifest mv3edge 213 | ZIP=$(build edge) 214 | [ -f "$ZIP" ] && mv "$ZIP" "$XPI$DBG-edge.zip" 215 | 216 | fix_manifest mv3chrome 217 | ZIP=$(build chromium) 218 | [ -f "$ZIP" ] && mv "$ZIP" "$XPI$DBG-chrome.zip" 219 | 220 | if [ "$SIGNED" ] && ! [ "$UNPACKED_ONLY" ]; then 221 | "$0" tag quiet 222 | nscl 223 | ../../we-publish "$XPI.xpi" 224 | fi 225 | -------------------------------------------------------------------------------- /html5_events/archive_gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (C) 2005-2024 Giorgio Maone 4 | # 5 | # SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | ARCHIVE=html5_events_archive.txt 8 | if [ -f "$ARCHIVE" ]; then 9 | echo >&2 "$ARCHIVE already exists!" 10 | exit 1 11 | fi 12 | cat historical/*_events.txt | sort | uniq | egrep -v '^only$' > "$ARCHIVE" 13 | echo "$ARCHIVE generated." -------------------------------------------------------------------------------- /html5_events/auto_archive.txt: -------------------------------------------------------------------------------- 1 | onabort 2 | onmozaccesskeynotfound 3 | onactivate 4 | onafterprint 5 | onafterscriptexecute 6 | onanimationcancel 7 | onanimationend 8 | onanimationiteration 9 | onanimationstart 10 | onAppCommand 11 | onappinstalled 12 | onaudioprocess 13 | onauxclick 14 | onbeforecopy 15 | onbeforecut 16 | onbeforepaste 17 | onbeforeprint 18 | onbeforescriptexecute 19 | onbeforeunload 20 | onblocked 21 | onblur 22 | onbounce 23 | onbroadcast 24 | onbufferedamountlow 25 | oncached 26 | oncancel 27 | onchange 28 | onchargingchange 29 | onchargingtimechange 30 | onchecking 31 | onCheckboxStateChange 32 | onclick 33 | onclose 34 | oncommand 35 | oncommandupdate 36 | oncomplete 37 | oncompositionend 38 | oncompositionstart 39 | oncompositionupdate 40 | onconnect 41 | onconnectionavailable 42 | oncontextmenu 43 | oncopy 44 | oncut 45 | ondblclick 46 | ondischargingtimechange 47 | ondownloading 48 | onDOMActivate 49 | onDOMAttrModified 50 | onDOMCharacterDataModified 51 | onDOMFocusIn 52 | onDOMFocusOut 53 | onDOMMouseScroll 54 | onDOMNodeInserted 55 | onDOMNodeInsertedIntoDocument 56 | onDOMNodeRemoved 57 | onDOMNodeRemovedFromDocument 58 | onDOMSubtreeModified 59 | ondata 60 | ondrag 61 | ondragdrop 62 | ondragend 63 | ondragenter 64 | ondragexit 65 | ondragleave 66 | ondragover 67 | ondragstart 68 | ondrain 69 | ondrop 70 | onerror 71 | onfinish 72 | onfocus 73 | onfocusin 74 | onfocusout 75 | onfullscreenchange 76 | onfullscreenerror 77 | onget 78 | onhashchange 79 | oninput 80 | oninstall 81 | oninvalid 82 | onkeydown 83 | onkeypress 84 | onkeyup 85 | onlanguagechange 86 | onlevelchange 87 | onload 88 | onloading 89 | onloadingdone 90 | onloadingerror 91 | onpopstate 92 | onmessage 93 | onmessageerror 94 | onmidimessage 95 | onmousedown 96 | onmouseenter 97 | onmouseleave 98 | onmouselongtap 99 | onmousemove 100 | onmouseout 101 | onmouseover 102 | onMozMouseHittest 103 | onmouseup 104 | onMozAfterPaint 105 | onmozfullscreenchange 106 | onmozfullscreenerror 107 | onmozkeydownonplugin 108 | onmozkeyuponplugin 109 | onmozpointerlockchange 110 | onmozpointerlockerror 111 | onMozMousePixelScroll 112 | onMozScrolledAreaChanged 113 | onmute 114 | onnotificationclick 115 | onnotificationclose 116 | onnoupdate 117 | onobsolete 118 | ononline 119 | onoffline 120 | onopen 121 | onorientationchange 122 | onoverflow 123 | onpagehide 124 | onpageshow 125 | onpaste 126 | onpaymentmethodchange 127 | onpointerlockchange 128 | onpointerlockerror 129 | onpopuphidden 130 | onpopuphiding 131 | onpopuppositioned 132 | onpopupshowing 133 | onpopupshown 134 | onprocessorerror 135 | onpush 136 | onpushsubscriptionchange 137 | onRadioStateChange 138 | onreadystatechange 139 | onrequestprogress 140 | onresourcetimingbufferfull 141 | onresponseprogress 142 | onRequest 143 | onreset 144 | onresize 145 | onscroll 146 | onselect 147 | onselectionchange 148 | onselectstart 149 | onset 150 | onshippingaddresschange 151 | onshippingoptionchange 152 | onshow 153 | onstatechange 154 | onstorage 155 | onsubmit 156 | onsuccess 157 | ontypechange 158 | onterminate 159 | ontext 160 | ontoggle 161 | ontouchstart 162 | ontouchend 163 | ontouchmove 164 | ontouchcancel 165 | ontransitioncancel 166 | ontransitionend 167 | ontransitionrun 168 | ontransitionstart 169 | onunderflow 170 | onunload 171 | onunmute 172 | onupdatefound 173 | onupdateready 174 | onupgradeneeded 175 | onversionchange 176 | onvisibilitychange 177 | onvoiceschanged 178 | onvrdisplayactivate 179 | onvrdisplayconnect 180 | onvrdisplaydeactivate 181 | onvrdisplaydisconnect 182 | onvrdisplaypresentchange 183 | onwebkitAnimationEnd 184 | onwebkitAnimationIteration 185 | onwebkitAnimationStart 186 | onwebkitTransitionEnd 187 | onwebkitanimationend 188 | onwebkitanimationiteration 189 | onwebkitanimationstart 190 | onwebkittransitionend 191 | onwheel 192 | onSVGLoad 193 | onSVGResize 194 | onSVGScroll 195 | onSVGUnload 196 | onSVGZoom 197 | onzoom 198 | onbegin 199 | onbeginEvent 200 | onend 201 | onendEvent 202 | onrepeat 203 | onrepeatEvent 204 | onMozSwipeGestureMayStart 205 | onMozSwipeGestureStart 206 | onMozSwipeGestureUpdate 207 | onMozSwipeGestureEnd 208 | onMozSwipeGesture 209 | onMozMagnifyGestureStart 210 | onMozMagnifyGestureUpdate 211 | onMozMagnifyGesture 212 | onMozRotateGestureStart 213 | onMozRotateGestureUpdate 214 | onMozRotateGesture 215 | onMozTapGesture 216 | onMozPressTapGesture 217 | onMozEdgeUIStarted 218 | onMozEdgeUICanceled 219 | onMozEdgeUICompleted 220 | onpointerdown 221 | onpointermove 222 | onpointerup 223 | onpointercancel 224 | onpointerover 225 | onpointerout 226 | onpointerenter 227 | onpointerleave 228 | ongotpointercapture 229 | onlostpointercapture 230 | ondevicemotion 231 | ondeviceorientation 232 | onabsolutedeviceorientation 233 | ondeviceproximity 234 | onmozorientationchange 235 | onuserproximity 236 | ondevicelight 237 | ondevicechange 238 | onloadend 239 | onloadstart 240 | onprogress 241 | onsuspend 242 | onemptied 243 | onstalled 244 | onplay 245 | onpause 246 | onloadedmetadata 247 | onloadeddata 248 | onwaiting 249 | onplaying 250 | oncanplay 251 | oncanplaythrough 252 | onseeking 253 | onseeked 254 | ontimeout 255 | ontimeupdate 256 | onended 257 | onratechange 258 | ondurationchange 259 | onvolumechange 260 | onaddtrack 261 | oncontrollerchange 262 | oncuechange 263 | onenter 264 | onexit 265 | onencrypted 266 | onwaitingforkey 267 | onkeystatuseschange 268 | onremovetrack 269 | ondataavailable 270 | onwarning 271 | onstart 272 | onstop 273 | onphoto 274 | ongamepadbuttondown 275 | ongamepadbuttonup 276 | ongamepadaxismove 277 | ongamepadconnected 278 | ongamepaddisconnected 279 | onfetch 280 | onaudiostart 281 | onaudioend 282 | onsoundstart 283 | onsoundend 284 | onspeechstart 285 | onspeechend 286 | onresult 287 | onnomatch 288 | onresume 289 | onmark 290 | onboundary 291 | onsourceopen 292 | onsourceended 293 | onsourceclosed 294 | onupdatestart 295 | onupdate 296 | onupdateend 297 | onaddsourcebuffer 298 | onremovesourcebuffer -------------------------------------------------------------------------------- /html5_events/historical/aurora_events.txt: -------------------------------------------------------------------------------- 1 | ona2dpstatuschanged 2 | onabort 3 | onmozaccesskeynotfound 4 | onactivate 5 | onadapteradded 6 | onadapterremoved 7 | onafterprint 8 | onafterscriptexecute 9 | onalerting 10 | onanimationcancel 11 | onanimationend 12 | onanimationiteration 13 | onanimationstart 14 | onantennaavailablechange 15 | onAppCommand 16 | onappinstalled 17 | onattributechanged 18 | onattributereadreq 19 | onattributewritereq 20 | onaudioprocess 21 | onauxclick 22 | onbeforecopy 23 | onbeforecut 24 | onbeforepaste 25 | onbeforeevicted 26 | onbeforeprint 27 | onbeforescriptexecute 28 | onbeforeunload 29 | onblocked 30 | onblur 31 | onbroadcast 32 | onbusy 33 | onbufferedamountlow 34 | oncached 35 | oncallschanged 36 | oncancel 37 | oncardstatechange 38 | oncfstatechange 39 | onchange 40 | oncharacteristicchanged 41 | onchargingchange 42 | onchargingtimechange 43 | onchecking 44 | onclick 45 | onclirmodechange 46 | onclose 47 | oncommand 48 | oncommandupdate 49 | oncomplete 50 | oncompositionend 51 | oncompositionstart 52 | oncompositionupdate 53 | onconnect 54 | onconnected 55 | onconnecting 56 | onconnectionavailable 57 | onconnectionstatechanged 58 | oncontextmenu 59 | oncopy 60 | oncurrentchannelchanged 61 | oncurrentsourcechanged 62 | oncut 63 | ondatachange 64 | ondataerror 65 | ondblclick 66 | ondeleted 67 | ondeliverysuccess 68 | ondeliveryerror 69 | ondevicefound 70 | ondevicepaired 71 | ondeviceunpaired 72 | ondialing 73 | ondisabled 74 | ondischargingtimechange 75 | ondisconnect 76 | ondisconnected 77 | ondisconnecting 78 | ondisplaypasskeyreq 79 | ondownloading 80 | onDOMActivate 81 | onDOMAttrModified 82 | onDOMCharacterDataModified 83 | onDOMFocusIn 84 | onDOMFocusOut 85 | onDOMMouseScroll 86 | onDOMNodeInserted 87 | onDOMNodeInsertedIntoDocument 88 | onDOMNodeRemoved 89 | onDOMNodeRemovedFromDocument 90 | onDOMSubtreeModified 91 | ondata 92 | ondrag 93 | ondragdrop 94 | ondragend 95 | ondragenter 96 | ondragexit 97 | ondraggesture 98 | ondragleave 99 | ondragover 100 | ondragstart 101 | ondrain 102 | ondrop 103 | oneitbroadcasted 104 | onenabled 105 | onenterpincodereq 106 | onemergencycbmodechange 107 | onerror 108 | onevicted 109 | onfailed 110 | onfetch 111 | onfinish 112 | onfocus 113 | onfocusin 114 | onfocusout 115 | onfrequencychange 116 | onfullscreenchange 117 | onfullscreenerror 118 | onspeakerforcedchange 119 | onget 120 | ongroupchange 121 | onhashchange 122 | onheadphoneschange 123 | onheld 124 | onhfpstatuschanged 125 | onhidstatuschanged 126 | onholding 127 | oniccchange 128 | oniccdetected 129 | oniccinfochange 130 | oniccundetected 131 | onincoming 132 | oninput 133 | oninstall 134 | oninvalid 135 | onkeydown 136 | onkeypress 137 | onkeyup 138 | onlanguagechange 139 | onlevelchange 140 | onLoad 141 | onload 142 | onloading 143 | onloadingdone 144 | onloadingerror 145 | onpopstate 146 | only 147 | onmessage 148 | onmousedown 149 | onmouseenter 150 | onmouseleave 151 | onmouselongtap 152 | onmousemove 153 | onmouseout 154 | onmouseover 155 | onMozMouseHittest 156 | onmouseup 157 | onMozAfterPaint 158 | onmozfullscreenchange 159 | onmozfullscreenerror 160 | onmozkeydownonplugin 161 | onmozkeyuponplugin 162 | onmozpointerlockchange 163 | onmozpointerlockerror 164 | onmoztimechange 165 | onMozMousePixelScroll 166 | onMozScrolledAreaChanged 167 | onmoznetworkupload 168 | onmoznetworkdownload 169 | onmapfolderlistingreq 170 | onmapmessageslistingreq 171 | onmapgetmessagereq 172 | onmapsetmessagestatusreq 173 | onmapsendmessagereq 174 | onmapmessageupdatereq 175 | onnewrdsgroup 176 | onnotificationclick 177 | onnotificationclose 178 | onnoupdate 179 | onobexpasswordreq 180 | onobsolete 181 | ononline 182 | onoffline 183 | onopen 184 | onorientationchange 185 | onotastatuschange 186 | onoverflow 187 | onoverflowchanged 188 | onpagehide 189 | onpageshow 190 | onpaint 191 | onpairingaborted 192 | onpairingconfirmationreq 193 | onpairingconsentreq 194 | onpaste 195 | onpendingchange 196 | onpichange 197 | onpointerlockchange 198 | onpointerlockerror 199 | onpopuphidden 200 | onpopuphiding 201 | onpopuppositioned 202 | onpopupshowing 203 | onpopupshown 204 | onpullphonebookreq 205 | onpullvcardentryreq 206 | onpullvcardlistingreq 207 | onpush 208 | onpushsubscriptionchange 209 | onpschange 210 | onptychange 211 | onradiostatechange 212 | onrdsdisabled 213 | onrdsenabled 214 | onreaderror 215 | onreadsuccess 216 | onready 217 | onreadystatechange 218 | onreceived 219 | onremoteheld 220 | onremoteresumed 221 | onresourcetimingbufferfull 222 | onretrieving 223 | onRequest 224 | onrequestmediaplaystatus 225 | onreset 226 | onresuming 227 | onresize 228 | onrtchange 229 | onscanningstatechanged 230 | onscostatuschanged 231 | onscroll 232 | onselect 233 | onselectionchange 234 | onselectstart 235 | onsending 236 | onsent 237 | onset 238 | onshow 239 | onstatechange 240 | onstatuschanged 241 | onstkcommand 242 | onstksessionend 243 | onstorage 244 | onstorageareachanged 245 | onsubmit 246 | onsuccess 247 | ontypechange 248 | onterminate 249 | ontext 250 | ontoggle 251 | ontouchstart 252 | ontouchend 253 | ontouchmove 254 | ontouchcancel 255 | ontransitioncancel 256 | ontransitionend 257 | ontransitionrun 258 | ontransitionstart 259 | onunderflow 260 | onunload 261 | onupdatefound 262 | onupdateready 263 | onupgradeneeded 264 | onussdreceived 265 | onversionchange 266 | onvoicechange 267 | onvoiceschanged 268 | onvrdisplayactivate 269 | onvrdisplayconnect 270 | onvrdisplaydeactivate 271 | onvrdisplaydisconnect 272 | onvrdisplaypresentchange 273 | onwebkitAnimationEnd 274 | onwebkitAnimationIteration 275 | onwebkitAnimationStart 276 | onwebkitTransitionEnd 277 | onwebkitanimationend 278 | onwebkitanimationiteration 279 | onwebkitanimationstart 280 | onwebkittransitionend 281 | onwebsocket 282 | onwheel 283 | onreloadpage 284 | onSVGLoad 285 | onSVGResize 286 | onSVGScroll 287 | onSVGUnload 288 | onSVGZoom 289 | onzoom 290 | onbegin 291 | onbeginEvent 292 | onend 293 | onendEvent 294 | onrepeat 295 | onrepeatEvent 296 | onMozSwipeGestureMayStart 297 | onMozSwipeGestureStart 298 | onMozSwipeGestureUpdate 299 | onMozSwipeGestureEnd 300 | onMozSwipeGesture 301 | onMozMagnifyGestureStart 302 | onMozMagnifyGestureUpdate 303 | onMozMagnifyGesture 304 | onMozRotateGestureStart 305 | onMozRotateGestureUpdate 306 | onMozRotateGesture 307 | onMozTapGesture 308 | onMozPressTapGesture 309 | onMozEdgeUIStarted 310 | onMozEdgeUICanceled 311 | onMozEdgeUICompleted 312 | onpointerdown 313 | onpointermove 314 | onpointerup 315 | onpointercancel 316 | onpointerover 317 | onpointerout 318 | onpointerenter 319 | onpointerleave 320 | ongotpointercapture 321 | onlostpointercapture 322 | ondevicemotion 323 | ondeviceorientation 324 | onabsolutedeviceorientation 325 | ondeviceproximity 326 | onmozorientationchange 327 | onuserproximity 328 | ondevicelight 329 | onmozinterruptbegin 330 | onmozinterruptend 331 | ondevicechange 332 | onloadend 333 | onloadstart 334 | onprogress 335 | onsuspend 336 | onemptied 337 | onstalled 338 | onplay 339 | onpause 340 | onloadedmetadata 341 | onloadeddata 342 | onwaiting 343 | onplaying 344 | oncanplay 345 | oncanplaythrough 346 | onseeking 347 | onseeked 348 | ontimeout 349 | ontimeupdate 350 | onended 351 | onratechange 352 | ondurationchange 353 | onvolumechange 354 | onaddtrack 355 | oncontrollerchange 356 | oncuechange 357 | onenter 358 | onexit 359 | onencrypted 360 | onwaitingforkey 361 | onkeystatuseschange 362 | onremovetrack 363 | ondataavailable 364 | onwarning 365 | onstart 366 | onstop 367 | onphoto 368 | onactivestatechanged 369 | ongamepadbuttondown 370 | ongamepadbuttonup 371 | ongamepadaxismove 372 | ongamepadconnected 373 | ongamepaddisconnected 374 | onaudiostart 375 | onaudioend 376 | onsoundstart 377 | onsoundend 378 | onspeechstart 379 | onspeechend 380 | onresult 381 | onnomatch 382 | onresume 383 | onmark 384 | onboundary 385 | onsourceopen 386 | onsourceended 387 | onsourceclosed 388 | onupdatestart 389 | onupdate 390 | onupdateend 391 | onaddsourcebuffer 392 | onremovesourcebuffer 393 | -------------------------------------------------------------------------------- /html5_events/historical/esr52_events.txt: -------------------------------------------------------------------------------- 1 | ona2dpstatuschanged 2 | onabort 3 | onmozaccesskeynotfound 4 | onactivate 5 | onadapteradded 6 | onadapterremoved 7 | onafterprint 8 | onafterscriptexecute 9 | onalerting 10 | onanimationend 11 | onanimationiteration 12 | onanimationstart 13 | onantennaavailablechange 14 | onAppCommand 15 | onappinstalled 16 | onattributechanged 17 | onattributereadreq 18 | onattributewritereq 19 | onaudioprocess 20 | onbeforecopy 21 | onbeforecut 22 | onbeforepaste 23 | onbeforeevicted 24 | onbeforeprint 25 | onbeforescriptexecute 26 | onbeforeunload 27 | onblocked 28 | onblur 29 | onbroadcast 30 | onbusy 31 | onbufferedamountlow 32 | oncached 33 | oncallschanged 34 | oncancel 35 | oncardstatechange 36 | oncfstatechange 37 | onchange 38 | oncharacteristicchanged 39 | onchargingchange 40 | onchargingtimechange 41 | onchecking 42 | onclick 43 | onclirmodechange 44 | onclose 45 | oncommand 46 | oncommandupdate 47 | oncomplete 48 | oncompositionend 49 | oncompositionstart 50 | oncompositionupdate 51 | onconnect 52 | onconnected 53 | onconnecting 54 | onconnectionavailable 55 | onconnectionstatechanged 56 | oncontextmenu 57 | oncopy 58 | oncurrentchannelchanged 59 | oncurrentsourcechanged 60 | oncut 61 | ondatachange 62 | ondataerror 63 | ondblclick 64 | ondeleted 65 | ondeliverysuccess 66 | ondeliveryerror 67 | ondevicefound 68 | ondevicepaired 69 | ondeviceunpaired 70 | ondialing 71 | ondisabled 72 | ondischargingtimechange 73 | ondisconnect 74 | ondisconnected 75 | ondisconnecting 76 | ondisplaypasskeyreq 77 | ondownloading 78 | onDOMActivate 79 | onDOMAttrModified 80 | onDOMCharacterDataModified 81 | onDOMFocusIn 82 | onDOMFocusOut 83 | onDOMMouseScroll 84 | onDOMNodeInserted 85 | onDOMNodeInsertedIntoDocument 86 | onDOMNodeRemoved 87 | onDOMNodeRemovedFromDocument 88 | onDOMSubtreeModified 89 | ondata 90 | ondrag 91 | ondragdrop 92 | ondragend 93 | ondragenter 94 | ondragexit 95 | ondraggesture 96 | ondragleave 97 | ondragover 98 | ondragstart 99 | ondrain 100 | ondrop 101 | oneitbroadcasted 102 | onenabled 103 | onenterpincodereq 104 | onemergencycbmodechange 105 | onerror 106 | onevicted 107 | onfailed 108 | onfetch 109 | onfinish 110 | onfocus 111 | onfocusin 112 | onfocusout 113 | onfrequencychange 114 | onfullscreenchange 115 | onfullscreenerror 116 | onspeakerforcedchange 117 | onget 118 | ongroupchange 119 | onhashchange 120 | onheadphoneschange 121 | onheld 122 | onhfpstatuschanged 123 | onhidstatuschanged 124 | onholding 125 | oniccchange 126 | oniccdetected 127 | oniccinfochange 128 | oniccundetected 129 | onincoming 130 | oninput 131 | oninstall 132 | oninvalid 133 | onkeydown 134 | onkeypress 135 | onkeyup 136 | onlanguagechange 137 | onlevelchange 138 | onLoad 139 | onload 140 | onloading 141 | onloadingdone 142 | onloadingerror 143 | onpopstate 144 | only 145 | onmessage 146 | onmousedown 147 | onmouseenter 148 | onmouseleave 149 | onmouselongtap 150 | onmousemove 151 | onmouseout 152 | onmouseover 153 | onMozMouseHittest 154 | onmouseup 155 | onMozAfterPaint 156 | onmozbrowserafterkeydown 157 | onmozbrowserafterkeyup 158 | onmozbrowserbeforekeydown 159 | onmozbrowserbeforekeyup 160 | onmozfullscreenchange 161 | onmozfullscreenerror 162 | onmozkeydownonplugin 163 | onmozkeyuponplugin 164 | onmozpointerlockchange 165 | onmozpointerlockerror 166 | onmoztimechange 167 | onMozMousePixelScroll 168 | onMozScrolledAreaChanged 169 | onmoznetworkupload 170 | onmoznetworkdownload 171 | onmapfolderlistingreq 172 | onmapmessageslistingreq 173 | onmapgetmessagereq 174 | onmapsetmessagestatusreq 175 | onmapsendmessagereq 176 | onmapmessageupdatereq 177 | onnewrdsgroup 178 | onnotificationclick 179 | onnotificationclose 180 | onnoupdate 181 | onobexpasswordreq 182 | onobsolete 183 | ononline 184 | onoffline 185 | onopen 186 | onorientationchange 187 | onotastatuschange 188 | onoverflow 189 | onoverflowchanged 190 | onpagehide 191 | onpageshow 192 | onpaint 193 | onpairingaborted 194 | onpairingconfirmationreq 195 | onpairingconsentreq 196 | onpaste 197 | onpendingchange 198 | onpichange 199 | onpointerlockchange 200 | onpointerlockerror 201 | onpopuphidden 202 | onpopuphiding 203 | onpopuppositioned 204 | onpopupshowing 205 | onpopupshown 206 | onpullphonebookreq 207 | onpullvcardentryreq 208 | onpullvcardlistingreq 209 | onpush 210 | onpushsubscriptionchange 211 | onpschange 212 | onptychange 213 | onradiostatechange 214 | onrdsdisabled 215 | onrdsenabled 216 | onreaderror 217 | onreadsuccess 218 | onready 219 | onreadystatechange 220 | onreceived 221 | onremoteheld 222 | onremoteresumed 223 | onresourcetimingbufferfull 224 | onretrieving 225 | onRequest 226 | onrequestmediaplaystatus 227 | onreset 228 | onresuming 229 | onresize 230 | onrtchange 231 | onscanningstatechanged 232 | onscostatuschanged 233 | onscroll 234 | onselect 235 | onselectionchange 236 | onselectstart 237 | onsending 238 | onsent 239 | onset 240 | onshow 241 | onstatechange 242 | onstatuschanged 243 | onstkcommand 244 | onstksessionend 245 | onstorage 246 | onstorageareachanged 247 | onsubmit 248 | onsuccess 249 | ontypechange 250 | onterminate 251 | ontext 252 | ontoggle 253 | ontouchstart 254 | ontouchend 255 | ontouchmove 256 | ontouchcancel 257 | ontransitionend 258 | ontransitionrun 259 | ontransitionstart 260 | onunderflow 261 | onunload 262 | onupdatefound 263 | onupdateready 264 | onupgradeneeded 265 | onussdreceived 266 | onversionchange 267 | onvoicechange 268 | onvoiceschanged 269 | onvrdisplayconnect 270 | onvrdisplaydisconnect 271 | onvrdisplaypresentchange 272 | onwebkitAnimationEnd 273 | onwebkitAnimationIteration 274 | onwebkitAnimationStart 275 | onwebkitTransitionEnd 276 | onwebkitanimationend 277 | onwebkitanimationiteration 278 | onwebkitanimationstart 279 | onwebkittransitionend 280 | onwebsocket 281 | onwheel 282 | onreloadpage 283 | onSVGLoad 284 | onSVGResize 285 | onSVGScroll 286 | onSVGUnload 287 | onSVGZoom 288 | onzoom 289 | onbegin 290 | onbeginEvent 291 | onend 292 | onendEvent 293 | onrepeat 294 | onrepeatEvent 295 | onMozSwipeGestureMayStart 296 | onMozSwipeGestureStart 297 | onMozSwipeGestureUpdate 298 | onMozSwipeGestureEnd 299 | onMozSwipeGesture 300 | onMozMagnifyGestureStart 301 | onMozMagnifyGestureUpdate 302 | onMozMagnifyGesture 303 | onMozRotateGestureStart 304 | onMozRotateGestureUpdate 305 | onMozRotateGesture 306 | onMozTapGesture 307 | onMozPressTapGesture 308 | onMozEdgeUIStarted 309 | onMozEdgeUICanceled 310 | onMozEdgeUICompleted 311 | onpointerdown 312 | onpointermove 313 | onpointerup 314 | onpointercancel 315 | onpointerover 316 | onpointerout 317 | onpointerenter 318 | onpointerleave 319 | ongotpointercapture 320 | onlostpointercapture 321 | ondevicemotion 322 | ondeviceorientation 323 | onabsolutedeviceorientation 324 | ondeviceproximity 325 | onmozorientationchange 326 | onuserproximity 327 | ondevicelight 328 | onmozinterruptbegin 329 | onmozinterruptend 330 | ondevicechange 331 | onloadend 332 | onloadstart 333 | onprogress 334 | onsuspend 335 | onemptied 336 | onstalled 337 | onplay 338 | onpause 339 | onloadedmetadata 340 | onloadeddata 341 | onwaiting 342 | onplaying 343 | oncanplay 344 | oncanplaythrough 345 | onseeking 346 | onseeked 347 | ontimeout 348 | ontimeupdate 349 | onended 350 | onratechange 351 | ondurationchange 352 | onvolumechange 353 | onaddtrack 354 | oncontrollerchange 355 | oncuechange 356 | onenter 357 | onexit 358 | onencrypted 359 | onwaitingforkey 360 | onkeystatuseschange 361 | onremovetrack 362 | ondataavailable 363 | onwarning 364 | onstart 365 | onstop 366 | onphoto 367 | onactivestatechanged 368 | ongamepadbuttondown 369 | ongamepadbuttonup 370 | ongamepadaxismove 371 | ongamepadconnected 372 | ongamepaddisconnected 373 | onaudiostart 374 | onaudioend 375 | onsoundstart 376 | onsoundend 377 | onspeechstart 378 | onspeechend 379 | onresult 380 | onnomatch 381 | onresume 382 | onmark 383 | onboundary 384 | onsourceopen 385 | onsourceended 386 | onsourceclosed 387 | onupdatestart 388 | onupdate 389 | onupdateend 390 | onaddsourcebuffer 391 | onremovesourcebuffer 392 | -------------------------------------------------------------------------------- /html5_events/html5_events.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # Copyright (C) 2005-2024 Giorgio Maone 4 | # 5 | # SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | use strict; 8 | 9 | require LWP::UserAgent; 10 | use LWP::Simple; 11 | use Regexp::List; 12 | use File::stat; 13 | use File::Basename; 14 | use List::MoreUtils qw(uniq); 15 | 16 | my $HTML_ATOMS_URL = "https://hg.mozilla.org/mozilla-central/raw-file/tip/xpcom/ds/StaticAtoms.py"; 17 | 18 | my $HERE = dirname($0); 19 | my $SOURCE_FILE = "$HERE/../src/xss/InjectionChecker.js"; 20 | 21 | sub create_re 22 | { 23 | my $cache = "$HERE/html5_events.re"; 24 | my $archive = "$HERE/html5_events_archive.txt"; 25 | 26 | my $sb = stat($cache); 27 | 28 | if ($sb && time() - $sb->mtime < 86400) 29 | { 30 | open IN, "<$cache"; 31 | my @content = ; 32 | close IN; 33 | return $content[0]; 34 | } 35 | 36 | sub fetch_url 37 | { 38 | my $url = shift(@_); 39 | my $ua = LWP::UserAgent->new; 40 | $ua->timeout(5); 41 | $ua->agent('Mozilla/5.0'); 42 | $ua->ssl_opts('verify_hostname' => 0); 43 | my $res = $ua->get($url); 44 | if ($res->is_success) 45 | { 46 | return $res->decoded_content; 47 | } 48 | else 49 | { 50 | my $err = $res->content; 51 | my $ca_file = $ua->ssl_opts('SSL_ca_file'); 52 | die ("Could not fetch $url: $err\n$ca_file"); 53 | } 54 | } 55 | 56 | 57 | my $content = fetch_url($HTML_ATOMS_URL); 58 | 59 | $content = join("\n", grep(/^\s*Atom\("on\w+"/, split(/[\n\r]/, $content))); 60 | 61 | $content =~ s/.*"(on\w+)".*/$1 /g; 62 | 63 | open IN, "<$archive"; 64 | my @archived = ; 65 | close IN; 66 | 67 | $content .= join("\n", @archived); 68 | 69 | $content =~ s/\s+/\n/g; 70 | $content =~ s/^\s+|\s+$//g; 71 | 72 | my @all_events = grep(!/^only$/, uniq(split("\n", $content))); 73 | 74 | open (OUT, ">$archive"); 75 | print OUT join("\n", @all_events); 76 | close OUT; 77 | 78 | my $l = Regexp::List->new; 79 | my $re = $l->list2re(@all_events); 80 | $re =~ s/\(\?[-^]\w+:(.*)\)/$1/; 81 | 82 | open (OUT, ">$cache"); 83 | print OUT $re; 84 | close OUT; 85 | 86 | $re; 87 | } 88 | 89 | sub patch 90 | { 91 | my $src = shift; 92 | my $dst = "$src.tmp"; 93 | my $re = create_re(); 94 | my $must_replace = 0; 95 | print "Patching $src...\n"; 96 | open IN, "<$src" or die ("Can't open $src!"); 97 | open OUT, ">$dst" or die ("Can't open $dst!"); 98 | 99 | while () 100 | { 101 | my $line = $_; 102 | $must_replace = $line ne $_ if s/^(\s*const IC_EVENT_PATTERN\s*=\s*")([^"]+)/$1$re/; 103 | 104 | print OUT $_; 105 | } 106 | close IN; 107 | close OUT; 108 | 109 | if ($must_replace) { 110 | rename $dst, $src; 111 | print "Patched.\n"; 112 | return 0; 113 | } 114 | 115 | unlink $dst; 116 | print "Nothing to do.\n"; 117 | return 1; 118 | } 119 | 120 | exit(patch($SOURCE_FILE)); 121 | -------------------------------------------------------------------------------- /html5_events/html5_events.re: -------------------------------------------------------------------------------- 1 | on(?:m(?:o(?:z(?:browser(?:beforekey(?:down|up)|afterkey(?:down|up))|(?:network(?:down|up)loa|accesskeynotfoun)d|showdropdown(?:_sourcetouch)?|pointerlock(?:change|error)|(?:orientation|time)change|fullscreen(?:change|error)|visual(?:resize|scroll)|interrupt(?:begin|end)|key(?:down|up)onplugin)|use(?:l(?:ongtap|eave)|o(?:ver|ut)|enter|wheel|down|move|up))|a(?:p(?:se(?:tmessagestatus|ndmessage)|message(?:slisting|update)|folderlisting|getmessage)req|rk)|e(?:rchantvalidation|ssage(?:error)?|tadatachange)|(?:idimessag|ut)e)|p(?:o(?:inter(?:l(?:ock(?:change|error)|eave)|o(?:ver|ut)|cancel|enter|down|move|up)|p(?:up(?:hid(?:den|ing)|show(?:ing|n)|positioned)|state)|sitionstatechange)|a(?:i(?:ring(?:con(?:firmation|sent)req|aborted)|nt)|(?:y(?:mentmethod|erdetail)chang|st|us)e|ge(?:hide|show))|u(?:ll(?:vcard(?:listing|entry)|phonebook)req|sh(?:subscriptionchange)?)|r(?:i(?:ntPreviewUpdat|oritychang)e|o(?:cessorerror|gress))|lay(?:backstatechange|ing)?|(?:[is]|ending|ty)change|hoto)|Moz(?:DOM(?:Fullscreen_(?:E(?:xit(?:ed)?|ntered)|NewOrigin|Request)|PointerLock_E(?:nter|xit)ed)|S(?:wipeGesture(?:(?:May)?Start|Update|End)?|(?:essionStorage|crolledArea)Changed)|M(?:ouse(?:ExploreByTouch|PixelScroll|Hittest)|agnifyGesture(?:Update|Start)?)|(?:EdgeUI(?:C(?:omplet|ancel)|Start)|LocalStorageChang)ed|(?:T(?:ogglePictureInPic|apGes)|PressTapGes)ture|A(?:pplicationManifes|fterPain)t|RotateGesture(?:Update|Start)?|OpenDateTimePicker|InvalidForm)|s(?:t(?:a(?:t(?:uschanged|echange)|lled|rt)|o(?:rage(?:areachanged)?|p)|k(?:sessione|comma)nd)|e(?:lect(?:ionchange|start|end)?|curitypolicyviolation|ek(?:ing|ed)|n(?:ding|t)|t)|c(?:(?:anningstate|ostatus)changed|roll(?:end)?)|ou(?:rce(?:closed?|ended|open)|nd(?:start|end))|pe(?:akerforcedchange|ech(?:start|end))|u(?:pportedkeyschange|ccess|spend|bmit)|h(?:ipping(?:address|option)change|ow)|queeze(?:start|end)?|ystemstatusbarclick|lotchange)|DOM(?:(?:C(?:haracterDataModifi|ontentLoad)|Link(?:Chang|Add)|DocElementInsert|InputPasswordAdd|HeadElementPars|SubtreeModifi|PopupBlock|TitleChang)ed|F(?:o(?:rm(?:BeforeSubmit|HasPassword)|cus(?:Out|In))|rameContentLoaded)|Node(?:Inserted(?:IntoDocument)?|Removed(?:FromDocument)?)|M(?:eta(?:Chang|Remov|Add)ed|ouseScroll)|A(?:(?:utoComple|ctiva)te|ttrModified)|Window(?:C(?:reated|lose)|Focus))|c(?:o(?:n(?:nect(?:i(?:on(?:statechanged|available)|ng)|ed)?|t(?:ext(?:restored|lost|menu)|rollerchange))|m(?:p(?:osition(?:update|start|end)|lete)|mand(?:update)?)|py)|h(?:a(?:r(?:ging(?:time)?change|acteristicchanged)|nge)|ecking)|a(?:n(?:play(?:through)?|cel)|(?:llschang|ch)ed|rdstatechange)|u(?:rrent(?:channel|source)changed|echange|t)|l(?:i(?:rmodechange|ck)|ose)|fstatechange)|d(?:e(?:vice(?:orientation(?:absolute)?|p(?:roximity|aired)|(?:unpaire|foun)d|change|motion|light)|l(?:ivery(?:success|error)|eted)|activated)|i(?:s(?:c(?:hargingtimechange|onnect(?:ing|ed)?)|playpasskeyreq|abled)|aling)|r(?:a(?:g(?:e(?:n(?:ter|d)|xit)|(?:gestur|leav)e|start|drop|over)?|in)|op)|ata(?:(?:availabl|chang)e|error)?|urationchange|ownloading|blclick)|a(?:n(?:imation(?:iteration|cancel|start|end)|tennaavailablechange)|d(?:d(?:sourcebuffer|track)|apter(?:remov|add)ed)|ttribute(?:(?:write|read)req|changed)|u(?:dio(?:process|start|end)|xclick)|b(?:solutedeviceorientation|ort)|(?:2dpstatuschang|ppinstall)ed|ctiv(?:estatechanged|ated?)|fter(?:scriptexecute|print)|lerting)|r(?:e(?:s(?:ourcetimingbufferfull|u(?:m(?:ing|e)|lt)|ponseprogress|ize|et)|mo(?:ve(?:sourcebuffer|track)?|te(?:resume|hel)d)|ad(?:y(?:statechange)?|success|error)|quest(?:mediaplaystatu|progres)s|(?:jectionhandl|ceiv)ed|pea(?:tEven)?t|loadpage|trieving)|(?:(?:adiost)?ate|t)change|ds(?:dis|en)abled)|b(?:e(?:fore(?:(?:evicte|unloa)d|p(?:aste|rint)|scriptexecute|c(?:opy|ut)|input)|gin(?:Event)?)|oun(?:d(?:schange|ary)|ce)|u(?:fferedamountlow|sy)|l(?:ocked|ur)|roadcast)|w(?:eb(?:kit(?:Animation(?:Iteration|Start|End)|animation(?:iteration|start|end)|(?:TransitionE|transitione)nd)|socket)|a(?:iting(?:forkey)?|rning)|heel)|v(?:rdisplay(?:(?:presentchang|activat)e|d(?:eactivate|isconnect)|connect)|o(?:ice(?:schanged|change)|lumechange)|(?:isibility|ersion)change)|u(?:n(?:handledrejection|capturederror|derflow|load|mute)|p(?:date(?:(?:fou|e)nd|ready|start)?|gradeneeded)|s(?:erproximity|sdreceived))|t(?:o(?:uch(?:cancel|start|move|end)|(?:nechang|ggl)e)|ransition(?:cancel|start|end|run)|ime(?:update|out)|e(?:rminate|xt)|ypechange)|e(?:n(?:ter(?:pincodereq)?|(?:crypt|abl)ed|d(?:Event|ed)?)|m(?:ergencycbmodechange|ptied)|(?:itbroadcas|vic)ted|rror|xit)|l(?:o(?:ad(?:e(?:d(?:meta)?data|nd)|ing(?:error|done)?|start)?|stpointercapture)|(?:anguage|evel)change)|o(?:(?:(?:rientation|tastatus)chang|(?:ff|n)lin)e|b(?:expasswordreq|solete)|verflow(?:changed)?|pen)|g(?:amepad(?:(?:dis)?connected|button(?:down|up)|axismove)|(?:otpointercaptur|roupchang)e|et)|f(?:o(?:cus(?:out|in)?|rmdata)|ullscreen(?:change|error)|requencychange|(?:inis|etc)h|ailed)|i(?:n(?:put(?:sourceschange)?|coming|stall|valid)|cc(?:(?:info)?change|(?:un)?detected))|P(?:lugin(?:(?:BindingAttac|Cras)h|(?:Instanti|Outd)at|Remov)ed|rintingError)|U(?:AWidget(?:SetupOrChange|Teardown)|nselectedTabHover_(?:Dis|En)able)|h(?:(?:fp|id)statuschanged|e(?:adphoneschange|ld)|ashchange|olding)|(?:(?:GloballyAutoplayBlock|ImageContentLoad)e|AppComman|Loa)d|n(?:o(?:tificationcl(?:ick|ose)|update|match)|ewrdsgroup)|Check(?:KeyPressEventModel|boxStateChange)|SVG(?:(?:Unl|L)oad|Resize|Scroll|Zoom)|key(?:statuseschange|press|down|up)|R(?:adioStateChange|equest)|ZoomChangeUsingMouseWheel|(?:Full|Text)ZoomChange|HiddenPlugin|zoom) -------------------------------------------------------------------------------- /manifest.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript Commons Library 3 | * Reusable building blocks for cross-browser security/privacy WebExtensions. 4 | * Copyright (C) 2020-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | "use strict"; 22 | 23 | const fs = require('fs'); 24 | const path = require('path'); 25 | const args = process.argv.slice(2); 26 | const MANIFEST_VER = /^m?v?[2-3]/i.test(args[0]) ? args.shift() : "mv3edge"; 27 | const MANIFEST_SRC = args[0] || "src/manifest.json"; 28 | const MANIFEST_DEST = args[1] || args[0] || "build/manifest.json"; 29 | 30 | 31 | console.log(`${MANIFEST_SRC} --[${MANIFEST_VER}]--> ${MANIFEST_DEST}`); 32 | 33 | const srcContent = fs.readFileSync(MANIFEST_SRC, 'utf8'); 34 | const json = JSON.parse(srcContent); 35 | const permissions = new Set(json.permissions); 36 | 37 | let extVer = json.version; 38 | const FIREFOX_UPDATE_URL = "https://secure.informaction.com/update/?v=" + extVer; 39 | const EDGE_UPDATE_URL = "https://edge.microsoft.com/extensionwebstorebase/v1/crx"; 40 | 41 | const isFirefox = MANIFEST_VER.includes("firefox"); 42 | 43 | if (isFirefox && /rc|\.9\d{2}$/.test(extVer)) { 44 | json.browser_specific_settings.gecko.update_url = FIREFOX_UPDATE_URL; 45 | } 46 | 47 | if (MANIFEST_VER.includes(3)) { 48 | // MV3 49 | json.manifest_version = 3; 50 | if (!isFirefox) { 51 | // convert ${ver}(a|b|rc)xx into ${ver--}.9xx 52 | json.version = extVer.replace(/(\d+)(?:\.0)*[a-z]+(\d+)$/, 53 | (all, maj, min) => `${parseInt(maj) - 1}.${900 + parseInt(min)}`); 54 | delete json.browser_specific_settings; 55 | delete json.content_security_policy; 56 | const {scripts} = json.background; 57 | delete json.background.scripts; 58 | delete json.background.persistent; 59 | const requiredPath = path.join(path.dirname(MANIFEST_DEST), "REQUIRED.js"); 60 | scripts && fs.writeFileSync(requiredPath, 61 | `include.REQUIRED = ${JSON.stringify(scripts, null, 2)};`) 62 | } 63 | 64 | if (MANIFEST_VER.includes("edge")) { 65 | json.update_url = EDGE_UPDATE_URL; 66 | } else if (json.update_url === EDGE_UPDATE_URL) { 67 | delete json.update_url; 68 | } 69 | 70 | for (const p of [ 71 | "", 72 | "webRequestBlocking", 73 | "webRequestBlocking", 74 | "webRequestFilterResponse", 75 | "webRequestFilterResponse.serviceWorkerScript", 76 | ]) { 77 | permissions.delete(p); 78 | } 79 | 80 | const excludedScriptsRx = /\bcontent\/(?:embeddingDocument|dirindex)\.js$/; 81 | for (const cs of json.content_scripts) { 82 | cs.js = cs.js.filter(src => !excludedScriptsRx.test(src)); 83 | } 84 | delete json.browser_action; 85 | delete json.commands._execute_browser_action; 86 | } else { 87 | // MV2 88 | json.manifest_version = 2; 89 | delete json.background.service_worker; 90 | delete json.web_accessible_resources; 91 | delete json.host_permissions; 92 | delete json.action; 93 | for (const p of [ 94 | "debugger", 95 | "declarativeNetRequest", 96 | "declarativeNetRequestFeedback", 97 | ]) { 98 | permissions.delete(p); 99 | } 100 | 101 | // Append MAIN world "*.main.js" scripts to their isolated counterparts 102 | // (on Gecko we will patch windows through xray) 103 | const isolatedWorldJS = json.content_scripts.find( 104 | cs => cs.world != "MAIN" && cs.js?.some(src => src.endsWith("/Worlds.js"))).js; 105 | 106 | json.content_scripts.find(cs => cs.world == "MAIN" && cs.js?.some(src => src.endsWith("/Worlds.main.js"))) 107 | .js.filter(src => src.endsWith(".main.js")) 108 | .forEach(src => { 109 | const isolatedSrc = src.replace(/.*(\/[\w+.]+)\.main(?=\.js$)/, "$1"); 110 | const idx = isolatedWorldJS.findIndex(src => src.endsWith(isolatedSrc)); 111 | if (idx > -1) { 112 | isolatedWorldJS.splice(idx + 1, 0, src) 113 | } else { 114 | isolatedWorldJS.push(src); 115 | } 116 | }); 117 | 118 | // remove all the MAIN world content script 119 | json.content_scripts = json.content_scripts.filter(cs => cs.world != "MAIN"); 120 | 121 | // match_origin_as_fallback is MV3 only 122 | json.content_scripts.forEach(cs => delete cs.match_origin_as_fallback); 123 | } 124 | 125 | // remove developer-only stuff 126 | permissions.delete("declarativeNetRequestFeedback"); 127 | 128 | json.permissions = [...permissions]; 129 | 130 | const destContent = JSON.stringify(json, null, 2); 131 | fs.writeFileSync(MANIFEST_DEST, destContent); 132 | console.log(`Written ${MANIFEST_DEST}`); 133 | -------------------------------------------------------------------------------- /manifest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | SRC=src/ 3 | if [[ $1 == "2" ]]; then 4 | mv=2 5 | min=128 6 | else 7 | mv=3 8 | min=666 9 | fi 10 | sed -i -re 's/("manifest_version":\s*)[0-9]/\1'$mv'/' -e 's/("strict_min_version":\s*")[0-9]+/\1'$min'/' "$SRC/manifest.json" -------------------------------------------------------------------------------- /nscl_gitsync.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | BASE="$(dirname "$0")" 3 | NSCL_PATH="$BASE/src/nscl" 4 | commit_range=$(git diff "$NSCL_PATH" | grep 'Subproject commit' | sed -r -e's/\+.* /../' -e's/.*commit //' | tr -d '\n') 5 | if ! [[ $commit_range ]]; then 6 | echo >&2 "nscl commits already in sync." 7 | exit 1 8 | fi 9 | pushd "$NSCL_PATH" 10 | git log --oneline "$commit_range" 11 | if ! git push ; then 12 | popd 13 | exit 1 14 | fi 15 | popd 16 | git commit -m'[nscl] Updated to latest NoScript Commons Library.' "$NSCL_PATH" 17 | 18 | -------------------------------------------------------------------------------- /reuse.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (C) 2021-2024 Giorgio Maone 4 | # 5 | # SPDX-License-Identifier: GPL-3.0-or-later 6 | 7 | files=$(reuse lint | grep -A1000 '# MISSING ' | grep -B1000 '# SUMMARY' | grep -e '^* ' | cut -c3-) 8 | YEAR=2005-$(date +%Y) 9 | COPY="Giorgio Maone " 10 | CSTYLE="string-c" 11 | LIC="GPL-3.0-or-later" 12 | JS_FILES=$(echo "$files" | grep -ve '\.js$') 13 | NON_JS_FILES=$(echo "$files" | grep -e '\.js$') 14 | if [[ "$JS_FILES" || "$NON_JS_FILES" ]]; then 15 | [[ "$JS_FILES" ]] && reuse addheader --year "$YEAR" --copyright-style "$CSTYLE" --copyright "$COPY" --license "$LIC" $JS_FILES 16 | [[ "$NON_JS_FILES" ]] && reuse addheader --year "$YEAR" --copyright-style "$CSTYLE" --copyright "$COPY" --license "$LIC" --template 'fsf-js' $NON_JS_FILES 17 | fi 18 | reuse lint -------------------------------------------------------------------------------- /src/bg/Defaults.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | 'use strict'; 22 | 23 | var Defaults = { 24 | async init() { 25 | let defaults = { 26 | local: { 27 | debug: false, 28 | showCtxMenuItem: true, 29 | showCountBadge: true, 30 | showFullAddresses: false, 31 | showProbePlaceholders: true, 32 | amnesticUpdates: false, 33 | }, 34 | sync: { 35 | global: false, 36 | xss: true, 37 | TabGuardMode: "incognito", 38 | TabGuardPrompt: "post", 39 | cascadeRestrictions : false, 40 | overrideTorBrowserPolicy: false, 41 | } 42 | }; 43 | 44 | const defaultsClone = JSON.parse(JSON.stringify(defaults)); 45 | 46 | for (let [k, v] of Object.entries(defaults)) { 47 | let store = await Storage.get(k, k); 48 | if (k in store) { 49 | Object.assign(v, store[k]); 50 | } 51 | v.storage = k; 52 | } 53 | 54 | Object.assign(ns, defaults); 55 | 56 | // dynamic settings 57 | if (!ns.local.uuid) { 58 | ns.local.uuid = uuid(); 59 | await ns.save(ns.local); 60 | } 61 | 62 | return ns.defaults = defaultsClone; 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /src/bg/ReportingCSP.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | "use strict"; 22 | 23 | function ReportingCSP(marker) { 24 | 25 | return Object.assign( 26 | new CapsCSP(new NetCSP(marker)), 27 | { 28 | patchHeaders(responseHeaders, capabilities) { 29 | let header = null; 30 | let blocker; 31 | if (capabilities) { 32 | let contentType = responseHeaders.filter(h => h.name.toLowerCase() === "content-type"); 33 | let blockHTTP = contentType.length === 0 || contentType.some(h => !/^(?:text|application)\/\S*\b(?:x?ht|x)ml\b/i.test(h.value)); 34 | blocker = this.buildFromCapabilities(capabilities, blockHTTP); 35 | } 36 | let extras = []; 37 | responseHeaders.forEach((h, index) => { 38 | if (this.isMine(h)) { 39 | header = h; 40 | if (h.value === blocker) { 41 | // make this equivalent but different than the original, otherwise 42 | // it won't be (re)set when deleted, see 43 | // https://dxr.mozilla.org/mozilla-central/rev/882de07e4cbe31a0617d1ae350236123dfdbe17f/toolkit/components/extensions/webrequest/WebRequest.jsm#138 44 | blocker += " "; 45 | } else { 46 | extras.push(...this.unmergeExtras(h)); 47 | } 48 | responseHeaders.splice(index, 1); 49 | } else if (blocker && /^(Location|Refresh)$/i.test(h.name)) { 50 | // neutralize any HTTP redirection to data: URLs, like Chromium 51 | let url = /^R/i.test(h.name) 52 | ? h.value.replace(/^[^,;]*[,;](?:\W*url[^=]*=)?[^!#$%&()*+,/:;=?@[\]\w.,~-]*/i, "") : h.value; 53 | if (/^data:/i.test(url)) { 54 | h.value = h.value.slice(0, -url.length) + "data:"; 55 | } 56 | } 57 | }); 58 | 59 | if (blocker) { 60 | header = this.asHeader(blocker); 61 | responseHeaders.push(header); 62 | } 63 | 64 | if (extras.length) { 65 | responseHeaders.push(...extras); 66 | } 67 | 68 | return header; 69 | } 70 | } 71 | ); 72 | } 73 | -------------------------------------------------------------------------------- /src/bg/popupHandler.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | browser.runtime.onConnect.addListener(port => { 22 | if (port.name === "noscript.popup") { 23 | console.debug("Creating popupHandler port"); // DEV_ONLY 24 | ns.popupOpened = true; 25 | let pendingReload = false; 26 | let tabId = -1; 27 | port.onMessage.addListener(m => { 28 | console.debug("popupHandler message", m); // DEV_ONLY 29 | if ("pendingReload" in m) { 30 | tabId = m.tabId; 31 | pendingReload = m.pendingReload; 32 | } 33 | }); 34 | port.onDisconnect.addListener(() => { 35 | ns.popupOpened = false; 36 | if (pendingReload) { 37 | browser.tabs.reload(tabId); 38 | } 39 | }); 40 | } 41 | }); 42 | -------------------------------------------------------------------------------- /src/common/Entities.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | var Entities = { 22 | get htmlNode() { 23 | delete this.htmlNode; 24 | return this.htmlNode = document.implementation.createHTMLDocument("") 25 | .createElement("body"); 26 | }, 27 | convert: function(e) { 28 | try { 29 | this.htmlNode.innerHTML = e; 30 | var child = this.htmlNode.firstChild || null; 31 | return child && child.nodeValue || e; 32 | } catch(ex) { 33 | return e; 34 | } 35 | }, 36 | convertAll: function(s) { 37 | return s.replace(/[\\&][^<>]+/g, function(e) { return Entities.convert(e) }); 38 | }, 39 | convertDeep: function(s) { 40 | for (var prev = null; (s = this.convertAll(s)) !== prev || (s = unescape(s)) !== prev; prev = s); 41 | return s; 42 | }, 43 | neutralize: function(e, whitelist) { 44 | var c = this.convert(e); 45 | return (c == e) ? c : (whitelist && whitelist.test(c) ? e : e.replace(";", ",")); 46 | }, 47 | neutralizeAll: function(s, whitelist) { 48 | return s.replace(/&[\w#-]*?;/g, function(e) { return Entities.neutralize(e, whitelist || null); }); 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /src/common/themes.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005-2024 Giorgio Maone 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | */ 6 | 7 | .__NoScript_Theme__ { 8 | --focus-color-dark: #c35a; 9 | --focus-color-light: #f68a; 10 | 11 | --img-noscript-options: url(/img/noscript-options.png); 12 | --img-ui-revoke-temp: url(/img/ui-revoke-temp64.png); 13 | --img-ui-temp-all: url(/img/ui-temp-all64.png); 14 | --img-ui-tab-no: url(/img/ui-tab-no64.png); 15 | --img-ui-tab: url(/img/ui-tab64.png); 16 | --img-ui-global-no: url(/img/ui-global-no64.png); 17 | --img-ui-global: url(/img/ui-global64.png); 18 | --img-noscript-options: url(/img/noscript-options.png); 19 | --img-ui-close: url(/img/ui-close64.png); 20 | --img-ui-reload: url(/img/ui-reload64.png); 21 | --img-warning: url(/img/warning64.png); 22 | --img-error: url(/img/error64.png); 23 | --img-logo: url(/img/logo.svg); 24 | --img-ui-http: url(/img/ui-http64.png); 25 | --img-ui-https: url(/img/ui-https64.png); 26 | --img-ui-no: url(/img/ui-no64.png); 27 | --img-ui-temp: url(/img/ui-temp64.png); 28 | --img-ui-yes: url(/img/ui-yes64.png); 29 | --img-ui-black: url(/img/ui-black64.png); 30 | --img-ui-custom: url(/img/ui-custom64.png); 31 | --img-ui-clock: url(/img/ui-clock64.png); 32 | 33 | --icon-size: 2.2em; 34 | --line-size: 1.5em; 35 | --popup-size: 600px; 36 | 37 | --bg-preset-color: var(--form-color2); 38 | --bg-odd-row: var(--bg-color2); 39 | --bg-even-row: var(--tab-color1); 40 | --bg-focused-row: linear-gradient(to bottom, var(--focus-color) 0, transparent 10%, transparent 90%, var(--focus-color) 100%); 41 | --border-row-sep: none; 42 | } 43 | 44 | 45 | .__NoScript_Theme__.vintage { 46 | 47 | --focus-color-dark: #35Ca !important; 48 | --focus-color-light: #8cfa !important; 49 | 50 | --img-noscript-options: url(/img/vintage/noscript-options.png); 51 | --img-ui-revoke-temp: url(/img/vintage/ui-revoke-temp64.png); 52 | --img-ui-temp-all: url(/img/vintage/ui-temp-all64.png); 53 | --img-ui-tab-no: url(/img/vintage/ui-tab-no64.png); 54 | --img-ui-tab: url(/img/vintage/ui-tab64.png); 55 | --img-ui-global-no: url(/img/vintage/ui-global-no64.png); 56 | --img-ui-global: url(/img/vintage/ui-global64.png); 57 | --img-noscript-options: url(/img/vintage/noscript-options.png); 58 | --img-ui-close: url(/img/vintage/ui-close64.png); 59 | --img-ui-reload: url(/img/vintage/ui-reload64.png); 60 | --img-warning: url(/img/vintage/warning64.png); 61 | --img-error: url(/img/vintage/error64.png); 62 | --img-logo: url(/img/vintage/logo.svg); 63 | --img-ui-http: url(/img/vintage/ui-http64.png); 64 | --img-ui-https: url(/img/vintage/ui-https64.png); 65 | --img-ui-no: url(/img/vintage/ui-no64.png); 66 | --img-ui-temp: url(/img/vintage/ui-temp64.png); 67 | --img-ui-yes: url(/img/vintage/ui-yes64.png); 68 | --img-ui-black: url(/img/vintage/ui-black64.png); 69 | --img-ui-custom: url(/img/vintage/ui-custom64.png); 70 | --img-ui-clock: url(/img/vintage/ui-clock64.png); 71 | 72 | --icon-size: 2em; 73 | --line-size: 1.5em; 74 | --popup-size: 600px; 75 | 76 | } 77 | 78 | .__NoScript_Theme__, .__NoScript_Theme__[data-theme="dark"] { 79 | color-scheme: dark; 80 | --accent-color: #d12027; 81 | --fg-color1: #ccc; 82 | --fg-color2: #fff; 83 | --text-color: #ddd; 84 | --bg-color1: #000; 85 | --bg-color2: #212026; 86 | --form-color1: #333; 87 | --form-color2: #111; 88 | --focus-color: var(--focus-color-dark); 89 | --tab-color1: #334; 90 | --tab-color2: #3348; 91 | --form-check-color: var(--form-color2); 92 | --form-check-bg-color: var(--accent-color); 93 | --form-radio-color: var(--accent-color); 94 | --form-radio-bg-color: var(--form-color1); 95 | --hilite-color: #8008; 96 | --unsafe-color: var(--accent-color); 97 | } 98 | 99 | .__NoScript_Theme__.vintage:not([data-theme="light"]) :is(input.preset, .icon) { 100 | filter: brightness(85%); 101 | } 102 | 103 | 104 | @media (prefers-color-scheme: light) { 105 | .__NoScript_Theme__ { 106 | color-scheme: light; 107 | --fg-color1: #555; 108 | --fg-color2: #000; 109 | --text-color: #333; 110 | --bg-color1: #ddd; 111 | --bg-color2: #fff; 112 | --form-color1: #ccc; 113 | --form-color2: #eee; 114 | --form-check-color: var(--bg-color2); 115 | --form-check-bg-color: var(--accent-color); 116 | --tab-color1: #d0d8e0; 117 | --tab-color2: #d0d8e088; 118 | --focus-color: var(--focus-color-light); 119 | --hilite-color: #f008; 120 | --unsafe-color: #811; 121 | } 122 | 123 | 124 | .__NoScript_Theme__.vintage:not([data-theme="light"]) :is(input.preset, .icon) { 125 | filter: none; 126 | } 127 | .__NoScript_Theme__.vintage[data-theme="dark"] :is(input.preset, .icon) { 128 | filter: brightness(85%); 129 | } 130 | 131 | } 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /src/common/themes.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | { 22 | let contentCSS; 23 | 24 | const VINTAGE = "vintageTheme"; 25 | const THEMES = ["dark", "light", "auto"]; 26 | 27 | globalThis.Themes = { 28 | VINTAGE, 29 | update() {}, 30 | refreshVintage() {}, 31 | async setup(theme = null) { 32 | if (theme) { 33 | if (browser && browser.storage) { 34 | browser.storage.local.set({theme}); 35 | } 36 | } else { 37 | if (self.localStorage) { 38 | theme = localStorage.getItem("theme"); 39 | if (!THEMES.includes(theme)) theme = null; 40 | } 41 | if (!theme && browser && browser.storage) { 42 | if (self.document?.readyState === "loading") { 43 | document.documentElement.style.visibility = "hidden"; 44 | } 45 | return browser.storage.local.get(["theme"]).then(({theme}) => { 46 | Themes.update(theme); 47 | if (self.document) { 48 | document.documentElement.style.visibility = ""; 49 | } 50 | return theme || "auto"; 51 | }); 52 | } 53 | } 54 | return Themes.update(theme); 55 | }, 56 | 57 | async isVintage() { 58 | let ret; 59 | if (self.localStorage) { 60 | ret = localStorage.getItem(VINTAGE); 61 | if (ret !== null) return !(ret === "false" || !ret); 62 | } 63 | ret = (await browser.storage.local.get([VINTAGE]))[VINTAGE]; 64 | return ret; 65 | }, 66 | 67 | async setVintage(b) { 68 | Themes.refreshVintage(b); 69 | await browser.storage.local.set({[VINTAGE]: b}); 70 | return b; 71 | }, 72 | 73 | async getContentCSS() { 74 | contentCSS ||= (async () => { 75 | const replaceAsync = async (string, regexp, replacerFunction) => { 76 | regexp.lastIndex = 0; 77 | const promises = []; 78 | for (let match; match = regexp.exec(string);) { 79 | promises.push(replacerFunction(...match)); 80 | } 81 | const replacements = await Promise.all(promises); 82 | regexp.lastIndex = 0; 83 | let i = 0; 84 | return string.replace(regexp, () => replacements[i++]); 85 | } 86 | const fetchAsDataURL = async (url) => { 87 | const blob = await (await fetch(browser.runtime.getURL(url))).blob(); 88 | return new Promise((resolve, reject) => { 89 | const reader = new FileReader(); 90 | reader.onload = e => { 91 | resolve(reader.result); 92 | }; 93 | reader.onerror = e => { 94 | reject(reader.error); 95 | }; 96 | reader.readAsDataURL(blob); 97 | }); 98 | } 99 | const fetchAsText = async (url) => await (await fetch(browser.runtime.getURL(url))).text(); 100 | 101 | const themesCSS = (await replaceAsync(await fetchAsText("/common/themes.css"), 102 | /(--img-logo:.*url\("?)(.*\.svg)"?/g, 103 | async (s, prop, url) => `${prop}"${await fetchAsDataURL(url)}"` 104 | )) 105 | .replace(/.*\burl\(\.*\/.*\n/g, '') 106 | .replace(/\/\*[^]*?\*\//g, '') 107 | .replace(/\n+/g, "\n"); 108 | return (await fetchAsText("/content/content.css")) 109 | .replace(/\b(THEMES_START\b.*\n)[^]*(\n.*\bTHEMES_END)\b/g, 110 | `$1${themesCSS}$2`); 111 | })(); 112 | return await contentCSS; 113 | } 114 | }; 115 | 116 | (async () => { 117 | if (self.document) { 118 | await include("/common/themesDOM.js"); 119 | } 120 | await Themes.setup(); 121 | Themes.refreshVintage(await Themes.isVintage()); 122 | })(); 123 | 124 | browser.storage.onChanged.addListener((changes, area) => { 125 | if (area !== "local") return; 126 | const ifChanged = (key, callback) => { 127 | if (key in changes) { 128 | let {oldValue, newValue} = changes[key]; 129 | if (oldValue !== newValue) { 130 | callback(newValue); 131 | self.dispatchEvent(new CustomEvent("NoScriptThemeChanged", {detail: {[key]: newValue}})); 132 | } 133 | } 134 | } 135 | ifChanged("theme", Themes.update); 136 | ifChanged(VINTAGE, Themes.refreshVintage); 137 | }); 138 | } -------------------------------------------------------------------------------- /src/common/themesDOM.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | if (self.document) { 22 | const PARENT_CLASS = "__NoScript_Theme__"; 23 | const patchSheet = s => { 24 | const PARENT_SELECTOR = `.${PARENT_CLASS}`; 25 | const rules = s.cssRules; 26 | for (let j = 0, len = rules.length; j < len; j++) { 27 | const rule = rules[j]; 28 | if (rule.styleSheet && patchSheet(rule.styleSheet)) { 29 | return true; 30 | } 31 | if (rule.conditionText !== "(prefers-color-scheme: light)") continue; 32 | for (let r of rule.cssRules) { 33 | let {selectorText} = r; 34 | if (selectorText.includes("[data-theme=") || !selectorText.startsWith(PARENT_SELECTOR)) continue; 35 | selectorText = selectorText.replace(PARENT_SELECTOR, `${PARENT_SELECTOR}[data-theme="light"]`); 36 | s.insertRule(`${selectorText} {${r.style.cssText}}`, j); 37 | } 38 | return true; 39 | } 40 | return false; 41 | } 42 | 43 | const patchAll = () => { 44 | for (const s of document.styleSheets) { 45 | try { 46 | if (patchSheet(s)) return true; 47 | } catch (e) { 48 | // cross-site stylesheet? 49 | debug(e, s.href); // DEV_ONLY 50 | } 51 | } 52 | return false; 53 | } 54 | 55 | if (!patchAll()) { 56 | debug("Couldn't patch sheets while loading, deferring to onload"); // DEV_ONLY 57 | const onload = e => { 58 | if (patchAll()) { 59 | removeEventListener(e.type, onload, true); 60 | } 61 | } 62 | addEventListener("load", onload, true); 63 | } 64 | 65 | const root = document.documentElement; 66 | root.classList.add(PARENT_CLASS); 67 | 68 | Themes.update = toTheme => { 69 | if (window.localStorage) try { 70 | localStorage.setItem("theme", toTheme); 71 | } catch (e) {} 72 | return root.dataset.theme = toTheme; 73 | } 74 | 75 | const updateFavIcon = isVintage => { 76 | let favIcon = document.querySelector("link[rel=icon]"); 77 | if (!favIcon) return; 78 | let {href} = favIcon; 79 | const BASE = new URL("/img/", location.href); 80 | if (!href.startsWith(BASE)) return alert("return"); 81 | const SUB = BASE + "vintage/"; 82 | let vintageIcon = href.startsWith(SUB); 83 | if (isVintage === vintageIcon) return; 84 | favIcon.href = isVintage ? href.replace(BASE, SUB) : href.replace(SUB, BASE); 85 | } 86 | 87 | Themes.refreshVintage = isVintage => { 88 | if (localStorage) try { 89 | localStorage.setItem(VINTAGE, isVintage || ""); 90 | } catch (e) {} 91 | document.documentElement.classList.toggle("vintage", isVintage === true); 92 | browser?.action?.setIcon({path: {64: `/img${isVintage ? "/vintage/" : "/"}ui-maybe64.png` }}); 93 | updateFavIcon(isVintage); 94 | } 95 | } -------------------------------------------------------------------------------- /src/content/content.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005-2024 Giorgio Maone 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | */ 6 | 7 | /* THEMES_START */ 8 | 9 | /* 10 | This section gets replaced at runtime with an extract of /themes/themes.css 11 | stripped of all the references to extension URLs. 12 | */ 13 | 14 | /* THEMES_END */ 15 | 16 | a.__NoScript_PlaceHolder__ { 17 | outline: 2px solid --var(--accent-color); 18 | color: var(--text-color) !important; 19 | text-decoration: none !important; 20 | text-align: center; 21 | background: var(--tab-color2) no-repeat center; 22 | background-size: contain; 23 | visibility: visible !important; 24 | cursor: pointer; 25 | opacity: 0.8; 26 | z-index: 2147483647 !important; 27 | background-image: var(--img-logo); 28 | border-radius: 4px; 29 | } 30 | 31 | a.__NoScript_PlaceHolder__.no-theme { 32 | visibility: hidden !important; 33 | } 34 | 35 | a.__NoScript_PlaceHolder__.mozilla { 36 | background-image: var(--img-logo); 37 | } 38 | 39 | .__ns__pop2top { 40 | z-index: 2147483647 !important; 41 | } 42 | 43 | .__ns__pop2top.html5-video-container { 44 | display: initial !important; 45 | } 46 | 47 | a.__NoScript_PlaceHolder__:hover { 48 | opacity: 1; 49 | text-decoration: none !important; 50 | } 51 | 52 | a.__NoScript_PlaceHolder__.__ns__closing { 53 | transition: .4s all; 54 | opacity: 0; 55 | transform: scale(0, 0); 56 | } 57 | 58 | a.__NoScript_PlaceHolder__ > span { 59 | display: flex !important; 60 | flex-direction: row; 61 | justify-content: space-around; 62 | align-items: center; 63 | position: relative; 64 | padding: 0; 65 | margin: 0; 66 | width: 100%; 67 | height: 100%; 68 | } 69 | 70 | .__NoScript_PlaceHolder__ button { 71 | appearance: none; 72 | -moz-appearance: none; 73 | border: none; 74 | position: absolute; 75 | top: 2px; 76 | right: 2px; 77 | display: block; 78 | color: var(--accent-color); 79 | font-size: 24px; 80 | font-family: sans-serif; 81 | font-weight: bold; 82 | padding: 0 4px; 83 | margin: 0; 84 | background: none; 85 | transition: .2s all; 86 | } 87 | .__NoScript_PlaceHolder__ button:hover { 88 | color: var(--fg-color2); 89 | text-shadow: 0 0 4px var(--focus-color); 90 | cursor: pointer; 91 | } 92 | 93 | .__NoScript_PlaceHolder__ > span > span { 94 | display: block; 95 | font-size: 18px; 96 | border-radius: 8px; 97 | padding: 8px; 98 | margin: 0; 99 | font-family: sans-serif; 100 | overflow-wrap: break-word; 101 | word-break: break-all; 102 | background-color: var(--tab-color1); 103 | background-color: rgba(from var(--tab-color1) r g b / 85%); 104 | } 105 | .__NoScript_PlaceHolder__:hover > span > span { 106 | background-color: var(--bg-color2); 107 | color: var(--accent-color); 108 | box-shadow: 0 0 4px 4px var(--focus-color); 109 | border: none; 110 | } 111 | a.__NoScript_PlaceHolder__.__ns__document { 112 | position: fixed !important; 113 | top: 0 !important; 114 | bottom: 0 !important; 115 | left: 0 !important; 116 | right: 0 !important; 117 | width: 100% !important; 118 | height: 100% !important; 119 | margin: 0 !important; 120 | } 121 | .__NoScript_Offscreen_PlaceHolders__ { 122 | display: flex; 123 | flex-direction: row-reverse; 124 | position: fixed; 125 | bottom: 0; 126 | right: 0; 127 | } 128 | .__NoScript_Offscreen_PlaceHolders__ a.__NoScript_PlaceHolder__ { 129 | height: 64px !important; 130 | width: auto !important; 131 | } 132 | .__NoScript_Offscreen_PlaceHolders__ a.__NoScript_PlaceHolder__ > span { 133 | align-items: bottom; 134 | } -------------------------------------------------------------------------------- /src/content/dirindex.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | if (FILE_OR_FTP && UA.isMozilla) (() => { 22 | // see https://searchfox.org/mozilla-central/rev/76c1ff5f0de23366fe952ab228610ee695a56e68/netwerk/streamconv/converters/nsIndexedToHTML.cpp#334 23 | 'use strict'; 24 | var gTable, gOrderBy, gTBody, gRows, gUI_showHidden; 25 | document.addEventListener("DOMContentLoaded", function() { 26 | if ("gUI_showHidden" in window.wrappedJSObject || // scripts are enabled 27 | !(document.scripts[0] && 28 | /\bgUI_showHidden\b/.test(document.scripts[0].textContent)) // not a FTP / file:// dir listing 29 | ) { 30 | return; 31 | } 32 | 33 | gTable = document.getElementsByTagName("table")[0]; 34 | gTBody = gTable.tBodies[0]; 35 | if (gTBody.rows.length < 2) 36 | return; 37 | gUI_showHidden = document.getElementById("UI_showHidden") 38 | var headCells = gTable.tHead.rows[0].cells, 39 | hiddenObjects = false; 40 | function rowAction(i) { 41 | return function(event) { 42 | event.preventDefault(); 43 | orderBy(i); 44 | } 45 | } 46 | for (var i = headCells.length - 1; i >= 0; i--) { 47 | var anchor = document.createElement("a"); 48 | anchor.href = ""; 49 | anchor.appendChild(headCells[i].firstChild); 50 | headCells[i].appendChild(anchor); 51 | headCells[i].addEventListener("click", rowAction(i), true); 52 | } 53 | if (gUI_showHidden) { 54 | gRows = Array.from(gTBody.rows); 55 | hiddenObjects = gRows.some(row => row.className == "hidden-object"); 56 | } 57 | gTable.setAttribute("order", ""); 58 | if (hiddenObjects) { 59 | gUI_showHidden.style.display = "block"; 60 | updateHidden(); 61 | } 62 | }, "false"); 63 | function compareRows(rowA, rowB) { 64 | var a = rowA.cells[gOrderBy].getAttribute("sortable-data") || ""; 65 | var b = rowB.cells[gOrderBy].getAttribute("sortable-data") || ""; 66 | var intA = +a; 67 | var intB = +b; 68 | if (a == intA && b == intB) { 69 | a = intA; 70 | b = intB; 71 | } else { 72 | a = a.toLowerCase(); 73 | b = b.toLowerCase(); 74 | } 75 | if (a < b) 76 | return -1; 77 | if (a > b) 78 | return 1; 79 | return 0; 80 | } 81 | function orderBy(column) { 82 | if (!gRows) 83 | gRows = Array.from(gTBody.rows); 84 | var order; 85 | if (gOrderBy == column) { 86 | order = gTable.getAttribute("order") == "asc" ? "desc" : "asc"; 87 | } else { 88 | order = "asc"; 89 | gOrderBy = column; 90 | gTable.setAttribute("order-by", column); 91 | gRows.sort(compareRows); 92 | } 93 | gTable.removeChild(gTBody); 94 | gTable.setAttribute("order", order); 95 | if (order == "asc") 96 | for (var i = 0; i < gRows.length; i++) 97 | gTBody.appendChild(gRows[i]); 98 | else 99 | for (var i = gRows.length - 1; i >= 0; i--) 100 | gTBody.appendChild(gRows[i]); 101 | gTable.appendChild(gTBody); 102 | } 103 | function updateHidden() { 104 | gTable.className = gUI_showHidden.getElementsByTagName("input")[0].checked ? 105 | "" : 106 | "remove-hidden"; 107 | } 108 | })(); 109 | -------------------------------------------------------------------------------- /src/content/embeddingDocument.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | if (ns.embeddingDocument) { 22 | let suspended; 23 | let suspender = new MutationObserver(records => { 24 | suspended = document.body && document.body.firstElementChild; 25 | if (suspended) { 26 | debug("Suspending ", suspended); 27 | suspended.autoplay = false; 28 | if (suspended.pause) suspended.pause(); 29 | suspended.src = suspended.currentSrc = "data:"; 30 | if (ns.policy) replace(); 31 | } 32 | }); 33 | suspender.observe(document, {childList: true, subtree: true}); 34 | 35 | let replace = () => { 36 | if (suspended) { 37 | debug("Restoring suspended", suspended); 38 | suspended.src = suspended.currentSrc = document.URL; 39 | } 40 | 41 | for (let policyType of ["object", "media"]) { 42 | let request = { 43 | id: `noscript-${policyType}-doc`, 44 | type: policyType, 45 | url: document.URL, 46 | documentUrl: document.URL, 47 | embeddingDocument: true, 48 | }; 49 | 50 | if (ns.allows(policyType)) { 51 | suspender.disconnect(); 52 | if (suspended) { 53 | suspended.autoplay = true; 54 | if (suspended.play) suspended.play(); 55 | } 56 | let handler = PlaceHolder.handlerFor(policyType); 57 | if (handler && handler.selectFor(request).length > 0) { 58 | seen.record({policyType, request, allowed: true}); 59 | } 60 | } else { 61 | let ph = PlaceHolder.create(policyType, request); 62 | if (ph.replacements.size > 0) { 63 | debug(`Created placeholder for ${policyType} at ${document.URL}`); 64 | seen.record({policyType, request, allowed: false}); 65 | } 66 | } 67 | } 68 | }; 69 | 70 | ns.on("capabilities", () => { 71 | if (!(document.body && document.body.firstChild)) { // we've been called early 72 | setTimeout(replace, 0); 73 | let types = { 74 | // Reminder: order is important because media matches also for 75 | // some /^application\// types 76 | "media": /^(?:(?:video|audio)\/|application\/(?:ogg|mp4|mpeg)$)/i, 77 | "object": /^application\//i, 78 | } 79 | for (let [type, rx] of Object.entries(types)) { 80 | if (rx.test(document.contentType)) { 81 | if (!ns.allows(type)) { 82 | window.stop(); 83 | } 84 | break; 85 | } 86 | } 87 | } else { 88 | replace(); 89 | } 90 | }); 91 | } 92 | -------------------------------------------------------------------------------- /src/content/eventsHook.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2025 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | 'use strict'; 22 | 23 | if (location.protocol == "file:") { 24 | // Regard as "mutually safe" directories sharing a common ancestor at least this deep 25 | const SAFE_PATH_DEPTH = 5; 26 | const safePath = path => path.split("/", SAFE_PATH_DEPTH).join("/"); 27 | const toDir = url => url.replace(/[^\/]+$/, "") 28 | const CURRENT_DIR = safePath(toDir(location.pathname)); 29 | 30 | const watchList = new WeakSet(); 31 | const blockedList = new WeakSet(); 32 | // Suppress load / error events triggered by resources outside current directory 33 | // (see tor-browser#43491) 34 | 35 | const isAllowedPath = url => { 36 | if (url.protocol != "file:") { 37 | return true; 38 | } 39 | 40 | const filePath = safePath(url.pathname); 41 | 42 | if (filePath.startsWith(CURRENT_DIR)) { 43 | return true; 44 | } 45 | const {href} = url; 46 | const allowed = ns?.canXLoad(href); 47 | notify(href, allowed); 48 | return allowed; 49 | }; 50 | 51 | const notify = (url, allowed) => { 52 | const type = "x-load"; 53 | const request = { 54 | id: "noscript-x-load", 55 | type, 56 | url: toDir(url), 57 | documentUrl: document.URL, 58 | embeddingDocument: true, 59 | }; 60 | seen.record({policyType: type, request, allowed}); 61 | notifyPage(); 62 | 63 | return request; 64 | 65 | } 66 | 67 | const block = (el, url = el.currentSrc) => { 68 | console.warn("Blocking path traversal", url, el); 69 | const request = notify(url, false); 70 | // restore full url, notify truncates to dir 71 | request.url = url; 72 | request.offscreen = !(el?.parentElement?.ownerDocument == document); 73 | try { 74 | const ph = PlaceHolder.create(request.type, request); 75 | ph.replace(!request.offscreen && el); 76 | } catch (e) { 77 | error(e); 78 | } 79 | if (el) { 80 | el.srcset = el.src = "data:"; 81 | } 82 | blockedList.add(el); 83 | }; 84 | 85 | const suppress = e => { 86 | if (!e.isTrusted) return; 87 | const { target } = e; 88 | const sURL = e.filename || 89 | target.currentSrc || 90 | target.src || 91 | target.data || 92 | target.href?.animVal || 93 | target.href; 94 | if (!sURL) return; 95 | 96 | const url = new URL(sURL, 97 | document.baseURI); 98 | if (!isAllowedPath(url)) { 99 | if (e.type == "loadstart") { 100 | block(target); 101 | } 102 | } else if (!blockedList.has(target)) { 103 | return; 104 | } 105 | console.warn(`Suppressing on${e.type} event from ${url}`, e.target, e.currentTarget); 106 | e.preventDefault(); 107 | e.stopPropagation(); 108 | e.stopImmediatePropagation(); 109 | } 110 | 111 | const EVENTS = [ 112 | "abort", 113 | "canplay", 114 | "canplaythrough", 115 | "durationchange", 116 | "emptied", 117 | "encrypted", 118 | "ended", 119 | "error", 120 | "loadeddata", 121 | "loadedmetadata", 122 | "loadstart", 123 | "pause", 124 | "play", 125 | "playing", 126 | "progress", 127 | "ratechange", 128 | "seeked", 129 | "seeking", 130 | "stalled", 131 | "suspend", 132 | "timeupdate", 133 | "volumechange", 134 | "waiting", 135 | "waitingforkey", 136 | ]; 137 | document.addEventListener("load", suppress, true); 138 | for (const e of EVENTS) { 139 | addEventListener(e, suppress, true); 140 | } 141 | EVENTS.push("load"); 142 | 143 | const srcUrls = srcset => { 144 | const urls = []; 145 | // remove data: URLs whose comma may mess with the splitting 146 | srcset = srcset.replace(/(?:^|,)\s*data:[^\s,]*,[^,]*/g, ''); 147 | for (const s of srcset.split(/\s*,s*/)) { 148 | if (s) { 149 | try { 150 | urls.push(new URL(s.trim().split(/\s+/)[0], document.baseURI)) 151 | } catch (e) { 152 | } 153 | } 154 | } 155 | return urls; 156 | } 157 | 158 | const checkSrc = (el, ...srcAttrs) => { 159 | if (srcAttrs.length == 0) { 160 | srcAttrs = ["currentSrc", "src", "srcset"]; 161 | } 162 | const badSrc = 163 | el instanceof HTMLImageElement && 164 | srcUrls(srcAttrs.map(a => el[a]).join(",")).find( 165 | (url) => !isAllowedPath(url) 166 | ); 167 | if (badSrc) { 168 | block(el, badSrc.toString()); 169 | } 170 | }; 171 | 172 | const watch = watching => { 173 | checkSrc(watching); 174 | if (watchList.has(watching)) { 175 | return; 176 | } 177 | observer.observe(watching, mutOpts); 178 | watchList.add(watching); 179 | for (const eventType of EVENTS) { 180 | watching.addEventListener(eventType, suppress, true); 181 | } 182 | }; 183 | 184 | const mutOpts = { 185 | childList: true, 186 | subtree: true, 187 | attributeFilter: ["src", "srcset"], 188 | }; 189 | 190 | const mutationsCallback = (records, observer) => { 191 | for (var r of records) { 192 | switch(r.type) { 193 | case "childList": 194 | [...r.addedNodes].forEach(watch); 195 | [...r.removedNodes].forEach(removed => { 196 | watch(removed); 197 | observer.observe(removed, mutOpts); 198 | }); 199 | break; 200 | case "attributes": 201 | checkSrc(r.target, r.attributeName); 202 | break; 203 | } 204 | } 205 | }; 206 | 207 | const observer = new MutationObserver(mutationsCallback); 208 | observer.observe(document.documentElement, mutOpts); 209 | 210 | Worlds.connect("eventsHook", { 211 | onConnect: port => {}, 212 | onMessage: ({watching}) => { 213 | watch(watching); 214 | }, 215 | }); 216 | } 217 | -------------------------------------------------------------------------------- /src/content/eventsHook.main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2025 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | 'use strict'; 22 | 23 | if (location.protocol == "file:") { 24 | const {exportFunction, patchWindow} = Worlds.main; 25 | 26 | function modifyWindow(scope, {port, xray}) { 27 | const { window } = xray; 28 | const { Proxy, document } = window; 29 | 30 | const { Reflect } = globalThis; 31 | 32 | const { addEventListener } = window.EventTarget.prototype; 33 | const nodeProps = {}; 34 | for (const prop of ["ownerDocument", "parentNode", "nextSibling"]) { 35 | nodeProps[prop] = xray.getSafeDescriptor(Node.prototype, prop, "get").get; 36 | } 37 | for (const method of ["insertBefore", "removeChild"]) { 38 | nodeProps[method] = Node.prototype[method]; 39 | } 40 | const adoptNode = document.adoptNode; 41 | const getDocumentElement = xray.getSafeDescriptor(Document.prototype, "documentElement", "get").get; 42 | 43 | const watchList = new WeakSet(); 44 | 45 | const NO_ARGS = []; 46 | const call = (func, obj, ...args) => Reflect.apply(func, obj, args || NO_ARGS); 47 | 48 | const watch = watching => { 49 | if (!watchList.has(watching)) { 50 | const ownerDocument = call(nodeProps.ownerDocument, watching); 51 | const crossDoc = ownerDocument != document; 52 | const parentNode = call(nodeProps.parentNode, watching); 53 | 54 | if (!crossDoc && parentNode) { 55 | // Nothing to do: eventHook.js' MutationObserver should kick in 56 | return; 57 | } 58 | 59 | if (xray.enabled) { 60 | port.postMessage({watching}); 61 | watchList.add(watching); 62 | return; 63 | } 64 | 65 | // Chromium cannot marshall DOM nodes in port.postMessage(): 66 | // following hack triggers eventHook.js' mutation observer instead. 67 | 68 | const nextSibling = call(nodeProps.nextSibling, watching); 69 | if (crossDoc) { 70 | call(adoptNode, document, watching); 71 | } 72 | const documentElement = call(getDocumentElement, document); 73 | call(nodeProps.insertBefore, documentElement, watching, null); 74 | call(nodeProps.removeChild, documentElement, watching); 75 | if (crossDoc) { 76 | // put the node back at its place in its document 77 | call(adoptNode, ownerDocument, watching); 78 | if (parentNode) { 79 | call(nodeProps.insertBefore, ownerDocument, watching, nextSibling); 80 | } 81 | } 82 | } 83 | } 84 | 85 | exportFunction(function(...args) { 86 | watch(this); 87 | return addEventListener.call(this, ...args); 88 | }, EventTarget.prototype, {defineAs: "addEventListener"}); 89 | 90 | // patch every new node 91 | const nodeCreators = { 92 | "Document": ["createElement", "createElementNS", "createDocumentFragment"], 93 | "Node": ["cloneNode"], 94 | } 95 | for (const clazz in nodeCreators) { 96 | const proto = window[clazz].prototype; 97 | for (const m of nodeCreators[clazz]) { 98 | const method = proto[m]; 99 | exportFunction(function (...args) { 100 | const node = method.call(this, ...args); 101 | watch(node); 102 | return node; 103 | }, proto, { defineAs: m }); 104 | } 105 | } 106 | 107 | // Intercept Image and Audio constructors 108 | 109 | const construct = Reflect.construct.bind(Reflect); 110 | const constructorHandler = { 111 | construct(target, args) { 112 | const i = construct(target, args); 113 | watch(i); 114 | return i; 115 | } 116 | }; 117 | xray.proxify("Image", constructorHandler); 118 | xray.proxify("Audio", constructorHandler); 119 | } 120 | 121 | Worlds.connect("eventsHook.main", { 122 | onConnect: port => { 123 | patchWindow(modifyWindow, {port}); 124 | }, 125 | onMessage: m => { 126 | }, 127 | }); 128 | } -------------------------------------------------------------------------------- /src/content/experiments.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | function testBlob() { 22 | if ((location.origin === "https://noscript.net" || location.origin === "null" || location.href.startsWith("data:")) && top !== window ) { 23 | if (!testBlob.log) testBlob.log = ""; 24 | testBlob.log += `${document.readyState} - ${document.URL} - Policy: ${JSON.stringify(ns.policy)}
`; 25 | if (document.body) { 26 | document.body.style.backgroundColor = "yellow"; 27 | let log = document.body.appendChild(document.createElement("div")); 28 | log.textContent = testBlob.log; 29 | testBlog.log = ""; 30 | } 31 | } 32 | } 33 | testBlob(); 34 | addEventListener("DOMContentLoaded", testBlob); 35 | 36 | patchWorkers(() => { self.patched = true; console.log("NoScript-patched worker", self, location) }); // DEV_ONLY 37 | -------------------------------------------------------------------------------- /src/content/onScriptDisabled.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | // depends on /nscl/content/NoscriptElements.js 22 | 23 | "use strict"; 24 | function onScriptDisabled() { 25 | onScriptDisabled = () => {}; // call me just once 26 | debug("onScriptDisabled state", document.readyState); 27 | if (ns.allows("noscript")) { 28 | NoscriptElements.emulate(true); 29 | } else { 30 | let reportNoscriptElements = () => { 31 | if (document.querySelector("noscript")) { 32 | let request = { 33 | id: "noscript-noscript", 34 | type: "noscript", 35 | url: document.URL, 36 | documentUrl: document.URL, 37 | embeddingDocument: true, 38 | }; 39 | seen.record({policyType: "noscript", request, allowed: false}); 40 | } 41 | }; 42 | if (document.readyState === "loading") { 43 | window.addEventListener("DOMContentLoaded", reportNoscriptElements, true); 44 | } else { 45 | reportNoscriptElements(); 46 | } 47 | } 48 | 49 | let eraser = { 50 | tapped: null, 51 | delKey: false, 52 | }; 53 | 54 | addEventListener("pagehide", ev => { 55 | if (!ev.isTrusted) return; 56 | eraser.tapped = null; 57 | eraser.delKey = false; 58 | }, false); 59 | 60 | addEventListener("keyup", ev => { 61 | if (!ev.isTrusted) return; 62 | let el = eraser.tapped; 63 | if (el && (ev.code === "Delete" || ev.code === "Backspace")) { 64 | eraser.tapped = null; 65 | eraser.delKey = true; 66 | let doc = el.ownerDocument; 67 | let w = doc.defaultView; 68 | if (w.getSelection().isCollapsed) { 69 | let root = doc.body || doc.documentElement; 70 | let posRx = /^(?:absolute|fixed|sticky)$/; 71 | do { 72 | if (posRx.test(w.getComputedStyle(el, '').position)) { 73 | (eraser.tapped = el.parentNode).removeChild(el); 74 | break; 75 | } 76 | } while ((el = el.parentNode) && el != root); 77 | } 78 | } 79 | }, true); 80 | 81 | addEventListener("mousedown", ev => { 82 | if (!ev.isTrusted) return; 83 | if (ev.button === 0) { 84 | eraser.tapped = ev.target; 85 | eraser.delKey = false; 86 | } 87 | }, true); 88 | 89 | addEventListener("mouseup", ev => { 90 | if (!ev.isTrusted) return; 91 | if (eraser.delKey) { 92 | eraser.delKey = false; 93 | ev.preventDefault(); 94 | ev.stopPropagation(); 95 | } 96 | eraser.tapped = null; 97 | }, true); 98 | } 99 | -------------------------------------------------------------------------------- /src/content/staticNS.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | 'use strict'; 22 | const FILE_OR_FTP = /^(?:file|ftp):$/.test(location.protocol); 23 | { 24 | debug(`Prerendering: ${document.prerendering}`, self.performance?.getEntriesByType?.('navigation')[0]?.activationStart); // DEV_ONLY 25 | 26 | let listenersMap = new Map(); 27 | let backlog = new Set(); 28 | 29 | let ns = { 30 | debug: true, // DEV_ONLY 31 | get embeddingDocument() { 32 | delete this.embeddingDocument; 33 | return this.embeddingDocument = CSP.isEmbedType(document.contentType); 34 | }, 35 | on(eventName, listener) { 36 | let listeners = listenersMap.get(eventName); 37 | if (!listeners) listenersMap.set(eventName, listeners = new Set()); 38 | listeners.add(listener); 39 | if (backlog.has(eventName)) this.fire(eventName, listener); 40 | }, 41 | detach(eventName, listener) { 42 | let listeners = listenersMap.get(eventName); 43 | if (listeners) listeners.delete(listener); 44 | }, 45 | fire(eventName, listener = null) { 46 | if (listener) { 47 | listener({type:eventName, source: this}); 48 | return; 49 | } 50 | let listeners = listenersMap.get(eventName); 51 | if (listeners) { 52 | for (let l of listeners) { 53 | this.fire(eventName, l); 54 | } 55 | } 56 | backlog.add(eventName); 57 | }, 58 | 59 | fetchPolicy(sync = false) { 60 | if (this.policy) return; 61 | let url = window.location.href; 62 | let origin = window.origin; 63 | 64 | debug(`Fetching policy from document %s (origin %s), readyState %s`, 65 | url, origin, document.readyState 66 | ); 67 | 68 | if (this.domPolicy) { 69 | debug("Injected policy found!"); 70 | try { 71 | this.setup(this.domPolicy); 72 | return; 73 | } catch(e) { 74 | error(e); 75 | } 76 | } 77 | 78 | if (this.syncFetchPolicy) { 79 | // extra hops to ensure that scripts don't run when CSP has not been set through HTTP headers 80 | this.syncFetchPolicy(); 81 | return; 82 | } 83 | 84 | this.pendingSyncFetchPolicy = true; 85 | 86 | if (!sync) { 87 | queueMicrotask(() => this.fetchPolicy(true)); 88 | return; 89 | } 90 | 91 | if (origin !== 'null' && (window.location.origin !== origin || url.startsWith(`blob:${origin}/`))) { 92 | debug("Fetching policy for actual URL %s (was %s)", origin, url); 93 | url = origin; 94 | } 95 | 96 | if (!this.syncFetchPolicy) { 97 | this.fetchLikeNoTomorrow(url); 98 | } 99 | }, 100 | 101 | fetchLikeNoTomorrow(url, setup = this.setup.bind(this)) { 102 | let msg = {id: "fetchChildPolicy", url}; 103 | 104 | let asyncFetch = (async () => { 105 | let policy = null; 106 | for (let attempts = 10; !(policy || this.policy) && attempts-- > 0;) { 107 | try { 108 | debug(`Retrieving policy asynchronously for ${document.readyState} ${url} (${attempts} attempts left).`); 109 | policy = await Messages.send(msg.id, msg) || this.domPolicy; 110 | debug("Asynchronous policy", policy); 111 | } catch (e) { 112 | error(e, "(Asynchronous policy fetch)"); 113 | } 114 | } 115 | setup(policy); 116 | }); 117 | const {readyState} = document; 118 | 119 | if (readyState == "complete" || !this.syncFetchPolicy && this.embeddingDocument) { 120 | asyncFetch(); 121 | return; 122 | } 123 | debug(`Synchronously fetching policy for ${readyState} ${url}.`); 124 | let policy = null; 125 | let attempts = readyState == "loading" ? 100 : 1; 126 | let refetch = () => { 127 | try { 128 | policy = browser.runtime.sendSyncMessage(msg) || this.domPolicy; 129 | } catch (e) { 130 | error(e); 131 | if (/sync-xhr is not allowed/.test(e.message)) { 132 | attempts = 0; 133 | } 134 | } 135 | if (policy) { 136 | setup(policy); 137 | } else if (attempts-- > 0) { 138 | debug(`Couldn't retrieve policy synchronously (${attempts} attempts left).`); 139 | if (asyncFetch) { 140 | asyncFetch(); 141 | asyncFetch = null; 142 | } 143 | queueMicrotask(refetch); 144 | } 145 | }; 146 | refetch(); 147 | }, 148 | 149 | setup(policy) { 150 | if (this.policy) return false; 151 | debug("%s, %s, fetched %o", document.URL, document.readyState, policy, new Error().stack); // DEV_ONLY 152 | if (!policy) { 153 | policy = {permissions: {capabilities: []}, localFallback: true}; 154 | } 155 | this.policy = policy; 156 | if (!policy.permissions || policy.unrestricted) { 157 | this.allows = () => true; 158 | this.capabilities = Object.assign( 159 | new Set(["script"]), { has() { return true; } }); 160 | } else { 161 | let perms = policy.permissions; 162 | if (!(UA.isMozilla || perms.capabilities.includes("script")) && 163 | /^file:\/\/\/(?:[^#?]+\/)?$/.test(document.URL)) { 164 | // Allow Chromium browser UI scripts for directory navigation 165 | // (for Firefox we rely on emulation in content/dirindex.js). 166 | perms.capabilities.push("script"); 167 | } 168 | this.capabilities = new Set(perms.capabilities); 169 | this.CSP = new DocumentCSP(document).apply(this.capabilities, this.embeddingDocument); 170 | } 171 | this.canScript = this.allows("script"); 172 | this.fire("capabilities"); 173 | return true; 174 | }, 175 | 176 | policy: null, 177 | 178 | allows(cap) { 179 | return this.capabilities && this.capabilities.has(cap); 180 | }, 181 | 182 | canXLoad(url) { 183 | return this.policy?.xLoadable?.some(parentDir => url.startsWith(parentDir)); 184 | } 185 | }; 186 | globalThis.ns = globalThis.ns ? Object.assign(ns, globalThis.ns) : ns; 187 | debug("StaticNS", Date.now(), JSON.stringify(globalThis.ns)); // DEV_ONLY 188 | globalThis.ns_setupCallBack = ns.domPolicy 189 | ? () => {} 190 | : ({domPolicy}) => { 191 | ns.domPolicy = domPolicy; 192 | if (ns.setup) { 193 | if (ns.syncSetup) ns.syncSetup(domPolicy); 194 | else ns.setup(domPolicy); 195 | } 196 | }; 197 | } 198 | -------------------------------------------------------------------------------- /src/img/error64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/error64.png -------------------------------------------------------------------------------- /src/img/icon256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/icon256.png -------------------------------------------------------------------------------- /src/img/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/icon48.png -------------------------------------------------------------------------------- /src/img/icon96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/icon96.png -------------------------------------------------------------------------------- /src/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | image/svg+xml 7 | 8 | No Script Logo 9 | 10 | 11 | 12 | 13 | 14 | 15 | No Script Logo 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/img/noscript-options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/noscript-options.png -------------------------------------------------------------------------------- /src/img/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/stop.png -------------------------------------------------------------------------------- /src/img/ui-black64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/ui-black64.png -------------------------------------------------------------------------------- /src/img/ui-clock64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/ui-clock64.png -------------------------------------------------------------------------------- /src/img/ui-close64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/ui-close64.png -------------------------------------------------------------------------------- /src/img/ui-custom64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/ui-custom64.png -------------------------------------------------------------------------------- /src/img/ui-global-no64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/ui-global-no64.png -------------------------------------------------------------------------------- /src/img/ui-global-sub-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/ui-global-sub-64.png -------------------------------------------------------------------------------- /src/img/ui-global64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/ui-global64.png -------------------------------------------------------------------------------- /src/img/ui-http64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/ui-http64.png -------------------------------------------------------------------------------- /src/img/ui-https64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/ui-https64.png -------------------------------------------------------------------------------- /src/img/ui-maybe64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/ui-maybe64.png -------------------------------------------------------------------------------- /src/img/ui-no64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/ui-no64.png -------------------------------------------------------------------------------- /src/img/ui-part64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/ui-part64.png -------------------------------------------------------------------------------- /src/img/ui-reload64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/ui-reload64.png -------------------------------------------------------------------------------- /src/img/ui-revoke-temp64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/ui-revoke-temp64.png -------------------------------------------------------------------------------- /src/img/ui-sub64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/ui-sub64.png -------------------------------------------------------------------------------- /src/img/ui-tab-no64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/ui-tab-no64.png -------------------------------------------------------------------------------- /src/img/ui-tab64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/ui-tab64.png -------------------------------------------------------------------------------- /src/img/ui-temp-all64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/ui-temp-all64.png -------------------------------------------------------------------------------- /src/img/ui-temp64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/ui-temp64.png -------------------------------------------------------------------------------- /src/img/ui-yes64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/ui-yes64.png -------------------------------------------------------------------------------- /src/img/vintage/error64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/error64.png -------------------------------------------------------------------------------- /src/img/vintage/icon256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/icon256.png -------------------------------------------------------------------------------- /src/img/vintage/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/icon48.png -------------------------------------------------------------------------------- /src/img/vintage/icon96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/icon96.png -------------------------------------------------------------------------------- /src/img/vintage/noscript-options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/noscript-options.png -------------------------------------------------------------------------------- /src/img/vintage/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/stop.png -------------------------------------------------------------------------------- /src/img/vintage/ui-black64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/ui-black64.png -------------------------------------------------------------------------------- /src/img/vintage/ui-clock64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/ui-clock64.png -------------------------------------------------------------------------------- /src/img/vintage/ui-close64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/ui-close64.png -------------------------------------------------------------------------------- /src/img/vintage/ui-custom64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/ui-custom64.png -------------------------------------------------------------------------------- /src/img/vintage/ui-global-no64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/ui-global-no64.png -------------------------------------------------------------------------------- /src/img/vintage/ui-global-sub64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/ui-global-sub64.png -------------------------------------------------------------------------------- /src/img/vintage/ui-global64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/ui-global64.png -------------------------------------------------------------------------------- /src/img/vintage/ui-http64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/ui-http64.png -------------------------------------------------------------------------------- /src/img/vintage/ui-https64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/ui-https64.png -------------------------------------------------------------------------------- /src/img/vintage/ui-maybe64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/ui-maybe64.png -------------------------------------------------------------------------------- /src/img/vintage/ui-no64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/ui-no64.png -------------------------------------------------------------------------------- /src/img/vintage/ui-part64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/ui-part64.png -------------------------------------------------------------------------------- /src/img/vintage/ui-reload64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/ui-reload64.png -------------------------------------------------------------------------------- /src/img/vintage/ui-revoke-temp64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/ui-revoke-temp64.png -------------------------------------------------------------------------------- /src/img/vintage/ui-sub64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/ui-sub64.png -------------------------------------------------------------------------------- /src/img/vintage/ui-tab-no64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/ui-tab-no64.png -------------------------------------------------------------------------------- /src/img/vintage/ui-tab64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/ui-tab64.png -------------------------------------------------------------------------------- /src/img/vintage/ui-temp-all64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/ui-temp-all64.png -------------------------------------------------------------------------------- /src/img/vintage/ui-temp64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/ui-temp64.png -------------------------------------------------------------------------------- /src/img/vintage/ui-yes64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/ui-yes64.png -------------------------------------------------------------------------------- /src/img/vintage/warning64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/vintage/warning64.png -------------------------------------------------------------------------------- /src/img/warning64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackademix/noscript/2f67f4b6517b78e09560ea1f24c6dced9a07f9c1/src/img/warning64.png -------------------------------------------------------------------------------- /src/lib/flextabs.css: -------------------------------------------------------------------------------- 1 | .flextabs { 2 | display: flex; 3 | flex-wrap: wrap; 4 | } 5 | 6 | .flextabs__tab { 7 | width: 100%; 8 | } 9 | 10 | .flextabs__content { 11 | display: none; 12 | width: 100%; 13 | } 14 | 15 | .flextabs__content--active { 16 | display: block; 17 | } 18 | -------------------------------------------------------------------------------- /src/lib/flextabs.css.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 mdmoreau 2 | 3 | SPDX-License-Identifier: MIT -------------------------------------------------------------------------------- /src/lib/flextabs.js: -------------------------------------------------------------------------------- 1 | (function(root, factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | define([], factory); 4 | } else if (typeof module === 'object' && module.exports) { 5 | module.exports = factory(); 6 | } else { 7 | root.flextabs = factory(); 8 | } 9 | }(this, function() { 10 | 11 | var flextabs = function(target) { 12 | 13 | var _ = {}; 14 | 15 | _.flextabs = target; 16 | 17 | _.toggle = _.flextabs.querySelectorAll('.flextabs__toggle'); 18 | 19 | _.content = _.flextabs.querySelectorAll('.flextabs__content'); 20 | 21 | _.reset = function() { 22 | for (var i = 0; i < _.toggle.length; i += 1) { 23 | _.toggle[i].classList.remove('flextabs__toggle--active--last'); 24 | _.content[i].classList.remove('flextabs__content--active--last'); 25 | } 26 | }; 27 | 28 | _.activate = function() { 29 | var i = Array.prototype.indexOf.call(_.toggle, this); 30 | _.toggle[i].classList.toggle('flextabs__toggle--active'); 31 | _.toggle[i].classList.add('flextabs__toggle--active--last'); 32 | _.content[i].classList.toggle('flextabs__content--active'); 33 | _.content[i].classList.add('flextabs__content--active--last'); 34 | }; 35 | 36 | _.aria = function() { 37 | for (var i = 0; i < _.toggle.length; i += 1) { 38 | var style = getComputedStyle(_.content[i]); 39 | if (style.getPropertyValue('display') !== 'none') { 40 | _.toggle[i].setAttribute('aria-expanded', true); 41 | } else { 42 | _.toggle[i].setAttribute('aria-expanded', false); 43 | } 44 | } 45 | }; 46 | 47 | _.click = function(e) { 48 | e.preventDefault(); 49 | _.reset(); 50 | _.activate.call(this); 51 | _.aria(); 52 | }; 53 | 54 | _.init = function() { 55 | for (var i = 0; i < _.toggle.length; i += 1) { 56 | window.addEventListener('load', _.aria); 57 | window.addEventListener('resize', _.aria); 58 | _.toggle[i].addEventListener('click', _.click); 59 | } 60 | }; 61 | 62 | return _; 63 | 64 | }; 65 | 66 | return flextabs; 67 | 68 | })); 69 | -------------------------------------------------------------------------------- /src/lib/flextabs.js.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2016 mdmoreau 2 | 3 | SPDX-License-Identifier: MIT -------------------------------------------------------------------------------- /src/lib/he.js.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2018 Mathias Bynens 2 | 3 | SPDX-License-Identifier: MIT -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "default_locale": "en", 4 | "name": "NoScript", 5 | "browser_specific_settings": { 6 | "gecko": { 7 | "id": "{73a6fe31-595d-460b-a920-fcc0f8843232}", 8 | "strict_min_version": "115.0" 9 | }, 10 | "gecko_android": {} 11 | }, 12 | "version": "13.0.8", 13 | "description": "__MSG_Description__", 14 | "incognito": "spanning", 15 | 16 | "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'none'", 17 | 18 | "icons": { 19 | "48": "img/icon48.png", 20 | "96": "img/icon96.png", 21 | "256": "img/icon256.png" 22 | }, 23 | 24 | "permissions": [ 25 | "contextMenus", 26 | "debugger", 27 | "storage", 28 | "tabs", 29 | "unlimitedStorage", 30 | "scripting", 31 | "declarativeNetRequest", 32 | "declarativeNetRequestFeedback", 33 | "webNavigation", 34 | "webRequest", 35 | "webRequestBlocking", 36 | "webRequestFilterResponse", 37 | "webRequestFilterResponse.serviceWorkerScript", 38 | "dns", 39 | "" 40 | ], 41 | "host_permissions": [ 42 | "" 43 | ], 44 | 45 | "background": { 46 | "service_worker": "sw.js", 47 | "scripts": [ 48 | "/nscl/lib/browser-polyfill.js", 49 | "/nscl/lib/punycode.js", 50 | "/nscl/service/Wakening.js", 51 | "/nscl/common/sha256.js", 52 | "/nscl/common/UA.js", 53 | "/nscl/common/uuid.js", 54 | "/nscl/common/SyncMessage.js", 55 | "/nscl/common/log.js", 56 | "/nscl/common/tld.js", 57 | "/nscl/common/Messages.js", 58 | "/nscl/common/CSP.js", 59 | "/nscl/common/NetCSP.js", 60 | "/nscl/common/CapsCSP.js", 61 | "/nscl/common/RequestKey.js", 62 | "/nscl/common/Sites.js", 63 | "/nscl/common/Permissions.js", 64 | "/nscl/common/Policy.js", 65 | "/nscl/common/locale.js", 66 | "/nscl/common/Storage.js", 67 | "/nscl/common/include.js", 68 | "/nscl/common/DNS.js", 69 | "/nscl/common/AddressMatcherWithDNS.js", 70 | "/nscl/common/iputil.js", 71 | "/nscl/common/SessionCache.js", 72 | "/nscl/service/Scripting.js", 73 | "/nscl/service/DocStartInjection.js", 74 | "/nscl/service/LastListener.js", 75 | "/nscl/service/patchWorkers.js", 76 | "/nscl/service/TabCache.js", 77 | "/nscl/service/TabTies.js", 78 | "ui/Prompts.js", 79 | "xss/XSS.js", 80 | "bg/ReportingCSP.js", 81 | "bg/Defaults.js", 82 | "bg/TabGuard.js", 83 | "bg/RequestGuard.js", 84 | "bg/Settings.js", 85 | "bg/popupHandler.js", 86 | "bg/main.js", 87 | "common/themes.js" 88 | ], 89 | "persistent": false 90 | }, 91 | 92 | "content_scripts": [ 93 | { 94 | "run_at": "document_start", 95 | "matches": [""], 96 | "match_about_blank": true, 97 | "match_origin_as_fallback": true, 98 | "all_frames": true, 99 | "js": [ 100 | "/nscl/lib/browser-polyfill.js", 101 | "/nscl/common/UA.js", 102 | "/nscl/common/uuid.js", 103 | "/nscl/common/log.js", 104 | "/nscl/common/SyncMessage.js", 105 | "/nscl/common/Messages.js", 106 | "/nscl/common/CSP.js", 107 | "/nscl/common/CapsCSP.js", 108 | "/nscl/common/RequestKey.js", 109 | "/nscl/content/DocRewriter.js", 110 | "/nscl/content/Worlds.js", 111 | "/nscl/content/patchWorkers.js", 112 | "/nscl/content/DocumentCSP.js", 113 | "/nscl/content/NoscriptElements.js", 114 | "/nscl/content/prefetchCSSResources.js", 115 | "/nscl/content/PlaceHolder.js", 116 | "/nscl/content/sanitizePaste.js", 117 | "content/onScriptDisabled.js", 118 | "content/staticNS.js", 119 | "/nscl/content/media.js", 120 | "/nscl/content/WebGLHook.js", 121 | "content/embeddingDocument.js", 122 | "content/content.js", 123 | "content/dirindex.js", 124 | "/nscl/content/DocumentFreezer.js", 125 | "content/eventsHook.js", 126 | "content/syncFetchPolicy.js" 127 | ] 128 | }, 129 | { 130 | "run_at": "document_start", 131 | "matches": [""], 132 | "match_about_blank": true, 133 | "match_origin_as_fallback": true, 134 | "all_frames": true, 135 | "world": "MAIN", 136 | "js": [ 137 | "/nscl/main/uuid.js", 138 | "/nscl/main/Worlds.js", 139 | "/nscl/main/Worlds.main.js", 140 | "/nscl/main/patchWorkers.main.js", 141 | "/nscl/main/WebGLHook.main.js", 142 | "content/eventsHook.main.js", 143 | "/nscl/main/prefetchCSSResources.main.js" 144 | ] 145 | } 146 | ], 147 | 148 | "web_accessible_resources": [ 149 | { 150 | "resources": [ "/nscl/common/SyncMessage/*" ], 151 | "matches": [ "" ] 152 | } 153 | ], 154 | 155 | 156 | "options_ui": { 157 | "page": "ui/options.html", 158 | "open_in_tab": true 159 | }, 160 | 161 | "action": { 162 | "default_area": "navbar", 163 | "default_title": "NoScript", 164 | "default_icon": { 165 | "64": "img/ui-maybe64.png" 166 | } 167 | }, 168 | 169 | "browser_action": { 170 | "default_area": "navbar", 171 | "default_title": "NoScript", 172 | "default_icon": { 173 | "64": "img/ui-maybe64.png" 174 | } 175 | }, 176 | 177 | "commands": { 178 | "toggleEnforcementForTab": { 179 | "description": "__MSG_toggleEnforcementForTab__", 180 | "suggested_key": { 181 | "default": "Alt+Shift+Space", 182 | "windows": "Alt+Shift+Comma" 183 | } 184 | }, 185 | 186 | "openPageUI": { 187 | "description": "__MSG_pagePermissionsUI__", 188 | "suggested_key": { 189 | "default": "Alt+Shift+N" 190 | } 191 | }, 192 | 193 | "tempTrustPage": { 194 | "description": "__MSG_TempTrustPage__" 195 | }, 196 | "revokeTemp": { 197 | "description": "__MSG_RevokeTemp__" 198 | }, 199 | 200 | "_execute_action": {}, 201 | "_execute_browser_action": {} 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/sw.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | importScripts("/nscl/common/include.js"); 22 | browser.browserAction = browser.action; 23 | console.log("NoScript Manifest V3 service worker"); 24 | -------------------------------------------------------------------------------- /src/test/Policy_test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | { 22 | let p1 = new Policy(); 23 | p1.set("noscript.net", new Permissions(["script"], true)); 24 | p1.set("https://noscript.net", new Permissions(["script", "object"])); 25 | p1.set("maone.net", p1.TRUSTED.tempTwin); 26 | p1.set(Sites.secureDomainKey("secure.informaction.com"), p1.TRUSTED); 27 | p1.set("https://flashgot.net", p1.TRUSTED); 28 | p1.set("http://flashgot.net", p1.UNTRUSTED); 29 | p1.set("perchè.com", p1.TRUSTED); 30 | p1.set("10", p1.TRUSTED); 31 | p1.set("192.168", p1.TRUSTED); 32 | p1.set("192.168.69", p1.UNTRUSTED); 33 | p1.set("facebook.net", new Permissions([], false, 34 | new Sites([[Sites.optimalKey("https://facebook.com"), p1.TRUSTED]]))); 35 | // secureDomainKey should be "downgraded" by UNTRUSTED, issue #126 36 | p1.set(Sites.secureDomainKey("evil.com"), p1.UNTRUSTED); 37 | let p2 = new Policy(p1.dry()); 38 | debug("p1", JSON.stringify(p1.dry())); 39 | debug("p2", JSON.stringify(p2.dry())); 40 | let onionSecureCurrent = Sites.onionSecure; 41 | Sites.onionSecure = true; 42 | p1.set("http://some.onion", p1.TRUSTED); 43 | for(let t of [ 44 | () => p2.can("https://noscript.net"), 45 | () => !p2.can("http://noscript.net"), 46 | () => p2.can("https://noscript.net", "object"), 47 | () => p1.snapshot !== p2.snapshot, 48 | () => JSON.stringify(p1.dry()) === JSON.stringify(p2.dry()), 49 | () => p1.can("http://perchè.com/test") /* IDN encoding */, 50 | () => Sites.toExternal(new URL("https://perché.com/test")) === 51 | "https://perché.com/test" /* IDN decoding */, 52 | () => !p1.can("http://secure.informaction.com"), 53 | () => p1.can("https://secure.informaction.com"), 54 | () => p1.can("https://www.secure.informaction.com"), 55 | () => !p1.can("https://192.168.69.1"), 56 | () => !p1.can("https://10.0.0.1"), 57 | () => p1.can("http://192.168.1.2"), 58 | () => p1.can("http://some.onion"), 59 | () => !p1.can("http://evil.com"), 60 | () => !p1.can("https://facebook.net"), 61 | () => p1.can("https://facebook.net", "script", "https://www.facebook.com"), 62 | () => !p1.can("https://facebook.net", "script", "http://facebook.com"), 63 | ]) Test.run(t); 64 | Sites.onionSecure = onionSecureCurrent; 65 | Test.report(); 66 | } 67 | -------------------------------------------------------------------------------- /src/test/Storage_test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | "use strict"; 22 | { 23 | let makeBigObj = propsNum => { 24 | let bigObj = {}; 25 | for (let j = propsNum; j-- > 0;) { 26 | let x = "0000".concat(j.toString(16)).slice(-4); 27 | bigObj[`k${x}`] = `v${x}`; 28 | } 29 | log("[TEST] created bigObj %s JSON characters long.", JSON.stringify(bigObj).length) 30 | return bigObj; 31 | } 32 | let HUGE_SIZE = 16000, 33 | BIG_SIZE = 1000; 34 | let bigObject = makeBigObj(BIG_SIZE); 35 | let hugeObject = makeBigObj(HUGE_SIZE); 36 | let items = {"small1": {x: 1, y: 2}, bigObject, "small2": {k:3, j: 4}}; 37 | let keys = Object.keys(items); 38 | keys.push("hugeObject"); 39 | 40 | let eq = async (key, prop, val) => { 41 | let current = (await Storage.get("sync", key))[key]; 42 | let ok = current[prop] === val; 43 | log("[TEST] sync.%s.%s %s %s\n(%o)", key, prop, ok ? "==" : "!=", val, current); 44 | return ok; 45 | }; 46 | 47 | let fallbackOrChunked = async key => { 48 | let fallback = await Storage.hasLocalFallback(key); 49 | let chunked = await Storage.isChunked(key); 50 | log("[TEST] %s fallback: %s, chunked: %s", key, fallback, chunked); 51 | return fallback ? !chunked : chunked; 52 | } 53 | 54 | let checkSize = async (key, size) => 55 | Object.keys((await Storage.get("sync", key))[key]).length === size; 56 | 57 | let all; 58 | 59 | (async () => { 60 | for(let t of [ 61 | async () => { 62 | await Storage.set("sync", items) 63 | await Storage.set("sync", {hugeObject}); // fallback to local 64 | all = await Storage.get("sync", keys); 65 | log("[TEST] Storage:\nsync %o\nlocal %o\nfiltered (%o) %o", 66 | await browser.storage.sync.get(), 67 | await browser.storage.local.get(), 68 | keys, all); 69 | return Object.keys(all).length === keys.length; 70 | }, 71 | async () => checkSize("hugeObject", HUGE_SIZE), 72 | async () => checkSize("bigObject", BIG_SIZE), 73 | async () => await fallbackOrChunked("bigObject"), 74 | async () => await fallbackOrChunked("hugeObject"), 75 | async () => await eq("small1", "y", 2), 76 | async () => await eq("small2", "k", 3), 77 | async () => await eq("bigObject", "k0000", "v0000"), 78 | async () => await eq("hugeObject", "k0001", "v0001"), 79 | async () => { 80 | let key = "bigObject"; 81 | let wasChunked = await Storage.isChunked(key); 82 | await Storage.set("sync", {[key]: {tiny: "prop"}}); 83 | return wasChunked && !(await Storage.isChunked(key)); 84 | }, 85 | async () => eq("bigObject", "tiny", "prop"), 86 | async () => { 87 | await Storage.remove("sync", keys); 88 | let myItems = await Storage.get("sync", keys); 89 | return Object.keys(myItems).length === 0; 90 | }, 91 | ]) { 92 | await Test.run(t); 93 | } 94 | Test.report(); 95 | })(); 96 | } 97 | -------------------------------------------------------------------------------- /src/test/XSS_test.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | if (UA.isMozilla) { 22 | let y = async (url, originUrl = '') => await XSS.test({originUrl, url, method: "GET"}); 23 | let n = async (...args) => !await y(...args); 24 | let xssTest = Promise.all([ 25 | () => y("https://noscript.net/ n("https://noscript.net/ y("https://vulnerabledoma.in/char_test?body=%80%3Cscript%3Ealert(1)%3C/script%3E"), 28 | () => y("https://vulnerabledoma.in/char_test?body=%3Cp%20id=x%3Ejavascrip%3Cx%3Et:alert(%3Cx%3E1)%3C/p%3E%3Cmath%3E%3Ca%20href=%22%23*/=x.innerText,a%22%20xml:base=javascript:location/*%3EClick%20HERE"), 29 | () => y("https://vulnerabledoma.in/char_test?body=%3Cp%20id=x%3E%26lt%3Bsv%3Cx%3Eg%20o%3Cx%3Enload=alert(%3Cx%3E1)%3E%3C/p%3E%3Cmath%3E%3Ca%20href=%23%250ax.innerText%20xml:base=javascript:%3C!--%3EClick%20HERE"), 30 | () => y("https://vulnerabledoma.in/char_test?body=%3Cp%20id=x%3E%26lt%3Bsv%3Cx%3Eg%20o%3Cx%3Enload=alert(%3Cx%3E1)%3E%3C/p%3E%3Cmath%3E%3Ca%20href=%23*/x.innerText%20xml:base=%01javascript:/*%3EClick%20HERE"), 31 | () => y("https://vulnerabledoma.in/char_test?body=%3Ca%20href=javascript%26colo%u0000n%3balert%281%u0029%3ECLICK"), 32 | () => y("https://vulnerabledoma.in/xss_link?url=javascript%26colo%00n%3Balert%u00281%29"), 33 | () => y("https://vulnerabledoma.in/xss_link?url=javascript:\\u{%0A6e}ame"), 34 | () => y("https://sandbox.hack.vet/issue/noscript/bypass/multibyte/?q=alert(document.cookie)//<"), 35 | () => y("https://sandbox.hack.vet/issue/noscript/bypass/multibyte/?q=/**🚫*/alert(document.cookie)"), 36 | ].map(t => Test.run(t)) 37 | ); 38 | 39 | let invalidCharsTest = async () => { 40 | 41 | await include("xss/InjectionChecker.js"); 42 | let IC = await XSS.InjectionChecker; 43 | let rx = new IC().invalidCharsRx; 44 | console.log("Testing invalidCharsRx", rx); 45 | let x = n => '\\u' + ("0000" + n.toString(16)).slice(-4); 46 | function check(ch) { 47 | Function(`let _${ch}_`); 48 | } 49 | let cur = 0x7e; 50 | let fail = false; 51 | while (cur++ < 0xffff && !fail) { 52 | let ch = String.fromCharCode(cur); 53 | try { 54 | check(ch); 55 | if (rx.test(ch)) { 56 | console.error(x(cur) + " should not test invalid!"); 57 | fail = true; 58 | } 59 | } catch (e) { 60 | if (!/illegal char/.test(e.message)) continue; 61 | if (!rx.test(ch)) { 62 | console.error(x(cur) + " must test invalid!"); 63 | fail = true; 64 | } 65 | } 66 | } 67 | return !fail; 68 | }; 69 | (async () => { 70 | await xssTest; 71 | Test.report(); 72 | await Test.run(invalidCharsTest, "InjectionChecker.invalidCharsRx"); 73 | Test.report(); 74 | })(); 75 | } 76 | -------------------------------------------------------------------------------- /src/test/run.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | globalThis.runTests = async () => { 22 | await include("/nscl/test/Test.js"); 23 | Test.include([ 24 | "/nscl/test/Policy_test.js", 25 | "/nscl/test/Storage_test.js", 26 | "/nscl/test/TLD_test.js", 27 | "XSS", 28 | "embargoed/XSS", 29 | ]); 30 | }; 31 | -------------------------------------------------------------------------------- /src/test/test: -------------------------------------------------------------------------------- 1 | ../src/test -------------------------------------------------------------------------------- /src/ui/Prompts.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | var Prompts = (() => { 22 | 23 | var promptData; 24 | var backlog = []; 25 | 26 | Messages.addHandler({ 27 | getPromptData() { return Prompts.promptData }, 28 | promptDone(data) { 29 | let promptData = promptDataMap.get(data.id); 30 | if (promptData) { 31 | Object.assign(promptData, data).done(); 32 | } 33 | } 34 | }); 35 | 36 | class WindowManager { 37 | constructor() { 38 | this.currentWindow = this.currentTab = null; 39 | browser.windows?.onRemoved.addListener(windowId => { 40 | if (windowId === this.currentWindow?.id) { 41 | promptData?.done(); 42 | } 43 | }); 44 | browser.tabs.onRemoved.addListener(tabId => { 45 | if (tabId === this.currentTab?.id) { 46 | promptData?.done(); 47 | } 48 | }); 49 | } 50 | 51 | async open(data) { 52 | promptData = data; 53 | this.close(); 54 | 55 | let url = browser.runtime.getURL("ui/prompt.html"); 56 | 57 | if (!("windows" in browser)) { 58 | // Android, most likely 59 | this.currentTab = await browser.tabs.create({url}); 60 | return; 61 | } 62 | 63 | let {width, height, left, top, parent } = data.features; 64 | 65 | let options = { 66 | url, 67 | type: "popup", 68 | } 69 | 70 | if (!parent) { 71 | parent = await browser.windows.getCurrent(); 72 | } 73 | 74 | if (UA.isMozilla) { 75 | options.allowScriptsToClose = true; 76 | } 77 | 78 | const centerOnParent = bounds => { 79 | for (const [p, s] of [["left", "width"], ["top", "height"]]) { 80 | if (bounds[s] && bounds[p] === undefined) { 81 | bounds[p] = Math.round(parent[p] + (parent[s] - bounds[s]) / 2); 82 | } 83 | } 84 | return bounds; 85 | }; 86 | 87 | if (width && height) { 88 | const bounds = { width, height, left, top }; 89 | url += `?winbounds=${JSON.stringify(bounds)}`; 90 | if (parent) { 91 | ({ left, top } = Object.assign(options, centerOnParent(bounds))); 92 | } 93 | } 94 | 95 | debug("Prompt pre-opening options", options, left, top, width, height); // DEV_ONLY 96 | let popup = (this.currentWindow = await browser.windows.create(options)); 97 | 98 | if (parent) { 99 | ({ left, top } = centerOnParent({ 100 | width: width || popup.width, 101 | height: height || popup.height, 102 | })); 103 | } else { 104 | // use given left & top or default to auto-centering on main screen 105 | if (left === undefined) ({ left } = popup); 106 | if (top === undefined) ({ top } = popup); 107 | } 108 | 109 | debug("Prompt post-opening options", popup, options, left, top, width, height); // DEV_ONLY 110 | 111 | // work around for resistFingerprinting new window rounding (https://bugzilla.mozilla.org/show_bug.cgi?id=1330882) 112 | if ( 113 | width && 114 | height && 115 | (popup.width !== width || 116 | popup.height !== height || 117 | popup.left !== left || 118 | popup.top !== top) 119 | ) { 120 | popup = await browser.windows.update(popup.id, { 121 | left, 122 | top, 123 | width, 124 | height, 125 | }); 126 | for (let attempts = 2; attempts-- > 0; ) { 127 | debug("Resizing", popup, { left, top, width, height }); // DEV_ONY 128 | popup = await browser.windows.update(popup.id, { width, height }); 129 | if (popup.width == width && popup.height == height) { 130 | break; 131 | } 132 | } 133 | } 134 | } 135 | 136 | async close() { 137 | if (this.currentWindow) { 138 | try { 139 | await browser.windows.remove(this.currentWindow.id); 140 | } catch (e) { 141 | } 142 | this.currentWindow = null; 143 | } else if (this.currentTab) { 144 | await browser.tabs.remove(this.currentTab.id); 145 | this.currentTab = null; 146 | } 147 | } 148 | 149 | async focus() { 150 | if (this.currentWindow) { 151 | try { 152 | await browser.windows.update(this.currentWindow.id, 153 | { 154 | focused: true, 155 | } 156 | ); 157 | } catch (e) { 158 | error(e, "Focusing popup window"); 159 | } 160 | } 161 | } 162 | 163 | async validateCurrent() { 164 | try { 165 | if (this.currentTab) { 166 | await browser.tabs.get(this.currentTab.id); 167 | } 168 | if (this.currentWindow) { 169 | await browser.windows.get(this.currentWindow.id); 170 | } 171 | return promptData; 172 | } catch (e) { 173 | promptData?.done(); 174 | return null; 175 | } 176 | } 177 | } 178 | 179 | var winMan = new WindowManager(); 180 | var id = 0; 181 | var promptDataMap = new Map(); 182 | var Prompts = { 183 | DEFAULTS: { 184 | title: "", 185 | message: "Proceed?", 186 | options: [], 187 | checks: [], 188 | buttons: [_("Ok"), _("Cancel")], 189 | multiple: "close", // or "queue", or "focus" 190 | width: 500, 191 | height: 400, 192 | alwaysOnTop: true, 193 | }, 194 | async prompt(features) { 195 | features = Object.assign({}, this.DEFAULTS, features || {}); 196 | return new Promise((resolve, reject) => { 197 | ++id; 198 | let data = { 199 | id, 200 | features, 201 | result: { 202 | button: -1, 203 | checks: [], 204 | option: null, 205 | }, 206 | done() { 207 | promptDataMap.delete(this.id); 208 | this.done = () => {}; 209 | winMan.close(); 210 | resolve(this.result); 211 | if (promptData === this) { 212 | promptData = null; 213 | if (backlog.length) { 214 | winMan.open(backlog.shift()); 215 | } 216 | } 217 | } 218 | }; 219 | promptDataMap.set(id, data); 220 | if (promptData && winMan.validateCurrent()) { 221 | backlog.push(data); 222 | switch(promptData.features.multiple) { 223 | case "focus": 224 | winMan.focus(); 225 | case "queue": 226 | break; 227 | default: 228 | promptData.done(); 229 | } 230 | } else { 231 | winMan.open(data); 232 | } 233 | }); 234 | }, 235 | 236 | get promptData() { 237 | return promptData; 238 | } 239 | } 240 | 241 | return Prompts; 242 | 243 | })(); 244 | -------------------------------------------------------------------------------- /src/ui/common.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005-2024 Giorgio Maone 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | */ 6 | @import url(../common/themes.css); 7 | 8 | body { 9 | background-color: var(--bg-color2); 10 | color: var(--text-color); 11 | font-size: 12px; 12 | font-family: system-ui, sans-serif; 13 | } 14 | 15 | html.tor:not(.mobile) > body { 16 | /* Tor Browser may fallback to a serif system-ui font, which is ugly */ 17 | font-family: sans-serif; 18 | } 19 | 20 | html.mobile > body { 21 | font-family: Inter, sans-serif; 22 | font-size: 4mm; 23 | } 24 | 25 | html.mobile .desktop { 26 | display: none !important; 27 | } 28 | 29 | html:not(.tor) .tor, html.tor .not-tor { 30 | display: none; 31 | } 32 | html.tor .tor, html:not(.tor) .not-tor { 33 | display: initial; 34 | } 35 | 36 | a, a:visited { 37 | color: var(--accent-color); 38 | } 39 | 40 | button, .button { 41 | appearance: none; 42 | background: var(--form-color1); 43 | border: 1px solid var(--fg-color1); 44 | border-radius: .5em; 45 | color: var(--text-color); 46 | font-weight: bold; 47 | padding: .6em; 48 | cursor: pointer; 49 | text-decoration: none; 50 | } 51 | 52 | input[type="text"], textarea, select { 53 | color: var(--text-color); 54 | background-color: var(--bg-color2); 55 | border: 1px solid var(--fg-color1); 56 | border-radius: .4em; 57 | padding: 0.5em; 58 | margin: 0.5em; 59 | } 60 | 61 | input[type="checkbox"]:not(.https-only, .temp) { 62 | -webkit-appearance: none; 63 | appearance: none; 64 | margin: .4em; 65 | font: inherit; 66 | color: currentColor; 67 | width: 1.2em; 68 | height: 1.2em; 69 | min-width: 1.2em; 70 | border: 0.15em solid currentColor; 71 | border-radius: 0.3em; 72 | transform: translateY(-0.075em); 73 | display: grid; 74 | place-content: center; 75 | background-color: var(--form-color1); 76 | } 77 | 78 | input[type="checkbox"]:not(.https-only, .temp)::before { 79 | content: ""; 80 | width: 0.7em; 81 | height: 0.7em; 82 | clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%); 83 | transform: scale(0); 84 | transform-origin: bottom left; 85 | transition: 120ms transform ease-in-out; 86 | box-shadow: inset 1em 1em var(--form-check-color); 87 | 88 | } 89 | input[type="checkbox"]:not(.https-only, .temp):checked::before { 90 | transform: scale(1); 91 | } 92 | input[type="checkbox"]:not(.https-only, .temp):checked { 93 | background-color: var(--form-check-bg-color); 94 | border-color: var(--form-check-bg-color); 95 | } 96 | 97 | input[type="radio"]:not(.preset) { 98 | -webkit-appearance: none; 99 | appearance: none; 100 | background-color: var(--form-color1); 101 | margin: .4em; 102 | 103 | font: inherit; 104 | color: currentColor; 105 | width: 1.2em; 106 | height: 1.2em; 107 | min-width: 1.2em; 108 | 109 | border: 0.15em solid currentColor; 110 | border-radius: 50%; 111 | transform: translateY(-0.075em); 112 | display: grid; 113 | place-content: center; 114 | } 115 | 116 | input[type="radio"]:not(.preset)::before { 117 | content: ""; 118 | width: 0.7em; 119 | height: 0.7em; 120 | border-radius: 50%; 121 | transform: scale(0); 122 | transition: 120ms transform ease-in-out; 123 | box-shadow: inset 1em 1em var(--form-radio-color); 124 | background-color: CanvasText; 125 | } 126 | 127 | input[type="radio"]:not(.preset):checked::before { 128 | transform: scale(1); 129 | } 130 | input[type="radio"]:not(.preset):checked { 131 | background-color: var(--form-radio-bg-color); 132 | } 133 | 134 | 135 | input:disabled, button:disabled, select:disabled { 136 | filter: grayscale(100%) contrast(33%) !important; 137 | opacity: .7; 138 | } 139 | 140 | :disabled { 141 | cursor: not-allowed !important; 142 | } 143 | 144 | :focus-visible, :is(.cap.needed, .switch):focus-within { 145 | outline: 0; 146 | filter: 147 | drop-shadow(1px 1px 1px var(--focus-color)) 148 | drop-shadow(-1px -1px 1px var(--focus-color)) 149 | drop-shadow(1px -1px 1px var(--focus-color)) 150 | drop-shadow(-1px -1px 1px var(--focus-color)) !important; 151 | } 152 | 153 | :is(label, .full-address, .full-address *):is(:focus, :focus-visible) { 154 | text-shadow: 0 0 .1em var(--focus-color); 155 | filter: none !important; 156 | } 157 | 158 | .donate { 159 | color: var(--text-color1) !important; 160 | border-color: transparent; 161 | background-color: transparent; 162 | transform: scale(1.2); 163 | transition: all .5s ease-in-out; 164 | text-transform: uppercase; 165 | } 166 | 167 | .donate:hover { 168 | transform: scale(1.8); 169 | } 170 | 171 | .donate:before { 172 | content: "♥"; 173 | color: var(--accent-color); 174 | padding: 0 .2em 0 .5em; 175 | text-shadow: 0.04em 0.04em 0.04em #0004; 176 | } -------------------------------------------------------------------------------- /src/ui/options.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005-2024 Giorgio Maone 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | */ 6 | @import url(slider.css); 7 | 8 | body { 9 | background: var(--img-noscript-options) no-repeat fixed top right; 10 | background-size: 7em; 11 | padding: 0 2em 0 0; 12 | margin: 0.5em 0.5em 0.5em 0.5em; 13 | } 14 | 15 | #header { 16 | display: flex; 17 | flex-flow: row wrap; 18 | padding: 0; 19 | margin: 0 6em 0 0; 20 | align-items: center; 21 | justify-content: space-between; 22 | } 23 | #header h1 { 24 | color: var(--accent-color); 25 | text-shadow: 0.06em 0.06em 0.06em #0008; 26 | font-size: 2em; 27 | padding: 0; 28 | margin: 0; 29 | text-align: right; 30 | } 31 | #version { 32 | color: var(--text-color); 33 | font-size: 0.75em; 34 | padding: 0; 35 | margin: 0 0 0.5em; 36 | display: block; 37 | text-align: right; 38 | } 39 | 40 | .buttons { 41 | display: flex; 42 | flex-flow: row wrap; 43 | justify-content: flex-end; 44 | margin: 0 0 1em 0; 45 | } 46 | 47 | .buttons :is(button, .button) { 48 | margin: .5em; 49 | } 50 | 51 | #sect-general { 52 | display: flex; 53 | flex-direction: column; 54 | justify-content: space-around; 55 | font-size: 1em; 56 | } 57 | 58 | section fieldset { 59 | margin: 1em 0; 60 | padding: .5em 1em; 61 | } 62 | 63 | section > form { 64 | padding: 0 .5em; 65 | } 66 | 67 | fieldset:disabled { 68 | opacity: .5; 69 | } 70 | 71 | .sect-sites form { 72 | display: flex; 73 | align-items: baseline; 74 | flex-wrap: wrap; 75 | justify-content: space-between; 76 | } 77 | 78 | .sect-sites form > label { 79 | white-space: nowrap; 80 | } 81 | #newsite { 82 | flex: 2 2; 83 | } 84 | 85 | #policy { 86 | display: block; 87 | margin-top: .5em; 88 | min-height: 20em; 89 | width: 90%; 90 | } 91 | .hide, body:not(.debug) div.debug { 92 | display: none; 93 | } 94 | 95 | #debug-tools { 96 | padding-left: 2.5em; 97 | font-weight: bold; 98 | } 99 | 100 | .error { 101 | background: #ff8; 102 | color: red; 103 | } 104 | 105 | #policy-error { 106 | background: red; 107 | color: #ff8; 108 | padding: 0; 109 | margin: 0; 110 | font-weight: bold; 111 | } 112 | 113 | input, button { 114 | font-size: 1em; 115 | } 116 | 117 | button.add { 118 | font-size: 1.4em; 119 | padding: .2em .4em; 120 | } 121 | 122 | #import-container { 123 | position: relative; 124 | display: flex; 125 | } 126 | #file-import { 127 | position: absolute; 128 | top: 0; 129 | left: 0; 130 | opacity: 0; 131 | width: 100%; 132 | padding: 0; 133 | margin: 0; 134 | border: none; 135 | -moz-appearance: none; 136 | appearance: none; 137 | } 138 | 139 | #xssFaq { 140 | white-space: nowrap; 141 | } 142 | 143 | #clearclick-options { 144 | display: none; 145 | } 146 | 147 | 148 | .flextabs__tab { 149 | /* shift all tabs to appear before content */ 150 | order: -1; 151 | /* let tabs scale to fit multiple on each row */ 152 | width: auto; 153 | margin: 0; 154 | } 155 | .flextabs__content--active { 156 | /* ignore states activated for multi (accordion) toggle view */ 157 | display: none; 158 | } 159 | .flextabs__content--active--last { 160 | /* show the last activated item */ 161 | display: block; 162 | } 163 | 164 | .flextabs__content, .flextabs__toggle[aria-expanded="true"] { 165 | background-color: var(--tab-color2) !important; 166 | border: 0 solid var(--fg--color1); 167 | } 168 | 169 | .flextabs__toggle { 170 | -moz-appearance: none; 171 | border-width: 0 !important; 172 | margin: 0 4px 0 0; 173 | background-color: var(--tab-color1); 174 | outline-width: 0 !important; 175 | border-radius: 1em 1em 0 0; 176 | padding: .4em .8em; 177 | } 178 | 179 | @media (max-width: 630px) { 180 | body { 181 | background-size: 8em ; 182 | padding: 0; 183 | margin: 0; 184 | } 185 | #header { 186 | margin: .5em 8em 0 .5em; 187 | } 188 | } 189 | 190 | @media (max-width: 440px) { 191 | .flextabs__toggle { 192 | border-radius: 1em; 193 | margin: -1em 0 0 0; 194 | } 195 | .flextabs__toggle[aria-expanded="true"] { 196 | font-weight: bold; 197 | box-shadow: 1px 1px 1px inset; 198 | z-index: 1; 199 | position: relative; 200 | } 201 | 202 | body { 203 | background-size: 6em; 204 | } 205 | #header { 206 | margin: 0; 207 | } 208 | #header > .title { 209 | margin-right: 6em; 210 | } 211 | 212 | } 213 | 214 | .flextabs__content { 215 | padding: .5em; 216 | } 217 | 218 | #xss-options { 219 | display: none; 220 | } 221 | 222 | .mozwebext #xss-options { 223 | display: block; 224 | } 225 | 226 | #vintageTheme-opt { 227 | background: url(/img/logo.svg) no-repeat center left, url(/img/vintage/logo.svg) no-repeat center right; 228 | background-size: 2em auto, 2em auto; 229 | } 230 | label[for="opt-vintageTheme"] { 231 | text-indent: -5000px; 232 | padding: 0 1.2em; 233 | } 234 | 235 | .hc #vintageTheme-opt { 236 | background: none; 237 | } 238 | .hc label[for="opt-vintageTheme"] { 239 | display: none !important; 240 | } -------------------------------------------------------------------------------- /src/ui/popup.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005-2024 Giorgio Maone 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | */ 6 | 7 | html:not(.mobile) { 8 | overflow: hidden; 9 | } 10 | 11 | body { 12 | margin: 0.5em; 13 | padding: 0; 14 | } 15 | 16 | html:not(.mobile) > body { 17 | width: var(--popup-size); 18 | padding: 0; 19 | } 20 | 21 | html:not(.mobile) #scrollable { 22 | overflow: auto; 23 | max-height: 550px; 24 | } 25 | 26 | #top { 27 | font-size: 1em; 28 | position: relative; 29 | padding: 0 0 .3em 0; 30 | margin: 0; 31 | min-width: 18.75em; 32 | display: flex; 33 | -moz-user-select: none; 34 | user-select: none; 35 | background: linear-gradient(to bottom, transparent 0, transparent 95%, var(--form-color1) 100%) no-repeat; 36 | } 37 | 38 | .icon { 39 | appearance: none !important; 40 | -webkit-appearance: none !important; 41 | -moz-appearance: none !important; 42 | cursor: pointer; 43 | color: var(--accent-color); 44 | background: transparent no-repeat left; 45 | padding-left: 1.6em; 46 | border: none; 47 | font-size: 1.2em; 48 | background-size: 1.2em; 49 | margin: 0 1.2em 0 1.2em; 50 | } 51 | 52 | #top .icon { 53 | width: var(--icon-size); 54 | height: var(--icon-size); 55 | margin: 0.25em; 56 | cursor: pointer; 57 | font-size: 1em; 58 | font-family: sans-serif; 59 | font-weight: bold; 60 | background-size: contain; 61 | background-position: center; 62 | transform: unset; 63 | transition: transform 0.3s; 64 | border: none; 65 | display: block; 66 | top: 0; 67 | padding: 0; 68 | align-items: center; 69 | line-height: 1em; 70 | position: relative; 71 | border-radius: 0; 72 | } 73 | 74 | #top .icon > div { 75 | position: absolute; 76 | width: 100%; 77 | height: 100%; 78 | background: transparent; 79 | top: 0; 80 | left: 0; 81 | } 82 | 83 | .icon:after { 84 | content: attr(title); 85 | } 86 | 87 | #top > .spacer { 88 | flex-grow: 1; 89 | display: block; 90 | cursor: pointer; 91 | } 92 | 93 | #top > .hider.open ~ .spacer { 94 | display: none; 95 | } 96 | 97 | .hider { 98 | background: var(--form-color1); 99 | box-shadow: inset 0 1px 3px #444; 100 | color: var(--text-color); 101 | border-radius: 1em 1em 0 0; 102 | display: none; 103 | position: relative; 104 | margin: .25em 1.5em; 105 | padding: 0; 106 | height: var(--icon-size); 107 | overflow: hidden; 108 | opacity: .5; 109 | } 110 | 111 | .hider.open { 112 | display: flex; 113 | flex-grow: 1; 114 | opacity: 1; 115 | padding-left: 2em; 116 | } 117 | .hider:hover { 118 | opacity: 1; 119 | } 120 | .hider:not(.open):not(.empty) { 121 | display: block; 122 | text-align: right; 123 | line-height: 1em; 124 | overflow: hidden; 125 | width: 2em; 126 | } 127 | 128 | 129 | .hider-close, .reveal { 130 | -webkit-appearance: none; 131 | -moz-appearance: none; 132 | appearance: none; 133 | border: none; 134 | border-radius: 0; 135 | background: transparent; 136 | color: var(--fg--color1); 137 | font-weight: bold; 138 | display: block; 139 | } 140 | .hider-close:hover, .reveal:hover { 141 | color: var(--accent-color); 142 | text-shadow: 0 0 4px var(--focus-color); 143 | cursor: pointer; 144 | } 145 | 146 | #top .hider .reveal { 147 | font-size: 1.2em; 148 | padding: .2em; 149 | text-align: center; 150 | margin: 0; 151 | } 152 | 153 | .hider-close { 154 | padding: 0; 155 | position: absolute; 156 | left: .2em; 157 | top: 0; 158 | font-size: 1.5em; 159 | z-index: 100; 160 | vertical-align: middle; 161 | padding: .2em; 162 | } 163 | 164 | .hider.open > .reveal, .hider:not(.open) > :not(.reveal) { 165 | display: none !important; 166 | } 167 | 168 | .hider-label { 169 | position: absolute; 170 | z-index: 100; 171 | top: .5em; 172 | right: .5em; 173 | text-align: right; 174 | vertical-align: middle; 175 | line-height: 100%; 176 | font-size: 1em; 177 | font-weight: bold; 178 | pointer-events: none; 179 | } 180 | 181 | .hider > .icon { 182 | opacity: .7; 183 | margin: 0 .25em; 184 | padding: 0; 185 | } 186 | 187 | #top .icon:hover:not(:disabled), #top > #top > .icon:active:not(:disabled) { 188 | outline: 0; 189 | filter: none; 190 | transform: scale(1.2); 191 | } 192 | 193 | #top .icon { 194 | text-indent: -500em; 195 | color: transparent; 196 | } 197 | 198 | #top .icon.drag, #top .drag > .icon { 199 | filter: none !important; 200 | opacity: 0.6 !important; 201 | } 202 | 203 | #revoke-temp { 204 | background-image: var(--img-ui-revoke-temp) !important; 205 | } 206 | #temp-trust-page { 207 | background-image: var(--img-ui-temp-all) !important; 208 | } 209 | 210 | #enforce-tab { 211 | background-image: var(--img-ui-tab-no) !important; 212 | } 213 | #enforce-tab[aria-pressed="true"] { 214 | background-image: var(--img-ui-tab) !important;; 215 | } 216 | 217 | #enforce { 218 | background-image: var(--img-ui-global-no) !important; 219 | } 220 | #enforce[aria-pressed="true"] { 221 | background-image: var(--img-ui-global) !important; 222 | } 223 | 224 | #options { 225 | background-image: var(--img-noscript-options) !important; 226 | } 227 | #close { 228 | background-image: var(--img-ui-close) !important; 229 | } 230 | 231 | #reload { 232 | background-image: var(--img-ui-reload) !important;; 233 | } 234 | 235 | #sites { 236 | margin: 0; 237 | } 238 | 239 | #content { 240 | text-align: center; 241 | } 242 | #buttons { 243 | text-align: center; 244 | margin: 0.5em; 245 | display: flex; 246 | justify-content: space-around; 247 | 248 | } 249 | #buttons button { 250 | flex-grow: 1; 251 | margin: .5em 2em; 252 | } 253 | 254 | .disabled .toggle.icon, .toggle.icon:disabled { 255 | opacity: .4; 256 | pointer-events: none; 257 | } 258 | 259 | #message { 260 | height: auto; 261 | margin: 1.5em 1em; 262 | padding: .8em 0 0.8em 3em; 263 | background-color: var(--bg-color1); 264 | background-size: 2em; 265 | background-position: .5em center; 266 | background-repeat: no-repeat; 267 | min-height: 2em; 268 | font-size: 1.2em; 269 | vertical-align: middle; 270 | white-space: normal; 271 | border-radius: 1em; 272 | box-shadow: 0 0 3px 3px var(--hilite-color); 273 | } 274 | #message.hidden { 275 | display: none; 276 | height: 0; 277 | min-height: 0; 278 | overflow: hidden; 279 | } 280 | .warning { 281 | background-image: var(--img-warning); 282 | } 283 | .error { 284 | background-image: var(--img-error); 285 | } 286 | 287 | #incognito-ui-chooser, html.incognito #message:not(.hidden) ~ #incognito-ui-chooser { 288 | display: none; 289 | } 290 | html.incognito #incognito-ui-chooser { 291 | display: initial; 292 | } 293 | 294 | #incognito-ui-chooser label { 295 | white-space: pre-wrap; 296 | } 297 | 298 | #incognito-ui-chooser input:checked + label { 299 | background: var(--bg-color1); 300 | color: var(--accent-color); 301 | } 302 | -------------------------------------------------------------------------------- /src/ui/popup.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | NoScript Settings 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
31 | 32 |
__MSG_Hider__
33 | × 34 |
35 |
36 | 37 | 38 | 39 | 40 |
41 | 42 |
43 | 44 |
45 | 46 | 47 | 48 | 49 |
50 |
51 | 52 | 53 | 55 | 56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/ui/prompt.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005-2024 Giorgio Maone 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | */ 6 | 7 | 8 | @import url("./common.css"); 9 | 10 | html { 11 | background: linear-gradient(to bottom, var(--form-color1) 0, var(--bg-color2) 41%, var(--bg-color1) 94%, var(--hilite-color) 100%) no-repeat; 12 | background-color: var(--bg-color1); 13 | } 14 | 15 | body { 16 | bottom: .8em; 17 | margin: 0px; 18 | padding: .8em; 19 | background-color: transparent; 20 | } 21 | 22 | html.mobile > body { 23 | font-family: Inter, sans-serif; 24 | font-size: 4vmin; 25 | min-width: auto; 26 | min-height: 90vh; 27 | padding-bottom: 72px; /* clear Fenix's navbar */ 28 | } 29 | 30 | #header { 31 | text-align: left; 32 | margin: 0; 33 | line-height: 2em; 34 | color: var(--accent-color); 35 | z-index: 500; 36 | padding: .8em .8em 0 .8em; 37 | display: block; 38 | background: var(--img-logo) no-repeat top right; 39 | background-size: contain; 40 | min-height: 5em; 41 | } 42 | 43 | #title { 44 | margin-right: 4em; 45 | text-shadow: 0.06em 0.06em 0.06em rgba(0,0,0,.5); 46 | font-size: 2em; 47 | bottom: 0; 48 | top: 0; 49 | } 50 | 51 | #main { 52 | display: flex; 53 | flex-direction: column; 54 | align-items: stretch; 55 | padding: 0 1.6em; 56 | top: 0; 57 | left: 0; 58 | right:0; 59 | bottom: 0; 60 | justify-content: center; 61 | overflow: auto; 62 | } 63 | #message { 64 | flex-grow: 1; 65 | max-height: 28em; 66 | padding: .8em; 67 | text-align: center; 68 | } 69 | #message.multiline { 70 | overflow: auto; 71 | font-size: 1em; 72 | text-align: justify; 73 | margin-bottom: 1.2em; 74 | background: var(--focus-color); 75 | } 76 | #message.multiline p { 77 | margin: .1em; 78 | padding: 0; 79 | } 80 | #options { 81 | display: flex; 82 | flex-grow: 2; 83 | flex-direction: column; 84 | text-align: left; 85 | align-items:baseline; 86 | justify-content: center; 87 | } 88 | 89 | 90 | #checks { 91 | display: flex; 92 | flex-direction: column; 93 | flex-grow: 1; 94 | text-align: left; 95 | } 96 | 97 | .choices div { 98 | display: flex; 99 | flex-direction: row; 100 | align-items: baseline; 101 | } 102 | .choices label { 103 | display: block; 104 | word-break: break-all; 105 | } 106 | 107 | #buttons { 108 | display: flex; 109 | flex-grow: 0; 110 | flex-direction: row; 111 | align-items: center; 112 | margin: .8em; 113 | justify-content: space-around; 114 | } 115 | #buttons button { 116 | min-width: 6em; 117 | font-size: 1em; 118 | } 119 | 120 | input[type="checkbox"], input[type="radio"] { 121 | min-width: 1em; 122 | min-height: 1em; 123 | font-size: 1em; 124 | } -------------------------------------------------------------------------------- /src/ui/prompt.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 |
28 |
29 |
30 |
31 | 32 |
33 |
34 | 35 |
36 |
37 | 38 |
39 |
40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/ui/prompt.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | (async () => { 22 | let data = await Messages.send("getPromptData"); 23 | debug("Prompt data", data); 24 | if (!data) { 25 | error("Missing promptData"); 26 | window.close(); 27 | return; 28 | } 29 | 30 | let done = async () => { 31 | await Messages.send("promptDone", data); 32 | done = () => {}; 33 | if ("windows" in browser) { 34 | try { 35 | await browser.windows.remove((await browser.windows.getCurrent()).id); 36 | } catch (e) { 37 | console.error(e); 38 | } 39 | } 40 | window.close(); 41 | } 42 | 43 | let {title, message, options, checks, buttons} = data.features; 44 | 45 | function labelFor(el, text) { 46 | let label = document.createElement("label"); 47 | label.setAttribute("for", el.id); 48 | label.textContent = text; 49 | return label; 50 | } 51 | 52 | function createInput(container, {label, type, name, checked}, count) { 53 | let input = document.createElement("input"); 54 | input.type = type; 55 | input.value = count; 56 | input.name = name; 57 | input.checked = checked; 58 | input.id = `${name}-${count}`; 59 | let sub = document.createElement("div"); 60 | sub.appendChild(input); 61 | sub.appendChild(labelFor(input, label)); 62 | container.appendChild(sub); 63 | } 64 | 65 | function createButton(container, label, count) { 66 | let button = document.createElement("button"); 67 | if (count === 0) button.type = "submit"; 68 | button.id = `${button}-${count}`; 69 | button.value = count; 70 | button.textContent = label; 71 | container.appendChild(button); 72 | } 73 | 74 | function renderInputs(container, dataset, type, name) { 75 | if (typeof container === "string") { 76 | container = document.querySelector(container); 77 | } 78 | if (typeof dataset === "string") { 79 | container.innerHTML = dataset; 80 | return; 81 | } 82 | container.innerHTML = ""; 83 | let count = 0; 84 | if (dataset && dataset[Symbol.iterator]) { 85 | let create = type === "button" ? createButton : createInput; 86 | for (let data of dataset) { 87 | data.type = type; 88 | data.name = name; 89 | create(container, data, count++); 90 | } 91 | } 92 | } 93 | if (title) { 94 | document.title = title; 95 | document.querySelector("#title").textContent = title; 96 | } 97 | if (message) { 98 | let lines = message.split(/\n/); 99 | let container = document.querySelector("#message"); 100 | container.classList.toggle("multiline", lines.length > 1); 101 | message.innerHTML = ""; 102 | for (let l of lines) { 103 | let p = document.createElement("p"); 104 | p.textContent = l; 105 | container.appendChild(p); 106 | } 107 | } 108 | renderInputs("#options", options, "radio", "opt"); 109 | renderInputs("#checks", checks, "checkbox", "flag"); 110 | renderInputs("#buttons", buttons, "button", "button"); 111 | addEventListener("hide", e => { 112 | done(); 113 | }); 114 | 115 | let buttonClicked = e => { 116 | let {result} = data; 117 | result.button = parseInt(e.currentTarget.value); 118 | let option = document.querySelector('#options [type="radio"]:checked'); 119 | result.option = option && parseInt(option.value); 120 | result.checks = [...document.querySelectorAll('#checks [type="checkbox"]:checked')] 121 | .map(c => parseInt(c.value)); 122 | done(); 123 | }; 124 | for (let b of document.querySelectorAll("#buttons button")) { 125 | b.addEventListener("click", buttonClicked); 126 | } 127 | 128 | addEventListener("keydown", e => { 129 | if (e.ctrlKey || e.metaKey || e.shiftKey) return; 130 | switch(e.code) { 131 | case "Escape": 132 | window.close(); 133 | return; 134 | case "Enter": 135 | let defButton = document.querySelector("#buttons button[type=submit]"); 136 | if (defButton) defButton.click(); 137 | return; 138 | } 139 | }); 140 | 141 | let fitHeight = async e => { 142 | if (!("windows" in browser)) { 143 | // tabbed (mobile?) - ensure buttons are visible 144 | document.querySelector("#buttons").scrollIntoView(); 145 | return; 146 | } 147 | let win = await browser.windows.getCurrent(); 148 | let delta = document.documentElement.offsetHeight - window.innerHeight; 149 | for (let attempts = 2; attempts-- > 0;) { 150 | await browser.windows.update(win.id, { 151 | height: win.height + delta, 152 | top: win.top - Math.round(delta / 2), 153 | }); 154 | } 155 | } 156 | if (document.readyState === "complete") { 157 | fitHeight(); 158 | } else { 159 | window.addEventListener("load", fitHeight); 160 | } 161 | })(); 162 | -------------------------------------------------------------------------------- /src/ui/resize_hack.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | if ("windows" in browser) document.addEventListener("DOMContentLoaded", async e => { 22 | // Fix for Fx57 bug where bundled page loaded using 23 | // browser.windows.create won't show contents unless resized. 24 | // See https://bugzilla.mozilla.org/show_bug.cgi?id=1402110 25 | let win = await browser.windows.getCurrent({populate: true}); 26 | if (win.tabs[0].url === document.URL) { 27 | let bounds = decodeURIComponent(location.href).match(/\bwinbounds=(\{[^}]*"width":[^}]+\})/); 28 | try { 29 | bounds = bounds && JSON.parse(bounds[1]); 30 | } catch (e) { 31 | bounds = null; 32 | } 33 | let {width} = bounds || win; 34 | debug("Resize hack", win, bounds); // DEV_ONLY 35 | await browser.windows.update(win.id, { 36 | width: width + 1 37 | }); 38 | await browser.windows.update(win.id, { 39 | width 40 | }); 41 | } 42 | }); 43 | -------------------------------------------------------------------------------- /src/ui/siteInfo.html: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/ui/siteInfo.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | (async () => { 22 | let [domain, tabId] = decodeURIComponent(location.hash.replace("#", "")).split(";"); 23 | const BASE = "https://noscript.net"; 24 | await include([ 25 | '/nscl/lib/punycode.js', 26 | '/nscl/common/Storage.js' 27 | ]); 28 | let {siteInfoConsent} = await Storage.get("sync", "siteInfoConsent"); 29 | if (!siteInfoConsent) { 30 | await include('/nscl/common/locale.js'); 31 | siteInfoConsent = confirm(_("siteInfo_confirm", [domain, BASE])); 32 | if (siteInfoConsent) { 33 | await Storage.set("sync", {siteInfoConsent}); 34 | } else { 35 | let current = await browser.tabs.getCurrent(); 36 | await browser.tabs.update(parseInt(tabId), {active: true}); 37 | await browser.tabs.remove(current.id); 38 | return; 39 | } 40 | } 41 | let ace = punycode.toASCII(domain); 42 | location.href = `${BASE}/about/${domain};${ace}`; 43 | })(); 44 | -------------------------------------------------------------------------------- /src/ui/slider.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005-2024 Giorgio Maone 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | */ 6 | 7 | .switch { 8 | position: relative; 9 | display: inline-block; 10 | width: 2.4em; 11 | height: 1.4em; 12 | margin: .2em; 13 | } 14 | 15 | .hc .switch { 16 | position: static; 17 | padding: 0 !important; 18 | margin: 0; 19 | } 20 | 21 | :root:not(.hc) .switch input { 22 | opacity: 0; 23 | width: 0; 24 | height: 0; 25 | } 26 | 27 | .hc .slider { 28 | display: none; 29 | } 30 | 31 | .slider { 32 | position: absolute; 33 | cursor: pointer; 34 | top: 0; 35 | left: 0; 36 | right: 0; 37 | bottom: 0; 38 | background-color: var(--form-color1); 39 | transition: .4s; 40 | border: .1em solid currentColor; 41 | } 42 | 43 | 44 | .switch .inner-label { 45 | display: none; 46 | } 47 | 48 | .hc .switch .inner-label { 49 | display: inline; 50 | padding: 0 .5em 0 0; 51 | } 52 | 53 | 54 | .slider:before { 55 | position: absolute; 56 | content: ""; 57 | height: 1em; 58 | width: 1em; 59 | left: .15em; 60 | bottom: .15em; 61 | background-color: currentColor; 62 | transition: .4s; 63 | } 64 | 65 | input:checked + .slider { 66 | background-color: var(--form-radio-bg-color); 67 | } 68 | 69 | 70 | input:checked + .slider:before { 71 | transform: translateX(1em); 72 | background-color: var(--form-radio-color); 73 | } 74 | 75 | /* Rounded sliders */ 76 | .slider.round { 77 | border-radius: 1.5em; 78 | } 79 | 80 | .slider.round:before { 81 | border-radius: 50%; 82 | } -------------------------------------------------------------------------------- /src/ui/toolbar.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | UI.toolbarInit = () => { 22 | if (UI.toolbarInit.done || UI.highContrast || UI.local.highContrast) 23 | return; 24 | UI.toolbarInit.done = true; 25 | let toolbar = document.getElementById("top"); 26 | let spacer = toolbar.querySelector(".spacer"); 27 | let hider = toolbar.querySelector(".hider"); 28 | 29 | if (UI.local.toolbarLayout) { 30 | let {left, right, hidden} = UI.local.toolbarLayout; 31 | for (let id of left) { 32 | toolbar.insertBefore(document.getElementById(id), hider); 33 | } 34 | for (let id of right) { 35 | toolbar.appendChild(document.getElementById(id)); 36 | } 37 | for (let id of hidden) { 38 | hider.appendChild(document.getElementById(id)); 39 | } 40 | } 41 | 42 | 43 | let makeDraggable = b => { 44 | // work-around for dragging disabled buttons 45 | let wrapper = document.createElement("div"); 46 | b.replaceWith(wrapper); 47 | // work-around for dragging empty (padding only) elements 48 | b.innerHTML = "
"; 49 | wrapper.appendChild(b); 50 | b = wrapper; 51 | b.setAttribute("draggable", "true"); 52 | } 53 | 54 | let toggleHider = b => { 55 | let cl = hider.classList; 56 | cl.toggle("open", b); 57 | cl.toggle("empty", !hider.querySelector(".icon")); 58 | } 59 | hider.querySelector(".hider-close").onclick = e => { 60 | toggleHider(false); 61 | }; 62 | 63 | toggleHider(false); 64 | 65 | let dnd = { 66 | dragstart(ev) { 67 | if (hider.querySelectorAll(".icon").length) { 68 | toggleHider(true); 69 | } 70 | let button = ev.target.querySelector(".icon"); 71 | if (!button) { 72 | ev.preventDefault(); 73 | return; 74 | } 75 | 76 | // work-around for Firefox unable to drag buttons, https://bugzilla.mozilla.org/show_bug.cgi?id=568313 77 | let placeHolder = document.createElement("div"); 78 | let {style} = placeHolder; 79 | style.backgroundImage = getComputedStyle(button).backgroundImage; 80 | style.backgroundSize = "contain"; 81 | let width = button.offsetWidth * 1.2; 82 | let height = button.offsetHeight * 1.2; 83 | style.width =`${width}px`; 84 | style.height = `${height}px` 85 | style.position = "absolute"; 86 | style.top = "-2000px"; 87 | toolbar.appendChild(placeHolder); 88 | setTimeout(() => placeHolder.remove(), 0); 89 | 90 | let dt = ev.dataTransfer; 91 | dt.setData("text/plain", button.id); 92 | dt.dropEffect = "move"; 93 | 94 | dt.setDragImage(placeHolder, width / 2, height / 2); 95 | 96 | toggleHider(true); 97 | this.draggedElement = ev.target; // the draggable wrapper around the button 98 | this.draggedElement.classList.add("drag"); 99 | }, 100 | dragend(ev) { 101 | this.draggedElement.classList.remove("drag"); 102 | this.draggedElement = null; 103 | }, 104 | dragover(ev) { 105 | ev.preventDefault(); 106 | }, 107 | dragenter(ev) { 108 | }, 109 | dragleave(ev) { 110 | }, 111 | drop(ev) { 112 | let t = ev.target; 113 | let d = this.draggedElement; 114 | if (!d) return; 115 | 116 | switch(t) { 117 | case hider: 118 | t.appendChild(d); 119 | break; 120 | default: 121 | if (!t.closest("#top")) return; // outside the toolbar? 122 | let stop = null; 123 | for (let c of toolbar.children) { 124 | if (ev.clientX < c.offsetLeft + c.offsetWidth / 2) { 125 | stop = c; 126 | break; 127 | } 128 | } 129 | toolbar.insertBefore(d, stop); 130 | } 131 | 132 | let left = [], right = []; 133 | let side = left; 134 | for (let el of toolbar.querySelectorAll(":scope > .spacer, :scope > [draggable] > .icon")) { 135 | if (el === spacer) { 136 | side = right; 137 | } else { 138 | side.push(el.id); 139 | } 140 | } 141 | UI.local.toolbarLayout = { 142 | left, right, 143 | hidden: Array.from(toolbar.querySelectorAll(".hider .icon")).map(el => el.id), 144 | }; 145 | 146 | debug("%o", UI.local); 147 | UI.updateSettings({local: UI.local}); 148 | }, 149 | 150 | click(ev) { 151 | let el = ev.target; 152 | if (el === spacer || el.classList.contains("reveal")) { 153 | toggleHider(true); 154 | } 155 | } 156 | 157 | }; 158 | 159 | 160 | for (let [action, handler] of Object.entries(dnd)) { 161 | toolbar.addEventListener(action, handler, true); 162 | } 163 | 164 | for (let b of toolbar.querySelectorAll(".icon")) { 165 | makeDraggable(b); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/ui/ui-hc.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005-2024 Giorgio Maone 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | */ 6 | 7 | input { 8 | transform: none !important; 9 | width: auto !important; 10 | min-width: auto !important; 11 | position: static !important; 12 | } 13 | 14 | input[type="radio"] { 15 | appearance: radio !important; 16 | -webkit-appearance: radio !important; 17 | -moz-appearance: radio !important; 18 | padding-right: .2em !important; 19 | } 20 | 21 | input[type="checkbox"] { 22 | appearance: checkbox !important; 23 | -webkit-appearance: checkbox !important; 24 | -moz-appearance: checkbox !important; 25 | display: initial !important; 26 | } 27 | 28 | 29 | input.preset { 30 | margin: 0 .5em !important; 31 | padding: 0 !important; 32 | } 33 | 34 | button { 35 | text-indent: 0 !important; 36 | } 37 | 38 | label { 39 | display: initial !important; 40 | position: static !important; 41 | transform: none !important; 42 | opacity: 1 !important; 43 | text-indent: 0 !Important; 44 | position: static; 45 | width: auto !important; 46 | padding: 4px !important; 47 | } 48 | 49 | .full-address { 50 | display: block; 51 | } 52 | 53 | .presets { 54 | display: flex; 55 | flex-wrap: wrap; 56 | } 57 | 58 | span.preset, .url { 59 | display: flex; 60 | align-items: center; 61 | } 62 | 63 | input.temp { 64 | position: static !important; 65 | opacity: 1 !important; 66 | } 67 | 68 | .full-address { 69 | font-size: 130%; 70 | } 71 | 72 | tr.site { 73 | border-top: 1px solid #888; 74 | display: flex; 75 | align-items: center; 76 | } 77 | 78 | #top { 79 | display: flex; 80 | flex-flow: row; 81 | justify-content: space-around; 82 | height: auto; 83 | } 84 | #top .icon { 85 | position: static; 86 | width: auto; 87 | appearance: initial !important; 88 | -moz-appearance: initial !important; 89 | width: auto; 90 | height: auto; 91 | display: inline-flex !important; 92 | font-size: 12px !important; 93 | font-family: sans-serif !important; 94 | text-indent: 0; 95 | color: var(--fg-color1) !important; 96 | background: var(--bg-color2) !important; 97 | background-image: none !important; 98 | border-radius: .3em; 99 | text-align: center; 100 | border: 1px solid var(--fg-color1); 101 | height: auto; 102 | padding: .2em; 103 | } 104 | #top a.icon:hover { 105 | transform: none; 106 | } 107 | 108 | #noscript-popup #high-contrast-chooser { 109 | display: block; 110 | } 111 | 112 | :focus { 113 | outline: max(2px, 0.15em) solid currentColor; 114 | outline-offset: max(2px, 0.15em); 115 | } 116 | -------------------------------------------------------------------------------- /src/ui/whirlpool.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005-2024 Giorgio Maone 3 | * 4 | * SPDX-License-Identifier: GPL-3.0-or-later 5 | */ 6 | 7 | .cssload-container{ 8 | position:relative; 9 | } 10 | 11 | .cssload-whirlpool, 12 | .cssload-whirlpool::before, 13 | .cssload-whirlpool::after { 14 | position: absolute; 15 | top: 50%; 16 | left: 50%; 17 | border: 1px solid rgb(204,204,204); 18 | border-left-color: rgb(0,0,0); 19 | border-radius: 974px; 20 | } 21 | 22 | .cssload-whirlpool { 23 | margin: -24px 0 0 -24px; 24 | height: 49px; 25 | width: 49px; 26 | animation: cssload-rotate 1150ms linear infinite; 27 | } 28 | 29 | .cssload-whirlpool::before { 30 | content: ""; 31 | margin: -22px 0 0 -22px; 32 | height: 43px; 33 | width: 43px; 34 | animation: cssload-rotate 1150ms linear infinite; 35 | } 36 | 37 | .cssload-whirlpool::after { 38 | content: ""; 39 | margin: -28px 0 0 -28px; 40 | height: 55px; 41 | width: 55px; 42 | animation: cssload-rotate 2300ms linear infinite; 43 | } 44 | 45 | 46 | @keyframes cssload-rotate { 47 | 100% { 48 | transform: rotate(360deg); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/xss/Exceptions.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | 'use strict'; 22 | 23 | XSS.Exceptions = (() => { 24 | 25 | var Exceptions = { 26 | 27 | async shouldIgnore(xssReq) { 28 | function logEx(...args) { 29 | debug("[XSS preprocessing] Ignoring %o", xssReq, ...args); 30 | } 31 | debug(`Processing exceptions for `, xssReq); // DEV_ONLY 32 | let { 33 | isCrossSite, 34 | srcObj, 35 | destObj, 36 | srcUrl, 37 | destUrl, 38 | srcOrigin, 39 | destOrigin, 40 | unescapedDest, 41 | isGet, 42 | isPost 43 | } = xssReq; 44 | 45 | // let same-site requests alone 46 | if (!isCrossSite || 47 | /^https:/.test(srcOrigin) && xssReq.srcDomain === xssReq.destDomain) { 48 | return true; 49 | } 50 | 51 | // same domain + https: source 52 | if (/^https:/.test(srcOrigin) && xssReq.srcDomain === xssReq.destDomain) { 53 | return true; 54 | } 55 | 56 | if (/^(?:chrome|resource|moz-extension|about):/.test(srcOrigin)) { 57 | debug("Privileged origin", srcOrigin); // DEV_ONLY 58 | } 59 | 60 | if (!srcOrigin && isGet) { 61 | if (/^https?:\/\/msdn\.microsoft\.com\/query\/[^<]+$/.test(unescapedDest)) { 62 | return true; // MSDN from Microsoft VS 63 | } 64 | } 65 | 66 | if (srcOrigin) { // srcUrl-specific exceptions 67 | 68 | if (/^about:(?!blank)/.test(srcOrigin)) 69 | return true; // any about: URL except about:blank 70 | 71 | if (srcOrigin === "https://www.youtube.com" && 72 | /^https:\/\/(?:plus\.googleapis|apis\.google)\.com\/[\w/]+\/widget\/render\/comments\?/.test(destUrl)) { 73 | logEx("YouTube comments exception"); 74 | return true; 75 | } 76 | 77 | // for any HTTPS origin... 78 | if (srcOrigin.startsWith("https://")) { 79 | // uptain.de e-commerce back-end 80 | if (destUrl.startsWith("https://app.uptain.de/static/index.html")) { 81 | return true; 82 | } 83 | } 84 | 85 | if (isPost) { 86 | 87 | if (/^https:\/\/(?:twitter|x).com$/.test(srcOrigin) && 88 | /^https:\/\/.*\.(?:twitter|x)\.com$/.test(destOrigin)) { 89 | return true; 90 | } 91 | 92 | { 93 | let rx = /^https:\/\/(?:[a-z]+\.)?unionbank\.com$/; 94 | if (rx.test(srcOrigin) && rx.test(destOrigin)) { 95 | return true; 96 | } 97 | } 98 | 99 | if (/^https?:\/\/csr\.ebay\.(?:\w{2,3}|co\.uk)\/cse\/start\.jsf$/.test(srcUrl) && 100 | /^https?:\/\/msa-lfn\.ebay\.(?:\w{2,3}|co\.uk)\/ws\/eBayISAPI\.dll\?[^<'"%]*$/.test(unescapedDest) && 101 | destObj.protocol === srcObj.protocol) { 102 | logEx("Ebay exception"); 103 | return true; 104 | } 105 | 106 | if (/^https:\/\/(?:cap\.securecode\.com|www\.securesuite\.net|(?:.*?\.)?firstdata\.com)$/.test(srcUrl)) { 107 | logEx("Verified by Visa exception"); 108 | return true; 109 | } 110 | 111 | if (/^https?:\/\/mail\.lycos\.com\/lycos\/mail\/MailCompose\.lycos$/.test(srcUrl) && 112 | /\.lycosmail\.lycos\.com$/.test(destOrigin)) { 113 | logEx("Lycos Mail exception"); 114 | return true; 115 | } 116 | 117 | if (/^https:.*\.livejournal\.com$/.test(srcOrigin) && 118 | /^https:\/\/www\.livejournal\.com\/talkpost_do\.bml$/.test(destUrl)) { 119 | logEx("Livejournal comments exception"); 120 | return true; 121 | } 122 | 123 | if (/^https:\/\/(?:draft|www)\.blogger\.com\/template-editor\.g\?/.test(srcUrl) && 124 | /^https:\/\/[\w\-]+\.blogspot\.com\/b\/preview\?/.test(destUrl) 125 | ) { 126 | logEx("blogspot.com template preview exception"); 127 | return true; 128 | } 129 | } 130 | } 131 | }, 132 | 133 | isBadException(host) { 134 | // TLD check for Google search 135 | let m = host.match(/\bgoogle\.((?:[a-z]{1,3}\.)?[a-z]+)$/i); 136 | return m && tld.getPublicSuffix(host) != m[1]; 137 | }, 138 | 139 | partial(xssReq) { 140 | let { 141 | srcObj, 142 | destObj, 143 | srcUrl, 144 | destUrl, 145 | srcOrigin, 146 | destOrigin, 147 | } = xssReq; 148 | 149 | let skipParams, skipRx; 150 | if (/^https:\/\/www\.paypal\.com\/(?:[\w\-]+\/)?cgi-bin\/webscr\b/.test(destUrl)) { 151 | // Paypal buttons encrypted parameter causes a DOS, strip it out 152 | skipParams = ['encrypted']; 153 | } else if (/\.adnxs\.com$/.test(srcOrigin) && /\.adnxs\.com$/.test(destOrigin)) { 154 | skipParams = ['udj']; 155 | } else if (/^https?:\/\/www\.mendeley\.com\/import\/bookmarklet\/$/.test(destUrl)) { 156 | skipParams = ['html']; 157 | } else if (destObj.hash && /^https:/.test(srcOrigin) && 158 | (/^https?:\/\/api\.facebook\.com\//.test(srcUrl) || 159 | /^https:\/\/tbpl\.mozilla\.org\//.test(srcUrl) || // work-around for hg reftest DOS 160 | /^https:\/\/[^\/]+\.googleusercontent\.com\/gadgets\/ifr\?/.test(destUrl) // Google gadgets 161 | )) { 162 | skipRx = /#[^#]+$/; // remove receiver's hash 163 | } else if (/^https?:\/\/apps\.facebook\.com\//.test(srcUrl)) { 164 | skipRx = /&invite_url=javascript[^&]+/; // Zynga stuff 165 | } else if (/^https?:\/\/l\.yimg\.com\/j\/static\/frame\?e=/.test(destUrl) && 166 | /\.yahoo\.com$/.test(srcOrigin)) { 167 | skipParams = ['e']; 168 | } else if (/^https?:\/\/wpcomwidgets\.com\/\?/.test(destUrl)) { 169 | skipParams = ["_data"]; 170 | } else if (/^https:\/\/docs\.google\.com\/picker\?/.test(destUrl)) { 171 | skipParams = ["nav", "pp"]; 172 | } else if (/^https:\/\/.*[\?&]scope=/.test(destUrl)) { 173 | skipRx = /[\?&]scope=[+\w]+(?=&|$)/; 174 | } 175 | if (skipParams) { 176 | skipRx = new RegExp("(?:^|[&?])(?:" + skipParams.join('|') + ")=[^&]+", "g"); 177 | } 178 | return { 179 | skipParams, 180 | skipRx 181 | }; 182 | } 183 | 184 | }; 185 | return Exceptions; 186 | })(); 187 | -------------------------------------------------------------------------------- /src/xss/InjectionCheckWorker.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | let include = src => { 22 | if (Array.isArray(src)) importScripts(...src); 23 | else importScripts(src); 24 | } 25 | 26 | let XSS = {}; 27 | include("/nscl/common/log.js"); 28 | 29 | for (let logType of ["log", "debug", "error"]) { 30 | this[logType] = (...log) => { 31 | postMessage({log, logType}); 32 | } 33 | } 34 | 35 | include("InjectionChecker.js"); 36 | 37 | { 38 | const timingsMap = new Map(); 39 | 40 | const Handlers = { 41 | async check({xssReq, skip}) { 42 | let {destUrl, request, debugging} = xssReq; 43 | let { 44 | skipParams, 45 | skipRx 46 | } = skip; 47 | let ic = new (await XSS.InjectionChecker)(); 48 | 49 | if (debugging) { 50 | ic.debugging = true; 51 | debug("[XSS] InjectionCheckWorker started in %s ms (%s).", 52 | Date.now() - xssReq.timestamp, destUrl); 53 | } else { 54 | debug = () => {}; 55 | } 56 | 57 | let {timing} = ic; 58 | timingsMap.set(request.requestId, timing); 59 | timing.pauseTime = 0; // skip the default 20ms nap 60 | 61 | let postInjection = xssReq.isPost && 62 | request.requestBody && request.requestBody.formData && 63 | await ic.checkPost(request.requestBody.formData, skipParams); 64 | 65 | let protectName = ic.nameAssignment; 66 | let urlInjection = await ic.checkUrl(destUrl, skipRx); 67 | protectName = protectName || ic.nameAssignment; 68 | if (timing.tooLong) { 69 | log("[XSS] Long check (%s ms) - %s", timing.elapsed, JSON.stringify(xssReq)); 70 | } else if (debugging) { 71 | debug("[XSS] InjectionCheckWorker done in %s ms (%s).", 72 | Date.now() - xssReq.timestamp, destUrl); 73 | } 74 | 75 | postMessage(!(protectName || postInjection || urlInjection) 76 | ? { xss: false } 77 | : { xss: true, protectName, postInjection, urlInjection } 78 | ); 79 | }, 80 | 81 | requestDone({requestId}) { 82 | let timing = timingsMap.get(requestId); 83 | if (timing) { 84 | timing.interrupted = true; 85 | timingsMap.delete(requestId); 86 | } 87 | } 88 | } 89 | 90 | onmessage = async e => { 91 | let msg = e.data; 92 | if (msg.handler in Handlers) try { 93 | await Handlers[msg.handler](msg); 94 | } catch (e) { 95 | postMessage({error: `${e.message}\n${e.stack}`}); 96 | } 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/xss/sanitizeName.js: -------------------------------------------------------------------------------- 1 | /* 2 | * NoScript - a Firefox extension for whitelist driven safe JavaScript execution 3 | * 4 | * Copyright (C) 2005-2024 Giorgio Maone 5 | * 6 | * SPDX-License-Identifier: GPL-3.0-or-later 7 | * 8 | * This program is free software: you can redistribute it and/or modify it under 9 | * the terms of the GNU General Public License as published by the Free Software 10 | * Foundation, either version 3 of the License, or (at your option) any later 11 | * version. 12 | * 13 | * This program is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 | * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License along with 18 | * this program. If not, see . 19 | */ 20 | 21 | ns.on("capabilities", event => { 22 | if (ns.allows("script")) { 23 | let dangerousRx = /[<"'\`(=:]/g; 24 | if (/[<"'\`(=:]/.test(window.name)) { 25 | console.log(`NoScript XSS filter sanitizing suspicious window.name "%s" on %s`, window.name, document.URL); 26 | window.name = window.name.replace(dangerousRx, ''); 27 | } 28 | } 29 | }); 30 | --------------------------------------------------------------------------------