Every value your PL/SQL writes into a page must be escaped for the place it lands, or a user's input can turn into markup or script: the classic cross-site scripting hole. Oracle APEX escapes the values it renders itself, but markup your own code builds, in a PL/SQL Dynamic Content region, a plug-in, or a generated file, is your responsibility. APEX_ESCAPE has a function for every output context, APEX_MARKDOWN turns Markdown into safe HTML, and APEX_JAVASCRIPT, APEX_CSS, and APEX_T_JAVASCRIPT_OBJECT build JavaScript and CSS for the page from PL/SQL.
This guide covers all five with tested examples and their real output.
Quick Reference
| Task | Subprogram |
|---|---|
| Escape for HTML content and attributes | APEX_ESCAPE.HTML, HTML_ATTRIBUTE, HTML_TRUNC, HTML_ALLOWLIST, STRIPHTML |
| Escape for JavaScript, JSON, CSS, regex, and LDAP | APEX_ESCAPE.JS_LITERAL, JSON, CSS_SELECTOR, REGEXP, LDAP_DN, LDAP_SEARCH_FILTER |
| Escape CSV fields | APEX_ESCAPE.CSV, SET_CSV_PARAMETERS |
| Switch HTML escaping mode | APEX_ESCAPE.SET_HTML_ESCAPING_MODE |
| Convert Markdown to HTML | APEX_MARKDOWN.TO_HTML |
| Build JavaScript values | APEX_JAVASCRIPT.ADD_ATTRIBUTE, ADD_VALUE, ESCAPE |
| Add scripts and styles to the page | APEX_JAVASCRIPT.ADD_ONLOAD_CODE, ADD_INLINE_CODE, ADD_LIBRARY; APEX_CSS.ADD, ADD_FILE |
| Build a JavaScript object with functions | APEX_T_JAVASCRIPT_OBJECT |
How to Run These Examples
The examples ran in Oracle APEX 26.1 and need nothing but the database: run them as your workspace schema in SQL Developer, SQLcl, SQL*Plus, or SQL Workshop with server output switched on. The output under each example is exactly what the database printed. The page-rendering procedures of APEX_JAVASCRIPT and APEX_CSS only have an effect while a page is being rendered, so they are shown with syntax and a plug-in snippet rather than a console run.
Escaping: APEX_ESCAPE
Pick the function by where the value ends up:
| Function | Escapes for |
|---|---|
| HTML(p_string), HTML_CLOB | Element content: &, <, >, and double quotes, plus single quotes and slashes in extended mode. |
| HTML_ATTRIBUTE(p_string), HTML_ATTRIBUTE_CLOB | Attribute values: everything except letters and digits is hex-escaped. |
| HTML_TRUNC(p_string, p_length) | Element content, truncated to a length first. |
| HTML_ALLOWLIST(p_html, p_allowlist_tags), HTML_ALLOWLIST_CLOB | Keeps the tags on an allow-list, by default basic formatting tags such as b, i, p, and lists, and escapes all others. |
| STRIPHTML(p_string) | Removes tags. |
| JS_LITERAL(p_string, p_quote), JS_LITERAL_CLOB | A quoted JavaScript string. |
| JSON(p_string), JSON_CLOB | A JSON string value, without the quotes. |
| CSV(p_string, p_quote, p_strip_html) | A CSV field, quoted, with spreadsheet formulas neutralized. |
| CSS_SELECTOR(p_string) | A CSS selector. |
| REGEXP(p_string) | A literal inside a regular expression. |
| LDAP_DN(p_string, ...), LDAP_SEARCH_FILTER(p_string, ...) | An LDAP distinguished name and search filter. |
| NOOP(p_string) | Nothing. It marks a value that needs no escaping, which helps code reviews. |
HTML Escaping
This example runs the HTML family against a value that contains a script tag and quotes:
Example:
declare
l_input varchar2(200) := 'O''Brien & Sons <script>alert("x")</script>';
begin
dbms_output.put_line('html: ' || apex_escape.html(l_input));
dbms_output.put_line('html_attribute: ' || apex_escape.html_attribute('Tent "Olive" & more'));
dbms_output.put_line('striphtml: ' || apex_escape.striphtml('<p>Order <b>ORD-12283</b> shipped</p>'));
dbms_output.put_line('html_trunc: ' || apex_escape.html_trunc(rpad('Tent & ', 30, 'x'), 12));
dbms_output.put_line('allowlist: ' || apex_escape.html_allowlist('<b>Bold</b> <img src=x onerror=alert(1)> <i>ok</i>'));
dbms_output.put_line('noop: ' || apex_escape.noop('<b>as is</b>'));
end;
/Output:
html: O'Brien & Sons <script>alert("x")</script> html_attribute: Tent "Olive" & more striphtml: Order ORD-12283 shipped html_trunc: Tent & x allowlist: <b>Bold</b> <img src=x onerror=alert(1)> <i>ok</i> noop: <b>as is</b>
HTML_ALLOWLIST is the one to use for rich text from users: it kept the bold and italic tags but neutralized the image tag with its onerror handler. HTML_TRUNC shortens and escapes in one step, and its result is still valid HTML. The browser-side equivalents are covered in the guide to apex.util templates, escaping, and debounce.
Escaping for JavaScript, JSON, CSS, Regular Expressions, and LDAP
Example:
begin
dbms_output.put_line('js_literal: ' || apex_escape.js_literal('It''s a "tent"' || chr(10) || '</script>'));
dbms_output.put_line('js_literal ("): ' || apex_escape.js_literal('It''s', p_quote => '"'));
dbms_output.put_line('json: ' || apex_escape.json('Line 1' || chr(10) || 'Tab' || chr(9) || '"quoted"'));
dbms_output.put_line('css_selector: ' || apex_escape.css_selector('order#12:new'));
dbms_output.put_line('regexp: ' || apex_escape.regexp('$4.99 (2 pcs)*'));
dbms_output.put_line('ldap_dn: ' || apex_escape.ldap_dn('Kim, Linda+Admin'));
dbms_output.put_line('ldap_filter: ' || apex_escape.ldap_search_filter('Linda*(Kim)'));
end;
/Output:
js_literal: 'It\u0027s a \u0022tent\u0022\u000A\u003C\u002Fscript\u003E'
js_literal ("): "It\u0027s"
json: Line 1\nTab\t\"quoted\"
css_selector: order\#12\:new
regexp: \$4\.99 \(2 pcs\)\*
ldap_dn: Kim\, Linda\+Admin
ldap_filter: Linda\2A\28Kim\29JS_LITERAL escapes every character that could end the string or the surrounding script as a Unicode escape, including the closing script tag, so its result is safe inside inline JavaScript. It also adds the quotes, so do not wrap the result in quotes of your own. JSON, by contrast, returns the value without quotes.
CSV, SET_CSV_PARAMETERS, GET_CSV_ENCLOSED_BY, and GET_CSV_SEPARATED_BY
CSV escapes a value for a CSV file using the session's enclosing and separator characters, which SET_CSV_PARAMETERS changes and the GET functions return. A value that starts like a spreadsheet formula gets a tab in front, so Excel does not run it.
Example:
begin
dbms_output.put_line('default: ' || apex_escape.csv('Tent, "Olive"') || ' | enclosed by ' || apex_escape.get_csv_enclosed_by
|| ', separated by ' || apex_escape.get_csv_separated_by);
dbms_output.put_line('formula: ' || apex_escape.csv('=SUM(A1:A9)')); -- spreadsheet formulas are neutralized
dbms_output.put_line('strip: ' || apex_escape.csv('<b>ORD-12283</b>', p_strip_html => true));
apex_escape.set_csv_parameters(p_enclosed_by => '''', p_separated_by => ';');
dbms_output.put_line('changed: ' || apex_escape.csv('Tent; Olive') || ' | separated by ' || apex_escape.get_csv_separated_by);
end;
/Output:
default: "Tent, ""Olive""" | enclosed by ", separated by , formula: " =SUM(A1:A9)" strip: ORD-12283 changed: 'Tent; Olive' | separated by ;
The formula protection matters more than it looks: a CSV export that includes a customer-entered value starting with = can run that formula when someone opens the file. If you build CSV files by hand, as in downloading CSV using a PL/SQL procedure, pass every field through this function.
SET_HTML_ESCAPING_MODE
Switches HTML between the extended mode, E and the default, which also escapes single quotes and slashes, and the basic mode, B. Use basic mode only for old code that depends on it.
Example:
begin
apex_escape.set_html_escaping_mode(p_mode => 'B'); -- basic: & < > " only
dbms_output.put_line('basic: ' || apex_escape.html('It''s a/b <tent>'));
apex_escape.set_html_escaping_mode(p_mode => 'E'); -- extended (the default): also ' and /
dbms_output.put_line('extended: ' || apex_escape.html('It''s a/b <tent>'));
end;
/Output:
basic: It's a/b <tent> extended: It's a/b <tent>
For escaping values passed in URLs between pages, see escaping special characters when passing parameters to another page.
Markdown: APEX_MARKDOWN
TO_HTML
Converts Markdown to HTML, the same way the Markdown Editor item and the Markdown report column type display it. Embedded HTML is escaped by default (c_embedded_html_escape), or can be kept or removed. p_extra_link_attributes adds attributes to every link.
Syntax:
apex_markdown.to_html(p_markdown in clob, p_embedded_html_mode in t_embedded_html_mode default c_embedded_html_escape,
p_softbreak in varchar2 default '<br />', p_extra_link_attributes in apex_t_varchar2 default apex_t_varchar2()) return clobExample:
declare
l_html clob;
begin
l_html := apex_markdown.to_html(
p_markdown => '## Order ORD-12283' || chr(10) || chr(10)
|| '- **Status:** shipped' || chr(10)
|| '- Track it [here](https://orbit-outfitters.example/track)' || chr(10) || chr(10)
|| '<script>alert(1)</script>',
p_extra_link_attributes => apex_t_varchar2('target', '_blank'));
dbms_output.put_line(l_html);
end;
/Output:
<h2>Order ORD-12283</h2> <ul> <li><strong>Status:</strong> shipped</li> <li>Track it <a target="_blank" href="https://orbit-outfitters.example/track">here</a></li> </ul> <script>alert(1)</script>
The script tag embedded in the Markdown came out escaped, which is what makes TO_HTML safe for user-written notes and comments. Adding target="_blank" through p_extra_link_attributes opens links in a new tab. The Markdown Editor item itself is covered in the guide to page items, types, and properties.
JavaScript and CSS from PL/SQL
APEX_JAVASCRIPT.ADD_ATTRIBUTE, ADD_VALUE, and ESCAPE
Return JavaScript values for building objects and arrays as strings, as region and item plug-ins do. ADD_ATTRIBUTE returns "name":value for strings, numbers, Booleans, and dates, through four overloads, and leaves the attribute out when the value is null. ADD_VALUE returns a value alone. Both add a trailing comma unless p_add_comma is false. ESCAPE escapes text for a JavaScript string.
Example:
declare
l_js varchar2(4000);
begin
l_js := '{' || apex_javascript.add_attribute('orderId', 2282)
|| apex_javascript.add_attribute('customer', 'Wildflower "Travel" Co.')
|| apex_javascript.add_attribute('express', true)
|| apex_javascript.add_attribute('note', cast(null as varchar2)) -- omitted: null
|| apex_javascript.add_attribute('total', 463.21, p_add_comma => false) || '}';
dbms_output.put_line(l_js);
dbms_output.put_line('[' || apex_javascript.add_value('Tents') || apex_javascript.add_value(42, false) || ']');
dbms_output.put_line('escape: ' || apex_javascript.escape('It''s "OK"'));
end;
/Output:
{"orderId":2282,"customer":"Wildflower \u0022Travel\u0022 Co.","express":true,"total":463.21}
["Tents",42]
escape: It\u0027s\u0020\u0022OK\u0022The null note attribute was left out entirely, and the last attribute passed p_add_comma false so the object closes cleanly.
APEX_JAVASCRIPT.ADD_ONLOAD_CODE, ADD_INLINE_CODE, ADD_LIBRARY, ADD_REQUIREJS, ADD_REQUIREJS_DEFINE, and ADD_JET
These procedures add JavaScript to the page being rendered, from a region or item plug-in or a PL/SQL Dynamic Content region, and have no effect at any other time. ADD_ONLOAD_CODE runs code once the page has loaded, and ADD_INLINE_CODE adds it to the page's script; with p_key, the same code is added only once however often it is called. ADD_LIBRARY loads a JavaScript file, using its minified version in production. ADD_REQUIREJS and ADD_REQUIREJS_DEFINE load RequireJS and define modules, and ADD_JET loads Oracle JET.
Syntax:
apex_javascript.add_onload_code(p_code in varchar2, p_key in varchar2 default null)
apex_javascript.add_inline_code(p_code in varchar2, p_key in varchar2 default null)
apex_javascript.add_library(p_name in varchar2, p_directory in varchar2, p_version in varchar2 default null,
p_check_to_add_minified in boolean default false, ...)A typical call from a plug-in's render function:
-- in a plug-in's render function
apex_javascript.add_onload_code(
p_code => 'orbit.initRatings(' || apex_javascript.add_value(p_region.static_id, false) || ');',
p_key => 'orbit.ratings.' || p_region.static_id);Building the plug-in around such a call is covered in building your first Oracle APEX plug-in.
APEX_CSS.ADD and ADD_FILE
Add inline CSS, or a CSS file with an optional media query, to the page being rendered. They are the CSS counterparts of ADD_INLINE_CODE and ADD_LIBRARY.
Syntax:
apex_css.add(p_css in varchar2, p_key in varchar2 default null)
apex_css.add_file(p_name in varchar2, p_directory in varchar2 default apex_application.g_image_prefix || 'css/',
p_version in varchar2 default null, p_skip_extension in boolean default false, p_media_query in varchar2 default null, ...)The ADD_3RD_PARTY_LIBRARY_FILE procedures of both packages are deprecated. For adding JavaScript and CSS declaratively instead, see the guide to JavaScript and CSS in Oracle APEX.
APEX_T_JAVASCRIPT_OBJECT
An object type that builds a JavaScript object or array. Unlike JSON, the result can contain functions and raw expressions, which widget options often need. OPEN_OBJECT, CLOSE_OBJECT, OPEN_ARRAY, and CLOSE_ARRAY build the structure. PUT adds a property and APPEND an array element, each with seven overloads for the value's data type, leaving out null values by default. PUT_FUNCTION and APPEND_FUNCTION add a function, a function body, or an expression, using apex_javascript.c_type_function, c_type_function_body, or c_type_expression. PUT_RAW and APPEND_RAW add trusted code as it is. TO_CLOB returns the result, RESET starts over, and IS_NULL tells whether it is empty.
Example:
declare
l_obj apex_t_javascript_object := apex_t_javascript_object();
begin
l_obj.open_object;
l_obj.put('orderNumber', 'ORD-12283');
l_obj.put('total', 463.21);
l_obj.put('shipped', true);
l_obj.put('note', cast(null as varchar2)); -- omitted by default
l_obj.put_function('onShip', 'apex.message.showPageSuccess("Shipped")', apex_javascript.c_type_function_body);
l_obj.put_raw('created', 'new Date(2026, 8, 23)');
l_obj.open_array('lines');
l_obj.append('Tent'); l_obj.append(2);
l_obj.append_function('return 1 + 1', apex_javascript.c_type_function_body);
l_obj.close_array;
l_obj.close_object;
dbms_output.put_line(l_obj.to_clob);
l_obj.reset;
dbms_output.put_line('after reset, is_null: ' || case when l_obj.is_null then 'yes' else 'no' end);
end;
/Output:
{"orderNumber":"ORD-12283","total":463.21,"shipped":true,"onShip":function(){
apex.message.showPageSuccess("Shipped")
},"created":new Date(2026, 8, 23),"lines":["Tent",2,function(){
return 1 + 1
}]}
after reset, is_null: yesOnly put trusted code into functions and raw values, because they are not escaped. For plain JSON with no functions, use JSON_OBJECT_T or APEX_JSON instead.
Conclusion
APEX_ESCAPE has a function for every place a value can land: HTML for element content, HTML_ATTRIBUTE for attributes, HTML_ALLOWLIST for user-written rich text, JS_LITERAL for JavaScript strings, JSON, CSV with formula protection, CSS_SELECTOR, REGEXP, and the LDAP functions. APEX_MARKDOWN.TO_HTML renders Markdown with embedded HTML escaped by default. APEX_JAVASCRIPT builds JavaScript values and adds scripts to the page being rendered, APEX_CSS does the same for styles, and APEX_T_JAVASCRIPT_OBJECT builds widget options that include functions. The rule of thumb is simple: escape at the point of output, for the context of that output.
