<?xml version="1.0" encoding="UTF-8"?><feed
	xmlns="http://www.w3.org/2005/Atom"
	xmlns:thr="http://purl.org/syndication/thread/1.0"
	xml:lang="en-US"
	>
	<title type="text">Philipp Salvisberg&#039;s Blog</title>
	<subtitle type="text">Database-centric development</subtitle>

	<updated>2026-07-27T09:17:27Z</updated>

	<link rel="alternate" type="text/html" href="https://www.salvis.com/blog/" />
	<id>https://www.salvis.com/blog/feed/atom/</id>
	<link rel="self" type="application/atom+xml" href="https://www.salvis.com/blog/feed/atom/" />

	<generator uri="https://wordpress.org/" version="7.0.2">WordPress</generator>
<icon>https://www.salvis.com/blog/wp-content/uploads/2014/04/favicon.png</icon>
<link rel="hub" href="https://pubsubhubbub.appspot.com" />
<link rel="hub" href="https://pubsubhubbub.superfeedr.com" />
<link rel="hub" href="https://websubhub.com/hub" />
<link rel="self" href="https://www.salvis.com/blog/feed/atom/" />
	<entry>
		<author>
			<name>Philipp Salvisberg</name>
							<uri>http://www.salvis.com/</uri>
						</author>

		<title type="html"><![CDATA[The Return of SQLcl&#8217;s SCRIPT Command]]></title>
		<link rel="alternate" type="text/html" href="https://www.salvis.com/blog/2026/07/25/the-return-of-sqlcls-script-command/" />

		<id>https://www.salvis.com/blog/?p=21049</id>
		<updated>2026-07-27T09:17:27Z</updated>
		<published>2026-07-25T14:02:15Z</published>
		<category scheme="https://www.salvis.com/blog/" term="Oracle" /><category scheme="https://www.salvis.com/blog/" term="JavaScript" /><category scheme="https://www.salvis.com/blog/" term="SQL Developer" /><category scheme="https://www.salvis.com/blog/" term="SQLcl" />
		<summary type="html"><![CDATA[<p>Introduction The script command was introduced with SQLcl 4.2. It is based on JSR 223, the Java Scripting API, and makes it possible to run JavaScript from SQLcl. This adds client-side control flow, file access, better control of host commands and access to Java classes and JavaScript libraries. JDK 8 was the<span class="excerpt-hellip"> […]</span></p>
<p>The post <a href="https://www.salvis.com/blog/2026/07/25/the-return-of-sqlcls-script-command/">The Return of SQLcl&#8217;s SCRIPT Command</a> appeared first on <a href="https://www.salvis.com/blog">Philipp Salvisberg&#039;s Blog</a>.</p>
]]></summary>

					<content type="html" xml:base="https://www.salvis.com/blog/2026/07/25/the-return-of-sqlcls-script-command/"><![CDATA[
<h2 id="introduction" class="wp-block-heading">Introduction</h2>



<p class="wp-block-paragraph">The <code>script</code> command was introduced with SQLcl 4.2. It is based on JSR 223, the Java Scripting API, and makes it possible to run JavaScript from SQLcl. This adds client-side control flow, file access, better control of host commands and access to Java classes and JavaScript libraries. </p>



<p class="wp-block-paragraph">JDK 8 was the latest and greatest back in 2015 when Kris Rice promoted this feature on his <a href="https://krisrice.io/2015-10-13-sqlcl-oct-13th-edition/" target="_blank" rel="noreferrer noopener">blog</a>. The <code>script</code> command worked out of the box because JDK 8 included the Nashorn JavaScript engine. Nashorn was deprecated in JDK 11 and removed in JDK 15, but continues as a <a href="https://github.com/openjdk/nashorn" data-type="link" data-id="https://github.com/openjdk/nashorn" target="_blank" rel="noreferrer noopener">standalone OpenJDK library</a> for JDK 11 and newer. The current version 26.2 of SQLcl requires JDK 11 or newer, but does not include a JavaScript engine. Therefore, <code>script</code> no longer works with a modern standard JDK.</p>



<p class="wp-block-paragraph">The <a href="https://docs.oracle.com/en/database/oracle/sql-developer-command-line/26.2/sqcug/working-sqlcl.html" target="_blank" rel="noreferrer noopener">documented solution</a> is to use GraalVM and install its JavaScript engine using the command <code>gu install js</code>. However, <code>gu</code> has been removed from more recent GraalVM distributions. Applications requiring a JavaScript engine must therefore provide the necessary Maven dependencies themselves.</p>



<p class="wp-block-paragraph">Furthermore, the SQL Developer extension for VS Code comes with its own JDK 25. We cannot simply replace it with an older GraalVM JDK since some components require a JDK 25 and are therefore incompatible with older JDK versions.</p>



<p class="wp-block-paragraph">The <code>script</code> command does not work in SQLcl embedded in SQL Developer for VS Code.</p>



<p class="wp-block-paragraph">Let’s change that.</p>



<h2 id="patching-sqlcl-in-sql-developer-for-vs-code" class="wp-block-heading">Patching SQLcl in SQL Developer for VS Code</h2>



<p class="wp-block-paragraph">I experimented with various approaches. The result is <a href="https://github.com/PhilippSalvisberg/sqlcl-js-patch/blob/main/sqlcl-js-patch.sh" data-type="link" data-id="https://github.com/PhilippSalvisberg/sqlcl-js-patch" target="_blank" rel="noreferrer noopener">sqlcl-js-patch.sh</a>. The idea is to download the missing JavaScript engine and its dependencies from Maven Central and add them to an SQLcl installation. This shell script supports GraalJS and Nashorn on JDK 17 and newer.</p>



<p class="wp-block-paragraph">For SQL Developer for VS Code, I prefer Nashorn. It is compatible with the behaviour we had with JDK 8 and JDK 11 and produces no warnings when SQLcl starts.</p>



<p class="wp-block-paragraph">Close all SQLcl sessions in VS Code. Then run the following command in a macOS, Linux or Git Bash shell:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">1) Patch SQLcl with Nashorn JavaScript engine</span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>curl --fail --location --silent --show-error \
  https://raw.githubusercontent.com/PhilippSalvisberg/sqlcl-js-patch/refs/heads/main/sqlcl-js-patch.sh |
  sh -s -- add nashorn "sqlcl_home=$HOME/.vscode"</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #DCDCAA">curl</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--fail</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--location</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--silent</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--show-error</span><span style="color: #D4D4D4"> </span><span style="color: #D7BA7D">\</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #CE9178">https://raw.githubusercontent.com/PhilippSalvisberg/sqlcl-js-patch/refs/heads/main/sqlcl-js-patch.sh</span><span style="color: #D4D4D4"> |</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #DCDCAA">sh</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">-s</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">add</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">nashorn</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;sqlcl_home=</span><span style="color: #9CDCFE">$HOME</span><span style="color: #CE9178">/.vscode&quot;</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>Downloading nashorn-core-15.7.jar
Downloading asm-7.3.1.jar
Downloading asm-commons-7.3.1.jar
Downloading asm-analysis-7.3.1.jar
Downloading asm-tree-7.3.1.jar
Downloading asm-util-7.3.1.jar
No matching libraries found in /Users/phs/.vscode/extensions/oracle.sql-developer-26.2.0-darwin-arm64/dbtools/sqlcl/lib.
Installed nashorn-core-15.7.jar
Installed asm-commons-7.3.1.jar
Installed asm-7.3.1.jar
Installed asm-analysis-7.3.1.jar
Installed asm-util-7.3.1.jar
Installed asm-tree-7.3.1.jar
No nashorn.args VM option found in macOS/Linux launcher; skipped Nashorn launcher patch.
Installed nashorn libraries in /Users/phs/.vscode/extensions/oracle.sql-developer-26.2.0-darwin-arm64/dbtools/sqlcl/lib.</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">Downloading nashorn-core-15.7.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Downloading asm-7.3.1.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Downloading asm-commons-7.3.1.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Downloading asm-analysis-7.3.1.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Downloading asm-tree-7.3.1.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Downloading asm-util-7.3.1.jar</span></span>
<span class="line"><span style="color: #D4D4D4">No matching libraries found in /Users/phs/.vscode/extensions/oracle.sql-developer-26.2.0-darwin-arm64/dbtools/sqlcl/lib.</span></span>
<span class="line"><span style="color: #D4D4D4">Installed nashorn-core-15.7.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Installed asm-commons-7.3.1.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Installed asm-7.3.1.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Installed asm-analysis-7.3.1.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Installed asm-util-7.3.1.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Installed asm-tree-7.3.1.jar</span></span>
<span class="line"><span style="color: #D4D4D4">No nashorn.args VM option found in macOS/Linux launcher; skipped Nashorn launcher patch.</span></span>
<span class="line"><span style="color: #D4D4D4">Installed nashorn libraries in /Users/phs/.vscode/extensions/oracle.sql-developer-26.2.0-darwin-arm64/dbtools/sqlcl/lib.</span></span></code></pre></div>



<p class="wp-block-paragraph">The patch script locates the most recent Oracle SQL Developer extension in the <code>$HOME/.vscode/extensions</code> directory. It adds the necessary JAR files to the embedded SQLcl, whose directory structure differs from that of standalone SQLcl.</p>



<p class="wp-block-paragraph">Now start a new SQLcl session in VS Code and run:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">2) Test script command</span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>script
print('Hello from Nashorn');
/</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #9CDCFE">script</span></span>
<span class="line"><span style="color: #DCDCAA">print</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;Hello from Nashorn&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">/</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-width:calc(2 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>SQLcl: Release 26.2 Production on Sat Jul 25 13:19:46 2026

Copyright (c) 1982, 2026, Oracle.  All rights reserved.

Connected to:
Oracle AI Database 26ai Free Release 23.26.2.0.0 - Develop, Learn, and Run for Free
Version 23.26.2.0.0

Hello from Nashorn
SQL> </textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">SQLcl: Release 26.2 Production on Sat Jul 25 13:19:46 2026</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">Copyright (c) 1982, 2026, Oracle.  All rights reserved.</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">Connected to:</span></span>
<span class="line"><span style="color: #D4D4D4">Oracle AI Database 26ai Free Release 23.26.2.0.0 - Develop, Learn, and Run for Free</span></span>
<span class="line"><span style="color: #D4D4D4">Version 23.26.2.0.0</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">Hello from Nashorn</span></span>
<span class="line"><span style="color: #D4D4D4">SQL&gt; </span></span></code></pre></div>



<p class="wp-block-paragraph">This does not work when the code is executed directly in a SQL Developer worksheet. It works when you select <strong>Run in SQLcl</strong>.</p>



<p class="wp-block-paragraph">That’s it. The <code>script</code> command is back.</p>



<p class="wp-block-paragraph">Please note that updating the SQL Developer extension installs a new embedded SQLcl. In that case, run the patch command again.</p>



<h2 id="does-it-work-with-graaljs" class="wp-block-heading">Does It Work With GraalJS?</h2>



<p class="wp-block-paragraph">Yes. </p>



<p class="wp-block-paragraph">Just pass <code>graaljs</code> instead of <code>nashorn</code>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">3) Patch SQLcl with GraalJS JavaScript engine</span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>curl --fail --location --silent --show-error \
  https://raw.githubusercontent.com/PhilippSalvisberg/sqlcl-js-patch/refs/heads/main/sqlcl-js-patch.sh |
  sh -s -- add graaljs "sqlcl_home=$HOME/.vscode"</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #DCDCAA">curl</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--fail</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--location</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--silent</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--show-error</span><span style="color: #D4D4D4"> </span><span style="color: #D7BA7D">\</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #CE9178">https://raw.githubusercontent.com/PhilippSalvisberg/sqlcl-js-patch/refs/heads/main/sqlcl-js-patch.sh</span><span style="color: #D4D4D4"> |</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">  </span><span style="color: #DCDCAA">sh</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">-s</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">add</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">graaljs</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;sqlcl_home=</span><span style="color: #9CDCFE">$HOME</span><span style="color: #CE9178">/.vscode&quot;</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>Downloading collections-25.1.3.jar
Downloading icu4j-25.1.3.jar
Downloading jniutils-25.1.3.jar
Downloading js-language-25.1.3.jar
Downloading js-scriptengine-25.1.3.jar
Downloading nativeimage-25.1.3.jar
Downloading polyglot-25.1.3.jar
Downloading regex-25.1.3.jar
Downloading truffle-api-25.1.3.jar
Downloading truffle-compiler-25.1.3.jar
Downloading truffle-runtime-25.1.3.jar
Downloading word-25.1.3.jar
Downloading xz-25.1.3.jar
Deleted nashorn-core-15.7.jar
Deleted asm-commons-7.3.1.jar
Deleted asm-7.3.1.jar
Deleted asm-analysis-7.3.1.jar
Deleted asm-util-7.3.1.jar
Deleted asm-tree-7.3.1.jar
Deleted asm-commons-7.3.1.jar
Deleted asm-analysis-7.3.1.jar
Deleted asm-tree-7.3.1.jar
Deleted asm-util-7.3.1.jar
Installed collections-25.1.3.jar
Installed regex-25.1.3.jar
Installed truffle-compiler-25.1.3.jar
Installed polyglot-25.1.3.jar
Installed truffle-api-25.1.3.jar
Installed jniutils-25.1.3.jar
Installed truffle-runtime-25.1.3.jar
Installed js-language-25.1.3.jar
Installed xz-25.1.3.jar
Installed word-25.1.3.jar
Installed icu4j-25.1.3.jar
Installed js-scriptengine-25.1.3.jar
Installed nativeimage-25.1.3.jar
Installed graaljs libraries in /Users/phs/.vscode/extensions/oracle.sql-developer-26.2.0-darwin-arm64/dbtools/sqlcl/lib.</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">Downloading collections-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Downloading icu4j-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Downloading jniutils-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Downloading js-language-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Downloading js-scriptengine-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Downloading nativeimage-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Downloading polyglot-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Downloading regex-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Downloading truffle-api-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Downloading truffle-compiler-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Downloading truffle-runtime-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Downloading word-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Downloading xz-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Deleted nashorn-core-15.7.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Deleted asm-commons-7.3.1.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Deleted asm-7.3.1.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Deleted asm-analysis-7.3.1.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Deleted asm-util-7.3.1.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Deleted asm-tree-7.3.1.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Deleted asm-commons-7.3.1.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Deleted asm-analysis-7.3.1.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Deleted asm-tree-7.3.1.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Deleted asm-util-7.3.1.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Installed collections-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Installed regex-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Installed truffle-compiler-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Installed polyglot-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Installed truffle-api-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Installed jniutils-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Installed truffle-runtime-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Installed js-language-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Installed xz-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Installed word-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Installed icu4j-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Installed js-scriptengine-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Installed nativeimage-25.1.3.jar</span></span>
<span class="line"><span style="color: #D4D4D4">Installed graaljs libraries in /Users/phs/.vscode/extensions/oracle.sql-developer-26.2.0-darwin-arm64/dbtools/sqlcl/lib.</span></span></code></pre></div>



<p class="wp-block-paragraph">Let&#8217;s start a new SQLcl session in VS Code and run:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">4) Test script command</span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>script
print('Hello from GraalJS');
/</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #9CDCFE">script</span></span>
<span class="line"><span style="color: #DCDCAA">print</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&#39;Hello from GraalJS&#39;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">/</span></span></code></pre></div>



<details class="wp-block-details is-layout-flow wp-block-details-is-layout-flow"><summary>Show/hide output</summary>
<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>SQLcl: Release 26.2 Production on Sat Jul 25 12:20:22 2026

Copyright (c) 1982, 2026, Oracle.  All rights reserved.

WARNING: A restricted method in java.lang.System has been called
WARNING: java.lang.System::load has been called by com.oracle.truffle.polyglot.JDKSupport in module org.graalvm.truffle (file:/Users/phs/.vscode/extensions/oracle.sql-developer-26.2.0-darwin-arm64/dbtools/sqlcl/lib/truffle-api-25.1.3.jar)
WARNING: Use --enable-native-access=org.graalvm.truffle to avoid a warning for callers in this module
WARNING: Restricted methods will be blocked in a future release unless native access is enabled

&#91;To redirect Truffle log output to a file use one of the following options:
* '--log.file=&lt;path>' if the option is passed using a guest language launcher.
* '-Dpolyglot.log.file=&lt;path>' if the option is passed using the host Java launcher.
* Configure logging using the polyglot embedding API.&#93;
&#91;engine&#93; WARNING: The polyglot engine uses a fallback runtime that does not support runtime compilation to native code.
Execution without runtime compilation will negatively impact the guest application performance.
The following cause was found: JVMCI is not enabled for this JVM. Enable JVMCI using -XX:+EnableJVMCI.
For more information see: https://www.graalvm.org/latest/reference-manual/embed-languages/#runtime-optimization-support.
To disable this warning use the '--engine.WarnInterpreterOnly=false' option or the '-Dpolyglot.engine.WarnInterpreterOnly=false' system property.
&#91;To redirect Truffle log output to a file use one of the following options:
* '--log.file=&lt;path>' if the option is passed using a guest language launcher.
* '-Dpolyglot.log.file=&lt;path>' if the option is passed using the host Java launcher.
* Configure logging using the polyglot embedding API.&#93;
&#91;engine&#93; WARNING: The polyglot engine uses a fallback runtime that does not support runtime compilation to native code.
Execution without runtime compilation will negatively impact the guest application performance.
The following cause was found: JVMCI is not enabled for this JVM. Enable JVMCI using -XX:+EnableJVMCI.
For more information see: https://www.graalvm.org/latest/reference-manual/embed-languages/#runtime-optimization-support.
To disable this warning use the '--engine.WarnInterpreterOnly=false' option or the '-Dpolyglot.engine.WarnInterpreterOnly=false' system property.
WARNING: A terminally deprecated method in sun.misc.Unsafe has been called
WARNING: sun.misc.Unsafe::objectFieldOffset has been called by com.oracle.truffle.api.strings.TStringUnsafe (file:/Users/phs/.vscode/extensions/oracle.sql-developer-26.2.0-darwin-arm64/dbtools/sqlcl/lib/truffle-api-25.1.3.jar)
WARNING: Please consider reporting this to the maintainers of class com.oracle.truffle.api.strings.TStringUnsafe
WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release
&#91;To redirect Truffle log output to a file use one of the following options:
* '--log.file=&lt;path>' if the option is passed using a guest language launcher.
* '-Dpolyglot.log.file=&lt;path>' if the option is passed using the host Java launcher.
* Configure logging using the polyglot embedding API.&#93;
&#91;engine&#93; WARNING: The polyglot engine uses a fallback runtime that does not support runtime compilation to native code.
Execution without runtime compilation will negatively impact the guest application performance.
The following cause was found: JVMCI is not enabled for this JVM. Enable JVMCI using -XX:+EnableJVMCI.
For more information see: https://www.graalvm.org/latest/reference-manual/embed-languages/#runtime-optimization-support.
To disable this warning use the '--engine.WarnInterpreterOnly=false' option or the '-Dpolyglot.engine.WarnInterpreterOnly=false' system property.
&#91;To redirect Truffle log output to a file use one of the following options:
* '--log.file=&lt;path>' if the option is passed using a guest language launcher.
* '-Dpolyglot.log.file=&lt;path>' if the option is passed using the host Java launcher.
* Configure logging using the polyglot embedding API.&#93;
&#91;engine&#93; WARNING: The polyglot engine uses a fallback runtime that does not support runtime compilation to native code.
Execution without runtime compilation will negatively impact the guest application performance.
The following cause was found: JVMCI is not enabled for this JVM. Enable JVMCI using -XX:+EnableJVMCI.
For more information see: https://www.graalvm.org/latest/reference-manual/embed-languages/#runtime-optimization-support.
To disable this warning use the '--engine.WarnInterpreterOnly=false' option or the '-Dpolyglot.engine.WarnInterpreterOnly=false' system property.
&#91;To redirect Truffle log output to a file use one of the following options:
* '--log.file=&lt;path>' if the option is passed using a guest language launcher.
* '-Dpolyglot.log.file=&lt;path>' if the option is passed using the host Java launcher.
* Configure logging using the polyglot embedding API.&#93;
&#91;engine&#93; WARNING: The polyglot engine uses a fallback runtime that does not support runtime compilation to native code.
Execution without runtime compilation will negatively impact the guest application performance.
The following cause was found: JVMCI is not enabled for this JVM. Enable JVMCI using -XX:+EnableJVMCI.
For more information see: https://www.graalvm.org/latest/reference-manual/embed-languages/#runtime-optimization-support.
To disable this warning use the '--engine.WarnInterpreterOnly=false' option or the '-Dpolyglot.engine.WarnInterpreterOnly=false' system property.
Connected to:
Oracle AI Database 26ai Free Release 23.26.2.0.0 - Develop, Learn, and Run for Free
Version 23.26.2.0.0

&#91;To redirect Truffle log output to a file use one of the following options:
* '--log.file=&lt;path>' if the option is passed using a guest language launcher.
* '-Dpolyglot.log.file=&lt;path>' if the option is passed using the host Java launcher.
* Configure logging using the polyglot embedding API.&#93;
&#91;engine&#93; WARNING: The polyglot engine uses a fallback runtime that does not support runtime compilation to native code.
Execution without runtime compilation will negatively impact the guest application performance.
The following cause was found: JVMCI is not enabled for this JVM. Enable JVMCI using -XX:+EnableJVMCI.
For more information see: https://www.graalvm.org/latest/reference-manual/embed-languages/#runtime-optimization-support.
To disable this warning use the '--engine.WarnInterpreterOnly=false' option or the '-Dpolyglot.engine.WarnInterpreterOnly=false' system property.
Hello from GraalJS
SQL> </textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">SQLcl: Release 26.2 Production on Sat Jul 25 12:20:22 2026</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">Copyright (c) 1982, 2026, Oracle.  All rights reserved.</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">WARNING: A restricted method in java.lang.System has been called</span></span>
<span class="line"><span style="color: #D4D4D4">WARNING: java.lang.System::load has been called by com.oracle.truffle.polyglot.JDKSupport in module org.graalvm.truffle (file:/Users/phs/.vscode/extensions/oracle.sql-developer-26.2.0-darwin-arm64/dbtools/sqlcl/lib/truffle-api-25.1.3.jar)</span></span>
<span class="line"><span style="color: #D4D4D4">WARNING: Use --enable-native-access=org.graalvm.truffle to avoid a warning for callers in this module</span></span>
<span class="line"><span style="color: #D4D4D4">WARNING: Restricted methods will be blocked in a future release unless native access is enabled</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">&#91;To redirect Truffle log output to a file use one of the following options:</span></span>
<span class="line"><span style="color: #D4D4D4">* &#39;--log.file=&lt;path&gt;&#39; if the option is passed using a guest language launcher.</span></span>
<span class="line"><span style="color: #D4D4D4">* &#39;-Dpolyglot.log.file=&lt;path&gt;&#39; if the option is passed using the host Java launcher.</span></span>
<span class="line"><span style="color: #D4D4D4">* Configure logging using the polyglot embedding API.&#93;</span></span>
<span class="line"><span style="color: #D4D4D4">&#91;engine&#93; WARNING: The polyglot engine uses a fallback runtime that does not support runtime compilation to native code.</span></span>
<span class="line"><span style="color: #D4D4D4">Execution without runtime compilation will negatively impact the guest application performance.</span></span>
<span class="line"><span style="color: #D4D4D4">The following cause was found: JVMCI is not enabled for this JVM. Enable JVMCI using -XX:+EnableJVMCI.</span></span>
<span class="line"><span style="color: #D4D4D4">For more information see: https://www.graalvm.org/latest/reference-manual/embed-languages/#runtime-optimization-support.</span></span>
<span class="line"><span style="color: #D4D4D4">To disable this warning use the &#39;--engine.WarnInterpreterOnly=false&#39; option or the &#39;-Dpolyglot.engine.WarnInterpreterOnly=false&#39; system property.</span></span>
<span class="line"><span style="color: #D4D4D4">&#91;To redirect Truffle log output to a file use one of the following options:</span></span>
<span class="line"><span style="color: #D4D4D4">* &#39;--log.file=&lt;path&gt;&#39; if the option is passed using a guest language launcher.</span></span>
<span class="line"><span style="color: #D4D4D4">* &#39;-Dpolyglot.log.file=&lt;path&gt;&#39; if the option is passed using the host Java launcher.</span></span>
<span class="line"><span style="color: #D4D4D4">* Configure logging using the polyglot embedding API.&#93;</span></span>
<span class="line"><span style="color: #D4D4D4">&#91;engine&#93; WARNING: The polyglot engine uses a fallback runtime that does not support runtime compilation to native code.</span></span>
<span class="line"><span style="color: #D4D4D4">Execution without runtime compilation will negatively impact the guest application performance.</span></span>
<span class="line"><span style="color: #D4D4D4">The following cause was found: JVMCI is not enabled for this JVM. Enable JVMCI using -XX:+EnableJVMCI.</span></span>
<span class="line"><span style="color: #D4D4D4">For more information see: https://www.graalvm.org/latest/reference-manual/embed-languages/#runtime-optimization-support.</span></span>
<span class="line"><span style="color: #D4D4D4">To disable this warning use the &#39;--engine.WarnInterpreterOnly=false&#39; option or the &#39;-Dpolyglot.engine.WarnInterpreterOnly=false&#39; system property.</span></span>
<span class="line"><span style="color: #D4D4D4">WARNING: A terminally deprecated method in sun.misc.Unsafe has been called</span></span>
<span class="line"><span style="color: #D4D4D4">WARNING: sun.misc.Unsafe::objectFieldOffset has been called by com.oracle.truffle.api.strings.TStringUnsafe (file:/Users/phs/.vscode/extensions/oracle.sql-developer-26.2.0-darwin-arm64/dbtools/sqlcl/lib/truffle-api-25.1.3.jar)</span></span>
<span class="line"><span style="color: #D4D4D4">WARNING: Please consider reporting this to the maintainers of class com.oracle.truffle.api.strings.TStringUnsafe</span></span>
<span class="line"><span style="color: #D4D4D4">WARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release</span></span>
<span class="line"><span style="color: #D4D4D4">&#91;To redirect Truffle log output to a file use one of the following options:</span></span>
<span class="line"><span style="color: #D4D4D4">* &#39;--log.file=&lt;path&gt;&#39; if the option is passed using a guest language launcher.</span></span>
<span class="line"><span style="color: #D4D4D4">* &#39;-Dpolyglot.log.file=&lt;path&gt;&#39; if the option is passed using the host Java launcher.</span></span>
<span class="line"><span style="color: #D4D4D4">* Configure logging using the polyglot embedding API.&#93;</span></span>
<span class="line"><span style="color: #D4D4D4">&#91;engine&#93; WARNING: The polyglot engine uses a fallback runtime that does not support runtime compilation to native code.</span></span>
<span class="line"><span style="color: #D4D4D4">Execution without runtime compilation will negatively impact the guest application performance.</span></span>
<span class="line"><span style="color: #D4D4D4">The following cause was found: JVMCI is not enabled for this JVM. Enable JVMCI using -XX:+EnableJVMCI.</span></span>
<span class="line"><span style="color: #D4D4D4">For more information see: https://www.graalvm.org/latest/reference-manual/embed-languages/#runtime-optimization-support.</span></span>
<span class="line"><span style="color: #D4D4D4">To disable this warning use the &#39;--engine.WarnInterpreterOnly=false&#39; option or the &#39;-Dpolyglot.engine.WarnInterpreterOnly=false&#39; system property.</span></span>
<span class="line"><span style="color: #D4D4D4">&#91;To redirect Truffle log output to a file use one of the following options:</span></span>
<span class="line"><span style="color: #D4D4D4">* &#39;--log.file=&lt;path&gt;&#39; if the option is passed using a guest language launcher.</span></span>
<span class="line"><span style="color: #D4D4D4">* &#39;-Dpolyglot.log.file=&lt;path&gt;&#39; if the option is passed using the host Java launcher.</span></span>
<span class="line"><span style="color: #D4D4D4">* Configure logging using the polyglot embedding API.&#93;</span></span>
<span class="line"><span style="color: #D4D4D4">&#91;engine&#93; WARNING: The polyglot engine uses a fallback runtime that does not support runtime compilation to native code.</span></span>
<span class="line"><span style="color: #D4D4D4">Execution without runtime compilation will negatively impact the guest application performance.</span></span>
<span class="line"><span style="color: #D4D4D4">The following cause was found: JVMCI is not enabled for this JVM. Enable JVMCI using -XX:+EnableJVMCI.</span></span>
<span class="line"><span style="color: #D4D4D4">For more information see: https://www.graalvm.org/latest/reference-manual/embed-languages/#runtime-optimization-support.</span></span>
<span class="line"><span style="color: #D4D4D4">To disable this warning use the &#39;--engine.WarnInterpreterOnly=false&#39; option or the &#39;-Dpolyglot.engine.WarnInterpreterOnly=false&#39; system property.</span></span>
<span class="line"><span style="color: #D4D4D4">&#91;To redirect Truffle log output to a file use one of the following options:</span></span>
<span class="line"><span style="color: #D4D4D4">* &#39;--log.file=&lt;path&gt;&#39; if the option is passed using a guest language launcher.</span></span>
<span class="line"><span style="color: #D4D4D4">* &#39;-Dpolyglot.log.file=&lt;path&gt;&#39; if the option is passed using the host Java launcher.</span></span>
<span class="line"><span style="color: #D4D4D4">* Configure logging using the polyglot embedding API.&#93;</span></span>
<span class="line"><span style="color: #D4D4D4">&#91;engine&#93; WARNING: The polyglot engine uses a fallback runtime that does not support runtime compilation to native code.</span></span>
<span class="line"><span style="color: #D4D4D4">Execution without runtime compilation will negatively impact the guest application performance.</span></span>
<span class="line"><span style="color: #D4D4D4">The following cause was found: JVMCI is not enabled for this JVM. Enable JVMCI using -XX:+EnableJVMCI.</span></span>
<span class="line"><span style="color: #D4D4D4">For more information see: https://www.graalvm.org/latest/reference-manual/embed-languages/#runtime-optimization-support.</span></span>
<span class="line"><span style="color: #D4D4D4">To disable this warning use the &#39;--engine.WarnInterpreterOnly=false&#39; option or the &#39;-Dpolyglot.engine.WarnInterpreterOnly=false&#39; system property.</span></span>
<span class="line"><span style="color: #D4D4D4">Connected to:</span></span>
<span class="line"><span style="color: #D4D4D4">Oracle AI Database 26ai Free Release 23.26.2.0.0 - Develop, Learn, and Run for Free</span></span>
<span class="line"><span style="color: #D4D4D4">Version 23.26.2.0.0</span></span>
<span class="line"><span style="color: #D4D4D4"></span></span>
<span class="line"><span style="color: #D4D4D4">&#91;To redirect Truffle log output to a file use one of the following options:</span></span>
<span class="line"><span style="color: #D4D4D4">* &#39;--log.file=&lt;path&gt;&#39; if the option is passed using a guest language launcher.</span></span>
<span class="line"><span style="color: #D4D4D4">* &#39;-Dpolyglot.log.file=&lt;path&gt;&#39; if the option is passed using the host Java launcher.</span></span>
<span class="line"><span style="color: #D4D4D4">* Configure logging using the polyglot embedding API.&#93;</span></span>
<span class="line"><span style="color: #D4D4D4">&#91;engine&#93; WARNING: The polyglot engine uses a fallback runtime that does not support runtime compilation to native code.</span></span>
<span class="line"><span style="color: #D4D4D4">Execution without runtime compilation will negatively impact the guest application performance.</span></span>
<span class="line"><span style="color: #D4D4D4">The following cause was found: JVMCI is not enabled for this JVM. Enable JVMCI using -XX:+EnableJVMCI.</span></span>
<span class="line"><span style="color: #D4D4D4">For more information see: https://www.graalvm.org/latest/reference-manual/embed-languages/#runtime-optimization-support.</span></span>
<span class="line"><span style="color: #D4D4D4">To disable this warning use the &#39;--engine.WarnInterpreterOnly=false&#39; option or the &#39;-Dpolyglot.engine.WarnInterpreterOnly=false&#39; system property.</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">Hello from GraalJS</span></span>
<span class="line"><span style="color: #D4D4D4">SQL&gt; </span></span></code></pre></div>
</details>



<p class="wp-block-paragraph">After several warnings, the expected result appears at the end.</p>



<p class="wp-block-paragraph">I explained the <a href="https://github.com/PhilippSalvisberg/sqlcl-js-patch/tree/main#graaljs-related-warnings" target="_blank" rel="noreferrer noopener">GraalJS-related warnings</a> in my GitHub repository. The following environment setting will suppress most of the warnings. Where you define this variable depends on your operating system and shell. In any case, the environment variable must be visible before you start VS Code.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">5) Non-breaking JDK_JAVA_OPTIONS to suppress warnings</span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>export JDK_JAVA_OPTIONS="-Dpolyglot.engine.WarnInterpreterOnly=false -Dpolyglot.js.nashorn-compat=true"</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">export</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">JDK_JAVA_OPTIONS</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;-Dpolyglot.engine.WarnInterpreterOnly=false -Dpolyglot.js.nashorn-compat=true&quot;</span></span></code></pre></div>



<p class="wp-block-paragraph">If you are only using JDK 24 or newer on your system, you can disable all warnings as follows. However, be aware that any JDK version below 24 will no longer work, because the Java runtime stops with an error when passing unknown JVM arguments.</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">6) Breaking JDK_JAVA_OPTIONS to suppress warnings</span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>export JDK_JAVA_OPTIONS="--illegal-native-access=allow --sun-misc-unsafe-memory-access=allow -Dpolyglot.engine.WarnInterpreterOnly=false -Dpolyglot.js.nashorn-compat=true"</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">export</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">JDK_JAVA_OPTIONS</span><span style="color: #D4D4D4">=</span><span style="color: #CE9178">&quot;--illegal-native-access=allow --sun-misc-unsafe-memory-access=allow -Dpolyglot.engine.WarnInterpreterOnly=false -Dpolyglot.js.nashorn-compat=true&quot;</span></span></code></pre></div>



<h2 id="can-i-also-use-this-for-standalone-sqlcl" class="wp-block-heading">Can I Also Use This for Standalone SQLcl?</h2>



<p class="wp-block-paragraph">Yes.</p>



<p class="wp-block-paragraph">Pass the SQLcl installation directory instead of the VS Code configuration directory:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">7) Patch standalone SQLcl with Nashorn JavaScript engine (macOS/Linux only)</span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>curl --fail --location --silent --show-error \
  https://raw.githubusercontent.com/PhilippSalvisberg/sqlcl-js-patch/refs/heads/main/sqlcl-js-patch.sh |
  sh -s -- add nashorn "sqlcl_home=/path/to/sqlcl"</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #DCDCAA">curl</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--fail</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--location</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--silent</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--show-error</span><span style="color: #D4D4D4"> </span><span style="color: #D7BA7D">\</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #CE9178">https://raw.githubusercontent.com/PhilippSalvisberg/sqlcl-js-patch/refs/heads/main/sqlcl-js-patch.sh</span><span style="color: #D4D4D4"> |</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">  </span><span style="color: #DCDCAA">sh</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">-s</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">add</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">nashorn</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;sqlcl_home=/path/to/sqlcl&quot;</span></span></code></pre></div>



<p class="wp-block-paragraph">This works very well on macOS and Linux. Besides installing the Nashorn libraries, the patch removes the following option from the <code>bin/sql</code> launcher:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>-Dnashorn.args=--no-deprecation-warning</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">-</span><span style="color: #9CDCFE">Dnashorn</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">args</span><span style="color: #D4D4D4">=--no-deprecation-warning</span></span></code></pre></div>



<p class="wp-block-paragraph">The standalone Nashorn engine does not support this option. It throws an exception while the engine is initialised, and SQLcl consequently reports <code>JavaScript engine not found.</code></p>



<p class="wp-block-paragraph">On Windows, <code>bin/sql.exe</code> is a binary executable. The patch script cannot modify it. It can add the Nashorn libraries when run from Git Bash, but it cannot apply the necessary launcher workaround. Therefore, Nashorn does not work with standalone SQLcl on Windows. GraalJS does not need this launcher change and is therefore the only option supported by the patch script on Windows:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">8) Patch standalone SQLcl with GraalJS JavaScript engine</span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>curl --fail --location --silent --show-error \
  https://raw.githubusercontent.com/PhilippSalvisberg/sqlcl-js-patch/refs/heads/main/sqlcl-js-patch.sh |
  sh -s -- add graaljs "sqlcl_home=/path/to/sqlcl"</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #DCDCAA">curl</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--fail</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--location</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--silent</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--show-error</span><span style="color: #D4D4D4"> </span><span style="color: #D7BA7D">\</span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #CE9178">https://raw.githubusercontent.com/PhilippSalvisberg/sqlcl-js-patch/refs/heads/main/sqlcl-js-patch.sh</span><span style="color: #D4D4D4"> |</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">  </span><span style="color: #DCDCAA">sh</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">-s</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">add</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">graaljs</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;sqlcl_home=/path/to/sqlcl&quot;</span></span></code></pre></div>



<p class="wp-block-paragraph">This raises an interesting question. SQL Developer for VS Code also includes a Windows <code>bin/sql.exe</code> that the patch script cannot modify. So why does Nashorn work there?</p>



<h2 id="but-why-does-nashorn-work-in-embedded-sqlcl" class="wp-block-heading">But Why Does Nashorn Work in Embedded SQLcl?</h2>



<p class="wp-block-paragraph">I mentioned before that the embedded SQLcl version has a different folder structure (there is no <code>lib/ext</code> folder for third-party libraries). But that&#8217;s not the only difference. </p>



<p class="wp-block-paragraph">The launcher is also different. Neither <code>bin/sql</code> nor <code>bin/sql.exe</code> includes the <code>-Dnashorn.args=--no-deprecation-warning</code> JVM option.</p>



<p class="wp-block-paragraph"> As a result, the Nashorn JavaScript engine works in the embedded SQLcl version of SQL Developer for VS Code.</p>



<h2 id="outlook" class="wp-block-heading">Outlook</h2>



<p class="wp-block-paragraph">I hope this helps you bring the SQLcl <code>script</code> command back to life in your environments, especially in SQL Developer for VS Code, where using another Java runtime is not a practical option.</p>



<p class="wp-block-paragraph">However, I also hope that we will not have to run <code>sqlcl-js-patch.sh</code> for every new SQLcl or SQL Developer extension version until the end of time. The patch consists mainly of adding libraries that are available from Maven Central. It would be great if the Oracle SQL Developer team included these libraries in a future update and made the <code>script</code> command work out of the box again.</p>



<p class="wp-block-paragraph"><em>Updated on 2026-07-27, mentioned OpenJDK Nashorn in paragraph 2</em>.</p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://www.salvis.com/blog/2026/07/25/the-return-of-sqlcls-script-command/">The Return of SQLcl&#8217;s SCRIPT Command</a> appeared first on <a href="https://www.salvis.com/blog">Philipp Salvisberg&#039;s Blog</a>.</p>
]]></content>
		
					<link rel="replies" type="text/html" href="https://www.salvis.com/blog/2026/07/25/the-return-of-sqlcls-script-command/#comments" thr:count="2" />
			<link rel="replies" type="application/atom+xml" href="https://www.salvis.com/blog/2026/07/25/the-return-of-sqlcls-script-command/feed/atom/" thr:count="2" />
			<thr:total>2</thr:total>
			</entry>
		<entry>
		<author>
			<name>Philipp Salvisberg</name>
							<uri>http://www.salvis.com/</uri>
						</author>

		<title type="html"><![CDATA[Detecting Security Vulnerabilities With the APEXlang Parser]]></title>
		<link rel="alternate" type="text/html" href="https://www.salvis.com/blog/2026/06/29/detecting-security-vulnerabilities-with-the-apexlang-parser/" />

		<id>https://www.salvis.com/blog/?p=19914</id>
		<updated>2026-07-01T22:57:34Z</updated>
		<published>2026-06-29T12:12:21Z</published>
		<category scheme="https://www.salvis.com/blog/" term="Oracle" /><category scheme="https://www.salvis.com/blog/" term="ANTLR" /><category scheme="https://www.salvis.com/blog/" term="APEXlang" /><category scheme="https://www.salvis.com/blog/" term="Code Analysis" /><category scheme="https://www.salvis.com/blog/" term="dbLinter" />
		<summary type="html"><![CDATA[<p>Introduction A parser for the APEXlang grammar enables the development of tools beyond the scope of the APEXlang compiler included in SQLcl, SQL Developer for VS Code, and ORDS. These tools can perform static code analysis, convert code, generate documentation, and visualise different aspects of an APEX application. A linter, for example,<span class="excerpt-hellip"> […]</span></p>
<p>The post <a href="https://www.salvis.com/blog/2026/06/29/detecting-security-vulnerabilities-with-the-apexlang-parser/">Detecting Security Vulnerabilities With the APEXlang Parser</a> appeared first on <a href="https://www.salvis.com/blog">Philipp Salvisberg&#039;s Blog</a>.</p>
]]></summary>

					<content type="html" xml:base="https://www.salvis.com/blog/2026/06/29/detecting-security-vulnerabilities-with-the-apexlang-parser/"><![CDATA[
<h2 id="introduction" class="wp-block-heading">Introduction</h2>



<p class="wp-block-paragraph">A parser for the APEXlang grammar enables the development of tools beyond the scope of the APEXlang compiler included in SQLcl, SQL Developer for VS Code, and ORDS. These tools can perform static code analysis, convert code, generate documentation, and visualise different aspects of an APEX application.</p>



<p class="wp-block-paragraph">A linter, for example, can check whether an APEXlang file conforms to defined quality standards. These standards may cover project- or company-specific conventions as well as more general concerns, such as detecting potential security vulnerabilities.</p>



<p class="wp-block-paragraph">In this blog post, I explain the elements of an APEXlang file that are the basis of Grisselbav&#8217;s <a href="https://github.com/Grisselbav/APEXlang-Parser" type="link" id="https://github.com/Grisselbav/APEXlang-Parser" target="_blank" rel="noreferrer noopener">APEXlang parser</a>. Then I demonstrate how to use the parser in a simple Java program to detect a security vulnerability.</p>



<h2 id="elements-of-an-apexlang-file" class="wp-block-heading">Elements of an APEXlang File</h2>



<p class="wp-block-paragraph">An APEXlang file consists of three basic building blocks: components, properties, and groups. These elements can also be nested, as shown later.</p>



<p class="wp-block-paragraph">Here&#8217;s an excerpt of Oracle&#8217;s Universal Theme demo application.apx file:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">1) Excerpt of UT&#8217;s application.apx</span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>app UT (
    name: Universal Theme 26.1 Reference
    version: 26.1.0
    group: @universal-theme
    logo {
        type: text
        text: Universal Theme
    }
    // ...removed code...
)</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">app</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">UT</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">    name: </span><span style="color: #569CD6">Universal</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Theme</span><span style="color: #D4D4D4"> 26.1 Reference</span></span>
<span class="line"><span style="color: #D4D4D4">    version: 26.1.0</span></span>
<span class="line"><span style="color: #D4D4D4">    group: @universal-theme</span></span>
<span class="line"><span style="color: #D4D4D4">    logo {</span></span>
<span class="line"><span style="color: #D4D4D4">        type: text</span></span>
<span class="line"><span style="color: #D4D4D4">        text: </span><span style="color: #569CD6">Universal</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Theme</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// ...removed code...</span></span>
<span class="line"><span style="color: #D4D4D4">)</span></span></code></pre></div>



<h5 id="component" class="wp-block-heading">Component</h5>



<p class="wp-block-paragraph">There is one component in this example. It starts on line 1 and ends on line 10. <code>app</code> is the type of the component and <code>UT</code> the component name.</p>



<p class="wp-block-paragraph">A component body starts with a left parenthesis <code>(</code> and ends with a right parenthesis <code>)</code>.</p>



<h5 id="property" class="wp-block-heading">Property</h5>



<p class="wp-block-paragraph">In this example, we have 5 properties. The property keys are <code>name</code>, <code>version</code>, <code>group</code>, <code>type</code> and <code>text</code>. The value of a property follows a colon <code>:</code>. For <code>name</code> The value is <code>Universal Theme 26.1 Reference</code>, for <code>version</code> the value is <code>26.1.0</code> and so on.</p>



<p class="wp-block-paragraph">Please note that a property starts on a new line, and the property value starts after the colon and ends on a new line. This way, no delimiter characters are required for most property values. However, if leading or trailing spaces are significant, you have to pass the property value as a single-line string, which is enclosed in double quotes.</p>



<h5 id="group" class="wp-block-heading">Group (of properties)</h5>



<p class="wp-block-paragraph">The properties <code>type</code> and <code>text</code> are part of a group named <code>logo</code>.  The group in this example covers lines 5 to 8.</p>



<p class="wp-block-paragraph">A group body starts with an open curly bracket <code>{</code> and ends with a close curly bracket <code>}</code>.</p>



<p class="wp-block-paragraph">Properties which are not part of a group are called direct properties. This means they are defined directly in a component. Examples of direct properties are <code>name</code>, <code>version</code> and <code>group</code>.</p>



<h2 id="nesed-elements" class="wp-block-heading">Nested Elements</h2>



<p class="wp-block-paragraph">We have seen that a component may contain properties and groups. But it is also possible to nest elements.</p>



<ul class="wp-block-list">
<li>Components may contain other components besides properties and groups</li>



<li>Property values may contain groups besides simple values</li>
</ul>



<p class="wp-block-paragraph">There is no limit to the number of levels for nested elements.</p>



<p class="wp-block-paragraph">Here&#8217;s an example:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">2) Nested Component and Group</span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>app UT (
    // ...removed code...
    pwaShortcut getting-started (
        name: Getting Started
        sequence: 10
        shortcut {
            target: {
                page: 500
            }
            description: Getting Started Page - Initial Page
        }
        comments {
            comments: -
        }
    )
   // ...removed code...
)</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">app</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">UT</span><span style="color: #D4D4D4"> (</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">// ...removed code...</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">pwaShortcut</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">getting</span><span style="color: #D4D4D4">-started (</span></span>
<span class="line"><span style="color: #D4D4D4">        name: </span><span style="color: #569CD6">Getting</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Started</span></span>
<span class="line"><span style="color: #D4D4D4">        sequence: 10</span></span>
<span class="line"><span style="color: #D4D4D4">        shortcut {</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">            target: {</span></span>
<span class="line"><span style="color: #D4D4D4">                page: 500</span></span>
<span class="line"><span style="color: #D4D4D4">            }</span></span>
<span class="line"><span style="color: #D4D4D4">            description: </span><span style="color: #569CD6">Getting</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Started</span><span style="color: #D4D4D4"> Page - </span><span style="color: #569CD6">Initial</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">Page</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">        comments {</span></span>
<span class="line"><span style="color: #D4D4D4">            comments: -</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    )</span></span>
<span class="line"><span style="color: #D4D4D4">   </span><span style="color: #6A9955">// ...removed code...</span></span>
<span class="line"><span style="color: #D4D4D4">)</span></span></code></pre></div>



<p class="wp-block-paragraph">A nested component starts on line 3 and a nested group on line 7.</p>



<h2 id="apexlang-grammar" class="wp-block-heading">APEXlang Grammar</h2>



<figure class="wp-block-image size-medium"><a href="https://grisselbav.github.io/APEXlang-Parser/grammar.html" target="_blank" rel=" noreferrer noopener"><img decoding="async" width="300" height="133" src="https://www.salvis.com/blog/wp-content/uploads/2026/06/APEXlang-apxFile-300x133.png" alt="apxFile rule of APEXlang grammar" class="wp-image-19988" srcset="https://www.salvis.com/blog/wp-content/uploads/2026/06/APEXlang-apxFile-300x133.png 300w, https://www.salvis.com/blog/wp-content/uploads/2026/06/APEXlang-apxFile-1024x453.png 1024w, https://www.salvis.com/blog/wp-content/uploads/2026/06/APEXlang-apxFile-768x340.png 768w, https://www.salvis.com/blog/wp-content/uploads/2026/06/APEXlang-apxFile-150x66.png 150w, https://www.salvis.com/blog/wp-content/uploads/2026/06/APEXlang-apxFile-480x212.png 480w, https://www.salvis.com/blog/wp-content/uploads/2026/06/APEXlang-apxFile.png 1456w" sizes="(max-width:767px) 300px, 300px" /></a></figure>



<p class="wp-block-paragraph">While the grammar documented in <a href="https://docs.oracle.com/en/database/oracle/apex/26.1/apxln/apexlang.ebnf" target="_blank" rel="noreferrer noopener">apexlang. ebnf</a> of the <a href="https://docs.oracle.com/en/database/oracle/apex/26.1/apxln/" target="_blank" rel="noreferrer noopener">API Reference</a> defines both the language structure and the valid APEXlang elements, the grammar used by Grisselbav&#8217;s APEXlang parser is limited to structural concerns. </p>



<p class="wp-block-paragraph">Because the parser focuses solely on syntax rather than semantic validation, the grammar remains remarkably compact.</p>



<p class="wp-block-paragraph">Click on the image above to view all the syntax diagrams. Alternatively, view the ANTLR4 source files, <a href="https://github.com/Grisselbav/APEXlang-Parser/blob/v0.2.0/src/main/antlr4/com/grisselbav/apexlang/grammar/ApexLangLexer.g4" target="_blank" rel="noreferrer noopener">ApexLangLexer.g4</a> and <a href="https://github.com/Grisselbav/APEXlang-Parser/blob/v0.2.0/src/main/antlr4/com/grisselbav/apexlang/grammar/ApexLangParser.g4" target="_blank" rel="noreferrer noopener">ApexLangParser.g4</a>, which were used to generate the parser available on <a href="https://central.sonatype.com/artifact/com.grisselbav/apexlang-parser" target="_blank" rel="noreferrer noopener">Maven Central</a>.</p>



<h2 id="apex-sert" class="wp-block-heading">APEX-SERT</h2>



<p class="wp-block-paragraph">APEX-SERT is an APEX application that scans a selected APEX application for security vulnerabilities. A vulnerability is detected by querying APEX dictionary views. All rules are defined in the <a href="https://github.com/oracle-samples/apex-sert/blob/v24.2.27.1/product/sert/sert_core/json_data/APEX-SERT%20Rules.json#L978-L1014" type="link" id="https://github.com/oracle-samples/apex-sert/blob/v24.2.27.1/product/sert/sert_core/json_data/APEX-SERT%20Rules.json#L978-L1014" target="_blank" rel="noreferrer noopener">APEX-SERT Rules.json</a> file. </p>



<p class="wp-block-paragraph">To demonstrate a practical use case, let’s look at the security check &#8220;Embed in Frames&#8221; implemented by APEX-SERT.</p>



<p class="wp-block-paragraph">When the property &#8220;Embed in Frames&#8221; is set to <code>Allow</code>, the application may be vulnerable to &#8220;clickjacking&#8221; attacks as explained in the help text of the page designer. See screenshot below.</p>



<figure class="wp-block-image size-large"><a href="https://www.salvis.com/blog/wp-content/uploads/2026/06/embed-in-frames-allow.png"><img fetchpriority="high" decoding="async" width="1024" height="698" src="https://www.salvis.com/blog/wp-content/uploads/2026/06/embed-in-frames-allow-1024x698.png" alt="" class="wp-image-20001" srcset="https://www.salvis.com/blog/wp-content/uploads/2026/06/embed-in-frames-allow-1024x698.png 1024w, https://www.salvis.com/blog/wp-content/uploads/2026/06/embed-in-frames-allow-300x205.png 300w, https://www.salvis.com/blog/wp-content/uploads/2026/06/embed-in-frames-allow-768x524.png 768w, https://www.salvis.com/blog/wp-content/uploads/2026/06/embed-in-frames-allow-1536x1047.png 1536w, https://www.salvis.com/blog/wp-content/uploads/2026/06/embed-in-frames-allow-2048x1396.png 2048w, https://www.salvis.com/blog/wp-content/uploads/2026/06/embed-in-frames-allow-110x75.png 110w, https://www.salvis.com/blog/wp-content/uploads/2026/06/embed-in-frames-allow-480x327.png 480w" sizes="(max-width:767px) 480px, (max-width:1024px) 100vw, 1024px" /></a></figure>



<p class="wp-block-paragraph">I changed the original value from <code>Allow from same origin</code> to <code>Allow</code> to simulate a security vulnerability.</p>



<p class="wp-block-paragraph">APEX-SERT runs a query similar to the following to find violations of this rule:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">3) Find Violations with SQL</span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>select application_id, application_name, browser_frame 
  from apex_applications
 where browser_frame not in ('Deny', 'Allow from same origin');</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">select</span><span style="color: #D4D4D4"> application_id, application_name, browser_frame </span></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #569CD6">from</span><span style="color: #D4D4D4"> apex_applications</span></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #569CD6">where</span><span style="color: #D4D4D4"> browser_frame </span><span style="color: #569CD6">not</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">in</span><span style="color: #D4D4D4"> (</span><span style="color: #CE9178">&#39;Deny&#39;</span><span style="color: #D4D4D4">, </span><span style="color: #CE9178">&#39;Allow from same origin&#39;</span><span style="color: #D4D4D4">);</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>APPLICATION_ID APPLICATION_NAME               BROWSER_FRAME
-------------- ------------------------------ -------------
        101252 Universal Theme 26.1 Reference Allow</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">APPLICATION_ID APPLICATION_NAME               BROWSER_FRAME</span></span>
<span class="line"><span style="color: #D4D4D4">-------------- ------------------------------ -------------</span></span>
<span class="line"><span style="color: #D4D4D4">        101252 Universal Theme 26.1 Reference Allow</span></span></code></pre></div>



<p class="wp-block-paragraph">So, the value of <code>Embed in Frames</code> is provided in the <code>browser_frame</code> column of the APEX dictionary view <code>apex_applications</code>.</p>



<p class="wp-block-paragraph">But where can we find this information in the APEXlang files?</p>



<h2 id="link-page-designer-property-to-apexlang" class="wp-block-heading">Link Page Designer Property to APEXlang</h2>



<p class="wp-block-paragraph">We find the &#8220;Embed in Frames&#8221; property in the page designer by navigating to &#8220;Shared Components&#8221; -&gt; &#8220;Application Definition&#8221; -&gt; &#8220;Security&#8221; -&gt; &#8220;Browser Security&#8221;. </p>



<p class="wp-block-paragraph">According to the <a href="https://docs.oracle.com/en/database/oracle/apex/26.1/apxln/apexlang.ebnf" target="_blank" rel="noreferrer noopener">apexlang. ebnf</a> there is a <code>security</code> group within the <code>app</code> component. Here&#8217;s the relevant excerpt:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-start:347;--cbp-line-number-width:calc(3 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">3) Property embedInFrames within apexlang.ebnf</span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;app-security> ::= &lt;indent> "security" &lt;ws> "{" &lt;line-end> { &lt;app-security-property-line> } &lt;indent> "}" &lt;line-end>
&lt;app-security-property-line> ::= &lt;indent> &lt;app-security-property> &lt;line-end>
&lt;app-security-property> ::= "deepLinking" ":" &lt;ws> ( "true" | "false" ) (* required; type: SELECT LIST *)
  | "enableDictation" ":" &lt;ws> &lt;boolean> (* required; type: YES NO *)
  | "browserCache" ":" &lt;ws> ( "true" | "false" ) (* required; type: SELECT LIST *)
  | "embedInFrames" ":" &lt;ws> ( "deny" | "allowSameOrigin" | "allow" ) (* required; type: SELECT LIST *)
  | "referrerPolicy" ":" &lt;ws> ( "noReferrer" | "noReferrerWhenDowngrade" | "origin" | "originWhenCrossOrigin" | "sameOrigin" | "strictOrigin" | "strictOriginWhenCrossOrigin" | "unsafeUrl" ) (* required; type: SELECT LIST *)
  | "htmlEscapingMode" ":" &lt;ws> ( "basic" | "extended" ) (* required; type: SELECT LIST *)
  | "httpResponseHeaders" ":" &lt;ws> &lt;multiline-string> (* type: TEXT EDITOR *)
  | "runtimeApiUsage" ":" &lt;ws> &lt;array-of-string-like-value> (* type: CHECKBOXES *)</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&lt;app-security&gt; ::= &lt;indent&gt; &quot;security&quot; &lt;ws&gt; &quot;</span><span style="color: #6A9955">{&quot; &lt;line-end&gt; { &lt;app-security-property-line&gt; }</span><span style="color: #D4D4D4"> &lt;indent&gt; &quot;}&quot; &lt;line-</span><span style="color: #569CD6">end</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;app-security-property-line&gt; ::= &lt;indent&gt; &lt;app-security-property&gt; &lt;line-</span><span style="color: #569CD6">end</span><span style="color: #D4D4D4">&gt;</span></span>
<span class="line"><span style="color: #D4D4D4">&lt;app-security-property&gt; ::= &quot;deepLinking&quot; &quot;:&quot; &lt;ws&gt; ( &quot;</span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">&quot; | &quot;</span><span style="color: #569CD6">false</span><span style="color: #D4D4D4">&quot; ) </span><span style="color: #6A9955">(* required; type: SELECT LIST *)</span></span>
<span class="line"><span style="color: #D4D4D4">  | &quot;enableDictation&quot; &quot;:&quot; &lt;ws&gt; &lt;</span><span style="color: #569CD6">boolean</span><span style="color: #D4D4D4">&gt; </span><span style="color: #6A9955">(* required; type: YES NO *)</span></span>
<span class="line"><span style="color: #D4D4D4">  | &quot;browserCache&quot; &quot;:&quot; &lt;ws&gt; ( &quot;</span><span style="color: #569CD6">true</span><span style="color: #D4D4D4">&quot; | &quot;</span><span style="color: #569CD6">false</span><span style="color: #D4D4D4">&quot; ) </span><span style="color: #6A9955">(* required; type: SELECT LIST *)</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">  | &quot;embedInFrames&quot; &quot;:&quot; &lt;ws&gt; ( &quot;deny&quot; | &quot;allowSameOrigin&quot; | &quot;allow&quot; ) </span><span style="color: #6A9955">(* required; type: SELECT LIST *)</span></span>
<span class="line"><span style="color: #D4D4D4">  | &quot;referrerPolicy&quot; &quot;:&quot; &lt;ws&gt; ( &quot;noReferrer&quot; | &quot;noReferrerWhenDowngrade&quot; | &quot;origin&quot; | &quot;originWhenCrossOrigin&quot; | &quot;sameOrigin&quot; | &quot;strictOrigin&quot; | &quot;strictOriginWhenCrossOrigin&quot; | &quot;unsafeUrl&quot; ) </span><span style="color: #6A9955">(* required; type: SELECT LIST *)</span></span>
<span class="line"><span style="color: #D4D4D4">  | &quot;htmlEscapingMode&quot; &quot;:&quot; &lt;ws&gt; ( &quot;basic&quot; | &quot;</span><span style="color: #569CD6">extended</span><span style="color: #D4D4D4">&quot; ) </span><span style="color: #6A9955">(* required; type: SELECT LIST *)</span></span>
<span class="line"><span style="color: #D4D4D4">  | &quot;httpResponseHeaders&quot; &quot;:&quot; &lt;ws&gt; &lt;multiline-</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4">&gt; </span><span style="color: #6A9955">(* type: TEXT EDITOR *)</span></span>
<span class="line"><span style="color: #D4D4D4">  | &quot;runtimeApiUsage&quot; &quot;:&quot; &lt;ws&gt; &lt;</span><span style="color: #569CD6">array</span><span style="color: #D4D4D4">-</span><span style="color: #569CD6">of</span><span style="color: #D4D4D4">-</span><span style="color: #569CD6">string</span><span style="color: #D4D4D4">-like-value&gt; </span><span style="color: #6A9955">(* type: CHECKBOXES *)</span></span></code></pre></div>



<p class="wp-block-paragraph">We find on line 352 the definition of the property <code>embedInFrames</code> with a list of valid values. In this case, the property key is unique. This means that no other component or group uses the property key <code>embedInFrames</code>. Therefore, we can search the folder containing the .apx files for the string <code>embedInFrames:</code> to find the position of this property. </p>



<p class="wp-block-paragraph">The matching property is located in the <span style="background-color: initial; font-family: inherit; font-size: inherit; text-align: initial;"><code>application.apx</code> file.</span></p>



<p class="wp-block-paragraph">Here&#8217;s an excerpt.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-start:46;--cbp-line-number-width:calc(2 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">4) Property embedInFrames in application.apx</span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>    authorization {
        runOnPublicPages: true
    }
    security {
        deepLinking: true
        embedInFrames: allow
        runtimeApiUsage: modifyThisApp
    }
    sessionStateProtection {
        allowUrlsCreatedAfter: 1999-08-04T00:00:00
        checksumSalt: 75BAAC4002F8CA56EF54FD242CCE7719B1AB85BE339E930260B3EC8EA3879365
    }</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">authorization</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">runOnPublicPages</span><span style="color: #D4D4D4">: </span><span style="color: #569CD6">true</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">security</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">deepLinking</span><span style="color: #D4D4D4">: </span><span style="color: #569CD6">true</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">embedInFrames</span><span style="color: #D4D4D4">: </span><span style="color: #9CDCFE">allow</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">runtimeApiUsage</span><span style="color: #D4D4D4">: </span><span style="color: #9CDCFE">modifyThisApp</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">sessionStateProtection</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">allowUrlsCreatedAfter</span><span style="color: #D4D4D4">: </span><span style="color: #B5CEA8">1999-08-04T00:00:00</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #9CDCFE">checksumSalt</span><span style="color: #D4D4D4">: 75</span><span style="color: #9CDCFE">BAAC4002F8CA56EF54FD242CCE7719B1AB85BE339E930260B3EC8EA3879365</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span></code></pre></div>



<p class="wp-block-paragraph">Please note that properties with default values are skipped when an APEX application is exported. Therefore, we would not see the <code>embedInFrames</code> property if its value were <code>Deny</code>.</p>



<h2 id="find-vulnerability-with-apexlang-parser" class="wp-block-heading">Find Vulnerability With APEXlang Parser</h2>



<p class="wp-block-paragraph">We now have all the information we need. We can write a small demo program. This program will find this vulnerability in an APEXlang file.</p>



<p class="wp-block-paragraph">The parser exposes the parse tree through ANTLR4-generated context classes. Each parser rule becomes a context class. For example, the class <code>ApexLangParser.PropertyContext</code> represents the <a href="https://github.com/Grisselbav/APEXlang-Parser/blob/v0.2.0/src/main/antlr4/com/grisselbav/apexlang/grammar/ApexLangParser.g4#L66-L68" target="_blank" rel="noreferrer noopener">property</a> parser rule. This makes it easy to traverse the <code>ApexLangDocument</code> using Java streams.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">5) DemoFindVulnerability.java</span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>//DEPS com.grisselbav:apexlang-parser:0.2.0

import com.grisselbav.apexlang.grammar.*;

class DemoFindVulnerability {
    public static void main(String[] args) {
        var apxSource = """
                app UT (
                    name: Universal Theme 26.1 Reference
                    version: 26.1.0
                    // ... removed code ...
                    authorization {
                        runOnPublicPages: true
                    }
                    security {
                        deepLinking: true
                        embedInFrames: allow
                        runtimeApiUsage: modifyThisApp
                    }
                    sessionStateProtection {
                        allowUrlsCreatedAfter: 1999-08-04T00:00:00
                        checksumSalt: 75BAAC4002F8CA56EF54FD242CCE7719B1AB85BE339E930260B3EC8EA3879365
                    }
                    // ... removed code ...
                )
                """;
        var doc = ApexLangDocument.parse(apxSource);
        var violations = doc.getAllContentsOfType(ApexLangParser.PropertyContext.class)
                .stream()
                .filter(p -> p.name.getText().equals("embedInFrames")
                        &amp;&amp; p.value().getText().equals("allow"))
                .toList();
        for (var violation : violations) {
            System.err.println("Found vulnerability: " + violation.getText());
        }
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">//DEPS com.grisselbav:apexlang-parser:0.2.0</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">import</span><span style="color: #D4D4D4"> com.grisselbav.apexlang.grammar.*;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">DemoFindVulnerability</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">static</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">void</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">main</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">String</span><span style="color: #D4D4D4">[] </span><span style="color: #9CDCFE">args</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">apxSource</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;&quot;&quot;</span></span>
<span class="line"><span style="color: #CE9178">                app UT (</span></span>
<span class="line"><span style="color: #CE9178">                    name: Universal Theme 26.1 Reference</span></span>
<span class="line"><span style="color: #CE9178">                    version: 26.1.0</span></span>
<span class="line"><span style="color: #CE9178">                    // ... removed code ...</span></span>
<span class="line"><span style="color: #CE9178">                    authorization {</span></span>
<span class="line"><span style="color: #CE9178">                        runOnPublicPages: true</span></span>
<span class="line"><span style="color: #CE9178">                    }</span></span>
<span class="line"><span style="color: #CE9178">                    security {</span></span>
<span class="line"><span style="color: #CE9178">                        deepLinking: true</span></span>
<span class="line"><span style="color: #CE9178">                        embedInFrames: allow</span></span>
<span class="line"><span style="color: #CE9178">                        runtimeApiUsage: modifyThisApp</span></span>
<span class="line"><span style="color: #CE9178">                    }</span></span>
<span class="line"><span style="color: #CE9178">                    sessionStateProtection {</span></span>
<span class="line"><span style="color: #CE9178">                        allowUrlsCreatedAfter: 1999-08-04T00:00:00</span></span>
<span class="line"><span style="color: #CE9178">                        checksumSalt: 75BAAC4002F8CA56EF54FD242CCE7719B1AB85BE339E930260B3EC8EA3879365</span></span>
<span class="line"><span style="color: #CE9178">                    }</span></span>
<span class="line"><span style="color: #CE9178">                    // ... removed code ...</span></span>
<span class="line"><span style="color: #CE9178">                )</span></span>
<span class="line"><span style="color: #CE9178">                &quot;&quot;&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">doc</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">ApexLangDocument</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">parse</span><span style="color: #D4D4D4">(apxSource);</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">violations</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">doc</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getAllContentsOfType</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">ApexLangParser</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">PropertyContext</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">                .</span><span style="color: #DCDCAA">stream</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #D4D4D4">                .</span><span style="color: #DCDCAA">filter</span><span style="color: #D4D4D4">(p </span><span style="color: #569CD6">-&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">p</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getText</span><span style="color: #D4D4D4">().</span><span style="color: #DCDCAA">equals</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;embedInFrames&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">                        &amp;&amp; </span><span style="color: #9CDCFE">p</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">value</span><span style="color: #D4D4D4">().</span><span style="color: #DCDCAA">getText</span><span style="color: #D4D4D4">().</span><span style="color: #DCDCAA">equals</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;allow&quot;</span><span style="color: #D4D4D4">))</span></span>
<span class="line"><span style="color: #D4D4D4">                .</span><span style="color: #DCDCAA">toList</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">for</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">violation</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">:</span><span style="color: #D4D4D4"> violations) {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">System</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">err</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">println</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Found vulnerability: &quot;</span><span style="color: #D4D4D4"> + </span><span style="color: #9CDCFE">violation</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getText</span><span style="color: #D4D4D4">());</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<p class="wp-block-paragraph">This program embeds the APEXlang source code to scan in the <code>apxSource</code> variable. To run the program, save it as <code>DemoFindVulnerability.java</code>, then run it with <a href="https://www.jbang.dev/" type="link" id="https://www.jbang.dev/" target="_blank" rel="noreferrer noopener">JBang</a>. This will automatically resolve the Maven dependency on the first line.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#1E1E1E"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>jbang DemoFindVulnerability.java</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #DCDCAA">jbang</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">DemoFindVulnerability.java</span></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&#91;jbang&#93; Resolving dependencies...
&#91;jbang&#93;    com.grisselbav:apexlang-parser:0.2.0
&#91;jbang&#93; Dependencies resolved
&#91;jbang&#93; Building jar for DemoFindVulnerability.java...
Found vulnerability: 
        embedInFrames: allow</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&#91;jbang&#93; Resolving dependencies...</span></span>
<span class="line"><span style="color: #D4D4D4">&#91;jbang&#93;    com.grisselbav:apexlang-parser:0.2.0</span></span>
<span class="line"><span style="color: #D4D4D4">&#91;jbang&#93; Dependencies resolved</span></span>
<span class="line"><span style="color: #D4D4D4">&#91;jbang&#93; Building jar for DemoFindVulnerability.java...</span></span>
<span class="line"><span style="color: #D4D4D4">Found vulnerability: </span></span>
<span class="line"><span style="color: #D4D4D4">        embedInFrames: allow</span></span></code></pre></div>



<h2 id="are-regular-expressions-an-alternative" class="wp-block-heading">Are Regular Expressions An Alternative?</h2>



<p class="wp-block-paragraph">In this case, we could have used a regular expression to identify the violation. </p>



<p class="wp-block-paragraph">However, as soon as the rules become more complicated, e.g. if we want to ensure that the property is part of the <code>security</code> group and the <code>security</code> group is part of the <code>app</code> component, regular expressions are no longer suited. But a parser is. Because we can navigate the parse tree and adjust the filter settings as follows.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">6) DemoFindVulnerability2.java with extended filter</span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>//DEPS com.grisselbav:apexlang-parser:0.2.0

import com.grisselbav.apexlang.grammar.*;

class DemoFindVulnerability2 {
    public static void main(String[] args) {
        var apxSource = """
                app UT (
                    name: Universal Theme 26.1 Reference
                    version: 26.1.0
                    // ... removed code ...
                    authorization {
                        runOnPublicPages: true
                    }
                    security {
                        deepLinking: true
                        embedInFrames: allow
                        runtimeApiUsage: modifyThisApp
                    }
                    sessionStateProtection {
                        allowUrlsCreatedAfter: 1999-08-04T00:00:00
                        checksumSalt: 75BAAC4002F8CA56EF54FD242CCE7719B1AB85BE339E930260B3EC8EA3879365
                    }
                    // ... removed code ...
                )
                """;
        var doc = ApexLangDocument.parse(apxSource);
        var violations = doc.getAllContentsOfType(ApexLangParser.PropertyContext.class)
                .stream()
                .filter(p -> p.name.getText().equals("embedInFrames")
                        &amp;&amp; p.value().getText().equals("allow")
                        &amp;&amp; p.parent.parent instanceof ApexLangParser.GroupContext g
                        &amp;&amp; g.name.getText().equals("security")
                        &amp;&amp; g.parent.parent.parent instanceof ApexLangParser.ComponentContext c
                        &amp;&amp; c.type.getText().equals("app"))
                .toList();
        for (var violation : violations) {
            System.err.println("Found vulnerability: " + violation.getText());
        }
    }
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">//DEPS com.grisselbav:apexlang-parser:0.2.0</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">import</span><span style="color: #D4D4D4"> com.grisselbav.apexlang.grammar.*;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">class</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">DemoFindVulnerability2</span><span style="color: #D4D4D4"> {</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">static</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">void</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">main</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">String</span><span style="color: #D4D4D4">[] </span><span style="color: #9CDCFE">args</span><span style="color: #D4D4D4">) {</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">apxSource</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">&quot;&quot;&quot;</span></span>
<span class="line"><span style="color: #CE9178">                app UT (</span></span>
<span class="line"><span style="color: #CE9178">                    name: Universal Theme 26.1 Reference</span></span>
<span class="line"><span style="color: #CE9178">                    version: 26.1.0</span></span>
<span class="line"><span style="color: #CE9178">                    // ... removed code ...</span></span>
<span class="line"><span style="color: #CE9178">                    authorization {</span></span>
<span class="line"><span style="color: #CE9178">                        runOnPublicPages: true</span></span>
<span class="line"><span style="color: #CE9178">                    }</span></span>
<span class="line"><span style="color: #CE9178">                    security {</span></span>
<span class="line"><span style="color: #CE9178">                        deepLinking: true</span></span>
<span class="line"><span style="color: #CE9178">                        embedInFrames: allow</span></span>
<span class="line"><span style="color: #CE9178">                        runtimeApiUsage: modifyThisApp</span></span>
<span class="line"><span style="color: #CE9178">                    }</span></span>
<span class="line"><span style="color: #CE9178">                    sessionStateProtection {</span></span>
<span class="line"><span style="color: #CE9178">                        allowUrlsCreatedAfter: 1999-08-04T00:00:00</span></span>
<span class="line"><span style="color: #CE9178">                        checksumSalt: 75BAAC4002F8CA56EF54FD242CCE7719B1AB85BE339E930260B3EC8EA3879365</span></span>
<span class="line"><span style="color: #CE9178">                    }</span></span>
<span class="line"><span style="color: #CE9178">                    // ... removed code ...</span></span>
<span class="line"><span style="color: #CE9178">                )</span></span>
<span class="line"><span style="color: #CE9178">                &quot;&quot;&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">doc</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">ApexLangDocument</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">parse</span><span style="color: #D4D4D4">(apxSource);</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">violations</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">doc</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getAllContentsOfType</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">ApexLangParser</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">PropertyContext</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">class</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">                .</span><span style="color: #DCDCAA">stream</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #D4D4D4">                .</span><span style="color: #DCDCAA">filter</span><span style="color: #D4D4D4">(p </span><span style="color: #569CD6">-&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">p</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getText</span><span style="color: #D4D4D4">().</span><span style="color: #DCDCAA">equals</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;embedInFrames&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">                        &amp;&amp; </span><span style="color: #9CDCFE">p</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">value</span><span style="color: #D4D4D4">().</span><span style="color: #DCDCAA">getText</span><span style="color: #D4D4D4">().</span><span style="color: #DCDCAA">equals</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;allow&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">                        &amp;&amp; </span><span style="color: #9CDCFE">p</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">parent</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">parent</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">instanceof</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">ApexLangParser</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">GroupContext</span><span style="color: #D4D4D4"> g</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">                        &amp;&amp; </span><span style="color: #9CDCFE">g</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">name</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getText</span><span style="color: #D4D4D4">().</span><span style="color: #DCDCAA">equals</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;security&quot;</span><span style="color: #D4D4D4">)</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">                        &amp;&amp; </span><span style="color: #9CDCFE">g</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">parent</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">parent</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">parent</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">instanceof</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">ApexLangParser</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">ComponentContext</span><span style="color: #D4D4D4"> c</span></span>
<span class="line cbp-line-highlight"><span style="color: #D4D4D4">                        &amp;&amp; </span><span style="color: #9CDCFE">c</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getText</span><span style="color: #D4D4D4">().</span><span style="color: #DCDCAA">equals</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;app&quot;</span><span style="color: #D4D4D4">))</span></span>
<span class="line"><span style="color: #D4D4D4">                .</span><span style="color: #DCDCAA">toList</span><span style="color: #D4D4D4">();</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #C586C0">for</span><span style="color: #D4D4D4"> (</span><span style="color: #569CD6">var</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">violation</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">:</span><span style="color: #D4D4D4"> violations) {</span></span>
<span class="line"><span style="color: #D4D4D4">            </span><span style="color: #9CDCFE">System</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">err</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">println</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Found vulnerability: &quot;</span><span style="color: #D4D4D4"> + </span><span style="color: #9CDCFE">violation</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">getText</span><span style="color: #D4D4D4">());</span></span>
<span class="line"><span style="color: #D4D4D4">        }</span></span>
<span class="line"><span style="color: #D4D4D4">    }</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>



<h2 id="integration-into-dblinter" class="wp-block-heading">Integration Into dbLinter</h2>



<p class="wp-block-paragraph">The APEX-SERT &#8220;Embed in Frames&#8221; rule has been added to the dbLinter repository as rule <a href="https://dblinter.app/ords/r/dblinter/dblinter-console/rules#P1000_SHOW_RULE=core%20a-1010" target="_blank" rel="noreferrer noopener">A-1010: Never allow application pages within an HTML frame</a>. The VS Code extension, the CLI and the SonarQube plugin now support checks implemented for APEXlang.</p>



<p class="wp-block-paragraph">Here&#8217;s a short silent video that demonstrates how this security vulnerability is detected and quickly fixed.</p>



<figure class="wp-block-video"><video height="720" style="aspect-ratio: 1280 / 720;" width="1280" controls src="https://www.salvis.com/blog/wp-content/uploads/2026/06/dblinter-a1010.mp4"></video></figure>



<h2 id="conclusion" class="wp-block-heading">Conclusion</h2>



<p class="wp-block-paragraph">The APEXlang grammar is intentionally simple through focusing on structural elements, leaving semantic validation, such as valid property values, to the APEXlang compiler in SQLcl, SQL Developer and VS Code.</p>



<p class="wp-block-paragraph">Integrating the APEXlang parser into dbLinter was straightforward, as was implementing the first APEXlang-based dbLinter rule.</p>



<p class="wp-block-paragraph">The next step is to identify which additional rules would be beneficial to bring to dbLinter. If you have any suggestions, please let me know. Even better, open a GitHub issue in the <a href="https://github.com/Grisselbav/dbLinter" target="_blank" rel="noreferrer noopener">dbLinter GitHub repository</a>. </p>



<p class="wp-block-paragraph">Thank you.</p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://www.salvis.com/blog/2026/06/29/detecting-security-vulnerabilities-with-the-apexlang-parser/">Detecting Security Vulnerabilities With the APEXlang Parser</a> appeared first on <a href="https://www.salvis.com/blog">Philipp Salvisberg&#039;s Blog</a>.</p>
]]></content>
		
		<link href="https://www.salvis.com/blog/wp-content/uploads/2026/06/dblinter-a1010.mp4" rel="enclosure" length="2752429" type="video/mp4" />
			<link rel="replies" type="text/html" href="https://www.salvis.com/blog/2026/06/29/detecting-security-vulnerabilities-with-the-apexlang-parser/#comments" thr:count="0" />
			<link rel="replies" type="application/atom+xml" href="https://www.salvis.com/blog/2026/06/29/detecting-security-vulnerabilities-with-the-apexlang-parser/feed/atom/" thr:count="0" />
			<thr:total>0</thr:total>
			</entry>
		<entry>
		<author>
			<name>Philipp Salvisberg</name>
							<uri>http://www.salvis.com/</uri>
						</author>

		<title type="html"><![CDATA[Syntax Diagrams for APEXlang]]></title>
		<link rel="alternate" type="text/html" href="https://www.salvis.com/blog/2026/06/19/syntax-diagrams-for-apexlang/" />

		<id>https://www.salvis.com/blog/?p=19515</id>
		<updated>2026-06-19T14:22:41Z</updated>
		<published>2026-06-19T14:22:37Z</published>
		<category scheme="https://www.salvis.com/blog/" term="Oracle" /><category scheme="https://www.salvis.com/blog/" term="APEXlang" /><category scheme="https://www.salvis.com/blog/" term="dbLinter" />
		<summary type="html"><![CDATA[<p>Introduction The APEXlang API Reference documentation went live a few days ago. You can also download the APEXlang EBNF grammar. In this post, Kris Rice explains the value of having an EBNF. Since the APEXlang grammar is available as EBNF, it can be used to generate syntax diagrams. These diagrams (also known<span class="excerpt-hellip"> […]</span></p>
<p>The post <a href="https://www.salvis.com/blog/2026/06/19/syntax-diagrams-for-apexlang/">Syntax Diagrams for APEXlang</a> appeared first on <a href="https://www.salvis.com/blog">Philipp Salvisberg&#039;s Blog</a>.</p>
]]></summary>

					<content type="html" xml:base="https://www.salvis.com/blog/2026/06/19/syntax-diagrams-for-apexlang/"><![CDATA[
<h2 id="introduction" class="wp-block-heading">Introduction</h2>



<p class="wp-block-paragraph">The <a href="https://docs.oracle.com/en/database/oracle/apex/26.1/apxln/" target="_blank" rel="noreferrer noopener">APEXlang API Reference documentation</a> went live a few days ago. You can also download the APEXlang EBNF grammar. In <a href="https://www.linkedin.com/pulse/apexlang-now-has-published-ebnf-grammar-heres-what-unlocks-kris-rice-5tsoc/" type="link" id="https://www.linkedin.com/pulse/apexlang-now-has-published-ebnf-grammar-heres-what-unlocks-kris-rice-5tsoc/" target="_blank" rel="noreferrer noopener">this post</a>, Kris Rice explains the value of having an EBNF.</p>



<p class="wp-block-paragraph">Since the APEXlang grammar is available as EBNF, it can be used to generate <a href="https://en.wikipedia.org/wiki/Syntax_diagram" type="link" id="https://en.wikipedia.org/wiki/Syntax_diagram" target="_blank" rel="noreferrer noopener">syntax diagrams</a>. These diagrams (also known as railroad diagrams) make the language easier to explore and provide a visual complement to the API Reference.</p>



<p class="wp-block-paragraph">In this blog post, I explain how to produce the syntax diagrams for APEXlang. </p>



<h2 id="what-is-ebnf" class="wp-block-heading">What Is EBNF</h2>



<p class="wp-block-paragraph">Wikipedia describes the <a href="https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_form" target="_blank" rel="noreferrer noopener">Extended Backus-Naur Form</a>. However, it&#8217;s important to notice that there are several variants of EBNF. </p>



<h2 id="apexlang-ebnf" class="wp-block-heading">APEXlang EBNF</h2>



<p class="wp-block-paragraph">The APEXlang EBNF grammar uses a variant of the BNF in the SQL:2023 standard, with some extensions explained at the top of the <a href="https://docs.oracle.com/en/database/oracle/apex/26.1/apxln/apexlang.ebnf" type="link" id="https://docs.oracle.com/en/database/oracle/apex/26.1/apxln/apexlang.ebnf" target="_blank" rel="noreferrer noopener">apexlang.ebnf</a> file.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);--cbp-line-highlight-color:rgba(234, 191, 191, 0.2);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">1) Syntax conventions in apexlang.ebnf</span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>(* Syntax conventions
   &lt;nl> is significant: component declarations, groups, properties, and closing delimiters are line-oriented.
   &#91; X &#93; means optional X. { X } means zero or more X. A | B means either A or B.
*)</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #6A9955">(* Syntax conventions</span></span>
<span class="line"><span style="color: #6A9955">   &lt;nl&gt; is significant: component declarations, groups, properties, and closing delimiters are line-oriented.</span></span>
<span class="line cbp-line-highlight"><span style="color: #6A9955">   &#91; X &#93; means optional X. { X } means zero or more X. A | B means either A or B.</span></span>
<span class="line"><span style="color: #6A9955">*)</span></span></code></pre></div>



