Coldbox reviewer
Use this skill when reviewing ColdBox application code for correctness, security, performance, testability, and adherence to ColdBox conventions. Covers handlers, services, models, interceptors, modules, routing, ORM/OBM usage, REST APIs, dependency injection patterns, and common anti-patterns to flag during pull request reviews.From its SKILL.md
npx -y skills add ColdBox/skills --skill coldbox-reviewerAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
12.8 KB, ~3.2k tokens by cl100k_base, as published. Nobody here has run it
ColdBox Code Reviewer
When to Use This Skill
Use this skill when:
- Reviewing pull requests for ColdBox (BoxLang or CFML) applications
- Auditing an existing codebase for quality and security issues
- Establishing a code review checklist for a development team
- Identifying anti-patterns or risky patterns in ColdBox code
Language Mode Reference
Examples use BoxLang (.bx) syntax by default. Adapt for your target language:
| Concept | BoxLang (.bx) | CFML (.cfc) |
|---|---|---|
| Class declaration | class [extends="..."] { | component [extends="..."] { |
| DI annotation | @inject above property name="svc"; | property name="svc" inject="svc"; |
| View templates | .bxm suffix | .cfm / .cfml suffix |
| Tag prefix | <bx:if>, <bx:output>, <bx:set> | <cfif>, <cfoutput>, <cfset> |
CFML Compat Mode: With BoxLang + CFML Compat module,
.bxand.cfcfiles coexist freely. BoxLang-native classes useclass {}(.bxfiles); CFML-compat classes usecomponent {}(.cfcfiles).
Review Checklist Overview
| Category | Key Questions |
|---|---|
| Architecture | Does the code follow the ColdBox layers correctly? |
| Handlers | Are actions thin? No business logic leaking in? |
| Services | Single responsibility? Proper error handling? |
| Security | Input validated? SQL injection / XSS / CSRF covered? |
| Dependency Injection | WireBox used correctly? No createObject() anti-patterns? |
| ORM / Queries | N+1 problem? Unsafe concatenation in HQL/SQL? |
| REST APIs | Consistent HTTP verbs, status codes, and response shape? |
| Performance | Unnecessary DB hits? Missing caching? |
| Testability | Logic in handler actions? Hard-coded dependencies? |
| Documentation | Public API documented? Deprecated APIs marked? |
Architecture & Layering
✅ Correct Layering
Handler → Service → DAO / ORM Entity
↕ (optional)
Interceptor / Event
- Handlers — route events, populate prc, delegate to services
- Services — all business logic, emit interception points
- DAOs / Repositories — only data access
- Models / Entities — data + domain behavior; no framework calls
🚩 Anti-Patterns to Flag
// ❌ Business logic inside a handler action
function create( event, rc, prc ) {
var user = new User()
user.setEmail( rc.email )
// hashing inside the handler — belongs in a service
user.setPassword( hash( rc.password, "bcrypt" ) )
entitySave( user )
event.setView( "users/created" )
}
// ✅ Delegate to service
function create( event, rc, prc ) {
prc.user = userService.create( rc )
event.setView( "users/created" )
}
Handler Review
Checklist
- Action is thin — only coordinates between service calls and view setup
-
rcinputs are validated before use (or validated inside the service) - No raw SQL or ORM queries directly in handler
-
allowedMethodsis declared to restrict HTTP verbs -
event.paramValue()used instead ofisDefined("rc.x")checks -
relocate()used after successful POST (PRG pattern) - No
cfoutput/ direct response writes in a non-REST handler
Common Handler Issues
// ❌ Missing allowedMethods guard — allows DELETE via GET
function delete( event, rc, prc ) {
userService.delete( rc.userId )
relocate( "users.index" )
}
// ✅ Always declare allowedMethods
this.allowedMethods = { delete: "DELETE,POST" }
// ❌ Direct array access without param check
function show( event, rc, prc ) {
prc.user = userService.find( rc.userId ) // rc.userId may not exist
}
// ✅ Use paramValue() to provide a safe default
function show( event, rc, prc ) {
event.paramValue( "userId", 0 )
prc.user = userService.find( rc.userId )
}
Security Review
Input Validation & Sanitization
// ❌ Using rc directly in a query without validation
var q = queryExecute( "SELECT * FROM users WHERE email = '#rc.email#'" )
// ✅ Use query params (always)
var q = queryExecute(
"SELECT * FROM users WHERE email = :email",
{ email: { value: rc.email, cfsqltype: "cf_sql_varchar" } }
)
Cross-Site Scripting (XSS)
<!-- ❌ Unescaped output in views -->
<p>#prc.user.getBio()#</p>
<!-- ✅ Always encode output -->
<p>#encodeForHTML( prc.user.getBio() )#</p>
Mass Assignment
// ❌ Populating an entity directly from rc — exposes all fields
getBeanPopulator().populateFromStruct( user, rc )
// ✅ Allowlist only expected keys
getBeanPopulator().populateFromStruct(
user,
rc,
include = "firstName,lastName,email"
)
CSRF
- Verify that mutating actions (POST/PUT/DELETE) check a CSRF token if the app uses session-based auth
- JWT-based REST APIs are generally exempt (token in header, not cookie)
Authentication / Authorization Checks
// ❌ No authorization check before operating on another user's data
function update( event, rc, prc ) {
userService.update( rc.userId, rc )
}
// ✅ Verify the current user owns the resource
function update( event, rc, prc ) {
if ( !authService.canEdit( rc.userId ) ) {
return event.renderData( type: "json", data: { error: "Forbidden" }, statusCode: 403 )
}
userService.update( rc.userId, rc )
}
Dependency Injection (WireBox)
Checklist
- All dependencies injected via
property name="..." inject="..."— nocreateObject() - Singletons (
singletonscope) hold no mutable request-level state - Request/transient objects use
transientscope orwirebox.getInstance() - No
application.wireboxaccess patterns in business code
Common DI Issues
// ❌ Manual instantiation defeats WireBox
class {
function init() {
variables.userService = createObject( "component", "models.UserService" ).init()
}
}
// ✅ Let WireBox inject it
class {
property name="userService" inject="UserService";
}
// ❌ Singleton service holds request-scoped state — causes data leakage between requests
class singleton {
property name="currentUser"; // ← DANGEROUS in a singleton
}
// ✅ Store per-request state in prc, not in a singleton service
ORM / Query Review
N+1 Query Problem
// ❌ N+1: one query to load orders, then one per order for user
for ( var order in orders ) {
print( order.getUser().getEmail() ) // triggers a SELECT for each order
}
// ✅ Use HQL with join fetch
var orders = ORMExecuteQuery(
"FROM Order o JOIN FETCH o.user WHERE o.status = :status",
{ status: "active" }
)
Unsafe HQL / SQL
// ❌ String concatenation in HQL — SQL injection risk
var results = ORMExecuteQuery( "FROM User WHERE email = '#arguments.email#'" )
// ✅ Named parameters
var results = ORMExecuteQuery(
"FROM User WHERE email = :email",
false,
{ email: arguments.email }
)
Query of Queries
// ❌ QoQ on a large query is slow; loop aggregation in CFML is faster
var result = new Query( sql: "SELECT * FROM myBigQuery WHERE status = 'active'", dbtype: "query", myBigQuery: myBigQuery ).execute().getResult()
// ✅ For aggregation, use the database query or array functions instead
REST API Review
Checklist
- Correct HTTP verbs used: GET (read), POST (create), PUT/PATCH (update), DELETE (delete)
- Correct HTTP status codes returned: 200, 201, 204, 400, 401, 403, 404, 422, 500
- Error responses follow a consistent JSON shape:
{ "error": "message" } - No sensitive data (passwords, tokens, PII) in response bodies
- Pagination info included in list endpoints:
{ data: [], meta: { page, total } } -
Content-Type: application/jsonset on all JSON responses
Common REST Issues
// ❌ Wrong status code for creation
function create( event, rc, prc ) {
prc.user = userService.create( rc )
event.renderData( type: "json", data: prc.user, statusCode: 200 ) // should be 201
}
// ✅
event.renderData( type: "json", data: prc.user, statusCode: 201 )
// ❌ Swallowing errors and returning 200
function update( event, rc, prc ) {
try {
userService.update( rc )
event.renderData( type: "json", data: { success: true } )
} catch( any e ) {
event.renderData( type: "json", data: { success: false } ) // ← 200 status even on failure
}
}
// ✅ Return appropriate 4xx/5xx codes
} catch( ValidationException e ) {
event.renderData( type: "json", data: { error: e.message }, statusCode: 422 )
} catch( any e ) {
event.renderData( type: "json", data: { error: "Internal error" }, statusCode: 500 )
}
Performance Review
Checklist
- No per-request database calls for data that rarely changes (use CacheBox)
- No wildcard
SELECT *on wide tables - Large result sets use pagination, not full loads
- View rendering does not execute queries (data loaded in action, not view)
- No expensive operations inside loops
Caching Anti-Pattern
// ❌ Hits the DB on every request for config that never changes
function getConfig( event, rc, prc ) {
prc.config = configService.loadAllSettings()
}
// ✅ Cache it
function getConfig( event, rc, prc ) {
prc.config = cache( "appConfig", 60, function() {
return configService.loadAllSettings()
} )
}
Testability Review
Checklist
- Business logic is in services — not in handlers or views — so it can be unit-tested
- Services accept dependencies via injection (not hard-coded
createObject()) - Methods return values rather than writing directly to
prcorapplication - Side effects (email, SMS, external calls) are behind an interface so they can be mocked
- No global state required to test a method in isolation
Hard-to-Test Pattern
// ❌ Hard to test — direct DB call + global scope write
function processOrder( required numeric orderId ) {
var order = entityLoad( "Order", orderId, true )
application.stats.orderCount++
sendEmail( to: order.getUser().getEmail(), subject: "Order confirmed" )
}
// ✅ Testable — dependencies injected, side effects behind interfaces
function processOrder( required numeric orderId ) {
var order = orderDAO.find( orderId )
statsService.incrementOrders()
emailService.sendOrderConfirmation( order )
return order
}
Interceptor Review
Checklist
- Interceptor has a single, clearly stated responsibility
- Does not perform heavy work on high-frequency points (
preProcess,postRender) - Terminates the request with
event.noExecution()correctly when intercepting - Does not swallow exceptions silently
// ❌ Heavy DB query on every single request
void function preProcess( event, interceptData, buffer ) {
prc.menuItems = menuService.loadAll() // hits DB on every request
}
// ✅ Cache it
void function preProcess( event, interceptData, buffer ) {
prc.menuItems = cache( "menuItems", 10, function() {
return menuService.loadAll()
} )
}
Module Review
Checklist
-
box.jsonhas accurateversion,name, and ColdBox version constraint -
ModuleConfig.cfcdeclares all settings with sensible defaults - Routes are prefixed with the module namespace to avoid conflicts
- Interceptors are registered in
interceptorsarray, not hard-coded - Module cleans up after itself in
onUnload()
Documentation Review
- Every public method has a
/** ... */comment with description +@return - Handler actions document
@rcinputs and@prcoutputs - Deprecated APIs are marked
@deprecatedwith a migration path - New public APIs have
@sinceset to the current version - Comments are accurate (not stale from a previous implementation)
Quick Reference: Red Flags
| Code Pattern | Issue |
|---|---|
"SELECT ... WHERE x = '#var#'" | SQL injection |
#prc.userInput# in a view with no encoding | XSS |
populateFromStruct(entity, rc) with no allowlist | Mass assignment |
createObject("component", "some.path").init() | Bypasses WireBox |
application.scope mutations in a handler | Global state; race conditions |
cfquery inside a cfloop | N+1 / chatty DB pattern |
Missing allowedMethods on mutating actions | CSRF / verb tampering |
catch(any e) { /* empty */ } | Swallowed exceptions |
| Singleton with mutable instance state | Request data leakage |
| Business logic in a view template | Untestable, violates MVC |
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.
Gives 0 of the 12 instructions most review quality skills give in ~3.2k tokens
Counted across 1,048 of the 1,783 authors here whose files we hold, read 2026-08-07
- Ask questions one at a timein 81 of 1048, across 64 files
- Provide a recommended answer for each questionin 73 of 1048, across 50 files
- Explore the codebase instead of asking answerable questionsin 66 of 1048, across 42 files
- Resolve dependencies between decisions one-by-onein 42 of 1048, across 17 files
- Interview the user relentlessly about the planin 38 of 1048, across 13 files
- Order findings by severityin 31 of 1048
- Resolve each branch of the decision treein 27 of 1048, across 5 files
- Run a grilling sessionin 26 of 1048, across 5 files
- Update CONTEXT.md immediately when a term is resolvedin 26 of 1048, across 11 files
- Propose precise canonical terms for vague languagein 25 of 1048, across 7 files
- Create documentation files lazilyin 24 of 1048, across 5 files
- Assign severity to every findingin 24 of 1048
Said here and by no other author read
- delegate business logic to services
- encode output to prevent xss
- restrict http verbs via allowedmethods
- use event.paramvalue for inputs
- inject dependencies via wirebox
- cache rarely changing database data
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.