Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to integrate subresource-integrity attributes to javascript and… #815

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,25 @@ A dictionary passed to compiler's ``compile_file`` method as kwargs. None of def

Defaults to ``{}``.

``crossorigin``
...............

**Optional**

Indicate if you want to add to the group this attribute that provides support for CORS, defining how the element handles cross-origin requests, thereby enabling the configuration of the CORS requests for the element's fetched data. .

Missing by default (the attribute is not added), the only valid values currently are ``anonymous`` and ``use-credentials``.

``integrity``
.............

**Optional**

Indicate if you want to add the sub-resource integrity (SRI) attribute to the group.
This attribute contains inline metadata that a user agent can use to verify that a fetched resource has been delivered free of unexpected manipulation

Missing by default, and only valid values are ``"sha256"``, ``"sha384"`` and ``"sha512"``.


Other settings
--------------
Expand Down
9 changes: 8 additions & 1 deletion pipeline/jinja2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ def render_css(self, package, path):
template_name = package.template_name or "pipeline/css.jinja"
context = package.extra_context
context.update(
{"type": guess_type(path, "text/css"), "url": staticfiles_storage.url(path)}
{
"type": guess_type(path, "text/css"),
"url": staticfiles_storage.url(path),
"crossorigin": package.config.get("crossorigin"),
"integrity": package.get_sri(path),
}
)
template = self.environment.get_template(template_name)
return template.render(context)
Expand All @@ -66,6 +71,8 @@ def render_js(self, package, path):
{
"type": guess_type(path, "text/javascript"),
"url": staticfiles_storage.url(path),
"crossorigin": package.config.get("crossorigin"),
"integrity": package.get_sri(path),
}
)
template = self.environment.get_template(template_name)
Expand Down
2 changes: 1 addition & 1 deletion pipeline/jinja2/pipeline/css.jinja
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<link href="{{ url }}" rel="stylesheet" type="{{ type }}"{% if media %} media="{{ media }}"{% endif %}{% if charset %} charset="{{ charset }}"{% endif %} />
<link href="{{ url }}" rel="stylesheet" type="{{ type }}"{% if media %} media="{{ media }}"{% endif %}{% if charset %} charset="{{ charset }}"{% endif %}{% if integrity %} integrity="{{ integrity }}"{% endif %}{% if crossorigin %} crossorigin="{{ crossorigin }}"{% endif %} />
2 changes: 1 addition & 1 deletion pipeline/jinja2/pipeline/js.jinja
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<script{% if async %} async{% endif %}{% if defer %} defer{% endif %} type="{{ type }}" src="{{ url }}" charset="utf-8"></script>
<script{% if async %} async{% endif %}{% if defer %} defer{% endif %} type="{{ type }}" src="{{ url }}" charset="utf-8"{% if integrity %} integrity="{{ integrity }}"{% endif %}{% if crossorigin %} crossorigin="{{ crossorigin }}"{% endif %}></script>
18 changes: 18 additions & 0 deletions pipeline/packager.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import base64
import hashlib
from functools import lru_cache

from django.contrib.staticfiles.finders import find, get_finders
from django.contrib.staticfiles.storage import staticfiles_storage
from django.core.files.base import ContentFile
Expand Down Expand Up @@ -61,6 +65,20 @@ def manifest(self):
def compiler_options(self):
return self.config.get("compiler_options", {})

@lru_cache
def get_sri(self, path):
method = self.config.get("integrity")
if method not in {"sha256", "sha384", "sha512"}:
return None
if staticfiles_storage.exists(path):
with staticfiles_storage.open(path) as fd:
h = getattr(hashlib, method)()
for data in iter(lambda: fd.read(16384), b""):
h.update(data)
digest = base64.b64encode(h.digest()).decode()
return f"{method}-{digest}"
return None


