boxlang-core-dev-interceptors — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited boxlang-core-dev-interceptors (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
BoxLang's interceptor system implements the Observer and Intercepting Filter design patterns. Interceptors listen for named events (interception points) announced by the runtime, modules, or application code. They enable cross-cutting concerns like logging, security, AOP, routing, and content manipulation without modifying core code.
Three pools exist in the runtime. Each pool is an independent event emitter:
| Pool | Scope | Use Case |
|---|---|---|
| Global Runtime | Entire BoxLang process | Module events, parse events, global AOP |
| Application Request Listener | Per-application request lifecycle | Auth, routing, request logging |
| CacheProviders | Per cache instance | Cache event hooks |
Method names in the class directly correspond to interception point names:
// interceptors/RequestLogger.bx
class {
// Auto-injected by the runtime
property name="name" setter="false"
property name="properties" setter="false"
property name="log" setter="false" // module logger
property name="interceptorService" setter="false"
property name="boxRuntime" setter="false"
property name="moduleRecord" setter="false" // if module-based
/**
* Called when the interceptor is registered.
* Use this to validate properties and set up resources.
*/
function configure() {
// Access settings passed at registration time
log.info( "RequestLogger interceptor configured. Level: #properties.logLevel ?: 'INFO'#" )
}
// ---------------------------------------------------------------
// Interception point methods — name must EXACTLY match the event
// ---------------------------------------------------------------
/**
* Fires before every HTTP request.
* @event Struct with request context data (mutable)
*/
function onRequestStart( struct event={} ) {
log.info( "→ #cgi.request_method# #cgi.script_name##cgi.query_string ?: ''#" )
event.requestStartTime = getTickCount()
}
/**
* Fires after every HTTP request.
*/
function onRequestEnd( struct event={} ) {
var elapsed = getTickCount() - (event.requestStartTime ?: getTickCount())
log.info( "← #cgi.script_name# completed in #elapsed#ms" )
}
/**
* Fires when an unhandled exception occurs.
*/
function onException( struct event={} ) {
var ex = event.exception ?: {}
log.error( "Exception: #ex.message ?: 'unknown'# | #ex.stackTrace ?: ''#" )
}
}// src/main/java/com/example/interceptors/RequestLogger.java
package com.example.interceptors;
import ortus.boxlang.runtime.events.BaseInterceptor;
import ortus.boxlang.runtime.events.InterceptionPoint;
import ortus.boxlang.runtime.scopes.Key;
import ortus.boxlang.runtime.types.IStruct;
@ortus.boxlang.runtime.events.Interceptor
public class RequestLogger extends BaseInterceptor {
@Override
public void configure() {
// this.properties is available for config
// this.log is available for logging
// this.interceptorService is available
// this.boxRuntime is available
}
// @InterceptionPoint marks this method as an event handler
@InterceptionPoint
public void onRequestStart( IStruct event ) {
String method = (String) event.getOrDefault( Key.of("requestMethod"), "GET" );
this.log.info( "Request started: " + method );
event.put( Key.of("startTime"), System.currentTimeMillis() );
}
@InterceptionPoint
public void onRequestEnd( IStruct event ) {
Long startTime = (Long) event.getOrDefault( Key.of("startTime"), System.currentTimeMillis() );
long elapsed = System.currentTimeMillis() - startTime;
this.log.info( "Request completed in " + elapsed + "ms" );
}
@InterceptionPoint
public void preFunctionInvoke( IStruct event ) {
// event contains: functionName, arguments, context
String fnName = event.getAsString( Key.of("functionName") );
this.log.debug( "Invoking function: " + fnName );
}
}Closures/lambdas must explicitly specify which events they handle:
// Closure interceptor — must specify states
var myListener = function( struct event={} ) {
writeOutput( "Request started!" )
}
// Register for specific events
boxRegisterInterceptor(
myListener,
["onRequestStart", "onRequestEnd"]
)
// Lambda form
boxRegisterInterceptor(
(event) -> logEvent( event ),
["onHTTPRequest"]
)// ModuleConfig.bx
class {
this.interceptors = [
{
// Full invocation path to the interceptor class
class : "#moduleRecord.invocationPath#.interceptors.RequestLogger",
properties : {
logLevel : "INFO",
maxErrors : 100
}
},
{
class : "#moduleRecord.invocationPath#.interceptors.SecurityFilter",
properties : { enabled: true }
}
]
}// Global runtime interceptor
boxRegisterInterceptor( new interceptors.MyInterceptor() )
// With properties
boxRegisterInterceptor(
new interceptors.AuditLog(),
[], // states: empty = listen to all matching methods
{ logPath: "/var/log/audit.log" }
)
// Application request listener (web context)
boxRegisterRequestInterceptor( new interceptors.AuthFilter() )InterceptorService service = BoxRuntime.getInstance().getInterceptorService();
// Register a Java-based interceptor
service.register( new RequestLogger() );
// Register with properties
IStruct props = new Struct();
props.put( Key.of("logLevel"), "DEBUG" );
service.register( new RequestLogger(), props );
// Register a BoxLang class
IClassRunnable bxInterceptor = (IClassRunnable) context.loadClass("interceptors.MyBxInterceptor");
service.register( bxInterceptor );
// Register a closure for specific states
service.register(
(IInterceptorLambda) event -> System.out.println("Event: " + event),
Key.of("onRequestStart"), Key.of("onRequestEnd")
);All canonical event names are defined in the BoxEvent enum: ortus.boxlang.runtime.events.BoxEvent
Use BoxEvent.ON_REQUEST_START.key() in Java, or the string name in BoxLang.
| Event | When | Payload Keys |
|---|---|---|
onRuntimeStart | Runtime fully started | runtime |
onRuntimeShutdown | Before shutdown | runtime |
onRuntimeConfigurationLoad | Config loaded | config |
onRuntimeBoxContextStartup | First context created | context |
onServerScopeCreation | Server scope initialized | serverScope |
onConfigurationLoad | Config file loaded | config |
onConfigurationOverrideLoad | Override config loaded | config |
onParse | Before source is parsed | source, result |
onMissingMapping | Mapping cannot be resolved | mapping |
onPreSourceInvoke | Before any source file runs | context, source |
onPostSourceInvoke | After any source file runs | context, source |
| Event | When |
|---|---|
onModuleServiceStartup | Module service starts |
onModuleServiceShutdown | Module service stops |
afterModuleRegistrations | All modules registered |
preModuleRegistration | Before a module is registered |
postModuleRegistration | After a module is registered |
afterModuleActivations | All modules activated |
preModuleLoad | Before a module is loaded |
postModuleLoad | After a module is loaded |
preModuleUnload | Before a module is unloaded |
postModuleUnload | After a module is unloaded |
| Event | When | Notes |
|---|---|---|
onApplicationStart | Application started | |
onApplicationEnd | Application ended | |
onApplicationRestart | Application restarted | |
onApplicationDefined | Application.bx found | |
beforeApplicationListenerLoad | Before Application.bx loads | |
afterApplicationListenerLoad | After Application.bx loads | |
onRequestStart | Before request starts | Fires before Application.bx lifecycle |
onRequest | Main request handling | |
onRequestEnd | After request completes | |
onClassRequest | Class/component requested | |
onRequestFlushBuffer | Output buffer flushed | |
onSessionStart | New session created | Session struct available |
onSessionEnd | Session expires or is invalidated | Session data available |
onSessionCreated | Session object created | |
onSessionDestroyed | Session object destroyed | |
onError | Unhandled exception | |
onMissingTemplate | Template not found | |
onAbort | Execution aborted | |
onRequestContextConfig | Request context configured |
| Event | When |
|---|---|
onHTTPRequest | Every HTTP request |
onHTTPRawResponse | Before raw HTTP response sent |
onHTTPResponse | Before HTTP response sent |
onWebExecutorRequest | Web executor request (route modification) |
| Event | When |
|---|---|
onBIFInstance | BIF instantiated |
onBIFInvocation | Before BIF invoked |
postBIFInvocation | After BIF invoked |
onComponentInstance | Component instantiated |
onComponentInvocation | Component invoked |
onCacheComponentAction | Cache component action |
onFileComponentAction | File component action |
onCreateObjectRequest | createObject() called |
afterDynamicObjectCreation | Java object wrapped in DynamicObject |
| Event | When | Payload |
|---|---|---|
preTemplateInvoke | Before template executes | template, context |
postTemplateInvoke | After template executes | template, context |
preFunctionInvoke | Before any function call | functionName, arguments, context |
postFunctionInvoke | After any function call | functionName, result |
onFunctionException | Function throws exception | functionName, exception |
| Event | When |
|---|---|
onQueryBuild | Before query is built |
preQueryExecute | Before query executes |
postQueryExecute | After query executes |
queryAddRow | Row added to query |
onTransactionBegin | Transaction started |
onTransactionEnd | Transaction ended |
onTransactionAcquire | Connection acquired |
onTransactionRelease | Connection released |
onTransactionCommit | Transaction committed |
onTransactionRollback | Transaction rolled back |
onTransactionSetSavepoint | Savepoint created |
| Event | When |
|---|---|
afterCacheElementInsert | Item inserted |
beforeCacheElementRemoved | Before item removed |
afterCacheElementRemoved | After item removed |
afterCacheElementUpdated | Item updated |
afterCacheClearAll | Cache cleared |
afterCacheRegistration | Cache provider registered |
afterCacheRemoval | Cache provider removed |
beforeCacheRemoval | Before cache provider removed |
beforeCacheReplacement | Before cache entry replaced |
beforeCacheShutdown | Before cache shuts down |
afterCacheShutdown | After cache shuts down |
afterCacheServiceStartup | CacheService started |
beforeCacheServiceShutdown | Before CacheService shuts down |
afterCacheServiceShutdown | After CacheService shuts down |
| Event | When |
|---|---|
onSchedulerStartup | Scheduler started |
onSchedulerShutdown | Scheduler stopped |
onSchedulerRestart | Scheduler restarted |
schedulerBeforeAnyTask | Before any task runs |
schedulerAfterAnyTask | After any task runs |
schedulerOnAnyTaskSuccess | Task succeeded |
schedulerOnAnyTaskError | Task threw an error |
onSchedulerServiceStartup | SchedulerService started |
onSchedulerServiceShutdown | SchedulerService stopped |
onAllSchedulersStarted | All schedulers running |
onSchedulerRemoval | Scheduler removed |
onSchedulerRegistration | Scheduler registered |
| Event | When |
|---|---|
onDatasourceConfigLoad | Datasource config loaded |
onDatasourceServiceStartup | DatasourceService started |
onDatasourceServiceShutdown | DatasourceService stopped |
onDatasourceStartup | Datasource initialized |
| Event | When |
|---|---|
beforeObjectMarshallSerialize | Before serialization |
afterObjectMarshallSerialize | After serialization |
beforeObjectMarshallDeserialize | Before deserialization |
afterObjectMarshallDeserialize | After deserialization |
onJSONQuerySerialize | Query serialized to JSON |
| Event | When |
|---|---|
onBXDump | writeDump() called |
onMissingDumpOutput | No dump output handler found |
logMessage | Log message emitted |
You can announce your own events for other interceptors to listen to:
// Announce a custom event with a data payload
boxAnnounce( "onUserRegistered", {
userId : newUser.getId(),
email : newUser.getEmail(),
source : "web"
})
// Listeners elsewhere can react to this:
// function onUserRegistered( struct event={} ) {
// sendWelcomeEmail( event.email )
// createDefaultWorkspace( event.userId )
// }// Java: announce via InterceptorService
IStruct data = new Struct();
data.put( Key.of("userId"), userId );
data.put( Key.of("email"), email );
BoxRuntime.getInstance()
.getInterceptorService()
.announce( Key.of("onUserRegistered"), data );// interceptors/FeatureFlagRouter.bx
// v1.11+: fires BEFORE Application.bx onRequestStart — enables rerouting
class {
function onRequestStart( struct event={} ) {
var userId = session.userId ?: ""
if ( len(userId) && featureFlags.isEnabled("new-ui", userId) ) {
event.overridePath = "/new-ui" & cgi.path_info
}
}
}// interceptors/MaintenanceMode.bx
class {
function onRequestStart( struct event={} ) {
if ( application.maintenanceMode && !isAdminIp( cgi.remote_addr ) ) {
include "maintenance.bxm"
abort
}
}
}// interceptors/MethodTracer.bx
class {
function preFunctionInvoke( struct event={} ) {
var fn = event.functionName ?: "unknown"
var start = getTickCount()
event.traceStart = start
log.debug( "→ #fn# called" )
}
function postFunctionInvoke( struct event={} ) {
var elapsed = getTickCount() - (event.traceStart ?: getTickCount())
log.debug( "← #event.functionName ?: 'unknown'# completed in #elapsed#ms" )
}
}// interceptors/HtmlMinifier.bx
class {
function onRequestEnd( struct event={} ) {
// Minify HTML output before sending to client
var output = event.output ?: ""
if ( len(output) ) {
event.output = minifyHtml( output )
}
}
}// Unregister by reference
boxUnregisterInterceptor( myInterceptorInstance )
// Via InterceptorService (Java)
BoxRuntime.getInstance().getInterceptorService().unregister( myInterceptor )~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.