diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 69082b94..46fc464d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,7 +1,7 @@
'name': 'build'
'env':
- 'GO_VERSION': '1.23.5'
+ 'GO_VERSION': '1.23.6'
'NODE_VERSION': '18'
'on':
@@ -39,7 +39,7 @@
'with':
'node-version': '${{ env.NODE_VERSION }}'
- 'name': 'Set up Go modules cache'
- 'uses': 'actions/cache@v2'
+ 'uses': 'actions/cache@v4'
'with':
'path': '~/go/pkg/mod'
'key': "${{ runner.os }}-go-${{ hashFiles('go.sum') }}"
@@ -48,7 +48,7 @@
'id': 'npm-cache'
'run': 'echo "::set-output name=dir::$( npm config get cache )"'
- 'name': 'Set up npm cache'
- 'uses': 'actions/cache@v2'
+ 'uses': 'actions/cache@v4'
'with':
'path': '${{ steps.npm-cache.outputs.dir }}'
'key': "${{ runner.os }}-node-${{ hashFiles('client/package-lock.json') }}"
@@ -80,7 +80,7 @@
'with':
'node-version': '${{ env.NODE_VERSION }}'
- 'name': 'Set up Go modules cache'
- 'uses': 'actions/cache@v2'
+ 'uses': 'actions/cache@v4'
'with':
'path': '~/go/pkg/mod'
'key': "${{ runner.os }}-go-${{ hashFiles('go.sum') }}"
@@ -89,7 +89,7 @@
'id': 'npm-cache'
'run': 'echo "::set-output name=dir::$(npm config get cache)"'
- 'name': 'Set up npm cache'
- 'uses': 'actions/cache@v2'
+ 'uses': 'actions/cache@v4'
'with':
'path': '${{ steps.npm-cache.outputs.dir }}'
'key': "${{ runner.os }}-node-${{ hashFiles('client/package-lock.json') }}"
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 735a3882..93d61c9a 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -1,7 +1,7 @@
'name': 'lint'
'env':
- 'GO_VERSION': '1.23.5'
+ 'GO_VERSION': '1.23.6'
'on':
'push':
diff --git a/.gitignore b/.gitignore
index 5fd6dc60..a9598645 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,10 @@
/agh-backup/
/bin/
/build/*
+/client/blob-report/
+/client/playwright-report/
+/client/playwright/.cache/
+/client/test-results/
/data/
/dist/
/filtering/tests/filtering.TestLotsOfRules*.pprof
@@ -33,9 +37,5 @@ AdGuardHome.exe
AdGuardHome.yaml*
coverage.txt
node_modules/
-/client/blob-report/
-/client/playwright-report/
-/client/playwright/.cache/
-/client/test-results/
!/build/gitkeep
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9c216f35..b4866ada 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,22 @@ See also the [v0.107.57 GitHub milestone][ms-v0.107.57].
NOTE: Add new changes BELOW THIS COMMENT.
-->
+### Security
+
+- Go version has been updated to prevent the possibility of exploiting the Go vulnerabilities fixed in [1.23.6][go-1.23.6].
+
+### Changed
+
+- The *Fastest IP adddress* upstream mode now collects statistics for the all upstream DNS servers.
+
+### Fixed
+
+- The formatting of large numbers in the upstream table and query log ([#7590]).
+
+[#7590]: https://github.com/AdguardTeam/AdGuardHome/issues/7590
+
+[go-1.23.6]: https://groups.google.com/g/golang-announce/c/xU1ZCHUZw3k
+
diff --git a/Makefile b/Makefile
index 91c5682c..0fda6061 100644
--- a/Makefile
+++ b/Makefile
@@ -27,7 +27,7 @@ DIST_DIR = dist
GOAMD64 = v1
GOPROXY = https://proxy.golang.org|direct
GOTELEMETRY = off
-GOTOOLCHAIN = go1.23.5
+GOTOOLCHAIN = go1.23.6
GPG_KEY = devteam@adguard.com
GPG_KEY_PASSPHRASE = not-a-real-password
NPM = npm
diff --git a/bamboo-specs/release.yaml b/bamboo-specs/release.yaml
index 32267cc7..70b5fc21 100644
--- a/bamboo-specs/release.yaml
+++ b/bamboo-specs/release.yaml
@@ -7,8 +7,8 @@
# Make sure to sync any changes with the branch overrides below.
'variables':
'channel': 'edge'
- 'dockerFrontend': 'adguard/home-js-builder:2.0-bullseye'
- 'dockerGo': 'adguard/go-builder:1.23.5--1'
+ 'dockerFrontend': 'adguard/home-js-builder:2.1-bullseye'
+ 'dockerGo': 'adguard/go-builder:1.23.6--1'
'stages':
- 'Build frontend':
@@ -277,8 +277,8 @@
# need to build a few of these.
'variables':
'channel': 'beta'
- 'dockerFrontend': 'adguard/home-js-builder:2.0-bullseye'
- 'dockerGo': 'adguard/go-builder:1.23.5--1'
+ 'dockerFrontend': 'adguard/home-js-builder:2.1-bullseye'
+ 'dockerGo': 'adguard/go-builder:1.23.6--1'
# release-vX.Y.Z branches are the branches from which the actual final
# release is built.
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
@@ -293,5 +293,5 @@
# are the ones that actually get released.
'variables':
'channel': 'release'
- 'dockerFrontend': 'adguard/home-js-builder:2.0-bullseye'
- 'dockerGo': 'adguard/go-builder:1.23.5--1'
+ 'dockerFrontend': 'adguard/home-js-builder:2.1-bullseye'
+ 'dockerGo': 'adguard/go-builder:1.23.6--1'
diff --git a/bamboo-specs/test.yaml b/bamboo-specs/test.yaml
index cd825541..2aaedf36 100644
--- a/bamboo-specs/test.yaml
+++ b/bamboo-specs/test.yaml
@@ -5,8 +5,8 @@
'key': 'AHBRTSPECS'
'name': 'AdGuard Home - Build and run tests'
'variables':
- 'dockerFrontend': 'adguard/home-js-builder:2.0-bullseye'
- 'dockerGo': 'adguard/go-builder:1.23.5--1'
+ 'dockerFrontend': 'adguard/home-js-builder:2.1-bullseye'
+ 'dockerGo': 'adguard/go-builder:1.23.6--1'
'channel': 'development'
'stages':
@@ -199,7 +199,7 @@
mv /tmp/AdGuardHome/AdGuardHome ./AdGuardHome
- make VERBOSE=1 js-test-e2e
+ make VERBOSE=1 js-deps js-test-e2e
'requirements':
- 'adg-docker': 'true'
@@ -233,6 +233,6 @@
# Set the default release channel on the release branch to beta, as we
# may need to build a few of these.
'variables':
- 'dockerFrontend': 'adguard/home-js-builder:2.0-bullseye'
- 'dockerGo': 'adguard/go-builder:1.23.5--1'
+ 'dockerFrontend': 'adguard/home-js-builder:2.1-bullseye'
+ 'dockerGo': 'adguard/go-builder:1.23.6--1'
'channel': 'candidate'
diff --git a/client/package-lock.json b/client/package-lock.json
index e76f521d..43835caf 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -52,7 +52,7 @@
"@babel/plugin-transform-runtime": "^7.24.3",
"@babel/preset-env": "^7.24.5",
"@babel/preset-react": "^7.24.1",
- "@playwright/test": "^1.49.1",
+ "@playwright/test": "1.50.1",
"@types/lodash": "^4.17.4",
"@types/node": "^22.10.2",
"@types/react": "^17.0.80",
@@ -3195,13 +3195,13 @@
}
},
"node_modules/@playwright/test": {
- "version": "1.49.1",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.1.tgz",
- "integrity": "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==",
+ "version": "1.50.1",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.50.1.tgz",
+ "integrity": "sha512-Jii3aBg+CEDpgnuDxEp/h7BimHcUTDlpEtce89xEumlJ5ef2hqepZ+PWp1DDpYC/VO9fmWVI1IlEaoI5fK9FXQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "playwright": "1.49.1"
+ "playwright": "1.50.1"
},
"bin": {
"playwright": "cli.js"
@@ -12575,13 +12575,13 @@
}
},
"node_modules/playwright": {
- "version": "1.49.1",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz",
- "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==",
+ "version": "1.50.1",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz",
+ "integrity": "sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "playwright-core": "1.49.1"
+ "playwright-core": "1.50.1"
},
"bin": {
"playwright": "cli.js"
@@ -12594,9 +12594,9 @@
}
},
"node_modules/playwright-core": {
- "version": "1.49.1",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz",
- "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==",
+ "version": "1.50.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.1.tgz",
+ "integrity": "sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -20103,12 +20103,12 @@
"dev": true
},
"@playwright/test": {
- "version": "1.49.1",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.1.tgz",
- "integrity": "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==",
+ "version": "1.50.1",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.50.1.tgz",
+ "integrity": "sha512-Jii3aBg+CEDpgnuDxEp/h7BimHcUTDlpEtce89xEumlJ5ef2hqepZ+PWp1DDpYC/VO9fmWVI1IlEaoI5fK9FXQ==",
"dev": true,
"requires": {
- "playwright": "1.49.1"
+ "playwright": "1.50.1"
}
},
"@rollup/rollup-android-arm-eabi": {
@@ -27071,13 +27071,13 @@
}
},
"playwright": {
- "version": "1.49.1",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz",
- "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==",
+ "version": "1.50.1",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.1.tgz",
+ "integrity": "sha512-G8rwsOQJ63XG6BbKj2w5rHeavFjy5zynBA9zsJMMtBoe/Uf757oG12NXz6e6OirF7RCrTVAKFXbLmn1RbL7Qaw==",
"dev": true,
"requires": {
"fsevents": "2.3.2",
- "playwright-core": "1.49.1"
+ "playwright-core": "1.50.1"
},
"dependencies": {
"fsevents": {
@@ -27090,9 +27090,9 @@
}
},
"playwright-core": {
- "version": "1.49.1",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz",
- "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==",
+ "version": "1.50.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.1.tgz",
+ "integrity": "sha512-ra9fsNWayuYumt+NiM069M6OkcRb1FZSK8bgi66AtpFoWkg2+y0bJSNmkFrWhMbEBbVKC/EruAHH3g0zmtwGmQ==",
"dev": true
},
"popper.js": {
diff --git a/client/package.json b/client/package.json
index 06bb2824..0673c21f 100644
--- a/client/package.json
+++ b/client/package.json
@@ -65,7 +65,7 @@
"@babel/plugin-transform-runtime": "^7.24.3",
"@babel/preset-env": "^7.24.5",
"@babel/preset-react": "^7.24.1",
- "@playwright/test": "^1.49.1",
+ "@playwright/test": "1.50.1",
"@types/lodash": "^4.17.4",
"@types/node": "^22.10.2",
"@types/react": "^17.0.80",
diff --git a/client/src/components/Dashboard/UpstreamAvgTime.tsx b/client/src/components/Dashboard/UpstreamAvgTime.tsx
index 48ed9e6a..8f5b4cab 100644
--- a/client/src/components/Dashboard/UpstreamAvgTime.tsx
+++ b/client/src/components/Dashboard/UpstreamAvgTime.tsx
@@ -10,6 +10,7 @@ import Card from '../ui/Card';
import DomainCell from './DomainCell';
import { DASHBOARD_TABLES_DEFAULT_PAGE_SIZE, TABLES_MIN_ROWS } from '../../helpers/constants';
+import { formatNumber } from '../../helpers/helpers';
interface TimeCellProps {
value?: string | number;
@@ -20,7 +21,7 @@ const TimeCell = ({ value }: TimeCellProps) => {
return '–';
}
- const valueInMilliseconds = round(Number(value) * 1000);
+ const valueInMilliseconds = formatNumber(round(Number(value) * 1000));
return (
diff --git a/client/src/components/Settings/Encryption/Form.tsx b/client/src/components/Settings/Encryption/Form.tsx
index 7968cc3e..be03433d 100644
--- a/client/src/components/Settings/Encryption/Form.tsx
+++ b/client/src/components/Settings/Encryption/Form.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useRef } from 'react';
+import React from 'react';
import { Trans, useTranslation } from 'react-i18next';
@@ -92,19 +92,7 @@ export type EncryptionFormValues = {
type Props = {
initialValues: EncryptionFormValues;
- processingConfig: boolean;
- processingValidate: boolean;
- status_key?: string;
- not_after?: string;
- warning_validation?: string;
- valid_chain?: boolean;
- valid_key?: boolean;
- valid_cert?: boolean;
- valid_pair?: boolean;
- dns_names?: string[];
- key_type?: string;
- issuer?: string;
- subject?: string;
+ encryption: EncryptionData;
onSubmit: (values: EncryptionFormValues) => void;
debouncedConfigValidation: (values: EncryptionFormValues) => void;
setTlsConfig: (values: Partial) => void;
@@ -130,24 +118,28 @@ const defaultValues = {
export const Form = ({
initialValues,
- processingConfig,
- processingValidate,
- not_after,
- valid_chain,
- valid_key,
- valid_cert,
- valid_pair,
- dns_names,
- key_type,
- issuer,
- subject,
- warning_validation,
+ encryption,
onSubmit,
setTlsConfig,
+ debouncedConfigValidation,
validateTlsConfig,
}: Props) => {
const { t } = useTranslation();
- const previousValuesRef = useRef(initialValues);
+
+ const {
+ not_after,
+ valid_chain,
+ valid_key,
+ valid_cert,
+ valid_pair,
+ dns_names,
+ key_type,
+ issuer,
+ subject,
+ warning_validation,
+ processingConfig,
+ processingValidate,
+ } = encryption;
const {
control,
@@ -166,8 +158,6 @@ export const Form = ({
mode: 'onBlur',
});
- const watchedValues = watch();
-
const {
enabled: isEnabled,
serve_plain_dns: servePlainDns,
@@ -178,16 +168,11 @@ export const Form = ({
private_key_saved: privateKeySaved,
certificate_path: certificatePath,
certificate_source: certificateSource,
- } = watchedValues;
+ } = watch();
- useEffect(() => {
- const previousValues = previousValuesRef.current;
-
- if (JSON.stringify(previousValues) !== JSON.stringify(watchedValues)) {
- // TODO(ik) onChange TLS config validation
- previousValuesRef.current = watchedValues;
- }
- }, [watchedValues]);
+ const handleBlur = () => {
+ debouncedConfigValidation(getValues());
+ };
const isSavingDisabled = () => {
const processing = isSubmitting || processingConfig || processingValidate;
@@ -243,7 +228,9 @@ export const Form = ({
}
+ render={({ field }) => (
+
+ )}
/>
@@ -288,6 +275,7 @@ export const Form = ({
placeholder={t('encryption_server_enter')}
error={fieldState.error?.message}
disabled={!isEnabled}
+ onBlur={handleBlur}
/>
)}
/>
@@ -337,6 +325,7 @@ export const Form = ({
const { value } = e.target;
field.onChange(toNumber(value));
}}
+ onBlur={handleBlur}
/>
)}
/>
@@ -368,6 +357,7 @@ export const Form = ({
const { value } = e.target;
field.onChange(toNumber(value));
}}
+ onBlur={handleBlur}
/>
)}
/>
@@ -399,6 +389,7 @@ export const Form = ({
const { value } = e.target;
field.onChange(toNumber(value));
}}
+ onBlur={handleBlur}
/>
)}
/>
@@ -457,6 +448,7 @@ export const Form = ({
placeholder={t('encryption_certificates_input')}
disabled={!isEnabled}
error={fieldState.error?.message}
+ onBlur={handleBlur}
/>
)}
/>
@@ -471,6 +463,7 @@ export const Form = ({
placeholder={t('encryption_certificate_path')}
error={fieldState.error?.message}
disabled={!isEnabled}
+ onBlur={handleBlur}
/>
)}
/>
@@ -527,6 +520,7 @@ export const Form = ({
}
field.onChange(checked);
}}
+ onBlur={handleBlur}
/>
)}
/>
@@ -540,6 +534,7 @@ export const Form = ({
placeholder={t('encryption_key_input')}
disabled={!isEnabled || privateKeySaved}
error={fieldState.error?.message}
+ onBlur={handleBlur}
/>
)}
/>
@@ -555,6 +550,7 @@ export const Form = ({
placeholder={t('encryption_private_key_path')}
error={fieldState.error?.message}
disabled={!isEnabled}
+ onBlur={handleBlur}
/>
)}
/>
diff --git a/client/src/components/Settings/Encryption/index.tsx b/client/src/components/Settings/Encryption/index.tsx
index 67c99842..a8c5dcda 100644
--- a/client/src/components/Settings/Encryption/index.tsx
+++ b/client/src/components/Settings/Encryption/index.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useCallback, useMemo } from 'react';
+import React, { useCallback, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { debounce } from 'lodash';
import { DEBOUNCE_TIMEOUT, ENCRYPTION_SOURCE } from '../../../helpers/constants';
@@ -18,19 +18,37 @@ type Props = {
export const Encryption = ({ encryption, setTlsConfig, validateTlsConfig }: Props) => {
const { t } = useTranslation();
- useEffect(() => {
- if (encryption.enabled) {
- validateTlsConfig(encryption);
- }
- }, [encryption, validateTlsConfig]);
-
const initialValues = useMemo((): EncryptionFormValues => {
- const { certificate_chain, private_key, private_key_saved } = encryption;
+ const {
+ enabled,
+ serve_plain_dns,
+ server_name,
+ force_https,
+ port_https,
+ port_dns_over_tls,
+ port_dns_over_quic,
+ certificate_chain,
+ private_key,
+ certificate_path,
+ private_key_path,
+ private_key_saved,
+ } = encryption;
const certificate_source = certificate_chain ? ENCRYPTION_SOURCE.CONTENT : ENCRYPTION_SOURCE.PATH;
const key_source = private_key || private_key_saved ? ENCRYPTION_SOURCE.CONTENT : ENCRYPTION_SOURCE.PATH;
return {
- ...encryption,
+ enabled,
+ serve_plain_dns,
+ server_name,
+ force_https,
+ port_https,
+ port_dns_over_tls,
+ port_dns_over_quic,
+ certificate_chain,
+ private_key,
+ certificate_path,
+ private_key_path,
+ private_key_saved,
certificate_source,
key_source,
};
@@ -75,7 +93,7 @@ export const Encryption = ({ encryption, setTlsConfig, validateTlsConfig }: Prop
}
}, []);
- const debouncedConfigValidation = useCallback(debounce(validateConfig, DEBOUNCE_TIMEOUT), [validateConfig]);
+ const debouncedConfigValidation = useMemo(() => debounce(validateConfig, DEBOUNCE_TIMEOUT), [validateConfig]);
return (
@@ -94,7 +112,7 @@ export const Encryption = ({ encryption, setTlsConfig, validateTlsConfig }: Prop
debouncedConfigValidation={debouncedConfigValidation}
setTlsConfig={setTlsConfig}
validateTlsConfig={validateTlsConfig}
- {...encryption}
+ encryption={encryption}
/>
)}
diff --git a/client/src/components/ui/Controls/Checkbox/index.tsx b/client/src/components/ui/Controls/Checkbox/index.tsx
index 32a97fba..09ed43fa 100644
--- a/client/src/components/ui/Controls/Checkbox/index.tsx
+++ b/client/src/components/ui/Controls/Checkbox/index.tsx
@@ -12,10 +12,14 @@ type Props = {
className?: string;
error?: string;
onChange: (value: boolean) => void;
+ onBlur?: () => void;
};
export const Checkbox = forwardRef(
- ({ title, subtitle, value, name, disabled, error, className = 'checkbox--form', onChange, ...rest }, ref) => (
+ (
+ { title, subtitle, value, name, disabled, error, className = 'checkbox--form', onChange, onBlur, ...rest },
+ ref,
+ ) => (
<>