|
| 1 | +--- |
| 2 | +outline: [2, 4] |
| 3 | +--- |
| 4 | +# Custom JS |
| 5 | + |
| 6 | +::: warning Not Recommended |
| 7 | +This functionality is still available, but its use is **strongly discouraged**. Injecting arbitrary JavaScript from the backend into the frontend introduces serious security risks. Only use it if you fully understand the consequences and have no alternative. |
| 8 | +::: |
| 9 | + |
| 10 | +## Why It Is a Security Risk |
| 11 | + |
| 12 | +Custom JS works by sending a JavaScript string from the ABAP backend to the frontend, where it is injected into the DOM as an HTML `<script>` tag and executed in the user's browser. This pattern is essentially a **self-inflicted Cross-Site Scripting (XSS) vector** and breaks several security assumptions UI5 normally protects you from: |
| 13 | + |
| 14 | +- **Bypasses output encoding.** UI5 escapes model data by default to prevent XSS. Raw `<script>` injection sidesteps that protection entirely. |
| 15 | +- **Executes with full user privileges.** The injected code runs in the same origin as your app and can read cookies, session tokens, the UI5 model, and any data the user has access to — and send it anywhere. |
| 16 | +- **Dynamic content is dangerous.** If any part of the injected JavaScript is built from user input, database values, translations, or other non-static sources, an attacker who controls that source can execute arbitrary code in every user's browser. |
| 17 | +- **Breaks Content Security Policy (CSP).** A strict CSP — one of the most effective defenses against XSS — typically forbids inline scripts. Custom JS forces you to weaken or disable CSP, removing that protection for the whole app. |
| 18 | +- **Hard to audit.** JavaScript assembled in ABAP strings is not covered by frontend linters, static analysis, or code review tools that normally catch dangerous patterns. |
| 19 | +- **No sandboxing.** The script has the same DOM and network access as the rest of the app. There is no isolation boundary. |
| 20 | + |
| 21 | +## Safer Alternatives |
| 22 | + |
| 23 | +Before reaching for Custom JS, consider: |
| 24 | + |
| 25 | +- Use the **standard UI5 controls and APIs** — most browser interactions are already covered. |
| 26 | +- Build a proper **[Custom Control](/advanced/extensibility/custom_control)** with a defined interface and reviewable frontend code. |
| 27 | +- Use the dedicated cookbook pages for [Clipboard](/cookbook/browser_interaction/clipboard), [Focus](/cookbook/browser_interaction/focus), [Scrolling](/cookbook/browser_interaction/scrolling), [Timer](/cookbook/browser_interaction/timer), [URL Handling](/cookbook/browser_interaction/url_handling), and similar. |
| 28 | + |
| 29 | +## How It Works (If You Still Need It) |
| 30 | + |
| 31 | +If you accept the risks and decide to use it anyway, the idea is: send the JavaScript function with the view to the frontend, then call it later when an event fires. |
| 32 | + |
| 33 | +The `_generic` method creates a custom XML/HTML element — here an HTML `<script>` tag (namespace `html`). The `_cc_plain_xml` method inserts raw content into that element — in this case, the JavaScript function definition. On the backend, `client->follow_up_action` then runs the function by name on the frontend: |
| 34 | + |
| 35 | +```abap |
| 36 | + METHOD z2ui5_if_app~main. |
| 37 | +
|
| 38 | + IF client->check_on_init( ). |
| 39 | + DATA(view) = z2ui5_cl_xml_view=>factory( ). |
| 40 | + view->_generic( name = `script` ns = `html` |
| 41 | + )->_cc_plain_xml( |
| 42 | + |function myFunction() \{ console.log( `Hello World` ); \}| |
| 43 | + ). |
| 44 | + view->page( |
| 45 | + )->button( text = `call custom JS` |
| 46 | + press = client->_event( `CUSTOM_JS` ) ). |
| 47 | + client->view_display( view->stringify( ) ). |
| 48 | + ENDIF. |
| 49 | +
|
| 50 | + IF client->get( )-event = `CUSTOM_JS`. |
| 51 | + client->follow_up_action( `myFunction()` ). |
| 52 | + ENDIF. |
| 53 | +
|
| 54 | +ENDMETHOD. |
| 55 | +``` |
| 56 | + |
| 57 | +::: danger Never Inject Untrusted Input |
| 58 | +If you must use this, ensure the JavaScript content is **entirely static and hardcoded**. Never concatenate user input, database values, translatable texts, or any other dynamic data into the script string — doing so turns the feature into a direct XSS vulnerability. |
| 59 | +::: |
| 60 | + |
| 61 | +## Embedding JavaScript Directly in an XML View |
| 62 | + |
| 63 | +::: warning Also Not Recommended |
| 64 | +The same security considerations apply: any `<script>` element embedded in an XML view runs with full app privileges and bypasses UI5's output encoding. Prefer a [Custom Control](/advanced/extensibility/custom_control) or one of the built-in actions instead. |
| 65 | +::: |
| 66 | + |
| 67 | +If you want to look at — or hand-craft — the raw XML view that abap2UI5 produces, a `<script>` tag is placed in the `html` namespace alongside the regular UI5 controls. The view stringified by `z2ui5_cl_xml_view=>factory( )` ends up looking like this: |
| 68 | + |
| 69 | +```xml |
| 70 | +<mvc:View |
| 71 | + xmlns:mvc="sap.ui.core.mvc" |
| 72 | + xmlns="sap.m" |
| 73 | + xmlns:html="http://www.w3.org/1999/xhtml"> |
| 74 | + <html:script> |
| 75 | + function myFunction() { console.log("Hello World"); } |
| 76 | + </html:script> |
| 77 | + <Page> |
| 78 | + <Button text="call custom JS" press="..." /> |
| 79 | + </Page> |
| 80 | +</mvc:View> |
| 81 | +``` |
| 82 | + |
| 83 | +The browser parses the `html:script` element and executes its content as JavaScript at view render time. The function becomes available globally and can then be triggered from the backend via the obsolete [`follow_up_action`](/cookbook/expert_more/follow_up_action) — or, ideally, replaced entirely with a built-in `client->action( )` call. |
| 84 | + |
| 85 | +This is exactly what the `_generic` + `_cc_plain_xml` helpers shown above produce; the two approaches are equivalent. Both ship raw JavaScript from the backend to the browser, and both carry the security risks described above. Use neither unless there is genuinely no alternative. |
0 commit comments