diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 29d250b5..ed1148fe 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -480,6 +480,7 @@ "whois": "Whois", "filtering_rules_learn_more": "<0>Learn more about creating your own hosts lists.", "blocked_by_response": "Blocked by CNAME or IP in response", + "blocked_by_cname_or_ip": "Blocked by CNAME or IP", "try_again": "Try again", "domain_desc": "Enter the domain name or wildcard you want to be rewritten.", "example_rewrite_domain": "rewrite responses for this domain name only.", @@ -560,5 +561,6 @@ "filter_category_general_desc": "Lists that block tracking and advertising on most of the devices", "filter_category_security_desc": "Lists that specialize on blocking malware, phishing or scam domains", "filter_category_regional_desc": "Lists that focus on regional ads and tracking servers", - "filter_category_other_desc": "Other blocklists" + "filter_category_other_desc": "Other blocklists", + "original_response": "Original response" } diff --git a/client/src/components/Logs/Cells/getClientCell.js b/client/src/components/Logs/Cells/getClientCell.js index 147dd3af..2ebca672 100644 --- a/client/src/components/Logs/Cells/getClientCell.js +++ b/client/src/components/Logs/Cells/getClientCell.js @@ -68,7 +68,7 @@ const getClientCell = ({ return (
- {processedData && getHintElement({ + {getHintElement({ className: hintClass, columnClass: 'grid grid--limited', tooltipClass: 'px-5 pb-5 pt-4 mw-75', diff --git a/client/src/components/Logs/Cells/getDomainCell.js b/client/src/components/Logs/Cells/getDomainCell.js index bff36c37..c4156920 100644 --- a/client/src/components/Logs/Cells/getDomainCell.js +++ b/client/src/components/Logs/Cells/getDomainCell.js @@ -33,16 +33,6 @@ const getDomainCell = (props) => { 'my-3': isDetailed, }); - const dnssecHint = getHintElement({ - className: lockIconClass, - tooltipClass: 'py-4 px-5 pb-45', - canShowTooltip: answer_dnssec, - xlinkHref: 'lock', - columnClass: 'w-100', - content: 'validated_with_dnssec', - placement: 'bottom', - }); - const protocol = t(SCHEME_TO_PROTOCOL_MAP[client_proto]) || ''; const ip = type ? `${t('type_table_header')}: ${type}` : ''; @@ -100,7 +90,15 @@ const getDomainCell = (props) => { return (
- {dnssec_enabled && dnssecHint} + {dnssec_enabled && getHintElement({ + className: lockIconClass, + tooltipClass: 'py-4 px-5 pb-45', + canShowTooltip: answer_dnssec, + xlinkHref: 'lock', + columnClass: 'w-100', + content: 'validated_with_dnssec', + placement: 'bottom', + })} {trackerHint}
{domain}
diff --git a/client/src/components/Logs/Cells/getHintElement.js b/client/src/components/Logs/Cells/getHintElement.js index dcb5fed1..93477bf3 100644 --- a/client/src/components/Logs/Cells/getHintElement.js +++ b/client/src/components/Logs/Cells/getHintElement.js @@ -6,6 +6,7 @@ import classNames from 'classnames'; import './Tooltip.css'; import 'react-popper-tooltip/dist/styles.css'; import { HIDE_TOOLTIP_DELAY } from '../../../helpers/constants'; +import { processContent } from '../../../helpers/helpers'; const getHintElement = ({ className, @@ -17,34 +18,34 @@ const getHintElement = ({ placement, tooltipClass, content, - renderContent = React.Children.map( - content, + renderContent = content ? React.Children.map( + processContent(content), (item, idx) =>
{item || '—'}
, - ), + ) : null, }) =>
- {title &&
- {title} -
} -
{renderContent}
-
- }>{({ - getTriggerProps, triggerRef, - }) => + ({ + tooltipRef, + getTooltipProps, + }) =>
+ {title &&
+ {title} +
} +
{renderContent}
+
+}>{({ + getTriggerProps, triggerRef, +}) => {xlinkHref && } } -
; +; getHintElement.propTypes = { className: PropTypes.string, diff --git a/client/src/components/Logs/Cells/getResponseCell.js b/client/src/components/Logs/Cells/getResponseCell.js index 6fd00aae..fcc2e5a0 100644 --- a/client/src/components/Logs/Cells/getResponseCell.js +++ b/client/src/components/Logs/Cells/getResponseCell.js @@ -9,18 +9,24 @@ import getHintElement from './getHintElement'; const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => { const { - reason, filterId, rule, status, upstream, elapsedMs, domain, response, + reason, filterId, rule, status, upstream, elapsedMs, + domain, response, originalResponse, } = row.original; const { filters, whitelistFilters } = filtering; const formattedElapsedMs = formatElapsedMs(elapsedMs, t); - const statusLabel = t(FILTERED_STATUS_TO_META_MAP[reason]?.label || reason); + const isBlocked = reason === FILTERED_STATUS.FILTERED_BLACK_LIST + || reason === FILTERED_STATUS.FILTERED_BLOCKED_SERVICE; + + const isBlockedByResponse = originalResponse.length > 0 && isBlocked; + + const statusLabel = t(isBlockedByResponse ? 'blocked_by_cname_or_ip' : FILTERED_STATUS_TO_META_MAP[reason]?.label || reason); const boldStatusLabel = {statusLabel}; const filter = getFilterName(filters, whitelistFilters, filterId, t); const renderResponses = (responseArr) => { - if (responseArr.length === 0) { + if (responseArr?.length === 0) { return ''; } @@ -50,6 +56,7 @@ const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => { filter, rule_label: rule, response_code: status, + original_response: renderResponses(originalResponse), }, [FILTERED_STATUS.NOT_FILTERED_WHITE_LIST]: { domain, @@ -78,9 +85,11 @@ const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => { domain, encryption_status: boldStatusLabel, filter, + rule_label: rule, install_settings_dns: upstream, elapsed: formattedElapsedMs, response_code: status, + original_response: renderResponses(originalResponse), }, }; @@ -88,13 +97,11 @@ const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => { ? Object.entries(FILTERED_STATUS_TO_FIELDS_MAP[reason]) : Object.entries(FILTERED_STATUS_TO_FIELDS_MAP.NotFilteredNotFound); - const detailedInfo = reason === FILTERED_STATUS.FILTERED_BLOCKED_SERVICE - || reason === FILTERED_STATUS.FILTERED_BLACK_LIST - ? filter : formattedElapsedMs; + const detailedInfo = isBlocked ? filter : formattedElapsedMs; return (
- {fields && getHintElement({ + {getHintElement({ className: classNames('icons mr-4 icon--small cursor--pointer icon--light-gray', { 'my-3': isDetailed }), columnClass: 'grid grid--limited', tooltipClass: 'px-5 pb-5 pt-4 mw-75 custom-tooltip__response-details', @@ -107,7 +114,8 @@ const getResponseCell = (row, filtering, t, isDetailed, getFilterName) => {
{statusLabel}
{isDetailed &&
{detailedInfo}
} + className="detailed-info d-none d-sm-block pt-1 text-truncate" + title={detailedInfo}>{detailedInfo}
}
); diff --git a/client/src/components/Logs/Table.js b/client/src/components/Logs/Table.js index a6272b65..7cc42d0d 100644 --- a/client/src/components/Logs/Table.js +++ b/client/src/components/Logs/Table.js @@ -12,7 +12,7 @@ import { FILTERED_STATUS_TO_META_MAP, TABLE_DEFAULT_PAGE_SIZE, SCHEME_TO_PROTOCOL_MAP, - CUSTOM_FILTERING_RULES_ID, + CUSTOM_FILTERING_RULES_ID, FILTERED_STATUS, } from '../../helpers/constants'; import getDateCell from './Cells/getDateCell'; import getDomainCell from './Cells/getDomainCell'; @@ -300,6 +300,8 @@ const Table = (props) => { type, client_proto, filterId, + rule, + originalResponse, } = rowInfo.original; const hasTracker = !!tracker; @@ -317,12 +319,16 @@ const Table = (props) => { const formattedElapsedMs = formatElapsedMs(elapsedMs, t); const isFiltered = checkFiltered(reason); + const isBlocked = reason === FILTERED_STATUS.FILTERED_BLACK_LIST + || reason === FILTERED_STATUS.FILTERED_BLOCKED_SERVICE; + const buttonType = isFiltered ? BLOCK_ACTIONS.UNBLOCK : BLOCK_ACTIONS.BLOCK; const onToggleBlock = () => { toggleBlocking(buttonType, domain); }; - const status = t(FILTERED_STATUS_TO_META_MAP[reason]?.label || reason); + const isBlockedByResponse = originalResponse.length > 0 && isBlocked; + const status = t(isBlockedByResponse ? 'blocked_by_cname_or_ip' : FILTERED_STATUS_TO_META_MAP[reason]?.label || reason); const statusBlocked =
{status}
; const protocol = t(SCHEME_TO_PROTOCOL_MAP[client_proto]) || ''; @@ -379,12 +385,14 @@ const Table = (props) => { install_settings_dns: upstream, elapsed: formattedElapsedMs, filter, + rule_label: rule, response_table_header: response?.join('\n'), + original_response: originalResponse?.join('\n'), [buttonType]:
{t(buttonType)}
, }; - const detailedDataCurrent = isFiltered ? detailedDataBlocked : detailedData; + const detailedDataCurrent = isBlocked ? detailedDataBlocked : detailedData; setDetailedDataCurrent(detailedDataCurrent); setButtonType(buttonType); diff --git a/client/src/components/Logs/index.js b/client/src/components/Logs/index.js index 6bfdcb86..1b4efdcb 100644 --- a/client/src/components/Logs/index.js +++ b/client/src/components/Logs/index.js @@ -24,6 +24,10 @@ const INITIAL_REQUEST_DATA = ['', TABLE_FIRST_PAGE, INITIAL_REQUEST]; export const processContent = (data, buttonType) => Object.entries(data) .map(([key, value]) => { + if (!value) { + return null; + } + const isTitle = value === 'title'; const isButton = key === buttonType; const isBoolean = typeof value === 'boolean'; diff --git a/client/src/helpers/formatClientCell.js b/client/src/helpers/formatClientCell.js index 9e125588..05baff38 100644 --- a/client/src/helpers/formatClientCell.js +++ b/client/src/helpers/formatClientCell.js @@ -52,7 +52,7 @@ export const formatClientCell = (row, t, isDetailed = false) => { } return ( -
+
<> {nameContainer} {whoisContainer} diff --git a/client/src/helpers/helpers.js b/client/src/helpers/helpers.js index 0879015d..3507e4ab 100644 --- a/client/src/helpers/helpers.js +++ b/client/src/helpers/helpers.js @@ -86,18 +86,16 @@ export const normalizeLogs = (logs) => logs.map((log) => { const { host: domain, type } = question; - const response = answer ? answer.map((response) => { + const processResponse = (data) => (data ? data.map((response) => { const { value, type, ttl } = response; return `${type}: ${value} (ttl=${ttl})`; - }) : []; - - const tracker = getTrackerData(domain); + }) : []); return { time, domain, type, - response, + response: processResponse(answer), reason, client, client_proto, @@ -106,7 +104,8 @@ export const normalizeLogs = (logs) => logs.map((log) => { status, serviceName: service_name, originalAnswer: original_answer, - tracker, + originalResponse: processResponse(original_answer), + tracker: getTrackerData(domain), answer_dnssec, elapsedMs, upstream, @@ -618,3 +617,8 @@ export const selectCompletedFields = (values) => Object.entries(values) } return acc; }, {}); + + +export const processContent = (content) => (Array.isArray(content) + ? content.filter(([, value]) => value) + .flat() : content);