diff --git a/VERSION b/VERSION index 6e18a01..ebe6af6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2026.08.5 +2026.08.6 diff --git a/scripts/test-wpadmin-gate.py b/scripts/test-wpadmin-gate.py index 16abc91..8900c7c 100644 --- a/scripts/test-wpadmin-gate.py +++ b/scripts/test-wpadmin-gate.py @@ -491,6 +491,25 @@ class UriNormalisation(unittest.TestCase): self.assertIn('.php', acl_line, 'wp_admin_asset must exclude .php explicitly') + def test_wp_admin_asset_pattern_is_end_of_flags_guarded(self): + """A pattern starting with "(" makes HAProxy warn on EVERY load/reload: + + parsing acl 'wp_admin_asset' : matching 'path_reg' for pattern + '(^|/)wp-admin/...' is likely a mistake ... Maybe you need to + remove the extraneous space before '('. + + "--" is the end-of-flags marker HAProxy itself names as the fix. It is + cosmetic to matching but not to operations: an unsilenced warning on + every reload on every host trains people to skim past warnings, which + is how a real one gets missed. Assert the pattern is still the one we + think it is, so this can never pass by the pattern having been changed. + """ + acl_line = require_rule(self.cfg, 'acl wp_admin_asset', 'wp_admin_asset ACL') + self.assertRegex( + acl_line, r'path_reg\s+--\s+\(', + 'wp_admin_asset pattern begins with "(" and MUST be preceded by "--"') + self.assertIn('(^|/)wp-admin/(css|js|images)/', acl_line) + def test_case_insensitive_acl_and_regsub_are_kept_in_sync(self): """A case-insensitive wp_admin_path with a case-sensitive regsub is an INFINITE REDIRECT LOOP: regsub finds no "/wp-admin/" in diff --git a/templates/hap_listener.tpl b/templates/hap_listener.tpl index 463afff..3035470 100644 --- a/templates/hap_listener.tpl +++ b/templates/hap_listener.tpl @@ -473,7 +473,22 @@ frontend web # substring, so it cannot cause the silent "login page renders unstyled" # regression that an extension allowlist would risk. Requires PCRE2, which # both the Debian (deployed) and Alpine haproxy builds have (+PCRE2). - acl wp_admin_asset path_reg (^|/)wp-admin/(css|js|images)/(?!.*\.php).*$ + # + # THE "--" IS LOAD-BEARING, DO NOT DELETE IT. HAProxy warns on any pattern + # whose first character is "(", because it cannot tell an intended regex + # from a fetch-argument list someone typo'd a space into: + # parsing acl 'wp_admin_asset' : matching 'path_reg' for pattern + # '(^|/)wp-admin/...' is likely a mistake and probably not what you want. + # Maybe you need to remove the extraneous space before '('. + # "--" is HAProxy's documented end-of-flags marker and is the fix it names + # itself ("please insert '--' between the match and the pattern"). It changes + # no matching semantics -- verified live on whp02: /wp-admin/css/login.min.css + # still passes and /wp-admin/css/x.php is still gated, before and after. + # Left unsilenced this fires on EVERY config load and reload on every host, + # where it trains operators to skim past warnings and can bury a real one. + # wp_admin_path escapes the warning only because its "-i" flag happens to + # consume the flag slot first; it is not otherwise special. + acl wp_admin_asset path_reg -- (^|/)wp-admin/(css|js|images)/(?!.*\.php).*$ acl wp_gate_exempt hdr(host),lower -f /etc/haproxy/wpadmin_gate_exempt.list # ENCODED SEPARATOR. percent-decode-unreserved deliberately does NOT decode # %2F -- "/" is a reserved character, and decoding it in the normalizer