Hi team,
Apologies in advance for the AI generated post, which of course may be completely inaccurate (but in case it isn’t, I thought it was worth posting for feedback here).
While setting up a custom Server.cfc under /opt/lucee/server/lucee-server/context/context/Server.cfc in Lucee 7, we ran into an unexpected path resolution failure when .CFConfig.json does not declare any custom mappings.
What Happens
When starting Lucee with a minimal .CFConfig.json that defines no custom mappings, Lucee fails to load Server.cfc, attempting to look for it at:
{lucee-server}/context/lucee-server/Server.cfc
instead of:
{lucee-server}/context/Server.cfc
Root Cause in ConfigUtil.java
Looking at lucee.runtime.config.ConfigUtil.getPageSourceExisting:
Mapping[] thisMappings = config.getMappings();
for (int i = 0; i < thisMappings.length - 1; i++) {
mapping = thisMappings[i];
if ((!onlyTopLevel || mapping.isTopLevel()) && lcRealPath.startsWith(mapping.getVirtualLowerCaseWithSlash(), 0)) {
ps = mapping.getPageSource(realPath.substring(mapping.getVirtual().length())); // Strips virtual prefix
if (ps.exists()) return ps;
}
}
if (useDefaultMapping) {
if (rootApp != null) mapping = rootApp;
else mapping = thisMappings[thisMappings.length - 1]; // Assumes the last mapping is default web root "/"
ps = mapping.getPageSource(realPath); // DOES NOT strip prefix!
if (ps.exists()) return ps;
}
- The
forloop iterates up toi < thisMappings.length - 1, intentionally skipping the last element under the assumption that the final mapping is always the web root (/). - In the Server Context, there is no default
/root mapping. When.CFConfig.jsondoes not specify mappings, Lucee only registers two default mappings:[0] = /lucee[1] = /lucee-server
- Because
/lucee-serveris at index1(length - 1), it is skipped by theforloop completely. - Execution then falls through to
useDefaultMapping, which selectsthisMappings[thisMappings.length - 1](/lucee-server) as the fallback default mapping. - However,
useDefaultMappingpassesrealPathdirectly tomapping.getPageSource(realPath)without stripping the virtual prefix/lucee-server. - As a result, Lucee attempts to resolve physical path +
/lucee-server/Server.cfc, nesting the path twice.
Workaround
Adding additional mappings to .CFConfig.json after /lucee-server (for example, /lucee/admin or /lucee/doc, matching what ships in vanilla Lucee builds) pushes /lucee-server before index length - 1. The loop then processes /lucee-server, strips the prefix, and resolves Server.cfc without error.
Suggested Fix
Rather than assuming thisMappings[thisMappings.length - 1] is always the root / mapping:
- Check
mapping.getVirtual().equals("/")to identify the root mapping, or - Ensure all entries are checked in the loop if no mapping with virtual path
"/"exists in the configuration (as is typical in the Server Context).