diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 748ef4b2..1a850543 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,7 +22,7 @@ jobs: uses: actions/checkout@v4.1.1 - name: Set up Node.js - uses: actions/setup-node@v4.0.1 + uses: actions/setup-node@v4.0.2 with: node-version: "20.x" cache: npm diff --git a/package-lock.json b/package-lock.json index add32b2e..005b1ad3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,11 +29,11 @@ "select2": "4.0.13" }, "devDependencies": { - "autoprefixer": "^10.4.16", + "autoprefixer": "^10.4.17", "eslint-plugin-compat": "^4.2.0", - "postcss": "^8.4.33", + "postcss": "^8.4.35", "postcss-cli": "^11.0.0", - "prettier": "^3.1.1", + "prettier": "^3.2.5", "xo": "^0.56.0" } }, @@ -1318,9 +1318,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.16", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.16.tgz", - "integrity": "sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==", + "version": "10.4.17", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.17.tgz", + "integrity": "sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==", "dev": true, "funding": [ { @@ -1337,9 +1337,9 @@ } ], "dependencies": { - "browserslist": "^4.21.10", - "caniuse-lite": "^1.0.30001538", - "fraction.js": "^4.3.6", + "browserslist": "^4.22.2", + "caniuse-lite": "^1.0.30001578", + "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", "picocolors": "^1.0.0", "postcss-value-parser": "^4.2.0" @@ -1771,9 +1771,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001572", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001572.tgz", - "integrity": "sha512-1Pbh5FLmn5y4+QhNyJE9j3/7dK44dGB83/ZMjv/qJk86TvDbjk0LosiZo0i0WB0Vx607qMX9jYrn1VLHCkN4rw==", + "version": "1.0.30001579", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz", + "integrity": "sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==", "dev": true, "funding": [ { @@ -5734,9 +5734,9 @@ } }, "node_modules/postcss": { - "version": "8.4.33", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", - "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", "dev": true, "funding": [ { @@ -5861,9 +5861,9 @@ } }, "node_modules/prettier": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz", - "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", + "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" diff --git a/package.json b/package.json index 8596316b..fb4376be 100644 --- a/package.json +++ b/package.json @@ -25,11 +25,11 @@ "testpr": "npm run prettier:fix && git diff --ws-error-highlight=all --color=always --exit-code && npm run xo:check" }, "devDependencies": { - "autoprefixer": "^10.4.16", + "autoprefixer": "^10.4.17", "eslint-plugin-compat": "^4.2.0", - "postcss": "^8.4.33", + "postcss": "^8.4.35", "postcss-cli": "^11.0.0", - "prettier": "^3.1.1", + "prettier": "^3.2.5", "xo": "^0.56.0" }, "browserslist": [ diff --git a/scripts/pi-hole/js/footer.js b/scripts/pi-hole/js/footer.js index b9a3d843..8df53d9a 100644 --- a/scripts/pi-hole/js/footer.js +++ b/scripts/pi-hole/js/footer.js @@ -37,10 +37,16 @@ function secondsTimeSpanToHMS(s) { return h + ":" + (m < 10 ? "0" + m : m) + ":" + (s < 10 ? "0" + s : s); //zero padding on minutes and seconds } -function piholeChanged(blocking) { - var status = $("#status"); - var ena = $("#pihole-enable"); - var dis = $("#pihole-disable"); +function piholeChanged(blocking, timer = null) { + const status = $("#status"); + const ena = $("#pihole-enable"); + const dis = $("#pihole-disable"); + const enaT = $("#enableTimer"); + + if (timer !== null && parseFloat(timer) > 0) { + enaT.html(Date.now() + parseFloat(timer) * 1000); + setTimeout(countDown, 100); + } switch (blocking) { case "enabled": { @@ -81,7 +87,7 @@ function piholeChanged(blocking) { function countDown() { var ena = $("#enableLabel"); var enaT = $("#enableTimer"); - var target = new Date(parseInt(enaT.html(), 10)); + var target = new Date(parseInt(enaT.text(), 10)); var seconds = Math.round((target.getTime() - Date.now()) / 1000); //Stop and remove timer when user enabled early @@ -95,7 +101,7 @@ function countDown() { ena.text("Enable Blocking (" + secondsTimeSpanToHMS(seconds) + ")"); } else { ena.text("Enable Blocking"); - piholeChanged("enabled"); + piholeChanged("enabled", null); if (localStorage) { localStorage.removeItem("countDownTarget"); } @@ -114,7 +120,7 @@ function checkBlocking() { method: "GET", }) .done(function (data) { - piholeChanged(data.blocking); + piholeChanged(data.blocking, data.timer); utils.setTimer(checkBlocking, REFRESH_INTERVAL.blocking); }) .fail(function (data) { @@ -124,8 +130,7 @@ function checkBlocking() { } function piholeChange(action, duration) { - var enaT = $("#enableTimer"); - var btnStatus; + let btnStatus = null; switch (action) { case "enable": @@ -153,11 +158,7 @@ function piholeChange(action, duration) { .done(function (data) { if (data.blocking === action + "d") { btnStatus.html(""); - piholeChanged(data.blocking); - if (duration > 0) { - enaT.html(Date.now() + duration * 1000); - setTimeout(countDown, 100); - } + piholeChanged(data.blocking, data.timer); } }) .fail(function (data) { diff --git a/scripts/pi-hole/js/groups-common.js b/scripts/pi-hole/js/groups-common.js index 653b72cf..86e84524 100644 --- a/scripts/pi-hole/js/groups-common.js +++ b/scripts/pi-hole/js/groups-common.js @@ -66,6 +66,7 @@ function delGroupItems(type, ids, table) { $.ajax({ url: url, data: JSON.stringify(ids), + contentType: "application/json", method: "POST", }) .done(function () { diff --git a/scripts/pi-hole/js/groups.js b/scripts/pi-hole/js/groups.js index e1c4fcd9..4f9cb8d3 100644 --- a/scripts/pi-hole/js/groups.js +++ b/scripts/pi-hole/js/groups.js @@ -7,8 +7,7 @@ /* global utils:false, apiFailure:false, updateFtlInfo:false, processGroupResult:false, delGroupItems:false */ -var table, - idNames = {}; +var table; function handleAjaxError(xhr, textStatus) { if (textStatus === "timeout") { @@ -73,20 +72,20 @@ $(function () { "\nDatabase ID: " + data.id; $("td:eq(1)", row).html( - '' + '' ); - var nameEl = $("#name_" + data.id, row); + var nameEl = $("#name_" + dataId, row); nameEl.val(data.name); nameEl.on("change", editGroup); $("td:eq(2)", row).html( '" ); - var enabledEl = $("#enabled_" + data.id, row); + var enabledEl = $("#enabled_" + dataId, row); enabledEl.bootstrapToggle({ on: "Enabled", off: "Disabled", @@ -96,9 +95,9 @@ $(function () { }); enabledEl.on("change", editGroup); - $("td:eq(3)", row).html(''); + $("td:eq(3)", row).html(''); var comment = data.comment !== null ? data.comment : ""; - var commentEl = $("#comment_" + data.id, row); + var commentEl = $("#comment_" + dataId, row); commentEl.val(comment); commentEl.on("change", editGroup); @@ -292,7 +291,7 @@ function editGroup() { const elem = $(this).attr("id"); const tr = $(this).closest("tr"); const id = tr.attr("data-id"); - const oldName = idNames[id]; + const oldName = utils.hexDecode(id); const name = tr.find("#name_" + id).val(); const enabled = tr.find("#enabled_" + id).is(":checked"); const comment = tr.find("#comment_" + id).val(); diff --git a/scripts/pi-hole/js/queries.js b/scripts/pi-hole/js/queries.js index 77ceb4aa..46952bef 100644 --- a/scripts/pi-hole/js/queries.js +++ b/scripts/pi-hole/js/queries.js @@ -200,6 +200,13 @@ function parseQueryStatus(data) { buttontext = ''; break; + case "SPECIAL_DOMAIN": + colorClass = "text-red"; + icon = "fa-solid fa-ban"; + fieldtext = data.status; + buttontext = ""; + blocked = true; + break; default: colorClass = "text-orange"; icon = "fa-solid fa-question"; @@ -276,17 +283,16 @@ function formatInfo(data) { ""; } - var regexInfo = "", + var listInfo = "", cnameInfo = ""; - if (data.regex_id !== null && data.regex_id > -1) { - var regexLink = - 'entry with ID ' + - data.regex_id + - ""; - regexInfo = - divStart + "Query was " + queryStatus.matchText + " by " + regexLink + ""; + if (data.list_id !== null && data.list_id !== -1) { + // Some list matched - add link to search page + + var listLink = + 'search lists'; + listInfo = divStart + "Query was " + queryStatus.matchText + ", " + listLink + ""; } if (queryStatus.isCNAME) { @@ -349,7 +355,7 @@ function formatInfo(data) { dnssecInfo + statusInfo + cnameInfo + - regexInfo + + listInfo + ttlInfo + replyInfo + dbInfo + diff --git a/scripts/pi-hole/js/search.js b/scripts/pi-hole/js/search.js index 26875241..dae432ec 100644 --- a/scripts/pi-hole/js/search.js +++ b/scripts/pi-hole/js/search.js @@ -6,6 +6,18 @@ * Please see LICENSE file for your rights under this license. */ /* global utils:false, apiFailure:false */ +var GETDict = {}; + +$(function () { + GETDict = utils.parseQueryString(); + if (GETDict.domain !== undefined) { + $("input[id^='domain']").val(GETDict.domain); + } + + if (GETDict.N !== undefined) { + $("#number").val(GETDict.number); + } +}); function eventsource(partial) { const ta = $("#output"); diff --git a/scripts/pi-hole/js/settings-advanced.js b/scripts/pi-hole/js/settings-advanced.js index b4165e9f..8d5cdd61 100644 --- a/scripts/pi-hole/js/settings-advanced.js +++ b/scripts/pi-hole/js/settings-advanced.js @@ -58,7 +58,7 @@ function generateRow(topic, key, value) { : "") + "" + "

" + - utils.escapeHtml(value.description).replace("\n", "
") + + utils.escapeHtml(value.description).replaceAll("\n", "
") + "

" + "" + '
' + diff --git a/scripts/pi-hole/js/settings-teleporter.js b/scripts/pi-hole/js/settings-teleporter.js index 7bd53c6b..8ed019cd 100644 --- a/scripts/pi-hole/js/settings-teleporter.js +++ b/scripts/pi-hole/js/settings-teleporter.js @@ -51,7 +51,7 @@ function importZIP() { $("#modal-import-success-title").text("Import successful"); var text = "

Processed files:

"; diff --git a/scripts/pi-hole/js/utils.js b/scripts/pi-hole/js/utils.js index 038ab1a4..54554538 100644 --- a/scripts/pi-hole/js/utils.js +++ b/scripts/pi-hole/js/utils.js @@ -152,6 +152,10 @@ function showAlert(type, icon, title, message) { } function datetime(date, html, humanReadable) { + if (date === 0 && humanReadable) { + return "Never"; + } + var format = html === false ? "Y-MM-DD HH:mm:ss z" : "Y-MM-DD []HH:mm:ss z"; var timestr = moment.unix(Math.floor(date)).format(format).trim(); return humanReadable @@ -391,7 +395,7 @@ function checkMessages() { }) .done(function (data) { if (data.count > 0) { - var more = '\nAccess "Tools/Pi-hole diganosis" for further details.'; + var more = '\nAccess "Tools/Pi-hole diagnosis" for further details.'; var title = data.count > 1 ? "There are " + data.count + " warnings." + more diff --git a/settings-api.lp b/settings-api.lp index d2cc7009..5ff1012d 100644 --- a/settings-api.lp +++ b/settings-api.lp @@ -24,14 +24,19 @@ mg.include('scripts/pi-hole/lua/settings_header.lp','r')
-

Domains to be excluded from Top Domains / Ads Lists

- -

Domains may be described by their domain name (like example.com)

+

Domains to be excluded from Top Domain Lists and Query Log

+ +

Domains may be described by their domain name (like ^example\.com$)

-

Clients to be excluded from Top Clients List

- -

Clients may be described either by their IP addresses (IPv4 and IPv6 are supported), or hostnames (like laptop.lan).

+

Clients to be excluded from Top Client Lists and Query Log

+ +

Clients may be described either by their IP addresses (IPv4 and IPv6 are supported), or hostnames (like ^laptop\.lan$).

+
+
+
+
+

Important: Those input fields accept regex entries only. Please refer to our guide how to construct valid regex entries.

diff --git a/settings-dhcp.lp b/settings-dhcp.lp index 76153c14..eb306c4a 100644 --- a/settings-dhcp.lp +++ b/settings-dhcp.lp @@ -61,16 +61,19 @@ mg.include('scripts/pi-hole/lua/settings_header.lp','r')
- +
Netmask
+ autocorrect="off" value="" placeholder="automatic">
+
+

Leave the netmask field empty to have Pi-hole infer it from the configured IP address of your device. If you want to specify a netmask, you can use the format 255.255.255.0.

+
 

Enable this option to enable IPv6 support for the Pi-hole DHCP server. This will allow the Pi-hole to hand out IPv6 addresses to clients and also provide IPv6 router advertisements (RA) to clients. This option is only useful if the Pi-hole is configured with an IPv6 address.

diff --git a/settings-dns.lp b/settings-dns.lp index ad2c9087..a67b6e6d 100644 --- a/settings-dns.lp +++ b/settings-dns.lp @@ -59,64 +59,48 @@ mg.include('scripts/pi-hole/lua/settings_header.lp','r')
-

Conditional forwarding

+

DNS domain settings

+
+
+
+
+ +
+
+
Domain
+ +
+
+

The DNS domains for your Pi-hole. If no domain is specified and you are using Pi-hole's DHCP server, then any hostnames with a domain part (i.e., with a period) will be disallowed. If a domain is specified, then hostnames with a domain parts matching the domain here are allowed. In addition, when a suffix is set then hostnames without a domain part have the suffix added as an optional domain part.

+
+ + +

If set, the domain is added to simple names (without a period) in /etc/hosts in the same way as for DHCP-derived names.

+
+
+
+
+
+
+
+

Rate-limiting

-

If not configured as your DHCP server, Pi-hole typically won't be able to - determine the names of devices on your local network. As a - result, tables such as Top Clients will only show IP addresses.

-

One solution for this is to configure Pi-hole to forward these - requests to your DHCP server (most likely your router), but only for devices on your - home network. To configure this we will need to know the IP - address of your DHCP server and which addresses belong to your local network. - Exemplary input is given below as placeholder in the text boxes (if empty).

-

If your local network spans 192.168.0.1 - 192.168.0.255, then you will have to input - 192.168.0.0/24. If your local network is 192.168.47.1 - 192.168.47.255, it will - be 192.168.47.0/24 and similar. If your network is larger, the CIDR has to be - different, for instance a range of 10.8.0.1 - 10.8.255.255 results in 10.8.0.0/16, - whereas an even wider network of 10.0.0.1 - 10.255.255.255 results in 10.0.0.0/8. - Setting up IPv6 ranges is exactly similar to setting up IPv4 here and fully supported. - Feel free to reach out to us on our - Discourse forum - in case you need any assistance setting up local host name resolution for your particular system.

-

You can also specify a local domain name (like fritz.box) to ensure queries to - devices ending in your local domain name will not leave your network, however, this is optional. - The local domain name must match the domain name specified - in your DHCP server for this to work. You can likely find it within the DHCP settings.

-

Enabling Conditional Forwarding will also forward all hostnames (i.e., non-FQDNs) to the router - when "Never forward non-FQDNs" is not enabled.

-
-
- - -
-
- - - - - - - - - - - - - - - -
Local network in CIDR notationIP address of your DHCP server (router)Local domain name (optional)
- - - - - -
-
-
+

Block clients making more than queries within + seconds.

+

When a client makes too many queries in too short time, it + gets rate-limited. Rate-limited queries are answered with a + REFUSED reply and not further processed by FTL + and prevent Pi-holes getting overwhelmed by rogue clients. + It is important to note that rate-limiting is happening on a + per-client basis. Other clients can continue to use FTL while + rate-limited clients are short-circuited at the same time.

+

Rate-limiting may be disabled altogether by setting both + values to zero. See + our documentation + for further details.

@@ -164,30 +148,6 @@ mg.include('scripts/pi-hole/lua/settings_header.lp','r')
-
-
-

DNS domain settings

-
-
-
-
- -
-
-
Domain
- -
-
-

The DNS domains for your Pi-hole. If no domain is specified and you are using Pi-hole's DHCP server, then any hostnames with a domain part (i.e., with a period) will be disallowed. If a domain is specified, then hostnames with a domain parts matching the domain here are allowed. In addition, when a suffix is set then hostnames without a domain part have the suffix added as an optional domain part.

-
- - -

If set, the domain is added to simple names (without a period) in /etc/hosts in the same way as for DHCP-derived names.

-
-
-
-
-

Advanced DNS settings

@@ -233,26 +193,40 @@ mg.include('scripts/pi-hole/lua/settings_header.lp','r')
+ +
-

Rate-limiting

+

Conditional forwarding

-

Block clients making more than queries within - seconds.

-

When a client makes too many queries in too short time, it - gets rate-limited. Rate-limited queries are answered with a - REFUSED reply and not further processed by FTL - and prevent Pi-holes getting overwhelmed by rogue clients. - It is important to note that rate-limiting is happening on a - per-client basis. Other clients can continue to use FTL while - rate-limited clients are short-circuited at the same time.

-

Rate-limiting may be disabled altogether by setting both - values to zero. See - our documentation - for further details.

+

If not configured as your DHCP server, Pi-hole typically won't be able to + determine the names of devices on your local network. As a + result, tables such as Top Clients will only show IP addresses.

+

One solution for this is to configure Pi-hole to forward these + requests to your DHCP server (most likely your router), but only for devices on your + home network. To configure this we will need to know the IP + address of your DHCP server and which addresses belong to your local network. + Exemplary input is given below as placeholder in the text boxes (if empty).

+

If your local network spans 192.168.0.1 - 192.168.0.255, then you will have to input + 192.168.0.0/24. If your local network is 192.168.47.1 - 192.168.47.255, it will + be 192.168.47.0/24 and similar. If your network is larger, the CIDR has to be + different, for instance a range of 10.8.0.1 - 10.8.255.255 results in 10.8.0.0/16, + whereas an even wider network of 10.0.0.1 - 10.255.255.255 results in 10.0.0.0/8. + Setting up IPv6 ranges is exactly similar to setting up IPv4 here and fully supported. + Feel free to reach out to us on our + Discourse forum + in case you need any assistance setting up local host name resolution for your particular system.

+

You can also specify a local domain name (like fritz.box) to ensure queries to + devices ending in your local domain name will not leave your network, however, this is optional. + The local domain name must match the domain name specified + in your DHCP server for this to work. You can likely find it within the DHCP settings.

+

Enabling Conditional Forwarding will also forward all hostnames (i.e., non-FQDNs) to the router + when "Never forward non-FQDNs" is not enabled.

+

The following list contains all reverse servers you want to add. The expected format is one server per line in form of <enabled>,<ip-address>[/<prefix-len>],<server>[#<port>][,<domain>]. A valid config line could look like true,192.168.0.0/24,192.168.0.1,fritz.box

+
diff --git a/settings-teleporter.lp b/settings-teleporter.lp index a33b1138..e6db7491 100644 --- a/settings-teleporter.lp +++ b/settings-teleporter.lp @@ -20,8 +20,8 @@ mg.include('scripts/pi-hole/lua/settings_header.lp','r')

Export your Pi-hole's configuration

-

Warning: This archive contains sensitive information about your Pi-hole installation, e.g. the API token and the 2FA-TOTP secret (if enabled). Please be careful with this file and do not share it with anyone even if they claim to help you.

-

Warning: You are currently not using an end-to-end encryption. This means that your API token and 2FA-TOTP secret will be transmitted in plain text. We recommend to use HTTPS when exporting your configuration.

+

Warning: This archive contains sensitive information about your Pi-hole installation, e.g. your 2FA-TOTP secret (if enabled). Please be careful with this file and do not share it with anyone even if they claim to help you.

+

Warning: You are currently not using an end-to-end encryption. This means that secrets like your 2FA-TOTP secret will be transmitted in plain text. We recommend to use HTTPS when exporting your configuration.