# CSP evaluator and generator

> Grade a Content-Security-Policy you paste or fetch from a URL, or build a nonce-, hash- or allowlist-based policy in your browser.

Source: https://docs.vigilbase.com/tools/csp

Two tools help with Content-Security-Policy (CSP). The [CSP Evaluator](https://vigilbase.com/tools/csp-evaluator) grades an existing policy and explains each finding. The [CSP Generator](https://vigilbase.com/tools/csp-generator) builds a new policy and grades it as you go. Both use the same rules.

## Evaluate a policy

You can give the evaluator a policy in two ways:

- **Paste it.** The policy is evaluated in your browser and nothing is sent to Vigilbase.
- **Fetch it from a URL.** Vigilbase runs the [Security headers](https://docs.vigilbase.com/tools/security-headers) scan on the URL and evaluates the CSP it returns. You can then email yourself the Security Headers report.

When a site sends both an enforced policy and a `Content-Security-Policy-Report-Only` policy, only the enforced one is graded; the Report-Only policy is listed for reference. A Report-Only policy on its own is graded as if it were enforced, with a note that it blocks nothing.

### How the evaluator grades

The score starts at 100 and each finding deducts points by severity:

| Severity | Points |
| --- | --- |
| Critical | 50 |
| High | 25 |
| Medium | 10 |
| Low | 5 |
| Info | 0 |

| Grade | Score |
| --- | --- |
| A+ | 95-100 |
| A | 85-94 |
| B | 70-84 |
| C | 55-69 |
| D | 40-54 |
| F | 0-39 |

A+ and A are **Secure**, B and C are **Warnings**, and D and F are **Vulnerable**.

> [!NOTE]
> These bands are stricter than the [Security headers](https://docs.vigilbase.com/tools/security-headers) grade, which grades all headers together.

### Main findings

| Finding | Severity | Fix |
| --- | --- | --- |
| Scripts are not restricted | Critical | Add a `script-src` directive, ideally nonce- or hash-based with `'strict-dynamic'`. |
| 'unsafe-inline' allows inline scripts | High | Remove `'unsafe-inline'` and allow your own inline scripts with a per-response nonce or their hashes. |
| Inline event handlers are allowed | High | Move event handlers into script files, or allow specific handlers with `'unsafe-hashes'` and their hashes. |
| Scripts can load from any host | High | Replace wildcards and schemes with a nonce or hashes plus `'strict-dynamic'`. |
| 'unsafe-eval' allows eval() | Medium | Remove `'unsafe-eval'` and refactor code that builds scripts from strings. For WebAssembly, `'wasm-unsafe-eval'` is enough. |
| Scripts allowed over plain HTTP | Medium | Load scripts over HTTPS only. |
| Scripts rely on a host allowlist | Medium | Move to a nonce- or hash-based policy with `'strict-dynamic'`. |
| object-src is not 'none' | Low | Set `object-src 'none'` unless your pages embed plugin content. |
| base-uri is missing | Medium | Add `base-uri 'none'`, or `base-uri 'self'` if your pages use a `<base>` tag. |
| frame-ancestors is missing | Low | Set `frame-ancestors 'none'` or `'self'`. |
| Directive ignored in a meta tag | Medium | Send the directive in the `Content-Security-Policy` response header. |

Informational findings, such as **Report-only policy** or **No violation reporting**, explain the policy without changing the score.

## Build a policy

The generator runs entirely in your browser.

:::steps
1. **Choose how scripts are trusted.** **Nonce with 'strict-dynamic' (recommended)** suits pages generated per request. **Hashes with 'strict-dynamic'** suits static pages whose inline scripts never change; paste each script to get its hash. **Host allowlist** is the weakest option.
2. **Add the hosts your pages need.** Fill in style, image, font, connection and frame hosts. With `'strict-dynamic'`, scripts that a trusted script loads need no host entry.
3. **Set the directives with no fallback.** Choose **base-uri**, **frame-ancestors** and **form-action**, and keep **Block plugins (object-src 'none')** on.
4. **Start in Report-Only.** Turn on **Report only** and add a **Report URL**, then fix what the reports show.
5. **Copy the policy.** Copy the header or the Nginx, Apache or meta tag snippet. Replace `{RANDOM}` with a new nonce on every response.
:::

A meta tag cannot carry `frame-ancestors`, `report-uri`, `report-to` or `sandbox`, and cannot deliver a Report-Only policy, so send the policy as a response header where you can. On Cloudflare, a response header transform rule can set a hash- or allowlist-based header for a zone; a nonce has to come from your application.

The generator has no stored run, so it cannot email a report. To check the deployed policy, run [Website Security](https://docs.vigilbase.com/tools/website-security) or the evaluator's URL mode.
