shabbat-gate 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,7 +3,37 @@
3
3
  All notable changes to this package are documented here. Format loosely follows
4
4
  [Keep a Changelog](https://keepachangelog.com/).
5
5
 
6
- ## [0.4.0] - Unreleased
6
+ ## [0.4.1] - Unreleased
7
+
8
+ ### Fixed
9
+
10
+ - **`adsbot-google` in the crawler allowlist was cloaking under Google Ads policy - caused a
11
+ real Ads + Merchant Center account suspension.** Found 2026-08-29 on a consumer site: during
12
+ a gate closure window, AdsBot-Google (the crawler Google Ads uses to verify what a real ad
13
+ landing page shows) saw the full real site, while a human clicking the same ad saw the holding
14
+ page. Google's suspension notice for "עקיפת מערכות: הסוואה" (circumventing systems: cloaking)
15
+ described exactly this - showing Google's verification bot different content than what users
16
+ see - almost verbatim.
17
+
18
+ Removing the explicit `adsbot-google` term from `BOT_PATTERN`'s alternation alone would **not**
19
+ have fixed this: the pattern's generic `bot` alternative already matches the substring "Bot" in
20
+ "AdsBot-Google", so it stayed allowed regardless. `isBot()` now checks a dedicated
21
+ `AD_VERIFICATION_BOT_PATTERN` first and returns `false` before the broad pattern ever runs, so
22
+ these crawlers are gated exactly like a human visitor. Every other crawler in `BOT_PATTERN`
23
+ (Googlebot, Bingbot, GPTBot, ClaudeBot, etc.) is unaffected - organic/AI indexing during a
24
+ closure window is a different, broadly-tolerated risk category from a dedicated ad-verification
25
+ bot, which ad-network policy specifically requires to see what humans see.
26
+
27
+ Two more crawlers in the same risk category are excluded alongside AdsBot-Google, for the same
28
+ reason and hitting the same "bot" substring problem: **`Storebot-Google`** (Google
29
+ Merchant Center/Shopping's product landing-page verifier - directly relevant here, since
30
+ Merchant Center was the linked account also suspended) and **`adidxbot`** (Microsoft
31
+ Advertising/Bing Ads' equivalent landing-page verification crawler).
32
+
33
+ Regression tests assert `isBot()` returns `false` for AdsBot-Google, AdsBot-Google-Mobile,
34
+ Storebot-Google, and adidxbot user agents.
35
+
36
+ ## [0.4.0] - 2026-07-28
7
37
 
8
38
  ### Fixed
9
39
 
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Broad, case-insensitive allowlist for search engine and AI crawlers.
3
3
  * Matches let a request through unconditionally, before any gate logic runs.
4
- * Erring toward "let more things through" is the safe direction here, since
5
- * the whole point of this list is protecting SEO/crawlability.
4
+ * Erring toward "let more things through" is the safe direction for organic/AI
5
+ * crawlers, since the whole point of this list is protecting SEO/crawlability.
6
6
  */
7
7
  export declare const BOT_PATTERN: RegExp;
8
8
  export declare function isBot(userAgent: string): boolean;
@@ -1,10 +1,37 @@
1
1
  /**
2
2
  * Broad, case-insensitive allowlist for search engine and AI crawlers.
3
3
  * Matches let a request through unconditionally, before any gate logic runs.
4
- * Erring toward "let more things through" is the safe direction here, since
5
- * the whole point of this list is protecting SEO/crawlability.
4
+ * Erring toward "let more things through" is the safe direction for organic/AI
5
+ * crawlers, since the whole point of this list is protecting SEO/crawlability.
6
6
  */
7
- export const BOT_PATTERN = /bot|crawl|spider|slurp|googlebot|google-inspectiontool|adsbot-google|bingbot|duckduckbot|baiduspider|yandex|facebookexternalhit|twitterbot|linkedinbot|whatsapp|telegrambot|discordbot|applebot|gptbot|chatgpt-user|oai-searchbot|ccbot|claudebot|claude-web|anthropic-ai|perplexitybot|google-extended|bytespider|semrushbot|ahrefsbot|mj12bot|petalbot/i;
7
+ export const BOT_PATTERN = /bot|crawl|spider|slurp|googlebot|google-inspectiontool|bingbot|duckduckbot|baiduspider|yandex|facebookexternalhit|twitterbot|linkedinbot|whatsapp|telegrambot|discordbot|applebot|gptbot|chatgpt-user|oai-searchbot|ccbot|claudebot|claude-web|anthropic-ai|perplexitybot|google-extended|bytespider|semrushbot|ahrefsbot|mj12bot|petalbot/i;
8
+ /**
9
+ * Dedicated ad/shopping-network verification crawlers are deliberately NOT
10
+ * allowed through the gate, even though their user agents also match the
11
+ * generic `bot` term above (e.g. "AdsBot-Google" and "Storebot-Google" both
12
+ * contain the substring "bot", "adidxbot" ends in it) - excluding them from
13
+ * BOT_PATTERN's alternation alone would do nothing, since that bare substring
14
+ * still matches.
15
+ *
16
+ * Unlike organic/AI indexing crawlers, these bots exist specifically to
17
+ * verify that a live ad or shopping listing's destination page matches what a
18
+ * human clicking it sees - Google Ads, Google Merchant Center/Shopping, and
19
+ * Microsoft Advertising all require exactly that. Letting one bypass the gate
20
+ * means it sees the real site during a closure window while a human visitor
21
+ * sees the holding page - a textbook cloaking pattern, and the exact
22
+ * violation Google's policy team cited when it suspended a consumer's Ads +
23
+ * Merchant Center accounts over this (2026-08-29).
24
+ *
25
+ * - `adsbot-google` - AdsBot-Google / AdsBot-Google-Mobile, verifies Google
26
+ * Ads landing pages.
27
+ * - `storebot-google` - verifies Google Shopping / Merchant Center product
28
+ * landing pages against the submitted feed.
29
+ * - `adidxbot` - Microsoft Advertising's (Bing Ads) equivalent landing-page
30
+ * verification crawler.
31
+ */
32
+ const AD_VERIFICATION_BOT_PATTERN = /adsbot-google|storebot-google|adidxbot/i;
8
33
  export function isBot(userAgent) {
34
+ if (AD_VERIFICATION_BOT_PATTERN.test(userAgent))
35
+ return false;
9
36
  return BOT_PATTERN.test(userAgent);
10
37
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shabbat-gate",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Cloudflare Pages/Workers middleware that closes a site to human visitors during Shabbat and major Jewish holidays (Israel-observance rules), while always letting search engines and AI crawlers through.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",