[Boxlang 1.3.0] ExpandPath Lists Wrong Directory In Testbox on Windows

I am running Windows 10 and using Boxlang 1.3.0-snapshot within Testbox with the following code at the beginning of my test:

component extends="coldbox.system.testing.BaseTestCase" appMapping="root" {

	testData = {
        "valid": {
            "id": "##id",
            "url": "https://test.example.com/"
        },
        "dirList" = directoryList( expandPath( "../../../../types/" ), true, "query" )
    };

    writeDump( expandPath( "../../../../types/" ) );
    writeDump( testData );

    // rest of test code

The output looks like this:

image

However, running the same code in Lucee 5/6 or ACF 2021/2023 produces results that look like this:

image

I’m not sure why it’s dumping twice, but the result from expandPath() is correct in Lucee/ACF, but not correct in Boxlang.

@DaveL are you executing this via the URL or via a runner?

@bdw429s ping

Not really enough information to go on here. Are there Symlinks involved? Can you submit a self-contained test case for us to run?

@lmajano I was using Testbox via the URL.

@bdw429s No symlinks in the folders I was using. I can create a repo for you to replicate the issue.

is the issue that BoxLang is backing up too many directories, or not enough directories? Without seeing your file system, it’s hard to understand the meaning of the dump outputs.

The issue is that Boxlang went up too many directories and displayed a directory that didn’t exist D:\Dropbox\Repositories\types\

It should have matched Lucee’s output:
D:\Dropbox\Repositories\schema.org\types\

I am working on reproducing the issue and pushing a branch to Github for your review.

Ok, I’ll be interested to see the repro case. This seems like a pretty big deal for no one to have noticed, so I’m curious if there are special circumstances. Here is our existing tests which hit this use case:

Here’s a branch I created from my new Coldbox module, which encounters the issue with expandPath():

To replicate:

  1. Clone this branch of the repo to a Windows 10 machine
  2. In the project root, fire up Commandbox and execute: run-script start:boxlang
  3. Navigate to http://127.0.0.1:60299/tests/specs/unit/BaseTypeTest.cfc?method=runRemote
  4. Note the output will show the wrong directory

In my case, Boxlang is going up one higher directory than it should

Now compare with Lucee:

  1. In commandbox execute: run-script start:lucee (I changed the port so they should be able to run side-by-side)
  2. Navigate to http://127.0.0.1:60300/tests/specs/unit/BaseTypeTest.cfc?method=runRemote
  3. Note the output will show the correct directory

Here’s another inconsistency I noticed:

Change expandPath( "../../../../types/") to expandPath( "/../../../../types/" ) (add a leading slash)

You will get this in Boxlang (this is what I expected from the original without the leading slash):
image

And this in Lucee:
image

Ok, after several hours of poking, here are the answers :slight_smile:

expandPath( "../../../../types/" ) returns an unexpected result when calling a remote method from a CFC which is defined in the super class. In this case, you’re invoking the runRemote() method, which comes from the test-harness\testbox\system\BaseSpec.cfc class. Furthermore, expandPath() expands paths relative to the BASE template on the request. The base template is currently seen by BoxLang as BaseSpect.cfc, so therefore, backing up 4 directories from the test-harness\testbox\system\ folder, puts you in the directory ABOVE your project on disk.

So BoxLang is ā€œcorrectā€ in it’s own way, we just interpret what the base template path is differently than Lucee does when the method you’re calling is in a super class. :slight_smile: I should be able to tweak how remote CFC methods are executed to make the CFC in the URL be the base template instead.

expandPath( "/../../../../types/" ) returns an unexpected result, in part, because BoxLang has a feature that Lucee doesn’t have. Adobe CF has this feature, but they do it a little differently with their custom little IIS connector. Basically, virtual directories (or web server aliases) are made ā€œvisibleā€ to BoxLang so if you have a virtual directory of /foo defined in your web server, then you can call expandPath( '/foo' ) and BoxLang will ā€œknowā€ about that alias and use it to expand the directory without having any CF mappings called /foo.

The way we accomplish this, is by deferring to the servlet container for any expanded paths that didn’t get expanded by another CF-level mapping.

String expandedPath = servletContext.getRealPath( yourRelativePath );

This allows the servlet container to have the final say in expanding paths which start with a / character. However, unlike expandpath, the servlet’s getRealPath() method will NOT obey ../ thus not allowing you to back up outside of the WAR’s web root (largely for security). As such, the web root becomes a ā€œceilingā€ you can’t back up past, and you end up with a final path inside your web root

C:\Users\brad\Documents\GitHub\schema-org\test-harness\types\

Note, this only applies to CommandBox or other WAR servers. This behavior isn’t present in the BoxLang MiniServer because there’s no servlet there.

There’s not a great workaround for this one as I can’t control how the servlet interprets relative paths. I’ll probably just put in some sort of caveat to skip that behavior if the relative path starts with /.. or something since the result will certainly not be what you expect. It really only makes sense to involve the servlet when the relative path starts with /someAliasName/.

1 Like

Ready for you to test:

https://ortussolutions.atlassian.net/browse/BL-1531

https://ortussolutions.atlassian.net/browse/BL-1532

Thank you so much for taking the time to dig deep into this issue! I also learned a lot about how BoxLang handles path resolution differently from Lucee. It’s all fascinating stuff.

This is my first time having to download a patched version of Boxlang for testing. How would I go about using it in my test repo? Will I need to alter the cfengine parameter of my server json file? If so, what should the new value be? It’s currently boxlang@be.

That should work. You can see on forgebox, the current be version was published around 30 minutes ago

Since you’re using a custom server home, make sure you forget your server first.

@bdw429s, I have good news and bad news.

The good news is that my tests all pass now and expandPath() now returns the same expected directory as Lucee and ACF!

image
image

The bad news is that I can no longer load the home page of the test-harness and I’m getting a Boxlang java.lang.StackOverflowError. This error doesn’t occur in ACF or Lucee, and worked prior to pulling the latest Boxlang@be

Full stacktrace:

Let me know how I can help troubleshoot the problem. You should be able to replicate the issue by simply running start:boxlang on the same repo I provided.

@lmajano comitted some changes to the cache service yesterday. I assume it caused an unrelated regression.

Can you dump out the version and build date of BoxLang (found in the server scope). @jclausen was looking at your error, but says none of the Java line numbers match up to the current source code.

@bdw429s, restarted the server and low and behold the error I received earlier is gone! I’m not sure what happened. I tried restarting the server before with no success. However, it worked now.

Anyway, to answer your question:
Build Date: 2025-06-10 22:48:36
Version: 1.3.0-snapshot+3423

Let it be known how I get thrown under the :bus: all the time

1 Like

@lmajano :joy: You could be a certified bus mechanic at this point.

@bdw429s I restarted the server again this morning and the error came back. It seems to be an intermittent issue. I tested 10 attempts where I stopped and restarted the server:

  1. Error
  2. Error
  3. Success
  4. Success
  5. Success
  6. Success
  7. Success
  8. Success
  9. Success
  10. Success

On both errors, the stacktrace was similar but had slight variations. I linked each one to a PasteBin.

Side note: Restarting the server from the Windows system tray doesn’t seem to do anything other than just add the log entry: ā€œRunwar: Tray menu async execution of: [cmd.exe, /c, ā€œC:\Program Files\commandbox\box.exeā€ server restart ā€˜schema-org-boxlang@1’]ā€ After that, the server remains online. For my tests, I ran run-script stop:boxlang waited for the server stopped confirmation and then run-script start:boxlang

Not out from under the bus yet :laughing: Any idea on that stackover flow. It appears to be related to caching and interceptors. Perhap related to his installed modules?

Dunno, that feature works for me. Did you check the server logs to see if it restarted? Did you try running that native command directly to see the output? Is it possible the server just restarted so quickly you didn’t notice?

Restarting via the tray on Lucee/ACF works, but still doesn’t work on Boxlang for me. This isn’t a huge deal because I can still restart from the CLI. I’m posting my findings just as an FYI. Also, this issue deviates from the original post. If you want me to create a separate thread, I can.

Anyway, here’s what I noticed:

  1. Right-clicking the tray and selecting Restart Server creates this log entry:
    [INFO ] Runwar: Tray menu async execution of: [cmd.exe, /c, "C:\Program Files\commandbox\box.exe" server restart 'schema-org-boxlang@1']

No other log entries appear and the server does not restart (I double-checked).

I copied and pasted server restart 'schema-org-boxlang@1' into commandbox, and the server stops and restarts as expected. Watching the log shows:

[INFO ] Runwar: Stopping server 'schema-org-boxlang@1'
[ERROR] SystemTray: Error hiding tray. ErrorCode: 0x80004005 [Unspecified error]
[INFO ] Runwar: Stopped server
etc...

Stopping server via tray icon works, so this only appears to impact restarting.

Here’s what I tried:

I checked my server list to make sure there weren’t any duplicate servers with the same name. I didn’t see any.

I also tried forgetting the server and then starting from scratch:
run-script forget:boxlang
Confirmed deletion via server list
run-script start:boxlang

Right-clicking on tray yields the same result. Tray menu async execution appears in logs, but the server never restarts.