mirror of
https://github.com/jason5ng32/MyIP.git
synced 2026-11-04 08:55:47 +00:00
Feat(report): add the Ad & Tracker Blocking Test to shareable reports
A blocker setup is part of the visitor's own environment, like the browser info and proxy-rule sections, and is worth handing to whoever helps debug it. The section stays deliberately small: request counts per category (blocked / not blocked / no answer / total), no per-address or per-vendor rows, so a report stays readable and the list can change without breaking old links. Group ids are capped strings rather than an enum for the same reason. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1163ea4a13
commit
88423522e3
16 files changed
+178
-2
No files matched your search
@@ -268,6 +268,19 @@ const SECTION_SPECS = {
|
|||||||
rtt: opt(int(0, 600000)),
|
rtt: opt(int(0, 600000)),
|
||||||
}))),
|
}))),
|
||||||
}),
|
}),
|
||||||
|
// Ad & Tracker Blocking Test: per-category request counts only. Group
|
||||||
|
// ids stay capped strings — the probe list lives in the front end and
|
||||||
|
// changes with it.
|
||||||
|
adblocktest: obj({
|
||||||
|
testedAt: isoDate(),
|
||||||
|
groups: arr(16, obj({
|
||||||
|
id: str(16),
|
||||||
|
total: int(0, 1024),
|
||||||
|
blocked: int(0, 1024),
|
||||||
|
open: int(0, 1024),
|
||||||
|
unknown: int(0, 1024),
|
||||||
|
})),
|
||||||
|
}),
|
||||||
invisibility: obj({
|
invisibility: obj({
|
||||||
testedAt: isoDate(),
|
testedAt: isoDate(),
|
||||||
ip: opt(ipValue()),
|
ip: opt(ipValue()),
|
||||||
|
|||||||
@@ -189,6 +189,7 @@
|
|||||||
import { ref, computed } from 'vue';
|
import { ref, computed } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { trackEvent } from '@/utils/analytics';
|
import { trackEvent } from '@/utils/analytics';
|
||||||
|
import { emitAppEvent } from '@/utils/app-events.js';
|
||||||
import { ADBLOCK_PROBE_GROUPS, ADBLOCK_PROBES_VERSION } from '@/data/adblock-probes.js';
|
import { ADBLOCK_PROBE_GROUPS, ADBLOCK_PROBES_VERSION } from '@/data/adblock-probes.js';
|
||||||
import {
|
import {
|
||||||
COSMETIC_BAITS, COSMETIC_POLL_MS, COSMETIC_WAIT_MS,
|
COSMETIC_BAITS, COSMETIC_POLL_MS, COSMETIC_WAIT_MS,
|
||||||
@@ -364,5 +365,9 @@ const startTest = async () => {
|
|||||||
runWithConcurrency(tasks, concurrency),
|
runWithConcurrency(tasks, concurrency),
|
||||||
]);
|
]);
|
||||||
status.value = 'finished';
|
status.value = 'finished';
|
||||||
|
// Domain event: per-address outcome for the report collector.
|
||||||
|
emitAppEvent('adblocktest:finished', {
|
||||||
|
items: plan.map((p) => ({ groupId: p.groupId, kind: p.kind, status: statusOf(p) })),
|
||||||
|
});
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@@ -85,6 +85,7 @@ import ReportPingtest from './sections/ReportPingtest.vue';
|
|||||||
import ReportMtrtest from './sections/ReportMtrtest.vue';
|
import ReportMtrtest from './sections/ReportMtrtest.vue';
|
||||||
import ReportRuletest from './sections/ReportRuletest.vue';
|
import ReportRuletest from './sections/ReportRuletest.vue';
|
||||||
import ReportBrowserinfo from './sections/ReportBrowserinfo.vue';
|
import ReportBrowserinfo from './sections/ReportBrowserinfo.vue';
|
||||||
|
import ReportAdblocktest from './sections/ReportAdblocktest.vue';
|
||||||
import ReportInvisibility from './sections/ReportInvisibility.vue';
|
import ReportInvisibility from './sections/ReportInvisibility.vue';
|
||||||
import ReportEnhanceddnsleak from './sections/ReportEnhanceddnsleak.vue';
|
import ReportEnhanceddnsleak from './sections/ReportEnhanceddnsleak.vue';
|
||||||
import ReportPersona from './sections/ReportPersona.vue';
|
import ReportPersona from './sections/ReportPersona.vue';
|
||||||
@@ -100,6 +101,7 @@ const SECTION_COMPONENTS = {
|
|||||||
mtrtest: ReportMtrtest,
|
mtrtest: ReportMtrtest,
|
||||||
ruletest: ReportRuletest,
|
ruletest: ReportRuletest,
|
||||||
browserinfo: ReportBrowserinfo,
|
browserinfo: ReportBrowserinfo,
|
||||||
|
adblocktest: ReportAdblocktest,
|
||||||
invisibility: ReportInvisibility,
|
invisibility: ReportInvisibility,
|
||||||
enhanceddnsleak: ReportEnhanceddnsleak,
|
enhanceddnsleak: ReportEnhanceddnsleak,
|
||||||
persona: ReportPersona,
|
persona: ReportPersona,
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<div class="overflow-x-auto">
|
||||||
|
<table class="w-full text-xs">
|
||||||
|
<thead>
|
||||||
|
<tr class="border-b">
|
||||||
|
<th scope="col" :class="TH_LEFT">{{ t('adblocktest.report.Category') }}</th>
|
||||||
|
<th v-for="key in STATUS_KEYS" :key="key" scope="col" :class="TH_RIGHT">
|
||||||
|
{{ t(`adblocktest.count.${key}`) }}
|
||||||
|
</th>
|
||||||
|
<th scope="col" :class="TH_RIGHT">{{ t('adblocktest.report.Total') }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="divide-y">
|
||||||
|
<tr v-for="group in section.groups" :key="group.id" class="hover:bg-muted/50 transition-colors">
|
||||||
|
<td class="px-3 py-2">{{ groupTitle(group.id) }}</td>
|
||||||
|
<td v-for="key in STATUS_KEYS" :key="key"
|
||||||
|
:class="[TD_NUM, group[key] > 0 ? textClass(statusTone(key)) : 'text-muted-foreground']">
|
||||||
|
{{ group[key] }}
|
||||||
|
</td>
|
||||||
|
<td :class="[TD_NUM, 'text-muted-foreground']">{{ group.total }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
// Read-only renderer for the Ad & Tracker Blocking Test report section: the
|
||||||
|
// request counts per category. Titles come from the tool's own i18n entries;
|
||||||
|
// an id the current list no longer knows renders as-is.
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { statusTone } from '@/utils/features/adblock-test.js';
|
||||||
|
import { useStatusTone } from '@/composables/use-status-tone.js';
|
||||||
|
import { TH_LEFT, TH_RIGHT, TD_NUM } from './table-classes.js';
|
||||||
|
|
||||||
|
defineProps({ section: { type: Object, required: true } });
|
||||||
|
|
||||||
|
const { t, te } = useI18n();
|
||||||
|
const { textClass } = useStatusTone();
|
||||||
|
|
||||||
|
const STATUS_KEYS = ['blocked', 'open', 'unknown'];
|
||||||
|
|
||||||
|
const groupTitle = (id) => {
|
||||||
|
const key = `adblocktest.group.${id}.title`;
|
||||||
|
return te(key) ? t(key) : id;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// Shared Tailwind class strings for the report tables — same visual language
|
// Shared Tailwind class strings for the report tables — same visual language
|
||||||
// as the ping-test result table (GlobalLatencyTest.vue). Kept as constants so
|
// as the ping-test result table (GlobalLatencyTest.vue). Kept as constants so
|
||||||
// the eleven renderers stay literally consistent.
|
// the renderers stay literally consistent.
|
||||||
|
|
||||||
export const TH_LEFT = 'px-3 py-2.5 text-xs font-medium text-muted-foreground uppercase tracking-wide text-left text-nowrap';
|
export const TH_LEFT = 'px-3 py-2.5 text-xs font-medium text-muted-foreground uppercase tracking-wide text-left text-nowrap';
|
||||||
export const TH_RIGHT = 'px-3 py-2.5 text-xs font-medium text-muted-foreground uppercase tracking-wide text-right text-nowrap';
|
export const TH_RIGHT = 'px-3 py-2.5 text-xs font-medium text-muted-foreground uppercase tracking-wide text-right text-nowrap';
|
||||||
|
|||||||
@@ -1060,6 +1060,10 @@
|
|||||||
"browser": "Bait elements were hidden: a blocker is working inside your browser, either an extension or the browser's own.",
|
"browser": "Bait elements were hidden: a blocker is working inside your browser, either an extension or the browser's own.",
|
||||||
"network": "Requests were blocked but the bait elements were not hidden: the blocking most likely happens outside the page, at a DNS service, VPN or router, or in the browser's tracking protection.",
|
"network": "Requests were blocked but the bait elements were not hidden: the blocking most likely happens outside the page, at a DNS service, VPN or router, or in the browser's tracking protection.",
|
||||||
"none": "Nothing was blocked: no ad or tracker blocking was found in this browser or on this network."
|
"none": "Nothing was blocked: no ad or tracker blocking was found in this browser or on this network."
|
||||||
|
},
|
||||||
|
"report": {
|
||||||
|
"Category": "Category",
|
||||||
|
"Total": "Total"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"advancedtools": {
|
"advancedtools": {
|
||||||
@@ -1530,6 +1534,7 @@
|
|||||||
"mtrtest": "Per-hop MTR traceroutes toward the user's IP from probes around the world; loss at intermediate hops can be mere ICMP rate-limiting, sustained loss from one hop onward indicates a real problem.",
|
"mtrtest": "Per-hop MTR traceroutes toward the user's IP from probes around the world; loss at intermediate hops can be mere ICMP rate-limiting, sustained loss from one hop onward indicates a real problem.",
|
||||||
"ruletest": "Egress IPs observed when fetching 8 test endpoints — more than one unique IP means traffic is split across different routes (e.g. proxy rules).",
|
"ruletest": "Egress IPs observed when fetching 8 test endpoints — more than one unique IP means traffic is split across different routes (e.g. proxy rules).",
|
||||||
"browserinfo": "Browser, OS and display environment of the tested device.",
|
"browserinfo": "Browser, OS and display environment of the tested device.",
|
||||||
|
"adblocktest": "Ad & tracker blocking check run in the browser: per category (ad networks, analytics, social trackers, error monitoring, device-maker telemetry, tracking scripts), how many requests to well-known endpoints were blocked, got through, or timed out (noAnswer). Not blocking everything can be a deliberate choice, not a fault.",
|
||||||
"invisibility": "Proxy/VPN detection scores (0-100, higher = more likely detected) with the individual detection signals that flagged.",
|
"invisibility": "Proxy/VPN detection scores (0-100, higher = more likely detected) with the individual detection signals that flagged.",
|
||||||
"enhanceddnsleak": "Resolver-level DNS capture: every resolver IP that queried the test domain, with transport, ECS exposure and DNSSEC flags (do/cd).",
|
"enhanceddnsleak": "Resolver-level DNS capture: every resolver IP that queried the test domain, with transport, ECS exposure and DNSSEC flags (do/cd).",
|
||||||
"persona": "How closely the browser matches a local resident of the chosen country: an overall grade plus each check's verdict. Verdicts only — the underlying values are deliberately not stored in a shared report."
|
"persona": "How closely the browser matches a local resident of the chosen country: an overall grade plus each check's verdict. Verdicts only — the underlying values are deliberately not stored in a shared report."
|
||||||
|
|||||||
@@ -1060,6 +1060,10 @@
|
|||||||
"browser": "Les éléments leurres ont été masqués : un bloqueur agit dans votre navigateur, qu'il s'agisse d'une extension ou d'une fonction intégrée.",
|
"browser": "Les éléments leurres ont été masqués : un bloqueur agit dans votre navigateur, qu'il s'agisse d'une extension ou d'une fonction intégrée.",
|
||||||
"network": "Des requêtes ont été bloquées mais les éléments leurres n'ont pas été masqués : le blocage a très probablement lieu hors de la page, au niveau d'un service DNS, d'un VPN, d'un routeur ou de la protection contre le pistage du navigateur.",
|
"network": "Des requêtes ont été bloquées mais les éléments leurres n'ont pas été masqués : le blocage a très probablement lieu hors de la page, au niveau d'un service DNS, d'un VPN, d'un routeur ou de la protection contre le pistage du navigateur.",
|
||||||
"none": "Rien n'a été bloqué : aucun blocage de publicités ou de traceurs n'a été détecté dans ce navigateur ni sur ce réseau."
|
"none": "Rien n'a été bloqué : aucun blocage de publicités ou de traceurs n'a été détecté dans ce navigateur ni sur ce réseau."
|
||||||
|
},
|
||||||
|
"report": {
|
||||||
|
"Category": "Catégorie",
|
||||||
|
"Total": "Total"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"advancedtools": {
|
"advancedtools": {
|
||||||
@@ -1530,6 +1534,7 @@
|
|||||||
"mtrtest": "Traceroutes MTR saut par saut vers l'IP de l'utilisateur depuis des sondes mondiales ; la perte sur des sauts intermédiaires peut n'être que du rate-limiting ICMP, une perte continue à partir d'un saut indique un vrai problème.",
|
"mtrtest": "Traceroutes MTR saut par saut vers l'IP de l'utilisateur depuis des sondes mondiales ; la perte sur des sauts intermédiaires peut n'être que du rate-limiting ICMP, une perte continue à partir d'un saut indique un vrai problème.",
|
||||||
"ruletest": "IP de sortie observées en accédant à 8 points de test — plusieurs IP distinctes signifient que le trafic est réparti sur différentes routes (règles de proxy, par ex.).",
|
"ruletest": "IP de sortie observées en accédant à 8 points de test — plusieurs IP distinctes signifient que le trafic est réparti sur différentes routes (règles de proxy, par ex.).",
|
||||||
"browserinfo": "Navigateur, système et environnement d'affichage de l'appareil testé.",
|
"browserinfo": "Navigateur, système et environnement d'affichage de l'appareil testé.",
|
||||||
|
"adblocktest": "Test de blocage des publicités et traceurs exécuté dans le navigateur : par catégorie (régies publicitaires, statistiques, traceurs sociaux, suivi des erreurs, télémétrie des fabricants d'appareils, scripts de pistage), le nombre de requêtes vers des adresses connues qui ont été bloquées, sont passées ou ont expiré (noAnswer). Ne pas tout bloquer peut être un choix délibéré, pas une panne.",
|
||||||
"invisibility": "Scores de détection proxy/VPN (0-100, plus haut = plus probablement détecté) avec les signaux de détection déclenchés.",
|
"invisibility": "Scores de détection proxy/VPN (0-100, plus haut = plus probablement détecté) avec les signaux de détection déclenchés.",
|
||||||
"enhanceddnsleak": "Capture DNS au niveau résolveur : chaque IP de résolveur ayant interrogé le domaine de test, avec transport, exposition ECS et indicateurs DNSSEC (do/cd).",
|
"enhanceddnsleak": "Capture DNS au niveau résolveur : chaque IP de résolveur ayant interrogé le domaine de test, avec transport, exposition ECS et indicateurs DNSSEC (do/cd).",
|
||||||
"persona": "À quel point le navigateur correspond à un habitant du pays choisi : une note globale et le verdict de chaque vérification. Verdicts uniquement — les valeurs sous-jacentes ne sont volontairement pas stockées dans un rapport partagé."
|
"persona": "À quel point le navigateur correspond à un habitant du pays choisi : une note globale et le verdict de chaque vérification. Verdicts uniquement — les valeurs sous-jacentes ne sont volontairement pas stockées dans un rapport partagé."
|
||||||
|
|||||||
@@ -1060,6 +1060,10 @@
|
|||||||
"browser": "Os elementos-isca foram ocultados: há um bloqueador funcionando dentro do seu navegador, seja uma extensão ou o próprio navegador.",
|
"browser": "Os elementos-isca foram ocultados: há um bloqueador funcionando dentro do seu navegador, seja uma extensão ou o próprio navegador.",
|
||||||
"network": "Requisições foram bloqueadas, mas os elementos-isca não foram ocultados: o bloqueio provavelmente acontece fora da página, em um serviço de DNS, VPN, roteador ou na proteção contra rastreamento do navegador.",
|
"network": "Requisições foram bloqueadas, mas os elementos-isca não foram ocultados: o bloqueio provavelmente acontece fora da página, em um serviço de DNS, VPN, roteador ou na proteção contra rastreamento do navegador.",
|
||||||
"none": "Nada foi bloqueado: nenhum bloqueio de anúncios ou rastreadores foi encontrado neste navegador ou nesta rede."
|
"none": "Nada foi bloqueado: nenhum bloqueio de anúncios ou rastreadores foi encontrado neste navegador ou nesta rede."
|
||||||
|
},
|
||||||
|
"report": {
|
||||||
|
"Category": "Categoria",
|
||||||
|
"Total": "Total"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"advancedtools": {
|
"advancedtools": {
|
||||||
@@ -1530,6 +1534,7 @@
|
|||||||
"mtrtest": "Traceroutes MTR por salto até o IP do usuário a partir de sondas pelo mundo; perda em saltos intermediários pode ser apenas limitação de ICMP, mas perda sustentada a partir de um salto indica problema real.",
|
"mtrtest": "Traceroutes MTR por salto até o IP do usuário a partir de sondas pelo mundo; perda em saltos intermediários pode ser apenas limitação de ICMP, mas perda sustentada a partir de um salto indica problema real.",
|
||||||
"ruletest": "IPs de saída observados ao buscar 8 endpoints de teste — mais de um IP único significa que o tráfego está dividido entre rotas diferentes (ex.: regras de proxy).",
|
"ruletest": "IPs de saída observados ao buscar 8 endpoints de teste — mais de um IP único significa que o tráfego está dividido entre rotas diferentes (ex.: regras de proxy).",
|
||||||
"browserinfo": "Ambiente de navegador, sistema operacional e tela do dispositivo testado.",
|
"browserinfo": "Ambiente de navegador, sistema operacional e tela do dispositivo testado.",
|
||||||
|
"adblocktest": "Teste de bloqueio de anúncios e rastreadores executado no navegador: por categoria (redes de anúncios, análise, rastreadores sociais, monitoramento de erros, telemetria de fabricantes de dispositivos, scripts de rastreamento), quantas requisições a endereços conhecidos foram bloqueadas, passaram ou esgotaram o tempo (noAnswer). Não bloquear tudo pode ser uma escolha deliberada, não uma falha.",
|
||||||
"invisibility": "Pontuações de detecção de proxy/VPN (0–100, maior = mais provável de ser detectado) com os sinais individuais de detecção que foram sinalizados.",
|
"invisibility": "Pontuações de detecção de proxy/VPN (0–100, maior = mais provável de ser detectado) com os sinais individuais de detecção que foram sinalizados.",
|
||||||
"enhanceddnsleak": "Captura DNS em nível de resolvedor: cada IP de resolvedor que consultou o domínio de teste, com transporte, exposição ECS e flags DNSSEC (do/cd).",
|
"enhanceddnsleak": "Captura DNS em nível de resolvedor: cada IP de resolvedor que consultou o domínio de teste, com transporte, exposição ECS e flags DNSSEC (do/cd).",
|
||||||
"persona": "O quão próximo o navegador está de uma pessoa local do país escolhido: uma nota geral mais o veredito de cada verificação. Apenas vereditos — os valores subjacentes deliberadamente não são armazenados em relatórios compartilhados."
|
"persona": "O quão próximo o navegador está de uma pessoa local do país escolhido: uma nota geral mais o veredito de cada verificação. Apenas vereditos — os valores subjacentes deliberadamente não são armazenados em relatórios compartilhados."
|
||||||
|
|||||||
@@ -1060,6 +1060,10 @@
|
|||||||
"browser": "Элементы-приманки скрыты: внутри браузера работает блокировщик — расширение или встроенная функция браузера.",
|
"browser": "Элементы-приманки скрыты: внутри браузера работает блокировщик — расширение или встроенная функция браузера.",
|
||||||
"network": "Запросы блокируются, но элементы-приманки не скрыты: скорее всего, блокировка происходит вне страницы — на DNS-сервисе, VPN, роутере или во встроенной защите браузера от отслеживания.",
|
"network": "Запросы блокируются, но элементы-приманки не скрыты: скорее всего, блокировка происходит вне страницы — на DNS-сервисе, VPN, роутере или во встроенной защите браузера от отслеживания.",
|
||||||
"none": "Ничего не заблокировано: в этом браузере и в этой сети блокировка рекламы и трекеров не обнаружена."
|
"none": "Ничего не заблокировано: в этом браузере и в этой сети блокировка рекламы и трекеров не обнаружена."
|
||||||
|
},
|
||||||
|
"report": {
|
||||||
|
"Category": "Категория",
|
||||||
|
"Total": "Всего"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"advancedtools": {
|
"advancedtools": {
|
||||||
@@ -1530,6 +1534,7 @@
|
|||||||
"mtrtest": "Пошаговые трассировки MTR до IP пользователя с точек по всему миру. Потеря на промежуточных узлах может быть лишь ограничением частоты ICMP; постоянная потеря, продолжающаяся от одного узла дальше, указывает на реальную проблему.",
|
"mtrtest": "Пошаговые трассировки MTR до IP пользователя с точек по всему миру. Потеря на промежуточных узлах может быть лишь ограничением частоты ICMP; постоянная потеря, продолжающаяся от одного узла дальше, указывает на реальную проблему.",
|
||||||
"ruletest": "Выходные IP, обнаруженные при обращении к 8 тестовым адресам. Несколько уникальных IP означают, что трафик разделён между маршрутами, например правилами прокси.",
|
"ruletest": "Выходные IP, обнаруженные при обращении к 8 тестовым адресам. Несколько уникальных IP означают, что трафик разделён между маршрутами, например правилами прокси.",
|
||||||
"browserinfo": "Браузер, ОС и параметры экрана проверяемого устройства.",
|
"browserinfo": "Браузер, ОС и параметры экрана проверяемого устройства.",
|
||||||
|
"adblocktest": "Проверка блокировки рекламы и трекеров в браузере: по категориям (рекламные сети, аналитика, трекеры соцсетей, мониторинг ошибок, телеметрия производителей устройств, скрипты отслеживания) — сколько запросов к известным адресам было заблокировано, прошло или завершилось тайм-аутом (noAnswer). Неполная блокировка может быть осознанным выбором, а не неисправностью.",
|
||||||
"invisibility": "Оценки обнаружения прокси/VPN (0–100; чем выше, тем вероятнее обнаружение) и отдельные сработавшие признаки.",
|
"invisibility": "Оценки обнаружения прокси/VPN (0–100; чем выше, тем вероятнее обнаружение) и отдельные сработавшие признаки.",
|
||||||
"enhanceddnsleak": "Перехват DNS на уровне резолверов: каждый IP резолвера, запросившего тестовый домен, с данными о транспорте, раскрытии ECS и флагах DNSSEC (do/cd).",
|
"enhanceddnsleak": "Перехват DNS на уровне резолверов: каждый IP резолвера, запросившего тестовый домен, с данными о транспорте, раскрытии ECS и флагах DNSSEC (do/cd).",
|
||||||
"persona": "Насколько браузер похож на местного жителя выбранной страны: общая оценка и вердикт каждой проверки. Только вердикты — стоящие за ними значения намеренно не сохраняются в общем отчёте."
|
"persona": "Насколько браузер похож на местного жителя выбранной страны: общая оценка и вердикт каждой проверки. Только вердикты — стоящие за ними значения намеренно не сохраняются в общем отчёте."
|
||||||
|
|||||||
@@ -1060,6 +1060,10 @@
|
|||||||
"browser": "誘餌元素被隱藏了:瀏覽器內有阻擋器在運作,可能是擴充功能,也可能是瀏覽器內建的功能。",
|
"browser": "誘餌元素被隱藏了:瀏覽器內有阻擋器在運作,可能是擴充功能,也可能是瀏覽器內建的功能。",
|
||||||
"network": "請求被阻擋,但誘餌元素沒有被隱藏:阻擋很可能發生在頁面之外,例如 DNS 服務、VPN、路由器,或瀏覽器的防追蹤功能。",
|
"network": "請求被阻擋,但誘餌元素沒有被隱藏:阻擋很可能發生在頁面之外,例如 DNS 服務、VPN、路由器,或瀏覽器的防追蹤功能。",
|
||||||
"none": "沒有任何阻擋:在這個瀏覽器和網路中沒有發現廣告或追蹤封鎖。"
|
"none": "沒有任何阻擋:在這個瀏覽器和網路中沒有發現廣告或追蹤封鎖。"
|
||||||
|
},
|
||||||
|
"report": {
|
||||||
|
"Category": "類別",
|
||||||
|
"Total": "總數"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"advancedtools": {
|
"advancedtools": {
|
||||||
@@ -1530,6 +1534,7 @@
|
|||||||
"mtrtest": "全球探針針對使用者 IP 進行 MTR 路由追蹤。途中某些節點發生封包遺失,可能只是 ICMP 速率限制;如果從某個節點開始,後續都持續出現封包遺失,才較可能代表實際問題。",
|
"mtrtest": "全球探針針對使用者 IP 進行 MTR 路由追蹤。途中某些節點發生封包遺失,可能只是 ICMP 速率限制;如果從某個節點開始,後續都持續出現封包遺失,才較可能代表實際問題。",
|
||||||
"ruletest": "存取 8 個測試端點時觀測到的出口 IP——出現多個不同 IP 表示流量被分流到不同線路(如代理規則)。",
|
"ruletest": "存取 8 個測試端點時觀測到的出口 IP——出現多個不同 IP 表示流量被分流到不同線路(如代理規則)。",
|
||||||
"browserinfo": "被測裝置的瀏覽器、作業系統與顯示環境。",
|
"browserinfo": "被測裝置的瀏覽器、作業系統與顯示環境。",
|
||||||
|
"adblocktest": "在瀏覽器中執行的廣告與追蹤封鎖檢測:依類別(廣告網路、統計分析、社群平台追蹤、錯誤監控、裝置廠商遙測、追蹤腳本)統計送往常見端點的請求中,被阻擋、未被阻擋和逾時(noAnswer)的數量。沒有全部阻擋可能是使用者的主動選擇,並不代表故障。",
|
||||||
"invisibility": "代理/VPN 檢測得分(0-100,越高越可能被識別)及各項命中的檢測訊號。",
|
"invisibility": "代理/VPN 檢測得分(0-100,越高越可能被識別)及各項命中的檢測訊號。",
|
||||||
"enhanceddnsleak": "解析器層級的 DNS 查詢紀錄:包含向測試網域名稱發出查詢的每個解析器 IP、傳輸協定、ECS 暴露情況與 DNSSEC 標記(DO/CD)。",
|
"enhanceddnsleak": "解析器層級的 DNS 查詢紀錄:包含向測試網域名稱發出查詢的每個解析器 IP、傳輸協定、ECS 暴露情況與 DNSSEC 標記(DO/CD)。",
|
||||||
"persona": "瀏覽器與所選國家本地居民的相符程度:一個總體評級,加上每項檢查的結論。只有結論——各項檢查依據的具體數值刻意不寫入可分享的報告。"
|
"persona": "瀏覽器與所選國家本地居民的相符程度:一個總體評級,加上每項檢查的結論。只有結論——各項檢查依據的具體數值刻意不寫入可分享的報告。"
|
||||||
|
|||||||
@@ -1060,6 +1060,10 @@
|
|||||||
"browser": "诱饵元素被隐藏了:浏览器内有拦截器在工作,可能是扩展,也可能是浏览器自带的功能。",
|
"browser": "诱饵元素被隐藏了:浏览器内有拦截器在工作,可能是扩展,也可能是浏览器自带的功能。",
|
||||||
"network": "请求被拦截,但诱饵元素没有被隐藏:拦截很可能发生在页面之外,例如 DNS 服务、VPN、路由器,或浏览器的防跟踪功能。",
|
"network": "请求被拦截,但诱饵元素没有被隐藏:拦截很可能发生在页面之外,例如 DNS 服务、VPN、路由器,或浏览器的防跟踪功能。",
|
||||||
"none": "没有任何拦截:在这个浏览器和网络中没有发现广告或跟踪屏蔽。"
|
"none": "没有任何拦截:在这个浏览器和网络中没有发现广告或跟踪屏蔽。"
|
||||||
|
},
|
||||||
|
"report": {
|
||||||
|
"Category": "类别",
|
||||||
|
"Total": "总数"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"advancedtools": {
|
"advancedtools": {
|
||||||
@@ -1530,6 +1534,7 @@
|
|||||||
"mtrtest": "全球探针到用户 IP 的逐跳 MTR 路由追踪;中间跳的丢包可能只是 ICMP 限速,从某一跳起持续丢包才代表真实问题。",
|
"mtrtest": "全球探针到用户 IP 的逐跳 MTR 路由追踪;中间跳的丢包可能只是 ICMP 限速,从某一跳起持续丢包才代表真实问题。",
|
||||||
"ruletest": "访问 8 个测试端点时观测到的出口 IP——出现多个不同 IP 说明流量被分流到不同线路(如代理规则)。",
|
"ruletest": "访问 8 个测试端点时观测到的出口 IP——出现多个不同 IP 说明流量被分流到不同线路(如代理规则)。",
|
||||||
"browserinfo": "被测设备的浏览器、操作系统与显示环境。",
|
"browserinfo": "被测设备的浏览器、操作系统与显示环境。",
|
||||||
|
"adblocktest": "在浏览器中运行的广告与跟踪屏蔽检测:按类别(广告网络、统计分析、社交平台跟踪、错误监控、设备厂商遥测、跟踪脚本)统计发往常见端点的请求中,被拦截、未被拦截和超时(noAnswer)的数量。没有全部拦截可能是用户的主动选择,并不代表故障。",
|
||||||
"invisibility": "代理/VPN 检测得分(0-100,越高越可能被识别)及各项命中的检测信号。",
|
"invisibility": "代理/VPN 检测得分(0-100,越高越可能被识别)及各项命中的检测信号。",
|
||||||
"enhanceddnsleak": "解析器级 DNS 捕获:向测试域名发起查询的每个解析器 IP,含传输协议、ECS 暴露与 DNSSEC 标志(do/cd)。",
|
"enhanceddnsleak": "解析器级 DNS 捕获:向测试域名发起查询的每个解析器 IP,含传输协议、ECS 暴露与 DNSSEC 标志(do/cd)。",
|
||||||
"persona": "浏览器与所选国家本地居民的相符程度:一个总体评级,加上每项检查的结论。只有结论——各项检查依据的具体数值刻意不写入可分享的报告。"
|
"persona": "浏览器与所选国家本地居民的相符程度:一个总体评级,加上每项检查的结论。只有结论——各项检查依据的具体数值刻意不写入可分享的报告。"
|
||||||
|
|||||||
@@ -283,6 +283,28 @@ const buildBrowserinfo = (payload) => {
|
|||||||
return Object.keys(section).length ? section : null;
|
return Object.keys(section).length ? section : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// adblocktest:finished — { items: [{ groupId, kind, status }] }, one item
|
||||||
|
// per probed address. Only the per-category request counts travel; cosmetic
|
||||||
|
// baits and unanswered (pending) items are dropped.
|
||||||
|
const ADBLOCK_STATUSES = new Set(['blocked', 'open', 'unknown']);
|
||||||
|
const buildAdblocktest = (payload) => {
|
||||||
|
const groups = new Map(); // first-appearance order = the probe list's
|
||||||
|
for (const item of Array.isArray(payload?.items) ? payload.items : []) {
|
||||||
|
if (typeof item?.groupId !== 'string' || item.kind === 'cosmetic'
|
||||||
|
|| !ADBLOCK_STATUSES.has(item?.status)) continue;
|
||||||
|
const id = clip(item.groupId, 16);
|
||||||
|
if (!groups.has(id)) {
|
||||||
|
if (groups.size >= 16) continue;
|
||||||
|
groups.set(id, { id, total: 0, blocked: 0, open: 0, unknown: 0 });
|
||||||
|
}
|
||||||
|
const counts = groups.get(id);
|
||||||
|
if (counts.total >= 1024) continue;
|
||||||
|
counts.total += 1;
|
||||||
|
counts[item.status] += 1;
|
||||||
|
}
|
||||||
|
return groups.size ? { groups: [...groups.values()] } : null;
|
||||||
|
};
|
||||||
|
|
||||||
// invisibility:result — { proxyScore, vpnScore, ip, flags: [{ key, flagged }] }
|
// invisibility:result — { proxyScore, vpnScore, ip, flags: [{ key, flagged }] }
|
||||||
const buildInvisibility = (payload) => {
|
const buildInvisibility = (payload) => {
|
||||||
const proxy = finiteNum(payload?.proxyScore, 0, 100);
|
const proxy = finiteNum(payload?.proxyScore, 0, 100);
|
||||||
@@ -381,6 +403,7 @@ export const REPORT_EVENT_BUILDERS = {
|
|||||||
'mtrtest:finished': { section: 'mtrtest', build: buildMtrtest },
|
'mtrtest:finished': { section: 'mtrtest', build: buildMtrtest },
|
||||||
'ruletest:finished': { section: 'ruletest', build: buildRuletest },
|
'ruletest:finished': { section: 'ruletest', build: buildRuletest },
|
||||||
'browserinfo:finished': { section: 'browserinfo', build: buildBrowserinfo },
|
'browserinfo:finished': { section: 'browserinfo', build: buildBrowserinfo },
|
||||||
|
'adblocktest:finished': { section: 'adblocktest', build: buildAdblocktest },
|
||||||
'invisibility:result': { section: 'invisibility', build: buildInvisibility },
|
'invisibility:result': { section: 'invisibility', build: buildInvisibility },
|
||||||
'enhanceddnsleak:finished': { section: 'enhanceddnsleak', build: buildEnhanceddnsleak },
|
'enhanceddnsleak:finished': { section: 'enhanceddnsleak', build: buildEnhanceddnsleak },
|
||||||
'persona:finished': { section: 'persona', build: buildPersona },
|
'persona:finished': { section: 'persona', build: buildPersona },
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export const SECTION_TITLE_KEYS = {
|
|||||||
mtrtest: 'mtrtest.Title',
|
mtrtest: 'mtrtest.Title',
|
||||||
ruletest: 'ruletest.Title',
|
ruletest: 'ruletest.Title',
|
||||||
browserinfo: 'browserinfo.Title',
|
browserinfo: 'browserinfo.Title',
|
||||||
|
adblocktest: 'adblocktest.Title',
|
||||||
invisibility: 'invisibilitytest.Title',
|
invisibility: 'invisibilitytest.Title',
|
||||||
enhanceddnsleak: 'enhanceddnsleaktest.Title',
|
enhanceddnsleak: 'enhanceddnsleaktest.Title',
|
||||||
persona: 'personacheck.Title',
|
persona: 'personacheck.Title',
|
||||||
@@ -157,6 +158,8 @@ const SECTION_RENDERERS = {
|
|||||||
['display', section.display && `${section.display.width}x${section.display.height} @${section.display.pixelRatio}x`],
|
['display', section.display && `${section.display.width}x${section.display.height} @${section.display.pixelRatio}x`],
|
||||||
['connection', section.connection && `${section.connection.effectiveType ?? ''} downlink=${section.connection.downlink ?? '—'}Mbps rtt=${section.connection.rtt ?? '—'}ms`.trim()],
|
['connection', section.connection && `${section.connection.effectiveType ?? ''} downlink=${section.connection.downlink ?? '—'}Mbps rtt=${section.connection.rtt ?? '—'}ms`.trim()],
|
||||||
]),
|
]),
|
||||||
|
adblocktest: (section) => mdTable(['group', 'total', 'blocked', 'notBlocked', 'noAnswer'],
|
||||||
|
section.groups.map((g) => [g.id, g.total, g.blocked, g.open, g.unknown])),
|
||||||
invisibility: (section) => [
|
invisibility: (section) => [
|
||||||
kvLines([
|
kvLines([
|
||||||
['ip', section.ip],
|
['ip', section.ip],
|
||||||
|
|||||||
@@ -81,6 +81,17 @@ const PAYLOADS = {
|
|||||||
{ id: 3, ip: '检测失败', country_code: '', org: '检测失败' },
|
{ id: 3, ip: '检测失败', country_code: '', org: '检测失败' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
'adblocktest:finished': {
|
||||||
|
items: [
|
||||||
|
{ groupId: 'ads', kind: 'domain', status: 'blocked' },
|
||||||
|
{ groupId: 'ads', kind: 'domain', status: 'open' },
|
||||||
|
{ groupId: 'ads', kind: 'domain', status: 'blocked' },
|
||||||
|
{ groupId: 'social', kind: 'domain', status: 'unknown' },
|
||||||
|
{ groupId: 'scripts', kind: 'script', status: 'open' },
|
||||||
|
{ groupId: 'scripts', kind: 'script', status: 'pending' },
|
||||||
|
{ groupId: 'cosmetic', kind: 'cosmetic', status: 'blocked' },
|
||||||
|
],
|
||||||
|
},
|
||||||
'browserinfo:finished': {
|
'browserinfo:finished': {
|
||||||
userAgent: {
|
userAgent: {
|
||||||
ua: 'Mozilla/5.0 …',
|
ua: 'Mozilla/5.0 …',
|
||||||
@@ -160,6 +171,26 @@ describe('report builders produce schema-valid sections', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('cleaning rules', () => {
|
describe('cleaning rules', () => {
|
||||||
|
it('adblocktest keeps per-category request counts only', () => {
|
||||||
|
const { build } = REPORT_EVENT_BUILDERS['adblocktest:finished'];
|
||||||
|
// Pending items and cosmetic baits never reach the counts.
|
||||||
|
assert.deepEqual(build(PAYLOADS['adblocktest:finished']), {
|
||||||
|
groups: [
|
||||||
|
{ id: 'ads', total: 3, blocked: 2, open: 1, unknown: 0 },
|
||||||
|
{ id: 'social', total: 1, blocked: 0, open: 0, unknown: 1 },
|
||||||
|
{ id: 'scripts', total: 1, blocked: 0, open: 1, unknown: 0 },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adblocktest reports nothing when only baits or unanswered items came back', () => {
|
||||||
|
const { build } = REPORT_EVENT_BUILDERS['adblocktest:finished'];
|
||||||
|
assert.equal(build({ items: [
|
||||||
|
{ groupId: 'cosmetic', kind: 'cosmetic', status: 'blocked' },
|
||||||
|
{ groupId: 'ads', kind: 'domain', status: 'pending' },
|
||||||
|
] }), null);
|
||||||
|
});
|
||||||
|
|
||||||
it('ipinfo drops cards whose ip slot holds a placeholder', () => {
|
it('ipinfo drops cards whose ip slot holds a placeholder', () => {
|
||||||
const { build } = REPORT_EVENT_BUILDERS['ipinfo:finished'];
|
const { build } = REPORT_EVENT_BUILDERS['ipinfo:finished'];
|
||||||
const section = build(PAYLOADS['ipinfo:finished']);
|
const section = build(PAYLOADS['ipinfo:finished']);
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ const makeSections = () => ({
|
|||||||
{ id: 'gps-location', axis: 'leak', verdict: 'leak' },
|
{ id: 'gps-location', axis: 'leak', verdict: 'leak' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
adblocktest: {
|
||||||
|
testedAt: '2026-07-14T08:25:00.000Z',
|
||||||
|
groups: [{ id: 'ads', total: 3, blocked: 2, open: 1, unknown: 0 }],
|
||||||
|
},
|
||||||
ipinfo: {
|
ipinfo: {
|
||||||
testedAt: '2026-07-14T08:00:00.000Z',
|
testedAt: '2026-07-14T08:00:00.000Z',
|
||||||
cards: [{ source: 'IPCheck.ing IPv4', ip: '1.2.3.4', countryCode: 'US', city: 'LA', timezone: 'America/Los_Angeles', asn: 'AS15169', isp: 'Google' }],
|
cards: [{ source: 'IPCheck.ing IPv4', ip: '1.2.3.4', countryCode: 'US', city: 'LA', timezone: 'America/Los_Angeles', asn: 'AS15169', isp: 'Google' }],
|
||||||
@@ -144,6 +148,20 @@ describe('reportToMarkdown', () => {
|
|||||||
assert.ok(md.includes('score: 72/100'));
|
assert.ok(md.includes('score: 72/100'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('renders the adblocktest section as its per-category table', () => {
|
||||||
|
const report = buildShareReport({
|
||||||
|
sections: makeSections(),
|
||||||
|
selectedIds: ['adblocktest'],
|
||||||
|
maskTail: false,
|
||||||
|
locale: 'en',
|
||||||
|
origin: 'ipcheck.ing',
|
||||||
|
});
|
||||||
|
assert.deepEqual(validateReport(report), { ok: true, errors: [] });
|
||||||
|
const md = reportToMarkdown(report, t);
|
||||||
|
assert.ok(md.includes('| group | total | blocked | notBlocked | noAnswer |'));
|
||||||
|
assert.ok(md.includes('| ads | 3 | 2 | 1 | 0 |'));
|
||||||
|
});
|
||||||
|
|
||||||
it('renders heading, intro, per-section blocks and the closing instruction', () => {
|
it('renders heading, intro, per-section blocks and the closing instruction', () => {
|
||||||
const md = reportToMarkdown(makeReport(), t);
|
const md = reportToMarkdown(makeReport(), t);
|
||||||
assert.ok(md.startsWith('# report.ai.Heading'));
|
assert.ok(md.startsWith('# report.ai.Heading'));
|
||||||
|
|||||||
@@ -95,6 +95,10 @@ const makeValidReport = () => ({
|
|||||||
display: { width: 1512, height: 982, pixelRatio: 2 },
|
display: { width: 1512, height: 982, pixelRatio: 2 },
|
||||||
connection: null,
|
connection: null,
|
||||||
},
|
},
|
||||||
|
adblocktest: {
|
||||||
|
testedAt: '2026-07-14T08:00:00.000Z',
|
||||||
|
groups: [{ id: 'ads', total: 3, blocked: 2, open: 1, unknown: 0 }],
|
||||||
|
},
|
||||||
invisibility: {
|
invisibility: {
|
||||||
testedAt: '2026-07-14T08:00:00.000Z',
|
testedAt: '2026-07-14T08:00:00.000Z',
|
||||||
ip: '1.2.3.4',
|
ip: '1.2.3.4',
|
||||||
@@ -299,7 +303,7 @@ describe('constants', () => {
|
|||||||
assert.deepEqual(REPORT_TTL_DAYS, [1, 3, 7]);
|
assert.deepEqual(REPORT_TTL_DAYS, [1, 3, 7]);
|
||||||
assert.ok(REPORT_SECTION_IDS.includes('ipinfo'));
|
assert.ok(REPORT_SECTION_IDS.includes('ipinfo'));
|
||||||
assert.ok(REPORT_SECTION_IDS.includes('enhanceddnsleak'));
|
assert.ok(REPORT_SECTION_IDS.includes('enhanceddnsleak'));
|
||||||
assert.equal(REPORT_SECTION_IDS.length, 12);
|
assert.equal(REPORT_SECTION_IDS.length, 13);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user