mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 04:50:59 +00:00
fix(support): redact host names under Brazilian, Portuguese and other app-region TLDs
The support report's host-name step only knew a short list of top-level domains, so saude.minhacasa.br:3000 or vida.example.com.br reached a report meant for a public issue unredacted, as did hosts under .pt, .be, .lu, .li, the Nordic codes and the English-speaking ccTLDs. The list now covers the country codes of every app language's home regions, pinned by a test against the shipped locales.
This commit is contained in:
1 parent
38941c55d8
commit
47ac2c6889
2 files changed
+106
-7
No files matched your search
@@ -21,11 +21,13 @@ import {
|
||||
type SupportReportFacts,
|
||||
} from "../report";
|
||||
import {
|
||||
HOST_TLDS,
|
||||
redactSupportText,
|
||||
SUPPORT_REDACTION_STEPS,
|
||||
type SupportRedactionContext,
|
||||
} from "../redact";
|
||||
import type { WideEvent } from "@/lib/logging/types";
|
||||
import { locales, type Locale } from "@/lib/i18n/config";
|
||||
|
||||
const ACCOUNT_EMAIL = "anna.schmidt@example.org";
|
||||
const ACCOUNT_ID = "cmq7x0abcdefghijklmnopqrs";
|
||||
@@ -366,3 +368,64 @@ describe("error classes never carry the message", () => {
|
||||
expect(JSON.stringify(top)).not.toContain(SEEDS.ids);
|
||||
});
|
||||
});
|
||||
|
||||
describe("host names under every app language's country codes", () => {
|
||||
// Where each shipped language is at home. A new locale fails here until its
|
||||
// countries are listed, and a listed country fails until HOST_TLDS has it.
|
||||
const HOST_TLD_LOCALE_REGIONS: Record<Locale, readonly string[]> = {
|
||||
de: ["de", "at", "ch", "li", "lu", "be"],
|
||||
en: ["uk", "ie", "us", "ca", "au", "nz"],
|
||||
fr: ["fr", "be", "ch", "lu", "ca"],
|
||||
es: ["es", "mx", "ar"],
|
||||
it: ["it", "ch"],
|
||||
pl: ["pl"],
|
||||
ko: ["kr"],
|
||||
nl: ["nl", "be"],
|
||||
"pt-BR": ["br", "pt", "ao", "mz"],
|
||||
};
|
||||
|
||||
it("covers every shipped locale", () => {
|
||||
expect(Object.keys(HOST_TLD_LOCALE_REGIONS).sort()).toEqual(
|
||||
[...locales].sort(),
|
||||
);
|
||||
});
|
||||
|
||||
it.each(Object.entries(HOST_TLD_LOCALE_REGIONS))(
|
||||
"redacts a host under each of %s's country codes",
|
||||
(_locale, tlds) => {
|
||||
for (const tld of tlds) {
|
||||
expect(HOST_TLDS, tld).toContain(tld);
|
||||
const text = redactSupportText(`reached nas.familie.${tld} late`, {
|
||||
knownValues: [],
|
||||
});
|
||||
expect(text, tld).toBe("reached [host] late");
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
it("redacts Brazilian and Portuguese hosts, with a port or a second-level registry", () => {
|
||||
const cases = [
|
||||
"saude.minhacasa.br:3000",
|
||||
"vida.example.com.br",
|
||||
"healthlog.casa.pt",
|
||||
"nas.lan.com.pt:8443",
|
||||
];
|
||||
for (const host of cases) {
|
||||
const text = redactSupportText(`unexpected_silence ${host}`, {
|
||||
knownValues: [],
|
||||
});
|
||||
expect(text, host).not.toMatch(/minhacasa|example|casa|lan\.com/);
|
||||
expect(text).toContain("[host]");
|
||||
}
|
||||
});
|
||||
|
||||
it("still leaves report prose and action names alone", () => {
|
||||
const text = redactSupportText(
|
||||
"- Actions: mental-health.create, coach.budget.exceeded, measurement.batch.ingest\n- Node: v22.11.0",
|
||||
{ knownValues: [] },
|
||||
);
|
||||
expect(text).toContain("mental-health.create");
|
||||
expect(text).toContain("coach.budget.exceeded");
|
||||
expect(text).toContain("measurement.batch.ingest");
|
||||
});
|
||||
});
|
||||
@@ -39,8 +39,16 @@ function escapeRegExp(value: string): string {
|
||||
*/
|
||||
const KNOWN_VALUE_MIN_LENGTH = 5;
|
||||
|
||||
/** Top-level domains a host name in a self-hosted setup ends in. */
|
||||
const HOST_TLDS = [
|
||||
/**
|
||||
* Top-level domains a host name in a self-hosted setup ends in: the generic
|
||||
* ones, the home-network suffixes, and the country codes of every place the
|
||||
* app's languages are at home (`HOST_TLD_LOCALE_REGIONS` in the test pins
|
||||
* that list against `locales`), plus the neighbours a self-hoster there is
|
||||
* as likely to register under. A second-level registry (`com.br`, `co.uk`)
|
||||
* needs nothing extra: the label before it is matched as a subdomain.
|
||||
*/
|
||||
export const HOST_TLDS = [
|
||||
// Generic.
|
||||
"com",
|
||||
"net",
|
||||
"org",
|
||||
@@ -50,18 +58,44 @@ const HOST_TLDS = [
|
||||
"cloud",
|
||||
"info",
|
||||
"me",
|
||||
"co",
|
||||
"xyz",
|
||||
"online",
|
||||
"site",
|
||||
"eu",
|
||||
// Country codes.
|
||||
"de",
|
||||
"at",
|
||||
"ch",
|
||||
"eu",
|
||||
"uk",
|
||||
"li",
|
||||
"lu",
|
||||
"be",
|
||||
"nl",
|
||||
"fr",
|
||||
"es",
|
||||
"pt",
|
||||
"br",
|
||||
"ao",
|
||||
"mz",
|
||||
"it",
|
||||
"pl",
|
||||
"nl",
|
||||
"cz",
|
||||
"sk",
|
||||
"kr",
|
||||
"jp",
|
||||
"uk",
|
||||
"ie",
|
||||
"us",
|
||||
"ca",
|
||||
"au",
|
||||
"nz",
|
||||
"mx",
|
||||
"ar",
|
||||
"se",
|
||||
"dk",
|
||||
"no",
|
||||
"fi",
|
||||
// Home-network and private suffixes.
|
||||
"local",
|
||||
"lan",
|
||||
"home",
|
||||
@@ -69,7 +103,9 @@ const HOST_TLDS = [
|
||||
"arpa",
|
||||
"localdomain",
|
||||
"ts",
|
||||
].join("|");
|
||||
];
|
||||
|
||||
const HOST_TLD_PATTERN = HOST_TLDS.join("|");
|
||||
|
||||
export const SUPPORT_REDACTION_STEPS: readonly SupportRedactionStep[] = [
|
||||
{
|
||||
@@ -132,7 +168,7 @@ export const SUPPORT_REDACTION_STEPS: readonly SupportRedactionStep[] = [
|
||||
apply: (text) =>
|
||||
text.replace(
|
||||
new RegExp(
|
||||
`\\b(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+(?:${HOST_TLDS})\\b`,
|
||||
`\\b(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+(?:${HOST_TLD_PATTERN})\\b`,
|
||||
"gi",
|
||||
),
|
||||
"[host]",
|
||||
|
||||
Reference in new issue
Block a user