Skip to content

Commit

Permalink
More verification tweaks (plausible#4177)
Browse files Browse the repository at this point in the history
* Stop reporting multiple snippets in case manual extension is in use

* Try detecting CookieBot/GTM case
  • Loading branch information
aerosol authored Jun 3, 2024
1 parent 1d3b068 commit 5fd4547
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 1 deletion.
17 changes: 17 additions & 0 deletions lib/plausible/verification/checks/scan_body.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ defmodule Plausible.Verification.Checks.ScanBody do
|> scan_wp_plugin()
|> scan_gtm()
|> scan_wp()
|> scan_cookie_banners()
end

def perform(state), do: state
Expand Down Expand Up @@ -62,4 +63,20 @@ defmodule Plausible.Verification.Checks.ScanBody do
state
end
end

defp scan_cookie_banners(%{assigns: %{raw_body: body}} = state) do
# We'll start with CookieBot. Not using the selectors yet, as seen at
# https://github.com/cavi-au/Consent-O-Matic/blob/master/rules/cookiebot.json
# because those don't seem to be appearing without JS evaluation.
# If this ever becomes an issue, we'll have to move that check to headless.
if String.contains?(body, "cookiebot") do
put_diagnostics(state, cookie_banner_likely?: true)
else
state
end
end

defp scan_cookie_banners(state) do
state
end
end
7 changes: 7 additions & 0 deletions lib/plausible/verification/checks/snippet.ex
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ defmodule Plausible.Verification.Checks.Snippet do
snippets_found_in_head: Enum.count(in_head),
snippets_found_in_body: Enum.count(in_body),
proxy_likely?: proxy_likely?(all),
manual_script_extension?: manual_script_extension?(all),
snippet_unknown_attributes?: unknown_attributes?(all),
data_domain_mismatch?:
data_domain_mismatch?(all, state.data_domain, state.assigns[:final_domain])
Expand All @@ -28,6 +29,12 @@ defmodule Plausible.Verification.Checks.Snippet do

def perform(state), do: state

defp manual_script_extension?(nodes) do
nodes
|> Floki.attribute("src")
|> Enum.any?(&String.contains?(&1, "manual."))
end

defp proxy_likely?(nodes) do
nodes
|> Floki.attribute("src")
Expand Down
30 changes: 29 additions & 1 deletion lib/plausible/verification/diagnostics.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ defmodule Plausible.Verification.Diagnostics do
service_error: nil,
body_fetched?: false,
wordpress_likely?: false,
cookie_banner_likely?: false,
gtm_likely?: false,
callback_status: 0,
proxy_likely?: false,
manual_script_extension?: false,
data_domain_mismatch?: false,
wordpress_plugin?: false

Expand Down Expand Up @@ -48,6 +50,13 @@ defmodule Plausible.Verification.Diagnostics do
%Result{ok?: true}
end

def interpret(
%__MODULE__{plausible_installed?: false, gtm_likely?: true, cookie_banner_likely?: true},
_url
) do
error(@errors.gtm_cookie_banner)
end

def interpret(%__MODULE__{plausible_installed?: false, gtm_likely?: true}, _url) do
error(@errors.gtm)
end
Expand Down Expand Up @@ -206,7 +215,11 @@ defmodule Plausible.Verification.Diagnostics do
end

def interpret(
%__MODULE__{snippets_found_in_head: count_head, snippets_found_in_body: count_body},
%__MODULE__{
snippets_found_in_head: count_head,
snippets_found_in_body: count_body,
manual_script_extension?: false
},
_url
)
when count_head + count_body > 1 do
Expand Down Expand Up @@ -308,6 +321,21 @@ defmodule Plausible.Verification.Diagnostics do
%Result{ok?: true}
end

def interpret(
%__MODULE__{
plausible_installed?: true,
snippets_found_in_head: count_head,
snippets_found_in_body: count_body,
callback_status: callback_status,
service_error: nil,
manual_script_extension?: true
},
_url
)
when count_head + count_body > 1 and callback_status in [200, 202] do
%Result{ok?: true}
end

def interpret(rating, url) do
Sentry.capture_message("Unhandled case for site verification: #{url}",
extra: %{
Expand Down
6 changes: 6 additions & 0 deletions lib/plausible/verification/errors.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ defmodule Plausible.Verification.Errors do
"As you're using Google Tag Manager, you'll need to use a GTM-specific Plausible snippet",
url: "https://plausible.io/docs/google-tag-manager"
},
gtm_cookie_banner: %{
message: "We couldn't verify your website",
recommendation:
"As you're using Google Tag Manager, you'll need to use a GTM-specific Plausible snippet. Please make sure no cookie consent banner is blocking our script.",
url: "https://plausible.io/docs/google-tag-manager"
},
csp: %{
message: "We encountered an issue with your site's CSP",
recommendation:
Expand Down
12 changes: 12 additions & 0 deletions test/plausible/site/verification/checks/scan_body_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ defmodule Plausible.Verification.Checks.ScanBodyTest do

assert state.diagnostics.gtm_likely?
refute state.diagnostics.wordpress_likely?
refute state.diagnostics.cookie_banner_likely?
end

test "detects GTM and cookie banner" do
state =
%State{}
|> State.assign(raw_body: "...googletagmanager.com/gtm.js...cookiebot...")
|> @check.perform()

assert state.diagnostics.gtm_likely?
assert state.diagnostics.cookie_banner_likely?
refute state.diagnostics.wordpress_likely?
end

for signature <- ["wp-content", "wp-includes", "wp-json"] do
Expand Down
20 changes: 20 additions & 0 deletions test/plausible/site/verification/checks/snippet_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ defmodule Plausible.Verification.Checks.SnippetTest do
refute state.diagnostics.data_domain_mismatch?
refute state.diagnostics.snippet_unknown_attributes?
refute state.diagnostics.proxy_likely?
refute state.diagnostics.manual_script_extension?
end

@multi_domain """
Expand All @@ -46,6 +47,7 @@ defmodule Plausible.Verification.Checks.SnippetTest do
refute state.diagnostics.data_domain_mismatch?
refute state.diagnostics.snippet_unknown_attributes?
refute state.diagnostics.proxy_likely?
refute state.diagnostics.manual_script_extension?
end

@crazy """
Expand All @@ -67,6 +69,7 @@ defmodule Plausible.Verification.Checks.SnippetTest do

assert state.diagnostics.snippets_found_in_head == 2
assert state.diagnostics.snippets_found_in_body == 3
refute state.diagnostics.manual_script_extension?
end

test "figures out data-domain mismatch" do
Expand All @@ -80,6 +83,7 @@ defmodule Plausible.Verification.Checks.SnippetTest do
assert state.diagnostics.data_domain_mismatch?
refute state.diagnostics.snippet_unknown_attributes?
refute state.diagnostics.proxy_likely?
refute state.diagnostics.manual_script_extension?
end

@proxy_likely """
Expand All @@ -99,6 +103,22 @@ defmodule Plausible.Verification.Checks.SnippetTest do
refute state.diagnostics.data_domain_mismatch?
refute state.diagnostics.snippet_unknown_attributes?
assert state.diagnostics.proxy_likely?
refute state.diagnostics.manual_script_extension?
end

@manual_extension """
<head>
<script defer data-domain="example.com" event-author="Me" src="http://localhost:8000/js/script.manual.js"></script>
</head>
"""

test "figures out manual script extension" do
state =
@manual_extension
|> new_state()
|> @check.perform()

assert state.diagnostics.manual_script_extension?
end

@unknown_attributes """
Expand Down
48 changes: 48 additions & 0 deletions test/plausible/site/verification/checks_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,27 @@ defmodule Plausible.Verification.ChecksTest do
|> assert_error(@errors.multiple_snippets)
end

@many_snippets_ok """
<html>
<head>
<script defer data-domain="example.com" src="https://plausible.io/js/script.js"></script>
<script defer data-domain="example.com" src="https://plausible.io/js/script.manual.js"></script>
</head>
<body>
Hello
</body>
</html>
"""

test "skipping many snippets when manual found" do
stub_fetch_body(200, @many_snippets_ok)
stub_installation()

run_checks()
|> Checks.interpret_diagnostics()
|> assert_ok()
end

test "detecting snippet after busting cache" do
stub_fetch_body(fn conn ->
conn = fetch_query_params(conn)
Expand Down Expand Up @@ -520,6 +541,33 @@ defmodule Plausible.Verification.ChecksTest do
|> assert_error(@errors.gtm)
end

@gtm_body_with_cookiebot """
<html>
<head>
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','XXXX');</script>
<!-- End Google Tag Manager -->
<script id="Cookiebot" src="https://consent.cookiebot.com/uc.js" data-cbid="some-uuid" data-blockingmode="auto" type="text/javascript"></script>
</head>
<body>
Hello
</body>
</html>
"""

test "detecting gtm with cookie consent" do
stub_fetch_body(200, @gtm_body_with_cookiebot)
stub_installation(200, plausible_installed(false))

run_checks()
|> Checks.interpret_diagnostics()
|> assert_error(@errors.gtm_cookie_banner)
end

test "non-html body" do
stub_fetch_body(fn conn ->
conn
Expand Down

0 comments on commit 5fd4547

Please sign in to comment.