| 95 | |
| 96 | |
| 97 | def build_policy(config, nonce=None): |
| 98 | policy = [] |
| 99 | |
| 100 | for directive, values in config.items(): |
| 101 | if values in (None, False): |
| 102 | continue |
| 103 | |
| 104 | if values is True: |
| 105 | rendered_value = "" |
| 106 | else: |
| 107 | if isinstance(values, set): |
| 108 | # Sort values for consistency, preventing cache invalidation |
| 109 | # between requests and ensuring reliable browser caching. |
| 110 | values = sorted(values) |
| 111 | elif not isinstance(values, list | tuple): |
| 112 | values = [values] |
| 113 | |
| 114 | # Replace the nonce sentinel with the actual nonce values, if the |
| 115 | # sentinel is found and a nonce is provided. Otherwise, remove it. |
| 116 | if (has_sentinel := CSP.NONCE in values) and nonce: |
| 117 | values = [f"'nonce-{nonce}'" if v == CSP.NONCE else v for v in values] |
| 118 | elif has_sentinel: |
| 119 | values = [v for v in values if v != CSP.NONCE] |
| 120 | |
| 121 | if not values: |
| 122 | continue |
| 123 | |
| 124 | rendered_value = " ".join(values) |
| 125 | |
| 126 | policy.append(f"{directive} {rendered_value}".rstrip()) |
| 127 | |
| 128 | return "; ".join(policy) |