diff --git a/Makefile b/Makefile index dee805e0..24613684 100644 --- a/Makefile +++ b/Makefile @@ -13,4 +13,7 @@ clean: DEFAULT_GOAL := default default: build -.PHONY: default build attest verify clean +check: + PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover -s contrib/docs/tests -v + +.PHONY: default build attest verify clean check diff --git a/README.md b/README.md index ea3ef180..58a4cfd1 100644 --- a/README.md +++ b/README.md @@ -1,60 +1,40 @@ -# Feather Wallet +# Wowlet -Feather is a free Monero desktop wallet for Linux, Tails, macOS and Windows. It is written in C++ with the Qt framework. - -- **easy-to-use**, **small** and **fast** - Feather runs well on any modern hardware, including virtual machines and live operating systems. -- **beginner friendly**, but also caters to advanced Monero users by providing a [feature set](https://docs.featherwallet.org/guides/features) that is on par with the official CLI. -- ships with **sane defaults** that suit most users, but can also be configured for high or uncommon threat models. -- serves as a testing grounds for **experimental features** that may later be adopted in the reference wallets. +Wowlet is a Wownero desktop wallet for Linux, macOS and Windows, built with C++ and +Qt. It is based on [Feather Wallet](https://github.com/feather-wallet/feather). ## Download -You can download Feather from **[featherwallet.org](https://featherwallet.org/download/)** or **[GitHub](https://github.com/feather-wallet/feather/releases)**. +Download Wowlet and its signed release hashes from +[Wowlet releases](https://github.com/wownero/wowlet/releases). The desktop wallet +link on [wownero.org](https://wownero.org/) also leads there. -If you need help installing Feather, check the [installation documentation](https://docs.featherwallet.org/). +## Support -We recommend that you verify downloads with GPG. Releases are signed with our [release signing key](https://docs.featherwallet.org/guides/release-signing-key). The fingerprint is: +- Matrix: [#wownero-gen:wowne.ro](https://matrix.to/#/#wownero-gen:wowne.ro) +- IRC: `#wownero` on [OFTC](https://www.oftc.net/), or use + [webchat](https://webchat.oftc.net/?randomnick=1&channels=wownero) +- Email: [wownero@wownero.org](mailto:wownero@wownero.org) +- Bug reports: [Wowlet issues](https://github.com/wownero/wowlet/issues) -``` -8185 E158 A333 30C7 FD61 BC0D 1F76 E155 CEFB A71C -``` +If nobody replies immediately, stay in the room and give volunteers time to respond. +Include your Wowlet version, operating system, reproduction steps and any error +message. Never share your wallet seed, private keys, password or wallet files. -Windows releases are code-signed for free by [SignPath.io](https://about.signpath.io/), certificate by [SignPath Foundation](https://signpath.org/). - -## Resources - -* [Official Site](https://featherwallet.org) -* [Documentation](https://docs.featherwallet.org) -* [Git Repository](https://github.com/feather-wallet/feather) -* [Matrix](https://matrix.to/#/#feather:monero.social) -* IRC: `#feather` on [OFTC](https://www.oftc.net/) -* Mail: dev@featherwallet.org - -If you need help with your wallet, please contact us via Matrix or IRC. -If you don't have an IRC client, you can join the room via [webchat](https://webchat.oftc.net/?randomnick=1&channels=feather). -If you don’t receive a response immediately please idle in the room. - -## Release Builds - -To learn how to run a bootstrappable release build, see: [contrib/guix/README.md](https://github.com/feather-wallet/feather/blob/master/contrib/guix/README.md) - -For release attestations, see the [feather-sigs](http://github.com/feather-wallet/feather-sigs) repo. - -For release policy, see: [RELEASE.md](https://github.com/feather-wallet/feather/blob/master/RELEASE.md) +Report vulnerabilities privately using [SECURITY.md](SECURITY.md). ## Development -If you are looking to set up a development environment for Feather, see [HACKING.md](https://github.com/feather-wallet/feather/blob/master/HACKING.md). +The application includes an offline documentation browser under Help. Its guides +are generated by `contrib/docs/generate.py` from the Feather documentation submodule. +Wowlet-specific guides in `contrib/docs/overrides` take precedence over upstream +guides with the same filename. Edit those sources, then regenerate; files under +`src/assets/docs` are generated and must not be edited directly. -It is highly recommended that you join our Matrix or IRC channel if you are hacking on Feather. -Idling in this channel is the best way to stay updated on best practices and new developments. - -For information on how Feather is maintained, see: [MAINTENANCE.md](https://github.com/feather-wallet/feather/blob/master/MAINTENANCE.md) - -To report a security vulnerability, see: [SECURITY.md](https://github.com/feather-wallet/feather/blob/master/SECURITY.md) +Run the documentation regression checks with `make check`. The inherited build +instructions are in [HACKING.md](HACKING.md). ## License -Feather is free and open-source software, [licensed under BSD-3](https://raw.githubusercontent.com/feather-wallet/feather/master/LICENSE). - -Copyright (c) 2020-2025, The Monero Project +Wowlet is free and open-source software under [BSD-3-Clause](LICENSE). +Upstream copyright notices are retained in the source. diff --git a/SECURITY.md b/SECURITY.md index 501e3121..f1e9fc90 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,43 +2,11 @@ ## Reporting a Vulnerability -Please do not open an issue to report security issues. +Report Wowlet security vulnerabilities privately to +[wownero@wownero.org](mailto:wownero@wownero.org). +Please do not disclose them in public issues or community chat before the +maintainers have had time to investigate. -To report a vulnerability email to dev@featherwallet.org - -The following keys may be used to communicate sensitive information to developers: - -| Name | Fingerprint | -|---------|---------------------------------------------------| -| tobtoht | E87B D921 CDD8 85C9 D78A 38C5 E45B 10DD 027D 2472 | - -Public keys can be found in [`utils/pubkeys`](utils/pubkeys). - -## Bug Bounty Program - -A bounty may be rewarded to a vulnerability report **if and only if** the issue can result in **a loss of funds**. - -You must describe a **plausible scenario** in which a loss of funds can occur (or has occurred) that **isn't solely attributable to user error**. - -Only **the code** of the **latest tagged release** of **[this repository](https://github.com/feather-wallet/feather/)** is in scope. - -**The bounty can only be rewarded in XMR**. The bounty amount for your report is determined by the maintainers and ranges from USD 150 to USD 3000 (in terms of XMR) and depends on the severity of the issue and other factors. - -Clarifications on scope: - -- The issue **must be present in a [signed](https://docs.featherwallet.org/guides/release-signing-key) [release build](https://github.com/feather-wallet/feather/blob/master/contrib/guix/README.md)**. Custom builds, including distribution packages, are out of scope. -- The developers **must be able to reproduce and fix the issue**. If the issue cannot be fixed **in our code** for any reason, it is out of scope. -- The live [websites](https://github.com/feather-wallet/feather-site/blob/master/mirrors.txt) and their repositories are out of scope. -- Loss of funds due to malware on the user's machine is out of scope. -- Memory imaging, including cold boot attacks, is out of scope. -- Social engineering against users is out of scope. This includes messages received from the [websocket server](https://docs.featherwallet.org/guides/websocket). -- Any form of coercion, physical or psychological, is out of scope. -- Vulnerabilities that are attributable to hardware are out of scope. -- If the issue was fixed in the `master` branch before we receive your report, it is invalid and not eligible for a bounty from this program. -- If the vulnerability involves binary exploitation, we may ask you to provide a proof of concept of secret key exfiltration. -- Vulnerabilities that are present in the monero submodule but were not introduced in patches made by the Feather developers must - be reported [upstream](https://github.com/monero-project/meta/blob/master/VULNERABILITY_RESPONSE_PROCESS.md) and are not eligible for a bounty from this program. -- Vulnerabilities that are present in any of our third-party dependencies must be reported upstream and are not eligible for a bounty from this program. -- Vulnerabilities that are present in [supported hardware wallets](https://docs.featherwallet.org/guides/hardware-wallet-support) must be reported upstream and are not eligible for a bounty from this program. -- A bounty will not be awarded if the reported vulnerability was already known. We may make an exception if you demonstrate that the severity of the issue was underestimated and no immediate fix was planned. -- If, during your research, you disrupt Feather's release infrastructure or services, or attempt to coerce its developers, you will not be awarded a bounty. +Include the affected Wowlet version, operating system, expected impact and steps +to reproduce the issue. Use a disposable wallet for any reproduction and never +send a real wallet seed, private keys, password or wallet files. diff --git a/contrib/docs/generate.py b/contrib/docs/generate.py index a0d0ec8e..8c32894f 100644 --- a/contrib/docs/generate.py +++ b/contrib/docs/generate.py @@ -2,6 +2,7 @@ import os import glob DOCS_DIR = "external/feather-docs/content/_guides" +OVERRIDES_DIR = "contrib/docs/overrides" OUT_DIR = "src/assets/docs" CATEGORY_MAP = { @@ -21,9 +22,12 @@ outfiles = glob.glob(f"{OUT_DIR}/*.md") for file in outfiles: os.remove(file) -files = glob.glob(f"{DOCS_DIR}/*.md") +# Wowlet owns its support contacts. Local sources take precedence so regenerating +# the bundled docs cannot restore Feather's contact details. +files = {os.path.basename(path): path for path in glob.glob(f"{DOCS_DIR}/*.md")} +files.update({os.path.basename(path): path for path in glob.glob(f"{OVERRIDES_DIR}/*.md")}) -for file in files: +for file in sorted(files.values()): with open(file) as f: doc = f.read() diff --git a/contrib/docs/overrides/report_an_issue.md b/contrib/docs/overrides/report_an_issue.md new file mode 100644 index 00000000..c9e338d7 --- /dev/null +++ b/contrib/docs/overrides/report_an_issue.md @@ -0,0 +1,29 @@ +--- +title: "How to report an issue" +nav_title: "Report an issue" +category: "help" +--- + +For help with Wowlet, contact the Wownero community: + +- Matrix: [#wownero-gen:wowne.ro](https://matrix.to/#/#wownero-gen:wowne.ro) +- IRC: `#wownero` on [OFTC](https://www.oftc.net/) +- Email: [wownero@wownero.org](mailto:wownero@wownero.org) +- Bug reports: [Wowlet issues](https://github.com/wownero/wowlet/issues) + +For live support, use Matrix or IRC. If you do not have an IRC client, join +[#wownero in your browser](https://webchat.oftc.net/?randomnick=1&channels=wownero). +If nobody replies immediately, stay in the room and give volunteers time to respond. + +Please include your Wowlet version, operating system, the steps that caused the +problem, and any error message. Review **Help → Debug Info** before sharing it and +remove any personal information. Never share your wallet seed, private keys or +password, and do not upload your wallet files. + +Report security vulnerabilities privately to +[wownero@wownero.org](mailto:wownero@wownero.org), rather than in a public issue or +chat. See the [Wowlet security policy](https://github.com/wownero/wowlet/blob/master/SECURITY.md). + +Downloads and signed release hashes are available from +[Wowlet releases](https://github.com/wownero/wowlet/releases), also linked from +[wownero.org](https://wownero.org/). diff --git a/contrib/docs/tests/test_generate.py b/contrib/docs/tests/test_generate.py new file mode 100644 index 00000000..198ae6c3 --- /dev/null +++ b/contrib/docs/tests/test_generate.py @@ -0,0 +1,85 @@ +import re +import shutil +import subprocess +import sys +import tempfile +import unittest +from pathlib import Path +from urllib.parse import parse_qs, urlsplit + + +ROOT = Path(__file__).resolve().parents[3] + + +class GenerateDocsTests(unittest.TestCase): + def setUp(self): + self.directory = tempfile.TemporaryDirectory() + self.addCleanup(self.directory.cleanup) + self.root = Path(self.directory.name) + self.upstream = self.root / "external/feather-docs/content/_guides" + self.upstream.mkdir(parents=True) + self.output = self.root / "src/assets/docs" + self.output.mkdir(parents=True) + shutil.copytree(ROOT / "contrib/docs/overrides", self.root / "contrib/docs/overrides") + + def write_upstream(self, name, body): + (self.upstream / name).write_text( + '---\ntitle: "Upstream guide"\nnav_title: "Upstream"\ncategory: "help"\n---\n' + + body + ) + + def generate(self): + result = subprocess.run( + [sys.executable, "-B", str(ROOT / "contrib/docs/generate.py")], + cwd=self.root, capture_output=True, text=True, + ) + self.assertEqual(result.returncode, 0, result.stdout + result.stderr) + + def assert_wownero_contacts(self, document): + urls = [urlsplit(url) for url in re.findall(r"\]\(([^)]+)\)", document)] + self.assertTrue(any( + url.hostname == "matrix.to" and url.fragment == "/#wownero-gen:wowne.ro" + for url in urls + ), "Matrix must lead to Wownero's published room") + self.assertTrue(any( + url.hostname == "webchat.oftc.net" + and "wownero" in parse_qs(url.query).get("channels", []) + for url in urls + ), "Webchat must join Wownero's IRC channel") + self.assertTrue(any( + url.hostname == "github.com" and url.path == "/wownero/wowlet/issues" + for url in urls + ), "Bug reports must go to the Wowlet tracker") + self.assertTrue(any( + url.scheme == "mailto" and url.path == "wownero@wownero.org" + for url in urls + ), "Email must reach the published Wownero contact") + for url in urls: + self.assertNotIn("feather", url.geturl().lower(), "Support must not route to Feather") + + def test_support_override_survives_regeneration(self): + self.write_upstream("report_an_issue.md", "Upstream support contacts") + self.write_upstream("accounts.md", "An upstream guide without an override") + (self.output / "stale.md").write_text("Obsolete generated guide") + for body in ("Upstream support contacts", "Changed upstream support contacts"): + self.write_upstream("report_an_issue.md", body) + self.generate() + support = (self.output / "report_an_issue.md").read_text() + self.assert_wownero_contacts(support) + self.assertNotIn(body, support) + self.assertRegex(support, r"(?m)^\[nav_title\]: # \(.+\)$") + self.assertRegex(support, r"(?m)^\[category\]: # \(.+\)$") + self.assertIn("An upstream guide without an override", (self.output / "accounts.md").read_text()) + self.assertFalse((self.output / "stale.md").exists()) + + def test_readme_and_security_contacts_agree_with_bundled_help(self): + readme = (ROOT / "README.md").read_text() + support = readme.split("## Support\n", 1)[1].split("\n## ", 1)[0] + self.assert_wownero_contacts(support) + policy = (ROOT / "SECURITY.md").read_text() + self.assertIn("mailto:wownero@wownero.org", policy) + self.assertNotIn("featherwallet.org", policy) + + +if __name__ == "__main__": + unittest.main()