<p class="wp-block-paragraph">Square brackets <code>[]</code> indicate optionality, while curly brackets <code>{}</code> enclose a repeated group as defined in <a href="https://www.cl.cam.ac.uk/~mgk25/iso-14977.pdf" target="_blank" rel="noreferrer noopener">ISO/IEC 14977</a>. However, the <code>apexlang.ebnf</code> file does not fully use this ISO EBNF standard. <code>apexlang.ebnf</code> uses <code>::=</code> while the ISO standard uses a simple <code>=</code> for symbol definitions. This variant resembles a <a href="https://en.wikipedia.org/wiki/Wirth_syntax_notation" target="_blank" rel="noreferrer noopener">Wirth EBNF</a> with comments.</p>



<p class="wp-block-paragraph">Here&#8217;s the definition of the <code>app</code> rule in the <code>apexlang.ebnf</code> file.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-start:248;--cbp-line-number-width:calc(3 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">2) app rule in apexlang.ebnf</span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&lt;app> ::= "app" &#91; &lt;required-ws> &lt;component-id> &#93; &lt;ws> "(" &lt;line-end> { &lt;app-body-line> } &lt;indent> ")" &lt;line-end></textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">&lt;</span><span style="color: #D4D4D4">app</span><span style="color: #569CD6">&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">::=</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;app&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">&#91;</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">&lt;</span><span style="color: #D4D4D4">required</span><span style="color: #569CD6">-</span><span style="color: #D4D4D4">ws</span><span style="color: #569CD6">&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">&lt;</span><span style="color: #D4D4D4">component</span><span style="color: #569CD6">-</span><span style="color: #D4D4D4">id</span><span style="color: #569CD6">&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">&#93;</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">&lt;</span><span style="color: #D4D4D4">ws</span><span style="color: #569CD6">&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;(&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">&lt;</span><span style="color: #D4D4D4">line</span><span style="color: #569CD6">-end&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">{</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">&lt;</span><span style="color: #D4D4D4">app</span><span style="color: #569CD6">-</span><span style="color: #D4D4D4">body</span><span style="color: #569CD6">-</span><span style="color: #D4D4D4">line</span><span style="color: #569CD6">&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">}</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">&lt;</span><span style="color: #D4D4D4">indent</span><span style="color: #569CD6">&gt;</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;)&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">&lt;</span><span style="color: #D4D4D4">line</span><span style="color: #569CD6">-end&gt;</span></span></code></pre></div>



