Repository navigation
Conversation
matthiask
left a comment
There was a problem hiding this comment.
One general comment: The hash cache might have to hook into the autoreloader in development, or not do any caching at all, because hashes might change all the time there when editing static files.
RealOrangeOne
left a comment
There was a problem hiding this comment.
As the author of django-sri, I absolutely love this! Great work all! I had to do some bodges to get this working 3rd party, so built in avoids a lot of that.
| ```python | ||
| class Storage: | ||
| def integrity(self, path) -> str: | ||
| """Return 1..N space-separated <algorithm>-<base64-digest> pairs.""" |
There was a problem hiding this comment.
Thought: should the storage return an intermediary format, for easier internal use? Eg a dict? Otherwise it couples the storage API to the HTML format
There was a problem hiding this comment.
We discussed this at the sprint. My initial hunch was to use a dict for this with the algorithm as the key and the hash as the value. The problem is that SRI allows using multiple hashes with the same algorithm. Which means either using a list of tuples or a multi value dict or something like this.
It seems not worth it given that people can just do hashes.split() when they really want to look at individual values. The format is easy to parse after all.
There was a problem hiding this comment.
Yes, we argued that except for the enthusiastic third-party vendors like us, nobody will care about what's in the string as long as it's a valid policy that can be dropped into the browser. 🤷
There was a problem hiding this comment.
multiple hashes with the same algorithm
Those will always be duplicates though right? If the algorithm is the same, so will the hash, so duplicate entries in the dict wouldn't make sense.
Being able to access a file's "integrity" feels like a useful concept for reasons outside SRI. Coupling the implementation to SRI (the initial implementation) sounds like it hinders flexibility.
There was a problem hiding this comment.
multiple hashes with the same algorithm
Those will always be duplicates though right? If the algorithm is the same, so will the hash, so duplicate entries in the
dictwouldn't make sense.
I believe not. You can deliver different files to different clients, for example when doing rolling deployments or something like that, and include the hashes of all possible variants.
There was a problem hiding this comment.
They don't have to be identical. A server may service different resources based on the request. For example, it could vary on User-Agent for polyfills. So Interator[tuple[str,str]] just like an HTTP header.
This is also now documented on MDN correctly. We updated the example to reflect the duplication, see also https://developer.mozilla.org/en-US/docs/Web/Security/Defenses/Subresource_Integrity#setting_the_integrity_attribute
There was a problem hiding this comment.
I keep just assuming static files are immutable 😅 Ok that does make sense. In that case, returning the Iterable[tuple[str, str]] feels better to me, so the format isn't tied to the browser representation.
There was a problem hiding this comment.
Since I don't want users to pipe this through a method to render it in a template, I'd suggest we add explicit types that allow you to access them individually, but str renders the integrity metadata according to spec, see also: https://www.w3.org/TR/sri-2/#integrity-metadata
So we get the best of both worlds :)
There was a problem hiding this comment.
I extended the draft to use stringable objects, which store the raw data. Please review. Its certainly more complicated, but a normal user shouldn't notice.
| ## Specification | ||
|
|
||
| ### Phase 1: Providing integrity digests | ||
|
|
There was a problem hiding this comment.
Question: Whilst the API will ship, do you imagine all built-in storage back ends implementing this in phase 1?
There was a problem hiding this comment.
Yes, that was the idea. And to my knowledge, whitenoise inherits from Django's manifest backend. And the hashing methods could be reused by 3rd-party vendors.
cc @jschneier
| - `check_integrity_policy_settings()` reports an `Error` when a setting is not a | ||
| mapping, or when `build_policy()` rejects it. `manage.py check` catches a bad | ||
| policy instead of a request. | ||
| - `check_integrity_policy_debug()` warns when a policy is enforced with `DEBUG` |
There was a problem hiding this comment.
Question: Why is this a bad thing worth warning about? If cached properly, it should work fine. Otherwise the warning implies it can't (or shouldn't) be tested locally
|
|
||
| ### Adoption | ||
|
|
||
| 1. Serve static files from a storage that can produce integrity digests, or run |
There was a problem hiding this comment.
if the storage pre-computes them
Question: will Django in this phase? This will need a manifest version rev (and bump its size a fair bit)
| CORS-enabled request, and browsers refuse a `no-cors` request that carries | ||
| integrity metadata.[^cors] So `integrity` without `crossorigin` blocks the | ||
| file. Does the render path add `crossorigin` when `STATIC_URL` has a host, or | ||
| does the project pass it per asset? |
There was a problem hiding this comment.
Question: can't crossorigin=anonymous be added by default? That should be a safe default
| a client on slow hardware stays free to take the short digest. Third-party | ||
| storages can return any number. | ||
|
|
||
| Attributes on by default. A feature behind an opt-in setting stays unused, and the |
There was a problem hiding this comment.
Question: is the intended use that people who don't want attributes output override the storage backend and return no integrity data?
|
FYI https://bugs.webkit.org/show_bug.cgi?id=326341, Safari has a bug and ignores the integrity hashes in importmaps when trying to load ES modules. Speaks for the poor adoption and confirms that we really need to push this. |
| class IntegrityMetadataList(list[IntegrityMetadata]): | ||
|
|
||
| def __str__(self): | ||
| return " ".join(str(metadata) for metadata in self) | ||
|
|
||
| def __html__(self): | ||
| return str(self) |
There was a problem hiding this comment.
Question: Why the extra class for the collection? It feels like a lot of extra code to avoid a few " ".join(str(metadata) for metadata in self) in a few places.
Ref django/new-features#125
Product of Djanog on the Med 2026