class Packager:
def __init__(
Expand Down
2 changes: 1 addition & 1 deletion pipeline/templates/pipeline/css.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<link href="{{ url }}" rel="stylesheet" type="{{ type }}" media="{{ media|default:"all" }}"{% if title %} title="{{ title }}"{% endif %}{% if charset %} charset="{{ charset }}"{% endif %} />
<link href="{{ url }}" rel="stylesheet" type="{{ type }}" media="{{ media|default:"all" }}"{% if title %} title="{{ title }}"{% endif %}{% if charset %} charset="{{ charset }}"{% endif %}{% if integrity %} integrity="{{ integrity }}"{% endif %}{% if crossorigin %} crossorigin="{{ crossorigin }}"{% endif %} />
2 changes: 1 addition & 1 deletion pipeline/templates/pipeline/css.jinja
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<link href="{{ url }}" rel="stylesheet" type="{{ type }}"{% if media %} media="{{ media }}"{% endif %}{% if charset %} charset="{{ charset }}"{% endif %} />
<link href="{{ url }}" rel="stylesheet" type="{{ type }}"{% if media %} media="{{ media }}"{% endif %}{% if charset %} charset="{{ charset }}"{% endif %}{% if integrity %} integrity="{{ integrity }}"{% endif %}{% if crossorigin %} crossorigin="{{ crossorigin }}"{% endif %} />
2 changes: 1 addition & 1 deletion pipeline/templates/pipeline/js.html
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<script{% if async %} async{% endif %}{% if defer %} defer{% endif %} type="{{ type }}" src="{{ url }}" charset="utf-8"></script>
<script{% if async %} async{% endif %}{% if defer %} defer{% endif %} type="{{ type }}" src="{{ url }}" charset="utf-8"{% if integrity %} integrity="{{ integrity }}"{% endif %}{% if crossorigin %} crossorigin="{{ crossorigin }}"{% endif %}></script>
2 changes: 1 addition & 1 deletion pipeline/templates/pipeline/js.jinja
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<script{% if async %} async{% endif %}{% if defer %} defer{% endif %} type="{{ type }}" src="{{ url }}" charset="utf-8"></script>
<script{% if async %} async{% endif %}{% if defer %} defer{% endif %} type="{{ type }}" src="{{ url }}" charset="utf-8"{% if integrity %} integrity="{{ integrity }}"{% endif %}{% if crossorigin %} crossorigin="{{ crossorigin }}"{% endif %}></script>
4 changes: 4 additions & 0 deletions pipeline/templatetags/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ def render_css(self, package, path):
{
"type": guess_type(path, "text/css"),
"url": mark_safe(staticfiles_storage.url(path)),
"crossorigin": package.config.get("crossorigin"),
"integrity": package.get_sri(path),
}
)
return render_to_string(template_name, context)
Expand Down Expand Up @@ -188,6 +190,8 @@ def render_js(self, package, path):
{
"type": guess_type(path, "text/javascript"),
"url": mark_safe(staticfiles_storage.url(path)),
"crossorigin": package.config.get("crossorigin"),
"integrity": package.get_sri(path),
}
)
return render_to_string(template_name, context)
Expand Down
76 changes: 76 additions & 0 deletions tests/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,42 @@ def local_path(path):
"title": "Default Style",
},
},
"screen_crossorigin": {
"source_filenames": (
"pipeline/css/first.css",
"pipeline/css/second.css",
"pipeline/css/urls.css",
),
"output_filename": "screen_crossorigin.css",
"crossorigin": "anonymous",
},
"screen_sri_sha256": {
"source_filenames": (
"pipeline/css/first.css",
"pipeline/css/second.css",
"pipeline/css/urls.css",
),
"output_filename": "screen_sri_sha256.css",
"integrity": "sha256",
},
"screen_sri_sha384": {
"source_filenames": (
"pipeline/css/first.css",
"pipeline/css/second.css",
"pipeline/css/urls.css",
),
"output_filename": "screen_sri_sha384.css",
"integrity": "sha384",
},
"screen_sri_sha512": {
"source_filenames": (
"pipeline/css/first.css",
"pipeline/css/second.css",
"pipeline/css/urls.css",
),
"output_filename": "screen_sri_sha512.css",
"integrity": "sha512",
},
},
"JAVASCRIPT": {
"scripts": {
Expand Down Expand Up @@ -137,6 +173,46 @@ def local_path(path):
"defer": True,
},
},
"scripts_crossorigin": {
"source_filenames": (
"pipeline/js/first.js",
"pipeline/js/second.js",
"pipeline/js/application.js",
"pipeline/templates/**/*.jst",
),
"output_filename": "scripts_crossorigin.js",
"crossorigin": "anonymous",
},
"scripts_sri_sha256": {
"source_filenames": (
"pipeline/js/first.js",
"pipeline/js/second.js",
"pipeline/js/application.js",
"pipeline/templates/**/*.jst",
),
"output_filename": "scripts_sha256.js",
"integrity": "sha256",
},
"scripts_sri_sha384": {
"source_filenames": (
"pipeline/js/first.js",
"pipeline/js/second.js",
"pipeline/js/application.js",
"pipeline/templates/**/*.jst",
),
"output_filename": "scripts_sha384.js",
"integrity": "sha384",
},
"scripts_sri_sha512": {
"source_filenames": (
"pipeline/js/first.js",
"pipeline/js/second.js",
"pipeline/js/application.js",
"pipeline/templates/**/*.jst",
),
"output_filename": "scripts_sha512.js",
"integrity": "sha512",
},
},
}

Expand Down
Loading
Loading