Skip to content

Commit 68f3520

Browse files
committed
Merge main & update to bikkelhart/internet-nl@c0776a43
git fetch bikkelhart git fetch internetnl git merge -X theirs internetnl/main git checkout --no-overlay bikkelhart/bikkelhart-redesign -- frontend interface/templates interface/static translations git checkout --no-overlay main -- interface/static/question@internet.nl_0x45028563.asc interface/templates/statistics.html msgcat --no-wrap translations/en/main.po <(git show internetnl/main:translations/en/main.po) | sponge translations/en/main.po convert -resize 48x48 interface/static/favicon.{png,ico} git add interface/static/favicon.ico git commit --amend -m "Merge main & update to bikkelhart/internet-nl@$(git rev-parse bikkelhart/bikkelhart-redesign | cut -c1-8)"
2 parents ee8dea8 + b3e813a commit 68f3520

File tree

15 files changed

+871
-552
lines changed

15 files changed

+871
-552
lines changed

.well-known/security.txt

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,20 @@ Encryption: https://internet.nl/static/question@internet.nl_0x45028563.asc
2828
# Our security policy
2929
Policy: https://internet.nl/disclosure/
3030

31-
Expires: 2025-09-09T00:00:00Z
31+
Expires: 2026-09-25T00:00:00Z
3232
-----BEGIN PGP SIGNATURE-----
3333

34-
iQIzBAEBCgAdFiEErLeIKUx+ErrpIoxg2JThX0UChWMFAmbe9QIACgkQ2JThX0UC
35-
hWOl9w//fNchS4XVtGG8m41YvPEh3jswxK2Fj7Oe5atxAjv8LNVwAWxVYjFpVPcp
36-
bkOrclK41SPX8BkYV8zKb/g641jeqZXKJxYGZRZyWVMlx7giwAD2hPi4l5QZ77UK
37-
6W468cScRfd51oKVfp91fXTkvQanVx23bYQ2Ti7kKd0ERsspEbX6NsuKUfLGA6rg
38-
7SpVxUBobkYLzR7RwvK5Qqs0+8k7syDnpi81Fy+dy/YLxzB9MERp3yUmve4xxdoW
39-
wuWSoJbynHfwHkD3QwjoOrLasowxi7dTW11Awe3Ll5p8GTa1UBbzfY7iwVJ2DesN
40-
gcBpoxREp9LdDynLOwHgX/k1uyadU7QVnm4bLjvmuDNIOaECBGcN15YDWC+mVap3
41-
h8OIgTW3oN3PtQA1WpsFaol8P0CHfo01MbC4EoOAMGv28GwR1AYjzs03FN+1MMCJ
42-
5PxbyB0Zs1s6DsD1xOAvXCyvf1nSdLH/uDReEN4E4zaL9aftnNv3Dxpu7i4l3z23
43-
ACqNPkNHPyad7JxZve5SXLJhYaCIcySEO/upEUdQIa58T/8cZxtMFiTO0kbbo8HE
44-
25H5OIwx/9voH9zd0B+g0xdbct4xYSL6iyZ6ebMlteaqgbHY7cjLW5o8RGZxTkQf
45-
mS2qRBl57PhvTubt09fq+b6LiQX5XnlXEEzp6+RhtfRXFLSvcJc=
46-
=tA8x
34+
iQIzBAEBCgAdFiEErLeIKUx+ErrpIoxg2JThX0UChWMFAmjWfAcACgkQ2JThX0UC
35+
hWNptw/8ChAXXJcowrTy5y0xYw2YPx0a26M/bTBIZ2TsH0ewmhOHJEOXK9S1V6+I
36+
oszXGBmm9fUDEXlMC7+f1QfXWXt8Xh1OVd9HOTqVvQ4lmb2XbefGuR6hFuHaveEx
37+
Ac5nVVpkxwUzBJOQ0idK3bA+cLdosx6JEMPQoKWVPceaTake3TckQ22TEHBRro5z
38+
i7FAdEfqCjMT1r682cjNa95Q2gj7To06SmU4f/+0O2sjA64FVpOGRjC/Ka5YolZ6
39+
xLnnHQGz3FuQnKKVSF29l2RnbtNiL9Arr9xUekY8RoO7c7t7/Hwslp/lyYBHWHUZ
40+
3eeh4bkLgbfLICsjFWhQjU5uHp92ObMF+2N55d4C2oZp+o5C6BfdXW5nACLBreOV
41+
AjIUpzn59qpRPwV8dni6qzE0+BodIp6yAzBynXoCFoc7+mRK4Njtsr5Ci8Mz7Y2R
42+
7iBkor6xHmEssyJi2tCWSuZlQUcKx/JKRAKZI3yIOQab7lqfB8cLXogaEgtpAt1U
43+
8miL7wxKONyJM5bYOcf7UmpQutvRIWB3ZKtw/b0V8i/HCl7IzpyRASgwGbi93cqD
44+
EQw6/W5v4AtqoktNdxvWKyi+3NORfBdYHkCnLDGWtMMJpbW17iD4RVg866ode0Sv
45+
ukBLiBbSZTIJjv9294oKjRgKnZgy9PZ1+6bxLjmBl8k9XQJrQ18=
46+
=LFEm
4747
-----END PGP SIGNATURE-----

