mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-14 14:03:47 -06:00
fix: align the privacy page with what is counted
This commit is contained in:
1 parent
093b1b21cf
commit
7a34c2ddec
3 files changed
+55
-12
No files matched your search
@@ -49,6 +49,25 @@ class TheCounterAsksNothingOfTheDevice(unittest.TestCase):
|
||||
self.assertIn("countEvent(`search-none/${searchWords(query)}`)", self.code)
|
||||
self.assertIn("message.startsWith('No results for')", self.code)
|
||||
|
||||
def _shape(self, name: str):
|
||||
"""A search-shape predicate of count.ts, its regexes run by Python
|
||||
(the counter's patterns use no syntax the two read differently)."""
|
||||
body = re.search(rf"const {name} = \(query: string\): boolean =>(.*?);\n", self.code, re.S).group(1)
|
||||
patterns = [re.compile(p) for p in re.findall(r"/(\^.*?\$|\[[^/]*?\]|\\d)/\.test\(query\)", body)]
|
||||
digits = "(query.match(/\\d/g) ?? []).length <= 4" in body
|
||||
return lambda q: all(p.search(q) for p in patterns) and (
|
||||
not digits or len(re.findall(r"\d", q)) <= 4)
|
||||
|
||||
def test_a_number_alone_is_never_sent(self) -> None:
|
||||
"""An 8-digit phone number is valid hex and went out as a hash."""
|
||||
shapes = [self._shape(n) for n in ("isHash", "isFileName", "isFileStem")]
|
||||
for query in ("55512345", "91234567", "0612345678", "+33612345678"):
|
||||
with self.subTest(query=query):
|
||||
self.assertFalse(any(shape(query) for shape in shapes))
|
||||
for query in ("b4430a3b", "scph5501", "scph5501.bin", "a" * 40):
|
||||
with self.subTest(query=query):
|
||||
self.assertTrue(any(shape(query) for shape in shapes))
|
||||
|
||||
def test_one_endpoint(self) -> None:
|
||||
self.assertEqual(set(re.findall(r"https://[\w.-]+", self.code)),
|
||||
{"https://retrobios.goatcounter.com"})
|
||||
@@ -70,6 +89,19 @@ class ThePageSaysWhatIsCounted(unittest.TestCase):
|
||||
with self.subTest(said=said):
|
||||
self.assertIn(said, page)
|
||||
|
||||
def test_the_page_names_what_the_counter_cannot_tell_apart(self) -> None:
|
||||
"""The page left out outbound clicks, which count.ts sends, said the
|
||||
referrer went by domain when the origin goes, and promised a search
|
||||
word goes out only as a file name while a nickname with digits does."""
|
||||
page = PRIVACY.read_text(encoding="utf-8")
|
||||
code = COUNTER.read_text(encoding="utf-8")
|
||||
self.assertIn("`out/${url.hostname}`", code)
|
||||
self.assertIn("link to another site", page)
|
||||
self.assertIn("from.origin", code)
|
||||
self.assertIn("`https://www.google.com`", page)
|
||||
self.assertIn("`dupont1985`", page)
|
||||
self.assertIn("phone number, never is", page)
|
||||
|
||||
def test_the_installer_has_no_counter(self) -> None:
|
||||
installer = (REPO_ROOT / "install.py").read_text(encoding="utf-8")
|
||||
self.assertNotIn("goatcounter", installer.lower())
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
// not to be tracked (Do Not Track, Global Privacy Control), from a local
|
||||
// build, or from a page not served at the site's address.
|
||||
//
|
||||
// Sent: the page path (never its query or #anchor), the domain of the site
|
||||
// the reader came from, and the name of an action. An action is a click on
|
||||
// Sent: the page path (never its query or #anchor), the origin of the site
|
||||
// the reader came from (scheme and host, no path), and the name of an
|
||||
// action. An action is a click on
|
||||
// a copy button, a pack or file download, a link that leaves the site, a
|
||||
// site search that found nothing (its words only when they are a file
|
||||
// name or a hash), and the answer to "Did this page help?".
|
||||
// site search that found nothing (its words only in the shapes below),
|
||||
// and the answer to "Did this page help?".
|
||||
|
||||
const ENDPOINT = 'https://retrobios.goatcounter.com/count';
|
||||
const HOST = 'abdess.github.io';
|
||||
@@ -37,7 +38,7 @@ export const countEvent = (name: string): void => {
|
||||
send({ p: name.slice(0, 120), t: pagePath(), e: 'true' });
|
||||
};
|
||||
|
||||
// The site the reader came from, by domain alone: a path can carry a
|
||||
// The site the reader came from, by its origin alone: a path can carry a
|
||||
// search or a private thread.
|
||||
const referrer = (): string => {
|
||||
try {
|
||||
@@ -104,11 +105,16 @@ const onClick = (event: MouseEvent): void => {
|
||||
// Search
|
||||
|
||||
// A search that found nothing names a missing page. Its words are sent
|
||||
// only in a shape no person's details take: a hash, a file name
|
||||
// ("scph5501.bin"), or one word of letters and at most four digits
|
||||
// ("scph5501", "ps3updat"). Any other search is counted with no words,
|
||||
// as "other": a name, a phone number or an address is never sent.
|
||||
const isHash = (query: string): boolean => /^(?:[0-9a-f]{8}|[0-9a-f]{32}|[0-9a-f]{40}|[0-9a-f]{64})$/.test(query);
|
||||
// only in three shapes: a hash with at least one letter, a file name
|
||||
// ("scph5501.bin"), or one word that starts with a letter and holds at most
|
||||
// four digits ("scph5501", "ps3updat"). Any other search is counted with no
|
||||
// words, as "other". A number alone, a phone number among them, is never
|
||||
// sent. A nickname of the third shape ("dupont1985") is, as wiki/privacy.md
|
||||
// says: nothing tells it from a file name.
|
||||
// An 8-digit phone number is valid hex: a hash needs a letter, and a CRC32
|
||||
// written in digits only is counted as "other".
|
||||
const isHash = (query: string): boolean =>
|
||||
/^(?:[0-9a-f]{8}|[0-9a-f]{32}|[0-9a-f]{40}|[0-9a-f]{64})$/.test(query) && /[a-f]/.test(query);
|
||||
// The extensions of the files the collection holds: "john.smith" is no file.
|
||||
const isFileName = (query: string): boolean =>
|
||||
/^[\w.+-]{1,60}\.(?:bin|rom|zip|7z|pup|keys|img|dat|bios|ic\d+|u\d+|sys|nvm|mec|rom1|j64|pce|col|sms|gb|gbc|gba|nds|cfg|chd|iso|txt|fw|xml|yaml|pak|pk3|grp)$/.test(query);
|
||||
|
||||
+7
-2
@@ -16,7 +16,8 @@ When a page opens, your browser sends one request to
|
||||
carries:
|
||||
|
||||
- the page address, without the part after `?` or `#`;
|
||||
- the site you came from, by its domain only (`google.com`, `reddit.com`).
|
||||
- the site you came from, by its address without a path
|
||||
(`https://www.google.com`).
|
||||
|
||||
GoatCounter reads the browser name and the country from that request. It does
|
||||
not keep your IP address. To tell a reload from a second visitor it keeps a
|
||||
@@ -25,9 +26,13 @@ code made from the request for 8 hours, then drops it.
|
||||
These actions are counted the same way, with the page they happened on:
|
||||
|
||||
- a click on a copy button, a pack download or a file link;
|
||||
- a click on a link to another site, by that site's domain (`reddit.com`);
|
||||
- a site search that found nothing, so a missing page shows up. Its words are
|
||||
sent only when they read as a file name (`scph5501.bin`, `scph5501`) or a
|
||||
hash. Any other search is counted as "other", without its words;
|
||||
hash with at least one letter. A word of letters with a few digits is sent
|
||||
as typed, so a nickname written that way (`dupont1985`) would be too. A
|
||||
number alone, such as a phone number, never is. Any other search is counted
|
||||
as "other", without its words;
|
||||
- the answer to "Did this page help?".
|
||||
|
||||
Not sent: your screen size, your language, a search that found results.
|
||||
|
||||
Reference in new issue
Block a user