From b9ad3cdde7bd29849b59561fdd9c4bb425bca01b Mon Sep 17 00:00:00 2001 From: KOLANICH Date: Tue, 15 Dec 2020 13:03:17 +0300 Subject: [PATCH] Initial commit --- .ci/aptPackagesToInstall.txt | 1 + .ci/pythonPackagesToInstallFromGit.txt | 1 + .editorconfig | 12 ++ .github/.templateMarker | 1 + .github/dependabot.yml | 8 ++ .github/workflows/CI.yml | 15 +++ .gitignore | 13 +++ .gitlab-ci.yml | 51 ++++++++ Code_Of_Conduct.md | 1 + MANIFEST.in | 4 + ReadMe.md | 106 +++++++++++++++++ UNLICENSE | 24 ++++ firefucks/__init__.py | 156 +++++++++++++++++++++++++ firefucks/__main__.py | 18 +++ firefucks/constants.py | 6 + firefucks/json.py | 5 + firefucks/patcher.py | 78 +++++++++++++ firefucks/preset.json | 7 ++ firefucks/py.typed | 0 pyproject.toml | 43 +++++++ snippet.js | 18 +++ tests/omni.ja | Bin 0 -> 872 bytes tests/tests.py | 25 ++++ 23 files changed, 593 insertions(+) create mode 100644 .ci/aptPackagesToInstall.txt create mode 100644 .ci/pythonPackagesToInstallFromGit.txt create mode 100644 .editorconfig create mode 100644 .github/.templateMarker create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/CI.yml create mode 100644 .gitignore create mode 100644 .gitlab-ci.yml create mode 100644 Code_Of_Conduct.md create mode 100644 MANIFEST.in create mode 100644 ReadMe.md create mode 100644 UNLICENSE create mode 100644 firefucks/__init__.py create mode 100644 firefucks/__main__.py create mode 100644 firefucks/constants.py create mode 100644 firefucks/json.py create mode 100644 firefucks/patcher.py create mode 100644 firefucks/preset.json create mode 100644 firefucks/py.typed create mode 100644 pyproject.toml create mode 100644 snippet.js create mode 100644 tests/omni.ja create mode 100755 tests/tests.py diff --git a/.ci/aptPackagesToInstall.txt b/.ci/aptPackagesToInstall.txt new file mode 100644 index 0000000..b9ca3b6 --- /dev/null +++ b/.ci/aptPackagesToInstall.txt @@ -0,0 +1 @@ +libzip4 diff --git a/.ci/pythonPackagesToInstallFromGit.txt b/.ci/pythonPackagesToInstallFromGit.txt new file mode 100644 index 0000000..bd79dda --- /dev/null +++ b/.ci/pythonPackagesToInstallFromGit.txt @@ -0,0 +1 @@ +https://github.com/KOLANICH-libs/libzip.py diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c9162b9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +indent_style = tab +indent_size = 4 +insert_final_newline = true +end_of_line = lf + +[*.{yml,yaml}] +indent_style = space +indent_size = 2 diff --git a/.github/.templateMarker b/.github/.templateMarker new file mode 100644 index 0000000..5e3a3e0 --- /dev/null +++ b/.github/.templateMarker @@ -0,0 +1 @@ +KOLANICH/python_project_boilerplate.py diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..89ff339 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "daily" + allow: + - dependency-type: "all" diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..7fe33b3 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,15 @@ +name: CI +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + build: + runs-on: ubuntu-22.04 + steps: + - name: typical python workflow + uses: KOLANICH-GHActions/typical-python-workflow@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89630d5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +__pycache__ +*.pyc +*.pyo +/*.egg-info +*.srctrlbm +*.srctrldb +build +dist +.eggs +monkeytype.sqlite3 +/.ipynb_checkpoints +omni.ja +/omni diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..8d4d1b3 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,51 @@ +image: registry.gitlab.com/kolanich-subgroups/docker-images/fixed_python:latest + +variables: + DOCKER_DRIVER: overlay2 + SAST_ANALYZER_IMAGE_TAG: latest + SAST_DISABLE_DIND: "true" + SAST_CONFIDENCE_LEVEL: 5 + CODECLIMATE_VERSION: latest + +include: + - template: SAST.gitlab-ci.yml + - template: Code-Quality.gitlab-ci.yml + - template: License-Management.gitlab-ci.yml + +build: + tags: + - shared + - linux + stage: build + variables: + GIT_DEPTH: "1" + PYTHONUSERBASE: ${CI_PROJECT_DIR}/python_user_packages + + before_script: + - export PATH="$PATH:$PYTHONUSERBASE/bin" # don't move into `variables` + - apt-get update + # todo: + #- apt-get -y install + #- pip3 install --upgrade + #- python3 ./fix_python_modules_paths.py + + script: + - python3 -m build -nw bdist_wheel + - mv ./dist/*.whl ./dist/firefucks-0.CI-py3-none-any.whl + - pip3 install --upgrade ./dist/*.whl + - coverage run --source=firefucks -m --branch pytest --junitxml=./rspec.xml ./tests/test.py + - coverage report -m + - coverage xml + + coverage: "/^TOTAL(?:\\s+\\d+){4}\\s+(\\d+%).+/" + + cache: + paths: + - $PYTHONUSERBASE + + artifacts: + paths: + - dist + reports: + junit: ./rspec.xml + cobertura: ./coverage.xml diff --git a/Code_Of_Conduct.md b/Code_Of_Conduct.md new file mode 100644 index 0000000..bcaa2bf --- /dev/null +++ b/Code_Of_Conduct.md @@ -0,0 +1 @@ +No codes of conduct! \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..20f0fa8 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,4 @@ +include UNLICENSE +include *.md +include tests +include .editorconfig diff --git a/ReadMe.md b/ReadMe.md new file mode 100644 index 0000000..5756348 --- /dev/null +++ b/ReadMe.md @@ -0,0 +1,106 @@ +firefucks.py [![Unlicensed work](https://raw.githubusercontent.com/unlicense/unlicense.org/master/static/favicon.png)](https://unlicense.org/) +============ +~~[wheel (GitLab)](https://gitlab.com/KOLANICH-tools/firefucks.py/-/jobs/artifacts/master/raw/dist/firefucks-0.CI-py3-none-any.whl?job=build)~~ +[wheel (GHA via `nightly.link`)](https://nightly.link/KOLANICH-tools/firefucks.py/workflows/CI/master/firefucks-0.CI-py3-none-any.whl) +~~![GitLab Build Status](https://gitlab.com/KOLANICH-tools/firefucks.py/badges/master/pipeline.svg)~~ +~~![GitLab Coverage](https://gitlab.com/KOLANICH-tools/firefucks.py/badges/master/coverage.svg)~~ +[![GitHub Actions](https://github.com/KOLANICH-tools/firefucks.py/workflows/CI/badge.svg)](https://github.com/KOLANICH-tools/firefucks.py/actions/) +[![Libraries.io Status](https://img.shields.io/librariesio/github/KOLANICH-tools/firefucks.py.svg)](https://libraries.io/github/KOLANICH-tools/firefucks.py) +[![Code style: antiflash](https://img.shields.io/badge/code%20style-antiflash-FFF.svg)](https://codeberg.org/KOLANICH-tools/antiflash.py) + +This is a tool for patching Firefox Web Browser into allowing unsigned addons. + +Can be used as an apt hook. + +Mozilla, requiring extensions signing and signing in and getting 2FA for AMO is not nice. 🖕🔥 + +This tool has been created as a response to +* will of Mozilla to disallow unsigned extensions in regular builds of Firefox; +* will of Mozilla to disallow WebExtensions Experiments in regular builds of Firefox; +* will of Mozilla to require authentication on AMO in order to sign extensions; +* unwillingness of devs of some distros to provide "Developer Edition" builds of Firefox. + +ToDo: Currently libzip is used for updating files witin the archive. It doesn't allow rewriting files in archives without creating a copy of the archive. [It is considered contradicting `libzip` goals according to its authors.](https://github.com/nih-at/libzip/issues/304) We need a lib allowing to do that. + +## Installation +0. Learn how to install python packages from git. +1. Install manually the latest versions of the dependencies mentioned in the `Dependencies` section of this ReadMe. +2. Install this tool. + +## How to use +1. Copy the original `omni.ja` to the current dir +```bash +cp /usr/lib/firefox/omni.ja ./omni.ja.bak +cp ./omni.ja.bak ./omni.ja +``` +2. Modify it with `firefucks` tool +```bash +firefucks ./omni.ja +``` +3. Copy it back +```bash +sudo fakeroot cp ./omni.ja /usr/lib/firefox/omni.ja +``` +4. **IMPORTANT, without this the changes will have no effect!** (ToDo: figure out what is the internal mechanism invalidating the caches, and maybe the way to patch the data within caches without needing root). Clean the startup caches: +```bash +rm -rf ~/.cache/mozilla/firefox/*/startupCache +``` + +## Check that it has worked +1. Open `Tools -> Browser Tools -> Browser Console`. +2. Paste there content of [`snippet.js`](./snippet.js) and execute it. It will print an object with the current values of the variables. +3. Compare them against the [`preset.json` file](./firefucks/preset.json) shipped as a part of this tool. + + +## Principle of operation + +Some critical browser-related code written in JS and some resources are stored in `omni.ja` files, which are zip archives. The location of these files is following: + +```bash +dpkg -L firefox | grep omni.ja +``` + +``` +/usr/lib/firefox/browser/omni.ja +/usr/lib/firefox/omni.ja +``` + +The latter of them (`/usr/lib/firefox/omni.ja`) contains: +* Module `modules/AppConstants.jsm`, which contains some constants used to distinguish between flavours of Firefox; +* Module `modules/addons/AddonSettings.jsm`, which contains some code, using the constants from `AppConstants` as input. Module `modules/addons/AddonConstants.jsm` [no longer exists](https://hg.mozilla.org/mozilla-central/rev/2766cd8808dd2d1d66bc4e9e9e313bbc60b9a197) because of this one. +* `jsloader/resource/gre` is no longer present. + + +Some of them are documented by the links: +* https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/internals/preferences.html +* https://firefox-source-docs.mozilla.org/toolkit/crashreporter/crashreporter/index.html +* https://wiki.mozilla.org/Platform/Channel-specific_build_defines + +We are particulary interested in the following properties: +* [`MOZ_REQUIRE_SIGNING`](https://searchfox.org/mozilla-central/search?q=symbol:AppConstants%23MOZ_REQUIRE_SIGNING), which is used to override the value `xpinstall.signatures.required`. +* [`MOZ_DEV_EDITION`](https://searchfox.org/mozilla-central/search?q=symbol%3AAppConstants%23MOZ_DEV_EDITION), which is used to restrict access to some advanced features. +* [`MOZ_TELEMETRY_REPORTING`](https://searchfox.org/mozilla-central/search?q=symbol:AppConstants%23MOZ_TELEMETRY_REPORTING) - used as an additional mean to disable telemetry. +* [`MOZ_CRASHREPORTER`](https://searchfox.org/mozilla-central/search?q=symbol:AppConstants%23MOZ_CRASHREPORTER) - disables crash reporting. +* [`MOZ_DATA_REPORTING`](https://searchfox.org/mozilla-central/search?q=symbol:AppConstants%23MOZ_DATA_REPORTING) - [disables initialization of data reporting system and disables recommendations](https://searchfox.org/mozilla-central/source/browser/components/preferences/privacy.js), + +Don't touch: +* `MOZILLA_OFFICIAL` ([var](https://searchfox.org/mozilla-central/search?q=symbol%3AAppConstants%23MOZILLA_OFFICIAL), [macro](https://searchfox.org/mozilla-central/search?q=symbol:M_4924396bb8356f31)) - controls lots of different things. If you change it, your Firefox will fail to start. +* `MOZ_WEBEXT_WEBIDL_ENABLED` ([var](https://searchfox.org/mozilla-central/search?q=symbol:%23MOZ_WEBEXT_WEBIDL_ENABLED), [macro](https://searchfox.org/mozilla-central/search?q=symbol:M_MOZ_WEBEXT_WEBIDL_ENABLED)) - [requires compile-time changes in C++ part](https://searchfox.org/mozilla-central/source/toolkit/components/extensions/webidl-api/ExtensionBrowser.cpp#67). + +## Thanks + +This tool stands on the shoulders of giants. + +### Dependencies + +* https://github.com/Kronuz/esprima-python - for JS parsing +* https://github.com/ksons/jscodegen.py - for serializing JS back +* https://github.com/nih-at/libzip + [its python bindings](https://github.com/KOLANICH-libs/libzip.py) - for replacing files in zip archives. **ToDo: replace with a lib doing in-place** + +### Sources of information + +* https://old.reddit.com/r/ReverseEngineering/comments/51bxuv/modifying_release_builds_of_firefox_to_allow/d7arltj/ +* https://github.com/zotero/zotero-standalone-build/blob/11e7c456732397d6b95b4b3a622990e50224b439/fetch_xulrunner.sh#L83-L90 +* https://github.com/SebastianSimon/firefox-omni-tweaks +* https://github.com/xiaoxiaoflood/firefox-scripts/blob/master/installation-folder/config.js + diff --git a/UNLICENSE b/UNLICENSE new file mode 100644 index 0000000..efb9808 --- /dev/null +++ b/UNLICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +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 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. + +For more information, please refer to diff --git a/firefucks/__init__.py b/firefucks/__init__.py new file mode 100644 index 0000000..577b94c --- /dev/null +++ b/firefucks/__init__.py @@ -0,0 +1,156 @@ +import typing +from pathlib import Path, PurePath + +import esprima +import escodegen +from esprima.nodes import Script +from libzip.Archive import Archive +from libzip.enums import OpenFlags +from libzip.Source import Source + +from .constants import appConstraintsInternalPath, omniJaLinuxPath +from .json import json +from .patcher import patchAppConstants + +# pylint:disable=too-few-public-methods + +__all__ = ("PatchingPipeline", "DEFAULT_PRESET") + +thisDir = Path(__file__).absolute().parent +presetFile = thisDir / "preset.json" +DEFAULT_PRESET = json.loads(presetFile.read_text()) + + +class InternalPaths: + __slots__ = ("appConstraints",) + + def __init__(self, appConstraints: PurePath = appConstraintsInternalPath) -> None: + self.appConstraints = appConstraints + + +class Paths: + __slots__ = ("root", "internal") + + def __init__(self, root: Path, internal: InternalPaths = None) -> None: + self.root = root + if internal is None: + internal = InternalPaths() + + self.internal = internal + + +class PathsPair: + __slots__ = ("root", "internal") + + def __init__(self, root: Path, internal: PurePath) -> None: + self.root = root + self.internal = internal + + +class DestinationBackend: + __slots__ = () + + def getFileText(self, pp: PathsPair) -> str: + raise NotImplementedError + + def writeBack(self, pp: PathsPair, source: str) -> None: + raise NotImplementedError + + @classmethod + def make(cls, patchee: Paths) -> "DestinationBackend": + if patchee.root.is_dir(): + return DirDestinationBackend() + return ArchiveDestinationBackend() + + +class DirDestinationBackend(DestinationBackend): + __slots__ = () + + def getFileText(self, pp: PathsPair) -> str: + return (pp.root / pp.internal).read_text() + + def writeBack(self, pp: PathsPair, source: str) -> None: + (pp.root / pp.internal).write_text(source) + + +class ArchiveDestinationBackend(DestinationBackend): + __slots__ = () + + def getFileText(self, pp: PathsPair) -> str: + with Archive(pp.root, OpenFlags.read_only | OpenFlags.check) as a: + f = a[pp.internal] + appConstsText = bytes(f.stat.originalSize) + with f as of: + of.read(appConstsText) + return appConstsText.decode("utf-8") + + def writeBack(self, pp: PathsPair, source: str) -> None: + with Archive(pp.root, OpenFlags.read_write | OpenFlags.check) as a: + f = a[pp.internal] + s = Source.make(source.encode("utf-8")) + f.replace(s) + + +class ParsedAST: + __slots__ = ("ast", "pp") + + def __init__(self, pp: PathsPair) -> None: + self.pp = pp + self.ast = None + + def load(self, destinationBackend: DestinationBackend) -> None: + source = destinationBackend.getFileText(self.pp) + self.ast = self.parse(source) + + def dump(self, destinationBackend: DestinationBackend) -> None: + res = self.serialize(esprima.toDict(self.ast)) + destinationBackend.writeBack(self.pp, res) + + def parse(self, source): + raise NotImplementedError + + def serialize(self, source): + raise NotImplementedError + + +class JSParsedAST(ParsedAST): + __slots__ = () + + def parse(self, source: str) -> Script: + return esprima.parse(source, {"comment": True}) + + def serialize(self, source: typing.Dict) -> str: + return escodegen.generate(esprima.toDict(self.ast)) + + +class PatchingPipeline: + __slots__ = ("preset", "patchee", "destinationBackend", "appConsts", "unpatchedProps") + + appConstraintsInternalPath = appConstraintsInternalPath + + def __init__(self, preset: typing.Dict[str, bool], patchee: typing.Union[Paths, Path], destinationBackend: typing.Optional[DestinationBackend] = None) -> None: + self.preset = preset + if not isinstance(patchee, Paths): + patchee = Paths(patchee) + + self.patchee = patchee + if destinationBackend is None: + destinationBackend = DestinationBackend.make(patchee) + self.destinationBackend = destinationBackend + self.appConsts = JSParsedAST(PathsPair(patchee.root, patchee.internal.appConstraints)) + self.unpatchedProps = None + + def __call__(self) -> typing.Dict[typing.Any, typing.Any]: + self.load() + self.patch() + self.dump() + return self.unpatchedProps + + def load(self) -> None: + self.appConsts.load(self.destinationBackend) + + def patch(self) -> None: + self.unpatchedProps = patchAppConstants(self.appConsts.ast, self.preset) + + def dump(self) -> None: + self.appConsts.dump(self.destinationBackend) diff --git a/firefucks/__main__.py b/firefucks/__main__.py new file mode 100644 index 0000000..472a165 --- /dev/null +++ b/firefucks/__main__.py @@ -0,0 +1,18 @@ +import argparse +from pathlib import Path + +from . import DEFAULT_PRESET, PatchingPipeline +from .constants import omniJaLinuxPath + + +def main() -> None: + p = argparse.ArgumentParser(prog=None, usage=None, description="A patcher to make Firefox into allowing unsigned addons.", exit_on_error=False) + p.add_argument(dest="path", metavar="", default="./omni.ja", type=Path, help="The path to `" + str(omniJaLinuxPath) + "` archive or unpacked dir") + args = p.parse_args() + pl = PatchingPipeline(DEFAULT_PRESET, args.path) + unpatched = pl() + print("unpatched", unpatched) + + +if __name__ == "__main__": + main() diff --git a/firefucks/constants.py b/firefucks/constants.py new file mode 100644 index 0000000..1e28de4 --- /dev/null +++ b/firefucks/constants.py @@ -0,0 +1,6 @@ +from pathlib import Path, PurePath + +__all__ = ("omniJaLinuxPath", "appConstraintsInternalPath") + +omniJaLinuxPath = Path("/usr/lib/firefox/omni.ja") +appConstraintsInternalPath = PurePath("modules/AppConstants.jsm") diff --git a/firefucks/json.py b/firefucks/json.py new file mode 100644 index 0000000..332ca9a --- /dev/null +++ b/firefucks/json.py @@ -0,0 +1,5 @@ +# pylint:disable=unused-import +try: + import mujson as json +except ImportError: + import json diff --git a/firefucks/patcher.py b/firefucks/patcher.py new file mode 100644 index 0000000..9ed633e --- /dev/null +++ b/firefucks/patcher.py @@ -0,0 +1,78 @@ +import typing + +import esprima +from esprima.nodes import CallExpression, Literal, ObjectExpression, Script, StaticMemberExpression + +from .json import json + + +def isPropParent(cand: StaticMemberExpression, pathComp: str) -> bool: + if pathComp == "this": + + def objPred0(o): + return o.type == "ThisExpression" + + else: + + def objPred0(o): + return o.type == "Identifier" and o.name == pathComp + + return cand.type == "MemberExpression" and objPred0(cand.object) + + +def isPropChild(cand: StaticMemberExpression, pathComp: str) -> bool: + p = cand.property + return p.type == "Identifier" and p.name == pathComp + + +def isProp2(cand: StaticMemberExpression, pathComp0: str, pathComp1: str) -> bool: + return isPropParent(cand, pathComp0) and isPropChild(cand, pathComp1) + + +def findThisAssignmentPropInProgram(progNode: Script, propsToFind: set, op: str = "=") -> typing.Dict[str, CallExpression]: + assert progNode.type == "Program" + res = {} + for assignmentExprCand1 in progNode.body: + if assignmentExprCand1 and assignmentExprCand1.type == "ExpressionStatement": + assignmentExprCand = assignmentExprCand1.expression + if assignmentExprCand and assignmentExprCand.type == "AssignmentExpression" and assignmentExprCand.operator == op: + lhs = assignmentExprCand.left + if isPropParent(lhs, "this"): + p = lhs.property + if p.type == "Identifier" and p.name in propsToFind: + res[p.name] = assignmentExprCand.right + return res + + +def literal2ast(v: bool) -> Literal: + return esprima.parse(json.dumps(v)).body[0].expression + + +def patchDictExpr(dictExpr: ObjectExpression, patch: dict) -> typing.Dict[typing.Any, typing.Any]: + patch = type(patch)(patch) + for cand in dictExpr.properties: + if cand.key.type == "Identifier" and cand.key.name in patch: + try: + replacementVal = patch[cand.key.name] + except KeyError: + continue + else: + targetP = cand + targetP.value = literal2ast(replacementVal) + del patch[cand.key.name] + if not patch: + break + return patch + + +def patchAppConstants(res: Script, patch: typing.Dict[str, bool]) -> typing.Dict[typing.Any, typing.Any]: + appConstantsExpr = findThisAssignmentPropInProgram(res, {"AppConstants"})["AppConstants"] + if isProp2(appConstantsExpr.callee, "Object", "freeze"): + aS = appConstantsExpr.arguments + if aS: + appConstantsExpr = aS[0] # unfrozen expression + + if appConstantsExpr.type == "ObjectExpression": + return patchDictExpr(appConstantsExpr, patch) + + raise ValueError("this.AppConstants is assigned with wrong thing:", appConstantsExpr) diff --git a/firefucks/preset.json b/firefucks/preset.json new file mode 100644 index 0000000..1570199 --- /dev/null +++ b/firefucks/preset.json @@ -0,0 +1,7 @@ +{ + "MOZ_REQUIRE_SIGNING": false, + "MOZ_DEV_EDITION": true, + "MOZ_TELEMETRY_REPORTING": false, + "MOZ_CRASHREPORTER": false, + "MOZ_DATA_REPORTING": false +} diff --git a/firefucks/py.typed b/firefucks/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..3e259e5 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,43 @@ +[build-system] +build-backend = "setuptools.build_meta" +requires = ["setuptools>=61.2.0", "wheel", "setuptools_scm[toml]>=3.4.3"] + +[project] +name = "firefucks" +description = "A tool for patching Firefox Web Browser into allowing unsigned addons." +readme = "ReadMe.md" +keywords = ["firefox", "icewasel", "mozilla", "webextensions"] +license = {text = "Unlicense"} +authors = [{name = "KOLANICH"}] +requires-python = ">=3.4" +dependencies = [ + "esprima", # @ git+https://github.com/Kronuz/esprima-python + #"jscodegen", # @ git+https://github.com/ksons/jscodegen.py + "escodegen", # @ git+https://github.com/0o120/escodegen-python + "libzip", # @ git+https://codeberg.org/KOLANICH-libs/libzip.py +] +dynamic = ["version"] +classifiers = [ + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Development Status :: 4 - Beta", + "Environment :: Other Environment", + "Intended Audience :: Developers", + "License :: Public Domain", + "Operating System :: OS Independent", + "Topic :: Software Development :: Libraries :: Python Modules", +] +[project.urls] +Homepage = "https://codeberg.org/KOLANICH-tools/firefucks.py" + +[project.scripts] +firefucks = "firefucks.__main__:main" + +[tool.setuptools] +zip-safe = true +include-package-data = false + +[tool.setuptools.packages] +find = {namespaces = false} + +[tool.setuptools_scm] diff --git a/snippet.js b/snippet.js new file mode 100644 index 0000000..dfa32e2 --- /dev/null +++ b/snippet.js @@ -0,0 +1,18 @@ +// Run it in browser console. +{ + let {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm"); + let res = {}; + let preset = { + "MOZ_REQUIRE_SIGNING": false, + "MOZILLA_OFFICIAL": false, + "MOZ_DEV_EDITION": true, + "MOZ_TELEMETRY_REPORTING": false, + "MOZ_CRASHREPORTER": false, + "MOZ_DATA_REPORTING": false + }; + for (let [k, v] of Object.entries(preset)) { + //AppConstants[k] = v; + res[k] = AppConstants[k]; + } + console.log(res); +} diff --git a/tests/omni.ja b/tests/omni.ja new file mode 100644 index 0000000000000000000000000000000000000000..8f3e87d5ec92b7bb838ba7160c839c8d93b5f7d0 GIT binary patch literal 872 zcmWIWW@Zs#00F}#*FwMyD8a$N#E_exQks)mtk1v@09MDuzyVhGTyNLG9gGYNubCJa zB!KE+>KqFSob&UFOA_-+iuJOJb9;l1PnvBYVRzeq;laJkE%n#uSJqX{JolB}6V;lNrl`H(u|0f!@@ox+CS6VqwGZ9;J67-BJv*9r+Z8dH zJRgzeWziDn?ti*k{`Z~+-{O}KJPU2R+3y(&J!Sflw%=UFUf0H6zea!FvBwwnn^(UK zo%oag*i93;*5fB;{d%48ivQ0-`?K@@q}EN2^<8vc+5UIoqYwP)h0nw%Tgd!9(X@(D z%}!R&b?wTz_g`H#eX{Ah*c~ONH4m2>YD2);rhv&lO$zyQ3tCW%2`wM1eK0{NAVqKJqQP` z>=wUR6fNoLa+Kp;(Kq`YOFsyLVw($`4jj=^1q)ELB(=CCz?+fDo&i_NQGuo*0VoYi zA6yIyU=c=Os?ijlZ5-EjgK@vhCNK@-!%W7W)?g-s%+Ua1n91lVk9eDK`4ibDF=AouSos|M3Bet