csp.py 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. from __future__ import annotations
  2. import collections.abc as cabc
  3. import typing as t
  4. from .structures import CallbackDict
  5. def csp_property(key: str) -> t.Any:
  6. """Return a new property object for a content security policy header.
  7. Useful if you want to add support for a csp extension in a
  8. subclass.
  9. """
  10. return property(
  11. lambda x: x._get_value(key),
  12. lambda x, v: x._set_value(key, v),
  13. lambda x: x._del_value(key),
  14. f"accessor for {key!r}",
  15. )
  16. class ContentSecurityPolicy(CallbackDict[str, str]):
  17. """Subclass of a dict that stores values for a Content Security Policy
  18. header. It has accessors for all the level 3 policies.
  19. Because the csp directives in the HTTP header use dashes the
  20. python descriptors use underscores for that.
  21. To get a header of the :class:`ContentSecuirtyPolicy` object again
  22. you can convert the object into a string or call the
  23. :meth:`to_header` method. If you plan to subclass it and add your
  24. own items have a look at the sourcecode for that class.
  25. .. versionadded:: 1.0.0
  26. Support for Content Security Policy headers was added.
  27. """
  28. base_uri: str | None = csp_property("base-uri")
  29. child_src: str | None = csp_property("child-src")
  30. connect_src: str | None = csp_property("connect-src")
  31. default_src: str | None = csp_property("default-src")
  32. font_src: str | None = csp_property("font-src")
  33. form_action: str | None = csp_property("form-action")
  34. frame_ancestors: str | None = csp_property("frame-ancestors")
  35. frame_src: str | None = csp_property("frame-src")
  36. img_src: str | None = csp_property("img-src")
  37. manifest_src: str | None = csp_property("manifest-src")
  38. media_src: str | None = csp_property("media-src")
  39. navigate_to: str | None = csp_property("navigate-to")
  40. object_src: str | None = csp_property("object-src")
  41. prefetch_src: str | None = csp_property("prefetch-src")
  42. plugin_types: str | None = csp_property("plugin-types")
  43. report_to: str | None = csp_property("report-to")
  44. report_uri: str | None = csp_property("report-uri")
  45. sandbox: str | None = csp_property("sandbox")
  46. script_src: str | None = csp_property("script-src")
  47. script_src_attr: str | None = csp_property("script-src-attr")
  48. script_src_elem: str | None = csp_property("script-src-elem")
  49. style_src: str | None = csp_property("style-src")
  50. style_src_attr: str | None = csp_property("style-src-attr")
  51. style_src_elem: str | None = csp_property("style-src-elem")
  52. worker_src: str | None = csp_property("worker-src")
  53. def __init__(
  54. self,
  55. values: cabc.Mapping[str, str] | cabc.Iterable[tuple[str, str]] | None = (),
  56. on_update: cabc.Callable[[ContentSecurityPolicy], None] | None = None,
  57. ) -> None:
  58. super().__init__(values, on_update)
  59. self.provided = values is not None
  60. def _get_value(self, key: str) -> str | None:
  61. """Used internally by the accessor properties."""
  62. return self.get(key)
  63. def _set_value(self, key: str, value: str | None) -> None:
  64. """Used internally by the accessor properties."""
  65. if value is None:
  66. self.pop(key, None)
  67. else:
  68. self[key] = value
  69. def _del_value(self, key: str) -> None:
  70. """Used internally by the accessor properties."""
  71. if key in self:
  72. del self[key]
  73. def to_header(self) -> str:
  74. """Convert the stored values into a cache control header."""
  75. from ..http import dump_csp_header
  76. return dump_csp_header(self)
  77. def __str__(self) -> str:
  78. return self.to_header()
  79. def __repr__(self) -> str:
  80. kv_str = " ".join(f"{k}={v!r}" for k, v in sorted(self.items()))
  81. return f"<{type(self).__name__} {kv_str}>"