diff --git a/tests/test_posting_guard.py b/tests/test_posting_guard.py
index bed1935f..d182993b 100644
--- a/tests/test_posting_guard.py
+++ b/tests/test_posting_guard.py
@@ -605,6 +605,39 @@ class Calibration(GuardFixture):
# href targets are attributes, not text: hidden nav links are not "hidden URLs".
self.assertEqual(self.findings_of(out)["hidden_urls"], [])
+ def test_decoration_and_custom_properties_do_not_hide_visible_posting_text(self):
+ # These declarations are serialized by the browser after assigning the
+ # DOM style properties; each retains black, visible text.
+ styles = [f"{name}: transparent" for name in ("background-color", "border-color", "text-decoration-color")]
+ styles += ["stroke-opacity: 0", "--opacity: 0", "--display: none"]
+ for style in styles:
+ with self.subTest(style=style):
+ page = self.write("posting.html", (
+ f''
+ 'Software Engineer: Python and SQL experience'
+ 'Hidden content'
+ ))
+ code, out, err = self.run_main("--in", str(page), "--html", "--json")
+ self.assertEqual(code, 0, err)
+ self.assertIn("Software Engineer: Python and SQL experience", self.text_of(out))
+ self.assertNotIn("Hidden content", self.text_of(out))
+ self.assertEqual(self.findings_of(out)["hidden_blocks"], ["Hidden content"])
+
+ def test_offscreen_margins_still_hide_once_names_are_anchored(self):
+ # Anchoring the property names must not lose the off-screen margin
+ # trick, which the unanchored pattern only caught as a suffix of "left".
+ for style in ("margin-left: -9999px", "position: absolute; margin-top: -10000px"):
+ with self.subTest(style=style):
+ page = self.write("posting.html", (
+ 'Software Engineer: Python and SQL experience'
+ f'Hidden content'
+ ))
+ code, out, err = self.run_main("--in", str(page), "--html", "--json")
+ self.assertEqual(code, 0, err)
+ self.assertIn("Software Engineer: Python and SQL experience", self.text_of(out))
+ self.assertNotIn("Hidden content", self.text_of(out))
+ self.assertEqual(self.findings_of(out)["hidden_blocks"], ["Hidden content"])
+
def test_leading_bom_is_encoding_noise(self):
code, out, err = self.run_main("--text", "--json", stdin_text="Plain posting.")
self.assertTrue(out.startswith("posting_guard: clean"), out.splitlines()[0])
diff --git a/tools/posting_guard.py b/tools/posting_guard.py
index 1c80eb63..1c4fb06e 100644
--- a/tools/posting_guard.py
+++ b/tools/posting_guard.py
@@ -97,14 +97,14 @@ VARIATION = re.compile("[︀-️\U000E0100-\U000E01EF]")
VARIATION_RUN = re.compile("[︀-️\U000E0100-\U000E01EF]{2,}")
HIDDEN_STYLE = re.compile(
- r"display\s*:\s*none"
+ r"(?:^|;)\s*(?:display\s*:\s*none"
r"|visibility\s*:\s*hidden"
r"|opacity\s*:\s*0(?![.\d]*[1-9])"
r"|font-size\s*:\s*0(?:\.0+)?(?:px|pt|em|rem|%)?(?![\d.])"
r"|color\s*:\s*transparent"
r"|clip\s*:\s*rect\(\s*0"
r"|clip-path\s*:\s*inset\(\s*100%"
- r"|(?:left|top|text-indent)\s*:\s*-\d{4,}px",
+ r"|(?:(?:margin-)?(?:left|top)|text-indent)\s*:\s*-\d{4,}px)",
re.I,
)
PRESTRIP = re.compile(r"]*>|<\?[^>]*\?>", re.I)