Changelog.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,22 @@ _Compared to the latest 1.10 release._
2020

2121
- ...
2222

23+
24+
## 1.10.6
25+
26+
- Fixed a syntax error in the OpenAPI specification.
27+
28+
## 1.10.5
29+
30+
- Fixed an issue in 1.10.x where [DNSSEC test could return false negatives](https://github.com/internetstandards/Internet.nl/issues/1869)
31+
due to an interaction between cached responses and the CD and AD flags.
32+
- Added news post.
33+
34+
## 1.10.4
35+
36+
- Updated our security.txt.
37+
- Updated Django version.
38+
2339
## 1.10.3
2440

2541
- Added [missing User-Agent](https://github.com/internetstandards/Internet.nl/issues/1048) to 0-RTT HTTP requests

checks/resolver.py

Lines changed: 64 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@
44
import dns
55
from django.conf import settings
66
from dns.edns import EDECode
7-
from dns.exception import ValidationFailure
87
from dns.flags import Flag, EDNSFlag
98
from dns.message import Message, make_query
109
from dns.query import udp_with_fallback
1110
from dns.rdatatype import RdataType
1211
from dns.rdtypes.ANY import TLSA, CAA
13-
from dns.resolver import Resolver, NXDOMAIN, NoAnswer
12+
from dns.resolver import Resolver, NXDOMAIN, NoAnswer, NoNameservers
1413
import socket
1514

1615
DNS_TIMEOUT = 5
@@ -41,44 +40,44 @@ def from_message(cls, message: Message):
4140
return cls(DNSSECStatus.INSECURE)
4241

4342

44-
def dns_resolve_a(qname: str, allow_bogus=True) -> list[str]:
45-
rrset, dnssec_status = dns_resolve(qname, RdataType.A, allow_bogus)
43+
def dns_resolve_a(qname: str) -> list[str]:
44+
rrset, dnssec_status = dns_resolve(qname, RdataType.A)
4645
return [rr.address for rr in rrset]
4746

4847

49-
def dns_resolve_aaaa(qname: str, allow_bogus=True) -> list[str]:
50-
rrset, dnssec_status = dns_resolve(qname, RdataType.AAAA, allow_bogus)
48+
def dns_resolve_aaaa(qname: str) -> list[str]:
49+
rrset, dnssec_status = dns_resolve(qname, RdataType.AAAA)
5150
return [rr.address for rr in rrset]
5251

5352

54-
def dns_resolve_mx(qname: str, allow_bogus=True) -> list[tuple[str, int]]:
55-
rrset, dnssec_status = dns_resolve(qname, RdataType.MX, allow_bogus)
53+
def dns_resolve_mx(qname: str) -> list[tuple[str, int]]:
54+
rrset, dnssec_status = dns_resolve(qname, RdataType.MX)
5655
return [(str(rr.exchange), rr.preference) for rr in rrset]
5756

5857

59-
def dns_resolve_ns(qname: str, allow_bogus=True) -> list[str]:
60-
rrset, dnssec_status = dns_resolve(qname, RdataType.NS, allow_bogus)
58+
def dns_resolve_ns(qname: str) -> list[str]:
59+
rrset, dnssec_status = dns_resolve(qname, RdataType.NS)
6160
return [str(rr.target) for rr in rrset]
6261

6362

64-
def dns_resolve_tlsa(qname: str, allow_bogus=True) -> tuple[list[TLSA], DNSSECStatus]:
65-
rrset, dnssec_status = dns_resolve(qname, RdataType.TLSA, allow_bogus)
63+
def dns_resolve_tlsa(qname: str) -> tuple[list[TLSA], DNSSECStatus]:
64+
rrset, dnssec_status = dns_resolve(qname, RdataType.TLSA, guarantee_accurate_secure=True)
6665
return rrset, dnssec_status
6766

6867

69-
def dns_resolve_txt(qname: str, allow_bogus=True) -> list[str]:
70-
rrset, dnssec_status = dns_resolve(qname, RdataType.TXT, allow_bogus)
68+
def dns_resolve_txt(qname: str) -> list[str]:
69+
rrset, dnssec_status = dns_resolve(qname, RdataType.TXT)
7170
return ["".join([dns.rdata._escapify(s) for s in rr.strings]) for rr in rrset]
7271

7372

74-
def dns_resolve_spf(qname: str, allow_bogus=True) -> Optional[str]:
75-
strings = dns_resolve_txt(qname, allow_bogus)
73+
def dns_resolve_spf(qname: str) -> Optional[str]:
74+
strings = dns_resolve_txt(qname)
7675
spf_records = [s for s in strings if s.lower().startswith("v=spf1")]
7776
return spf_records[0] if len(spf_records) == 1 else None
7877

7978

80-
def dns_resolve_soa(qname: str, allow_bogus=True, raise_on_no_answer=True) -> DNSSECStatus:
81-
rrset, dnssec_status = dns_resolve(qname, RdataType.SOA, allow_bogus, raise_on_no_answer)
79+
def dns_resolve_soa(qname: str, raise_on_no_answer=True) -> DNSSECStatus:
80+
rrset, dnssec_status = dns_resolve(qname, RdataType.SOA, raise_on_no_answer, guarantee_accurate_secure=True)
8281
return dnssec_status
8382

8483

@@ -89,7 +88,9 @@ def dns_resolve_caa(qname: str) -> tuple[str, Iterable[CAA.CAA]]:
8988
"""
9089
while True:
9190
try:
92-
answer = _get_resolver().resolve(dns.name.from_text(qname), RdataType.CAA, raise_on_no_answer=True)
91+
answer = _get_resolver(cd_flag=True).resolve(
92+
dns.name.from_text(qname), RdataType.CAA, raise_on_no_answer=True
93+
)
9394
return str(answer.canonical_name), answer.rrset
9495
except (NoAnswer, NXDOMAIN):
9596
qname = dns_climb_tree(qname)
@@ -98,7 +99,7 @@ def dns_resolve_caa(qname: str) -> tuple[str, Iterable[CAA.CAA]]:
9899

99100

100101
def dns_resolve_reverse(ipaddr: str) -> list[str]:
101-
answer = _get_resolver().resolve_address(ipaddr)
102+
answer = _get_resolver(cd_flag=True).resolve_address(ipaddr)
102103
return [rr.to_text() for rr in answer.rrset]
103104

104105

@@ -111,11 +112,32 @@ def dns_check_ns_connectivity(probe_qname: str, target_ip: str, port: int = 53)
111112
return False
112113

113114

114-
def dns_resolve(qname: str, rr_type: RdataType, allow_bogus=True, raise_on_no_answer=True):
115-
answer = _get_resolver().resolve(dns.name.from_text(qname), rr_type, raise_on_no_answer=raise_on_no_answer)
116-
dnssec_status = DNSSECStatus.from_message(answer.response)
117-
if dnssec_status == DNSSECStatus.BOGUS and not allow_bogus:
118-
raise ValidationFailure()
115+
def dns_resolve(qname: str, rr_type: RdataType, raise_on_no_answer=True, guarantee_accurate_secure=False):
116+
"""
117+
Resolve the provided qname/record type.
118+
Returns the RRset and the DNSSEC status, with a caveat.
119+
120+
raise_on_no_answer: if True, raises NoAnswer for no answer, if False, no exception raised
121+
122+
guarantee_accurate_secure:
123+
Certain caching scenarios may lead us to falsely mark a response as insecure, when it is secure,
124+
due to a missing AD bit when a response for the same qname was cached to resolve a different query,
125+
in combination with our CD flag.
126+
This is OK in most cases, but when it is not, we need to do a double query to prevent this,
127+
enabled with guarantee_accurate_secure=True.
128+
https://github.com/internetstandards/Internet.nl/issues/1869
129+
"""
130+
resolve_params = {"qname": dns.name.from_text(qname), "rdtype": rr_type, "raise_on_no_answer": raise_on_no_answer}
131+
if guarantee_accurate_secure:
132+
try:
133+
answer = _get_resolver(cd_flag=False).resolve(**resolve_params)
134+
dnssec_status = DNSSECStatus.from_message(answer.response)
135+
except NoNameservers: # dnspython's translation for servfail
136+
answer = _get_resolver(cd_flag=True).resolve(**resolve_params)
137+
dnssec_status = DNSSECStatus.BOGUS
138+
else:
139+
answer = _get_resolver(cd_flag=True).resolve(**resolve_params)
140+
dnssec_status = DNSSECStatus.from_message(answer.response)
119141
return answer.rrset, dnssec_status
120142

121143

@@ -126,22 +148,29 @@ def dns_climb_tree(qname: str) -> Optional[str]:
126148
return parent.to_text()
127149

128150

129-
_resolver = None
151+
_resolver_without_cd = None
152+
_resolver_with_cd = None
130153

131154

132-
def _get_resolver():
155+
def _get_resolver(cd_flag: bool):
133156
# Resolvers are thread safe once configured
134-
global _resolver
135-
if not _resolver:
136-
_resolver = _create_resolver()
137-
return _resolver
138-
139-
140-
def _create_resolver() -> Resolver:
157+
global _resolver_with_cd
158+
if not _resolver_with_cd:
159+
_resolver_with_cd = _create_resolver(cd_flag=True)
160+
global _resolver_without_cd
161+
if not _resolver_without_cd:
162+
_resolver_without_cd = _create_resolver(cd_flag=False)
163+
if cd_flag:
164+
return _resolver_with_cd
165+
return _resolver_without_cd
166+
167+
168+
def _create_resolver(cd_flag: bool) -> Resolver:
141169
resolver = Resolver(configure=False)
142170
resolver.nameservers = [socket.gethostbyname(settings.RESOLVER_INTERNAL_VALIDATING)]
143171
resolver.edns = True
144-
resolver.flags = Flag.CD
172+
if cd_flag:
173+
resolver.flags = Flag.CD
145174
resolver.ednsflags = EDNSFlag.DO
146175
resolver.lifetime = DNS_TIMEOUT
147176
return resolver

docker/integration-tests/www/well-known/security.txt

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,20 @@ Encryption: https://internet.nl/static/question@internet.nl_0x45028563.asc
2828
# Our security policy
2929
Policy: https://internet.nl/disclosure/
3030

31-
Expires: 2025-09-09T00:00:00Z
31+
Expires: 2026-09-25T00:00:00Z
3232
-----BEGIN PGP SIGNATURE-----
3333

34-
iQIzBAEBCgAdFiEErLeIKUx+ErrpIoxg2JThX0UChWMFAmbe9QIACgkQ2JThX0UC
35-
hWOl9w//fNchS4XVtGG8m41YvPEh3jswxK2Fj7Oe5atxAjv8LNVwAWxVYjFpVPcp
36-
bkOrclK41SPX8BkYV8zKb/g641jeqZXKJxYGZRZyWVMlx7giwAD2hPi4l5QZ77UK
37-
6W468cScRfd51oKVfp91fXTkvQanVx23bYQ2Ti7kKd0ERsspEbX6NsuKUfLGA6rg
38-
7SpVxUBobkYLzR7RwvK5Qqs0+8k7syDnpi81Fy+dy/YLxzB9MERp3yUmve4xxdoW
39-
wuWSoJbynHfwHkD3QwjoOrLasowxi7dTW11Awe3Ll5p8GTa1UBbzfY7iwVJ2DesN
40-
gcBpoxREp9LdDynLOwHgX/k1uyadU7QVnm4bLjvmuDNIOaECBGcN15YDWC+mVap3
41-
h8OIgTW3oN3PtQA1WpsFaol8P0CHfo01MbC4EoOAMGv28GwR1AYjzs03FN+1MMCJ
42-
5PxbyB0Zs1s6DsD1xOAvXCyvf1nSdLH/uDReEN4E4zaL9aftnNv3Dxpu7i4l3z23
43-
ACqNPkNHPyad7JxZve5SXLJhYaCIcySEO/upEUdQIa58T/8cZxtMFiTO0kbbo8HE
44-
25H5OIwx/9voH9zd0B+g0xdbct4xYSL6iyZ6ebMlteaqgbHY7cjLW5o8RGZxTkQf
45-
mS2qRBl57PhvTubt09fq+b6LiQX5XnlXEEzp6+RhtfRXFLSvcJc=
46-
=tA8x
34+
iQIzBAEBCgAdFiEErLeIKUx+ErrpIoxg2JThX0UChWMFAmjWfAcACgkQ2JThX0UC
35+
hWNptw/8ChAXXJcowrTy5y0xYw2YPx0a26M/bTBIZ2TsH0ewmhOHJEOXK9S1V6+I
36+
oszXGBmm9fUDEXlMC7+f1QfXWXt8Xh1OVd9HOTqVvQ4lmb2XbefGuR6hFuHaveEx
37+
Ac5nVVpkxwUzBJOQ0idK3bA+cLdosx6JEMPQoKWVPceaTake3TckQ22TEHBRro5z
38+
i7FAdEfqCjMT1r682cjNa95Q2gj7To06SmU4f/+0O2sjA64FVpOGRjC/Ka5YolZ6
39+
xLnnHQGz3FuQnKKVSF29l2RnbtNiL9Arr9xUekY8RoO7c7t7/Hwslp/lyYBHWHUZ
40+
3eeh4bkLgbfLICsjFWhQjU5uHp92ObMF+2N55d4C2oZp+o5C6BfdXW5nACLBreOV
41+
AjIUpzn59qpRPwV8dni6qzE0+BodIp6yAzBynXoCFoc7+mRK4Njtsr5Ci8Mz7Y2R
42+
7iBkor6xHmEssyJi2tCWSuZlQUcKx/JKRAKZI3yIOQab7lqfB8cLXogaEgtpAt1U
43+
8miL7wxKONyJM5bYOcf7UmpQutvRIWB3ZKtw/b0V8i/HCl7IzpyRASgwGbi93cqD
44+
EQw6/W5v4AtqoktNdxvWKyi+3NORfBdYHkCnLDGWtMMJpbW17iD4RVg866ode0Sv
45+
ukBLiBbSZTIJjv9294oKjRgKnZgy9PZ1+6bxLjmBl8k9XQJrQ18=
46+
=LFEm
4747
-----END PGP SIGNATURE-----

frontend/src/css/components.css

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -230,14 +230,6 @@
230230
}
231231
}
232232

233-
[data-theme="dark"] {
234-
color-scheme: dark;
235-
236-
.card.large.knowledge-card {
237-
border: var(--card-border);
238-
}
239-
}
240-
241233
.card {
242234
border-radius: 0.5rem;
243235

@@ -457,6 +449,7 @@
457449
.accordion {
458450
appearance: none;
459451
overflow: clip;
452+
border: 1px solid var(--accordion-border-color);
460453

461454
&::details-content {
462455
@media (prefers-reduced-motion: no-preference) {
@@ -513,8 +506,13 @@
513506
.card.large.knowledge-card {
514507
border: none;
515508
}
509+
}
516510

517-
.accordion {
518-
border: 1px solid var(--green-400);
511+
/* Overwrites for dark theme */
512+
[data-theme="dark"] {
513+
color-scheme: dark;
514+
515+
.card.large.knowledge-card {
516+
border: var(--card-border);
519517
}
520518
}

frontend/src/css/header.css

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,15 @@ header nav {
291291
padding: 0.5rem 0.75rem;
292292
}
293293

294-
.current-language {
294+
.current-language-desktop {
295+
@media (width <= 73.125rem) {
296+
display: none;
297+
}
298+
299+
text-transform: uppercase;
300+
}
301+
302+
.current-language-mobile {
295303
@media (width >= 73.125rem) {
296304
display: none;
297305
}
@@ -353,7 +361,7 @@ header nav {
353361
content: "";
354362
position: absolute;
355363
left: 0;
356-
width: 150%;
364+
width: 100%;
357365
height: 1.25rem;
358366
}
359367
}
@@ -401,10 +409,7 @@ header nav {
401409
.nav-subitem {
402410
display: flex;
403411
width: 100%;
404-
405-
406412

407-
408413
a, button {
409414
display: flex;
410415
font: var(--subnav-link);
@@ -499,10 +504,17 @@ header nav {
499504
translate: -50% 0;
500505
left: 50%;
501506
margin-top: 1.25rem;
507+
z-index: 10;
502508

503509
@media (scripting: none) {
504510
z-index: 2;
505511
}
512+
513+
/* Bring focused dropdown to front */
514+
&:focus-within,
515+
&.focused {
516+
z-index: 20;
517+
}
506518
}
507519

508520
&.expanded {

0 commit comments

Comments
 (0)