<h2 id="syntax-diagram" class="wp-block-heading">Syntax Diagram</h2>



<p class="wp-block-paragraph">This is the syntax diagram, or railroad diagram, for the app rule.</p>



<figure class="wp-block-image size-full"><a href="https://www.salvis.com/blog/wp-content/uploads/2026/06/app.svg"><img wpfc-lazyload-disable="true" decoding="async" src="https://www.salvis.com/blog/wp-content/uploads/2026/06/app.svg" alt="" class="wp-image-19562"/></a></figure>



<p class="wp-block-paragraph">I produced this syntax diagram with the <a href="https://www.bottlecaps.de/rr/ui" target="_blank" rel="noreferrer noopener">Railroad Diagram Generator (RR)</a> by Gunter Rademacher. However, this tool requires a <a href="https://www.w3.org/TR/xml/#sec-notation">W3</a><a href="https://www.w3.org/TR/xml/#sec-notation" target="_blank" rel="noreferrer noopener">C EBNF</a> as input. In other words, the original <code>apexlang.ebnf</code> produces runtime errors. </p>



<h2 id="w3c-ebnf" class="wp-block-heading">W3C EBNF</h2>



<p class="wp-block-paragraph">Fortunately, it is straightforward to convert the APEXlang EBNF grammar to W3C EBNF. For the current APEXlang grammar, the conversion can be performed entirely through automated text transformations without manual edits.</p>



