Developing CSP-safe applications

When the system administrator enables Enable Strict Content Security Policy (see General, Integrations, and Security tabs), the browser will refuse to execute scripts that do not match a per-response nonce, and will block runtime code generation. This page lists the patterns app authors need to avoid and what to use instead.

Why strict CSP matters

With strict CSP, the server sets script-src to 'nonce-<random>' 'strict-dynamic'. This has the following effects:

  • Inline <script> tags that the platform does not insert itself are blocked, because they have no nonce.

  • Inline event-handler attributes (onclick="…​", onchange="…​", and so on) are blocked, because CSP treats them as inline scripts.

  • eval() and new Function() are blocked.

  • 'unsafe-inline' and 'unsafe-eval' are not present, and host whitelisting via the CSP whitelist setting does not extend script-src (only default-src, img-src, frame-src, and style-src).

The platform handles its own pages (Cockpit, launchpads, login, Swagger UI, single sign-on redirect pages, and compiled apps from the App Designer) by injecting a fresh nonce into every <script> it emits. Code that you write inside app scripts, event handlers, and HTML controls runs after that and does not benefit from the nonce automatically.

Do not use inline event-handler attributes

CSP blocks onclick, onchange, oninput, and similar attributes when set in HTML.

Do not do this:

<input type="file" id="favUploader" accept=".csv" onchange="uploadFav(event)" />

Do this instead — declare the markup without the handler, then wire it up in the app’s Init script:

<input type="file" id="favUploader" accept=".csv" />
sap.n.Shell.attachBeforeDisplay(localAppID, function (data) {
    const uploader = document.getElementById("favUploader");
    if (uploader && !uploader.dataset.listenerAttached) {
        uploader.addEventListener("change", uploadFav, { once: true });
        uploader.dataset.listenerAttached = "true";
    }
});
For one-shot interactions such as a file upload input, { once: true } is recommended. It removes the listener after the first invocation and avoids double-handling if the control is re-rendered.

Do not use eval or new Function

Both are blocked. The common case is user-defined formatters in tile groups, launchpad customization, or dynamic templating.

Do not do this:

const formatter = new Function("value", "return '$' + value");

Use the platform-provided CSP-safe helper instead:

const formatter = neptune.Utils.compileFunctionGlobal(["value"], "return '$' + value");

compileFunctionGlobal produces an equivalent function in a way that the platform whitelists for strict CSP.

Do not put hand-written <script> tags into HTML controls

A <script> element that you place into the markup of an HTML control is not nonced by the platform and will be blocked. Move the code into the app’s Init script or into a dedicated event handler instead of embedding it in markup.

Inline styles are still permitted

style-src keeps 'unsafe-inline', so under strict CSP:

  • <style>…​</style> blocks and style="…​" attributes in HTML controls work normally.

  • Stylesheets loaded from external hosts still require the host to be on the CSP whitelist.

style-src 'self' 'unsafe-inline' allows inline CSS only. This is a different and much lower risk than allowing inline JavaScript: CSS cannot execute code, read cookies, or make network requests, so it is not an attack vector in the way inline scripts are. Strict CSP keeps inline styles enabled for convenience while still blocking inline scripts. For example:

<!-- Blocked: inline script execution -->
<script>alert('foo')</script>

<!-- Allowed: inline style -->
<style> .css-class { color: red } </style>

Inline styles being permitted is therefore expected and acceptable from a penetration-testing standpoint; the security-relevant restriction is on script-src, which strict CSP locks down to nonce and 'strict-dynamic'.

Third-party scripts and iframes

Adding a host to the CSP whitelist does not let its scripts run when strict CSP is enabled — script-src is locked to nonce and 'strict-dynamic'.

  • For scripts, use the 'strict-dynamic' behaviour: a trusted (nonced) script may dynamically inject another <script> element, and that script is then allowed to load further scripts regardless of host. This is the recommended pattern for loading third-party libraries at runtime.

  • For iframes, add the host to the CSP whitelist and embed the frame with a standard <iframe> tag — frames are governed by frame-src, which the whitelist does extend.

  • For images, fonts, and data fetches, add the host to the CSP whitelist.

Hosts that are always allowed

You do not need to add the following hosts to the CSP whitelist; they are included by default:

  • openui5.hana.ondemand.com, ui5.sap.com — UI5 CDN

  • template.neptune-software.com, portal.neptune-software.com, wss://portal.neptune-software.com — Neptune template gallery and Neptune DXP Portal

  • requirejs.org

  • registry.npmjs.com, registry.npmjs.org — npm package search in the Script Editor

  • www.gravatar.com

  • validator.swagger.io

Behavior differences under strict CSP

  • ABAP support in the Script Editor is disabled, because the underlying language service relies on dynamic code generation.

  • UI5 libraries are preloaded by the server rather than fetched on demand.

  • CSP is applied only to responses with Content-Type: text/html. JSON, OData, and binary endpoints are unaffected.

As of now, the Neptune DXP Marketplace does not work when strict CSP is enabled. This may change in a future release. If you need to install Marketplace items, we recommend installing them on a development system (with strict CSP disabled) and then deploying them to your production system.

Testing your changes

  • Enable Enable Strict Content Security Policy on a non-production system, restart the server, and reload the app you are working on.

  • Open the browser developer tools console. CSP violations are reported as Refused to execute inline script… or Refused to evaluate a string as JavaScript…, together with the source location of the offending code.