Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Wagtail Tag Manager (WTM for short) is a Wagtail addon that allows for easier and GDPR compliant administration of scripts and tags.
In this package the term "tag" is being used for code snippets being injected into HTML. This is not to be confused with tags used to identify content in the CMS, such as pictures and documents.
As of version 2.0, consent is stored differently in the wtm
cookie. The reason for this change is
to prevent so-called "cache-busting" and only refresh the WTM cookie after a certain period of time
(specified in WTM_COOKIE_REFRESH
).
The cookie will now contain a base64
and uri
encoded JSON string which stores both the consent state
and additional information. Old versions of the cookie
(e.g. necessary:true|preferences:unset|statistics:pending|marketing:false
) will automatically be
upgraded when detected. Once decoded, it will look something like this:
{
"meta": {
"id": "b402114a-352f-488f-8481-834cd91a1b2b",
"refresh_timestamp": 1694520568.531865,
"set_timestamp": 1694520573.685324
},
"state": {
"necessary": "true",
"preferences": "unset",
"statistics": "pending",
"marketing": "false"
}
}
To easily parse the cookie using JavaScript, you can use this code snippet:
const { meta, state } = JSON.parse(atob(decodeURIComponent(cookie_content_goes_here)));
You can ready more on how to easily access this state from your front-end here.
Wagtail Tag Manager offers functionality similar to platforms like Adobe Dynamic Tag Management, Google Tag Manager and Tealium iQ without the need of a third party. It's fully integrated into Wagtail.
This approach comes with a couple of advantages, most prominently the ability to inject tags into a page before the response is send to a client.
This package attempts to ease the implementation of tags by the new ePrivacy rules as defined by the European Union. I urge you to read about these new rules and ensure you are properly configuring your tags. This package is free and the author can not be held responsible for the correctness of your implementation, or the assumptions made in this package to comply with the new ePrivacy regulation.
Read more about the ePrivacy Regulation.
Included in this package is a cookie bar which admittedly provides too little
information to end users regarding the purpose of the scripts you are placing
on the website. For compliance, please use the cookie_bar.html
template to
change the text shown in the cookie bar.
Package | Version(s) |
---|---|
Django | 3.2, 4.0, 4.1, 4.2 |
Wagtail | 4.1, 4.2, 5.0. 5.1 |
Python | 3.8, 3.9, 3.10, 3.11 |
Installation:
pip install wagtail-tag-manager
Add the application to your INSTALLED_APPS
:
INSTALLED_APPS = [
# ...
'wagtail_modeladmin',
'wagtail_tag_manager',
# ...
]
If you wish to enable the cookie bar settings (allowing you to change to title
and text displayed in the cookie bar), also include wagtail.contrib.settings
in the INSTALLED_APPS
.
Include the middleware:
MIDDLEWARE = [
# ...
"wagtail_tag_manager.middleware.CookieConsentMiddleware",
'wagtail_tag_manager.middleware.TagManagerMiddleware', # optional
# ...
]
WTM offers two ways to implement it's functionality. You can either choose to
use the TagManagerMiddleware
(which will rewrite the html on each request)
or use the {% wtm_instant_tags %}
and {% wtm_lazy_manager %}
template
tags.
If you prefer to use the template tags to inject tags into your templates,
set the WTM_INJECT_TAGS
and WTM_INJECT_SCRIPT
settings to False
and implement the template tags as follows:
{% load wtm_tags %}
<head>
{% wtm_instant_tags 'top_head' %} ... {% wtm_instant_tags 'bottom_head' %}
</head>
<body>
{% wtm_instant_tags 'top_body' %} ... {% wtm_instant_tags 'bottom_body' %} {% wtm_lazy_manager %}
</body>
Include the urls:
from django.urls import include, path
from wagtail_tag_manager import urls as wtm_urls
urlpatterns = [
# ...
path('wtm/', include(wtm_urls)),
# ...
path('', include(wagtail_urls)),
# ...
]
As an alternative to using the middleware you can use the wtm_instant_tags
and wtm_lazy_manager
template tags. Please be sure to use the
TagManagerMiddleware
OR the template tags, never both.
wtm_instant_tags
To load all instant tags at once:
{% load wtm_tags %}
<head>
... {% wtm_instant_tags %}
</head>
To load tags corresponding to a certain position:
{% load wtm_tags %}
<head>
{% wtm_instant_tags 'top_head' %} ... {% wtm_instant_tags 'bottom_head' %}
</head>
<body>
{% wtm_instant_tags 'top_body' %} ... {% wtm_instant_tags 'bottom_body' %}
</body>
wtm_lazy_manager
{% load wtm_tags %}
<body>
... {% wtm_lazy_manager %}
</body>
Optionally, you can disable either the script and/or the styling.
{% load wtm_tags %}
<body>
... {% wtm_lazy_manager include_style=False include_script=False %}
</body>
wtm_cookie_bar
{% load wtm_tags %}
<body>
{% wtm_cookie_bar %} ...
</body>
wtm_include
WTM comes with the wtm_include
template tag to accommodate loading of
resources and markup based on the tag strategy and consent given. It can be
used as a way to load html, css or javascript files.
{% load wtm_tags %}
<body>
... {% wtm_include "necessary" "css/style.css" %} {% wtm_include "necessary" "js/style.js" %} {%
wtm_include "necessary" "content.html" %} ...
</body>
Alternatively, you can use it as a block:
{% load wtm_tags %}
<body>
... {% wtm_include "statistics" %}
<script>
console.log("Included conditionally");
</script>
{% wtm_endinclude %} ...
</body>
You can use the following provided template tags to render a tag status overview, a table with cookie declarations or a consent form.
{% wtm_tag_table %} {% wtm_declaration_table %} {% wtm_manage_form %}
To enable the context processors, add the following to your settings:
"context_processors": [
# ...
"wagtail_tag_manager.context_processors.consent_state",
]
You can now use the following value in your templates:
{{ wtm_consent_state.necessary }} {{ wtm_consent_state.preferences }} {{
wtm_consent_state.statistics }} {{ wtm_consent_state.marketing }}
These will return a boolean indicating wether or not tags specific to the corresponding state should load.
WTM_TAG_TYPES
WTM_TAG_TYPES = {
# key, verbose name, setting
"necessary": (_("Necessary"), "required"),
"preferences": (_("Preferences"), "initial"),
"statistics": (_("Statistics"), "initial"),
"marketing": (_("Marketing"), ""),
}
Allows you to define the tag types available. This can be helpful if you'd like
the change the terminology used, or when you'd prefer to split a type in
multiple sections. Notice the two keywords (required
and initial
) used.
Tags marked as required
can not be disabled and will always be included on
every page.
Tags marked as initial
will be included as long as no explicit consent has
been given by the end user, provided the browser allows cookies. While no
consent has been given, these tags will be loaded lazily to honor the browser
settings (which we can only read using javascript).
The third option is to mark a tag as delayed
. This will ensure the tag will
not load on the first page load, but only from the second load forward.
WTM_INJECT_TAGS
WTM_INJECT_TAGS = True
Instructs the middleware to inject all tags marked "instant load" in the
document. Disable this if you would rather use the {% wtm_instant_tags %}
template tags.
WTM_MANAGE_VIEW
WTM_MANAGE_VIEW = True
Allows you to enable or disable the included "manage" view allowing users to get insight in the tags running on your site and adjust their preferences. The view is enabled by default.
WTM_COOKIE_EXPIRE
WTM_COOKIE_EXPIRE = 365
Sets the expiration time in days of WTM's cookies. Notice that this is only applicable to the consent cookies used by WTM, not any cookies placed by tags.
WTM_COOKIE_REFRESH
WTM_COOKIE_REFRESH = 30
Sets the refresh time in days of WTM's cookies. Notice that this is only applicable to the consent cookies used by WTM, not any cookies placed by tags.
WTM_CACHE_TIMEOUT
WTM_CACHE_TIMEOUT = 1800
Sets the amount of seconds the cache will be preserved. At the moment, caching is only applied to constants, which will refresh when a constant is saved. Default is 30 minutes.
WTM_PRESERVE_VARIABLES
WTM_PRESERVE_VARIABLES = True
Configures whether the variables are preserved for each request, or refreshed
for each tag applied to a response. When set to False
, a query will be done
for each single tag which will add up quickly.
WTM_INJECT_STYLE
WTM_INJECT_STYLE = True
Change to False
to prevent WTM's included styles from loading. This is useful
if you wish to style the cookiebar yourself.
WTM_INJECT_SCRIPT
WTM_INJECT_SCRIPT = True
Change to False
to prevent WTM's included scripts from loading. This is
useful if you don't want to use the inlcuded lazy loading and cookie bar
functionality. While enabled, the script will expose the window.wtm.consent()
function. When called, the function will retrieve the current consent state
and information from the wtm
cookie. This will like something like this:
{
"meta": {
"id": "b402114a-352f-488f-8481-834cd91a1b2b",
"refresh_timestamp": 1694520568.531865,
"set_timestamp": 1694520573.685324
},
"state": {
"marketing": "false",
"necessary": "true",
"preferences": "true",
"statistics": "true"
}
}
WTM_SUMMARY_PANELS
WTM_SUMMARY_PANELS = False
Disables or enables the summary panels visible on the Wagtail admin dashboard.
WTM_ENABLE_SCANNER
This is an experimental feature.
WTM_ENABLE_SCANNER = False
When enabled, allows scanning of cookies placed on the website. Use this to automatically generate cookie declarations. Will attempt to use Chrome Driver when available, and will fall back to regular requests if not.
WTM_CHROMEDRIVER_URL
This is an experimental feature.
WTM_CHROMEDRIVER_URL = "http://0.0.0.0:4444/wd/hub"
Allows configuration of the docker container running an instance of
selenium/standalone-chrome
.
When developing, use the following command to run the docker container and ensure that your site is configured be accessible over your computer's public ip. Otherwise the docker container won't be able to access the website.
https://hub.docker.com/r/selenium/standalone-chrome/
In addition to managing variables in the admin interface, variables can also be
created in your source code by registering a CustomVariable
.
from wagtail_tag_manager.decorators import register_variable
from wagtail_tag_manager.options import CustomVariable
@register_variable
class Variable(CustomVariable):
name = "Custom variable"
description = "Returns a custom value."
key = "custom"
def get_value(self, request):
return "This is a custom variable."
If you would like to include tags on a page, include the TagMixin
mixin.
Under the "Settings" tab of the corresponding page type a list of tags will be
shown. By selecting these, these tags will be included when the page loads.
Additionally, by selecting the "Include children" field, all descending pages of the configured page will also load the chosen tags.
Note that the consent state is being applied to these tags. If the selected tag is marked as, for example, "marketing", the end-user still must allow this type of tags before is is being injected.
from wagtail_tag_manager.mixins import TagMixin
class HomePage(TagMixin, Page):
pass
Triggers allow you to monitor events on the frontend of your website and load a tag after a specified event has occurred. By using conditions you are able to harness (custom) variables to only trigger a tag once your event complies with the conditions that you specified.
To experiment with the package you can use the sandbox provided in this
repository. To install this you will need to create and activate a
virtualenv and then run make sandbox
. This will start a fresh Wagtail
install, with the tag manager module enabled, on http://localhost:8000
and http://localhost:8000/cms/. The superuser credentials are
superuser
with the password testing
.
Various types of tags, constants and variables are enabled out of the box. Check out the console in your browser to see them in action.
WTM comes with the following tag types pre-configured:
Name | Setting |
---|---|
Necessary | Required |
Preferences | Initial |
Statistics | Initial |
Marketing | Default |
These types correspond to the segmentation made by the EU here.
State | Required | Initial | Delayed | Default |
---|---|---|---|---|
No cookies accepted. | yes | no | no | no |
Cookies implicitly accepted through browser settings. | yes | yes | yes¹ | no |
Cookies explicitly accepted, noting tracking functionality.² | yes | yes | yes¹ | yes |
¹ From the second page load onward.
² According to the ePrivacy regulation, mentioning that you are using tracking functionality is mandatory.
Note that in the case of Statistics cookies or local storage, you are obliged to still show a notification at least once, noting that you are using cookies for analytical and performance measurement purposes.
When implementing Marketing cookies, the user has to explicitly give permission for you to enable them for their session. When asking for permission, you must explicitly state the tracking functionality of the script you are using.
To ease the implementation by this concept, Wagtail Tag Manager allows you to define a tag as "Necessary", "Preferences", "Statistics" or "Marketing". When properly configured, it'll take care of loading the correct tag at the correct time, taking in account the following scenario's:
The user has not accepted cookies.
Required | Initial | Delayed | Default | |
---|---|---|---|---|
Instant | yes | no | no | no |
Lazy | yes | no | no | no |
The user has accepted cookies through browser settings.
Required | Initial | Delayed | Default | |
---|---|---|---|---|
Instant | yes | yes¹ | yes² | no |
Lazy | yes | yes | yes² | no |
¹ Will be loaded lazily.
² From the second page load onward.
As the acceptance of "Initial" tags can only be verified client side, we'll first load all the "Initial" tags lazy (whether they are instant or not).
Please note that we still have to show a message stating that we are using tags with analytical purposes.
The user has explicitly accepted cookies for your site.
Required | Initial | Delayed | Default | |
---|---|---|---|---|
Instant | yes | yes | yes | yes |
Lazy | yes | yes | yes | yes |
I'd love to hear from sites and applications where WTM is being used. Please contact me if you'd like your implementation to be listed here!
To make Wagtail Tag Manager accessible, it's is published under the BSD 3-Clause "New" or "Revised" License. For more information, please refer to the LICENSE file in this repository.
FAQs
A Wagtail add-on for managing tags.
We found that wagtail-tag-manager demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.