<p class="wp-block-paragraph">Here is a list of the most important replacement actions:</p>



<figure class="wp-block-table is-style-regular"><table><thead><tr><th class="has-text-align-left" data-align="left">Action</th><th class="has-text-align-left" data-align="left">Example From</th><th class="has-text-align-left" data-align="left">Example To</th></tr></thead><tbody><tr><td class="has-text-align-left" data-align="left">Replace multiline comments</td><td class="has-text-align-left" data-align="left"><code>(* ... *)</code></td><td class="has-text-align-left" data-align="left"><code>/* ... */</code></td></tr><tr><td class="has-text-align-left" data-align="left">Remove leading <code>&lt;</code> and trailing <code>></code> in symbol names</td><td class="has-text-align-left" data-align="left"><code>&lt;app></code></td><td class="has-text-align-left" data-align="left"><code>app</code></td></tr><tr><td class="has-text-align-left" data-align="left">Use expression and <code>?</code> to express optionality</td><td class="has-text-align-left" data-align="left"><code>[ ... ]</code></td><td class="has-text-align-left" data-align="left"><code>( ... )?</code></td></tr><tr><td class="has-text-align-left" data-align="left">Use expression and <code>*</code> for repetitions</td><td class="has-text-align-left" data-align="left"><code>{ ... }</code></td><td class="has-text-align-left" data-align="left"><code>( ... )*</code></td></tr></tbody></table></figure>



