May 2025 (version 2.25.05.09)

reviewed: 7 October 2025

Untrusted Expression Evaluation

When developing systems, we often process user provided expressions on the backend server. These expressions typically take the form of an <@bo/attribute/expression> attribute where the data type is Expression, usually on a reference data BO, to determine whether an entry is active or to execute operations such as a count on a dashboard entry etc. In the case of dashboards, we may also define an text attribute that specifies the WHERE clause of the dashboard query. This where clause can include {} inline expressions supplied by the user who created the dashboard entry. These expressions are then evaluated server side during dashboard execution.

Although these attributes are generally limited to reference data, where access is restricted to defined users and groups, they still present a potential security risk. For example, an active expression should not require or be granted access to sensitive handlers such as the fileSystem, httpRequests, smtp, ftp handlers etc.

To address this concern, we have introduced the concept of untrusted expression evaluation alongside privileged functions and handlers. A privileged function or handler is one that is explicitly restricted to trusted environments. These functions are not permitted in untrusted contexts, ensuring that sensitive functions can only be used where appropriate security guarantees are in place.

The runtime defines what constitutes a privileged function or handler. Generally, these include operations that access the file system, network, descriptors, or directly interact with the database; essentially, the areas where elevated permissions are required. All standard functions typically used in reference data expressions and WHERE clauses remain available, ensuring that common use cases are fully supported in untrusted evaluation contexts.

A list of the privileged handlers and functions can be found at the end of this release log.

Even evaluating an expression attribute a developer will typically do something like the following:

VSCode diagnostics

For backward compatibility, the eval() function will continue to operate in a trusted context, as it has in the past. This ensures that the introduction of untrusted expression evaluation does not affect existing attribute expressions. Expressions already in use will continue to be evaluated as trusted, preserving current behaviour and avoiding disruption.

A new attribute property called evalSafe has been introduced. This property functions similarly to the htmlSafe property and defaults to false(), meaning attributes are treated as unsafe (untrusted) by default. To support this, a new bo.attrEval() function has been added. This function evaluates the attribute expression while considering the attributes evalSafe property, and by default, it evaluates the expression in untrusted mode

VSCode diagnostics

As such, developers are encouraged to review their use of eval() within applications and transition to the new bo.attrEval() function where appropriate. The evalSafe property should only be explicitly set to true for attributes whose expressions require access to privileged functions. This approach helps ensure a more secure and controlled execution environment by default moving forwards.

A new untrusted script statement block has also been introduced, allowing .cms statements to be explicitly wrapped and evaluated within an untrusted environment.

VSCode diagnostics

All {} inline expressions used within WHERE clauses are now evaluated in untrusted mode across the runtime. This behaviour is non configurable. There is no justifiable use case for requiring privileged functions or handlers within a WHERE clause expression. However, if such functionality is absolutely necessary, the required values can still be safely concatenated into the WHERE clause server-side, outside of the untrusted expression context.

Tooling

  • The runtime and its components now include formal versioning
    • The version follows the format 2.YY.MM.DD+HHmm, reflecting the year, month, day, and build time
    • The assembly version remains at 1.0.0.0 to maintain compatibility and prevent breaking changes in dependent applications
    • The file version remains 2.0.0.0 for now, but may be updated inline moving forwards

VSCode diagnostics

- A new `app.runtimeVersion()` function has been added, which returns a property bag containing the version details

VSCode diagnostics

  • Improved documentation for access modifiers

VSCode diagnostics

New Features

  • A new ddl function handler has been added, deprecating the older devUtil functions.

VSCode diagnostics

  • Added tdg.probability() to retrieve values based on weighted probabilities.

VSCode diagnostics

  • Added tdg.pk() for retrieving a valid random primary key from a specified entity, with an optional where clause.

VSCode diagnostics

  • Added BO descriptor support for getting and setting an attributes testDataValue

VSCode diagnostics

Enhancements

  • Added excluded support to <@page/list/item>
  • Added excluded support to <@page/data/form/tabs/tab>
  • Added tooltip support to <@page/list/item>
  • Added tooltip support to <@page/tabs/tab>
  • Added T-SQL GO batch separators to CREATE and ALTER DDL statement generation.
    • This improves Microsoft SQL Server scripts that require explicit batch boundaries for execution
  • The mul() function now supports an unlimited number of parameters
    • This now matches sum() and sub(), in allowing more than the basic two parameters
  • httpRequest.delete() now support sending a body payload as part of the DELETE request
    • Previously DELETE requests could only include headers and query parameters, but now you can send a request body, just like in other HTTP methods such as POST or PUT
  • <@page/data/table> exports now use the StreamLowMemory iterator mode by default, greatly reducing the server RAM usage for large data exports
  • The page/base:header resource has been updated to a fully named and pathed property bag
    • This allows greater flexibility when overriding the header on the application or layer level, allowing for greater control of the positioning of custom items within the header
    • The current headerInclude logic has not changed, and will continue to work, however we recommend using the new approach moving forwards

Header include:

VSCode diagnostics

  • Support has been added for quoted property bag item names, enabling the inclusion of characters that were previously illegal in item names

VSCode diagnostics

  • json.pb2json() now supports an optional qualified: qualified parameter to omit @qualifier properties from the output JSON

Standard (Qualified) Output:

VSCode diagnostics

Unqualified Output:

VSCode diagnostics

  • Added support for optional HMAC encoding, to allow for Base64 encoding

VSCode diagnostics

  • A BOs attributes persist statuses are now set to LOADED after an INSERT
    • This simulates that the BO has just been loaded from the data source, mirroring the current UPDATE logic

Fixes

  • bo2bo was incorrectly turning off validation on the from BO, instead of the to BO
  • pb.dump() was incorrectly converting empty property bags <> to null()
  • The full table name alias is now used when generating alias for inline views
  • BO descriptor unquiet constrains are now only applied on INSERT if the resolved group has attributes
  • Invalid attribute names in BO descriptor attribute groups no longer prevent DDL generation for the affected BO
  • The script context stack was not being popped on SQL query builder exceptions
    • Previously when a parsing error occurred in a WHERE clause, the script context stack was not properly unwound, resulting in misleading error messages
  • Setting a none existent session context entry to null() would result in the a broken data source connection
  • The following qualifier properties have been updated to no longer explicitly handle the provided values as <@text> qualifiers:
    • @page/link: confirmation
    • @page/calendar/link: confirmation
    • @page/table/link: confirmation
    • @page/input/radio: label
    • @page/input/select: label
    • @page/javascript/setTitle: section
    • @page/link/target/modal: title

Privileged Handlers

  • imap
  • pop3
  • smtp
  • ftp
  • sftp
  • webSocket
  • httpRequest
  • fileSystem
  • textFile
  • document
  • eml
  • excel
  • word
  • docx
  • spreadsheet
  • csv
  • image
  • response
  • cache
  • devUtils
  • shell
  • sql
  • trace
  • tx
  • session
  • host
  • error

Privileged Functions

  • request.saveFile()

  • qs.create()

  • qs.set*()
  • qs.clear*()
  • qs.remove*()

  • encrypt()

  • sb.addTarget()
  • app.reinitialize()
  • app.getFileInfo()
  • process.start()
  • process.terminate()
  • process.impersonate()
  • language.set()
  • page.render()
  • test.call()

  • boDesc.parse()

  • boDesc.add*()
  • boDesc.remove*()
  • boDesc.set*()

  • bo.addAttrError()

  • bo.addError()
  • bo.delete()
  • bo.insert()
  • bo.update()
  • bo.persist()
  • bo.setAlias()
  • bo.setAttr()
  • bo.writeAudit()