<p class="wp-block-paragraph">The rule <code>app</code> in W3C EBNF looks as follows after applying these changes:</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-start:248;--cbp-line-number-width:calc(3 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#2b2b2b;color:#c7c7c7">3) app rule in apexlang.w3c.ebnf</span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>app ::= "app" ( required-ws component-id )? ws "(" line-end ( app-body-line )* indent ")" line-end</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">app </span><span style="color: #569CD6">::=</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">&quot;app&quot;</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">(</span><span style="color: #D4D4D4"> required</span><span style="color: #569CD6">-</span><span style="color: #D4D4D4">ws component</span><span style="color: #569CD6">-</span><span style="color: #D4D4D4">id </span><span style="color: #569CD6">)</span><span style="color: #D4D4D4">? ws </span><span style="color: #CE9178">&quot;(&quot;</span><span style="color: #D4D4D4"> line</span><span style="color: #569CD6">-end</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">(</span><span style="color: #D4D4D4"> app</span><span style="color: #569CD6">-</span><span style="color: #D4D4D4">body</span><span style="color: #569CD6">-</span><span style="color: #D4D4D4">line </span><span style="color: #569CD6">)*</span><span style="color: #D4D4D4"> indent </span><span style="color: #CE9178">&quot;)&quot;</span><span style="color: #D4D4D4"> line</span><span style="color: #569CD6">-end</span></span></code></pre></div>



<p class="wp-block-paragraph">See <a href="https://github.com/Grisselbav/APEXlang/blob/main/Convert.java" type="link" id="https://github.com/Grisselbav/APEXlang/blob/main/Convert.java" target="_blank" rel="noreferrer noopener">Convert.java</a> for the complete conversion program. </p>



<h2 id="syntax-diagrams-on-github" class="wp-block-heading">Syntax Diagrams on GitHub</h2>



<p class="wp-block-paragraph">You cannot produce the complete syntax diagrams in the online version of the Railroad Diagram Generator because the APEXlang grammar is huge, and you will get a timeout when trying.</p>



<p class="wp-block-paragraph">So, you need to run the tool locally. I&#8217;ve created a <a href="https://github.com/Grisselbav/APEXlang/blob/main/.github/workflows/ci.yml" type="link" id="https://github.com/Grisselbav/APEXlang/blob/main/.github/workflows/ci.yml" target="_blank" rel="noreferrer noopener">GitHub workflow</a> that downloads <code>apexlang.ebnf</code>, converts it to a W3C EBNF, and generates an HTML file containing all syntax diagrams. Finally, the workflow publishes the result:</p>



<ul class="wp-block-list">
<li><a href="https://grisselbav.github.io/APEXlang/apexlang.w3c.ebnf.txt" target="_blank" rel="noreferrer noopener">APEXlang grammar converted to W3C EBNF</a> </li>



<li><a href="https://grisselbav.github.io/APEXlang/apexlang.html#app" type="link" id="https://grisselbav.github.io/APEXlang/apexlang.html#app" target="_blank" rel="noreferrer noopener">APEXlang syntax diagrams produced by RR</a></li>
</ul>



<p class="wp-block-paragraph">The advantage of syntax diagrams is that they show all usages of a symbol, allowing you to navigate quickly to the relevant definition.</p>



<h2 id="outlook" class="wp-block-heading">Outlook</h2>



<p class="wp-block-paragraph">The next step is to build a parser for APEXlang and use this as the basis for linting. This would enable APEXlang source code to be automatically validated, helping both developers and AI agents to produce code that conforms to defined quality standards.</p>



<p class="wp-block-paragraph">Looking further ahead, we are considering providing full support for APEXlang within the <a href="https://www.united-codes.com/products/dblinter/" type="link" id="https://www.united-codes.com/products/dblinter/" target="_blank" rel="noreferrer noopener">dbLinter tool suite</a>. Currently, dbLinter analyses only SQL and PL/SQL code blocks embedded in APEXlang files. Full APEXlang support is a natural evolution. Stay tuned!</p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://www.salvis.com/blog/2026/06/19/syntax-diagrams-for-apexlang/">Syntax Diagrams for APEXlang</a> appeared first on <a href="https://www.salvis.com/blog">Philipp Salvisberg&#039;s Blog</a>.</p>
]]></content>
		
					<link rel="replies" type="text/html" href="https://www.salvis.com/blog/2026/06/19/syntax-diagrams-for-apexlang/#comments" thr:count="3" />
			<link rel="replies" type="application/atom+xml" href="https://www.salvis.com/blog/2026/06/19/syntax-diagrams-for-apexlang/feed/atom/" thr:count="3" />
			<thr:total>3</thr:total>
			</entry>
	</feed>
