Skip to content

Move to Path::Tiny#1264

Closed
xsawyerx wants to merge 26 commits intomainfrom
cleanup/path-tiny
Closed

Move to Path::Tiny#1264
xsawyerx wants to merge 26 commits intomainfrom
cleanup/path-tiny

Conversation

@xsawyerx
Copy link
Copy Markdown
Member

@xsawyerx xsawyerx commented Oct 6, 2016

Path::Tiny provides a clean, correct, and consistent interface to various path-related modules. We have our own module, which could be improved.

I want to at least replace the core of it with Path::Tiny and maybe remove it at some point in favor.

This is a work in progress. I have more work to commit once the tests pass.

  • Clean up FileUtils internals.
  • Clean up usages of File::Spec.
  • Clean up usages of other File::* modules.
  • File::Basename -> path($path)->basename.
    (basedir is not available.)
  • Clean up usages of -d -> path($path)->is_dir.
  • Clean up usages of -f -> path($path)->is_file.
  • Clean up usages of -e -> path($path)->exists.

@xsawyerx xsawyerx changed the title Move to Path::Tin Move to Path::Tiny Oct 6, 2016
@xsawyerx
Copy link
Copy Markdown
Member Author

I have rewritten and rebased this branch. It's basically waiting on me to finish merge_path from the File Handler, but I need to first figure out what it's doing. :/

@xsawyerx
Copy link
Copy Markdown
Member Author

xsawyerx commented Mar 1, 2017

Last commit fixes that.
My next part is cleaning up the explicit calls to Path::Tiny::path() and then I'll start removing the stringify that exists everywhere.

@wchristian
Copy link
Copy Markdown
Contributor

wchristian commented Mar 1, 2017

That merge_paths commit is no good in that form, i'm afraid.

All of these should throw errors instead of "succeeding", otherwise they present huge security problems and/or break tests on systems with more than one file system tree. @haarg probably has other ideas of how this could break as well.

C:\Users\Mithaldu>perl -e "use Path::Tiny; print Path::Tiny::path( 'c:/meep', 'c:/marp' )->stringify"
C:/meep/c:/marp
C:\Users\Mithaldu>perl -e "use Path::Tiny; print Path::Tiny::path( 'c:/meep', 'd:/marp' )->stringify"
C:/meep/d:/marp
C:\Users\Mithaldu>perl -e "use Path::Tiny; print Path::Tiny::path( 'c:/meep', '../marp' )->stringify"
C:/marp

@xsawyerx
Copy link
Copy Markdown
Member Author

xsawyerx commented Mar 2, 2017

@wchristian, Thank you for testing them out. I'm not sure what these should have been doing to begin with. What does it mean to ask for "c:/meep", "d:/marp"? These are different volumes.

@wchristian
Copy link
Copy Markdown
Contributor

The first two are both straight-up non-sense in the vein of merge_path( "~/.ssh", "ftp://some.server/whatever" ) so they should error out, nothing else.

The third is "merely" dangerous and should be prevented.

@ambs
Copy link
Copy Markdown
Member

ambs commented Oct 8, 2017

@xsawyerx, ,what is missing here? Probably update the todo list above?

@xsawyerx
Copy link
Copy Markdown
Member Author

xsawyerx commented Sep 8, 2019

I just rebased this branch (had a few conflicts) and I wrote a solution to the issues raised by @Mithaldu.

The short answer is: 2 out of 3 cases get it wrong with both merge_paths (our code that I'm removing) and with Path::Tiny, but it's okay because our code will catch it.

The problem with the third case is a security issue, but not because of what Path::Tiny does. It does the right thing - but we assume the user is correct in doing this, which is a possible security issue.

I've resolved the third issue but I want to write some tests before I commit and push these too.

@xsawyerx
Copy link
Copy Markdown
Member Author

xsawyerx commented Sep 8, 2019

I added the fix and the tests. Seems be be good. I originally also patched Dancer2::Handler::File but coudn't find any test for it. Then I couldn't find anything using it. I made a syntax error in i and ran the entire testing suite - successful. Are we even using this module anymore?

@cromedome
Copy link
Copy Markdown
Contributor

You replaced the one place we were using Dancer2::Handler::File, so I think at this point we can effectively retire it.

This is good stuff. 👍 from me.

@SysPete SysPete force-pushed the cleanup/path-tiny branch from 77ef8a0 to c0fe438 Compare April 11, 2020 16:29
@SysPete
Copy link
Copy Markdown
Member

SysPete commented Apr 11, 2020

@xsawyerx I just rebased this against current master as I'd really like to see it merged. I'm going to go back over all of the comments to check that all issues are addressed, and then will add a further comment.

@SysPete
Copy link
Copy Markdown
Member

SysPete commented Apr 11, 2020

Some local test failures, but Travis is busy with maintenance atm. Lots of interesting appveyor failures too. I'll start looking tomorrow.

Comment on lines +38 to +39
$location->is_dir
or Carp::croak("Caller $script is not an existing file");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xsawyerx this is causing the only current (non-Windows) test failure, and was the only failure before I rebased. I know it is a long time ago, but can you remember why this was added?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm..... no memory at all.

I want to get back to this, but I can't promise when. Maybe Saturday?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xsawyerx Saturday would be cool, but no sweat. With luck I'll have time to look at the Windows test failures this weekend.

@cromedome cromedome added this to the April 2020 Release milestone Apr 13, 2020
@SysPete
Copy link
Copy Markdown
Member

SysPete commented May 30, 2020

Removing from milestone since this still needs a huge amount of work.

@SysPete SysPete removed this from the April 2020 Release milestone May 30, 2020
cromedome added a commit that referenced this pull request Mar 23, 2021
"The journey of a thousand miles begins with a single step." We have
discussed migrating to Path::Tiny (and started, see #1264 and #1544 for
details), and this is the first concrete, discrete step in doing so. It
takes a problem of small scope (app scaffolding and File::Find) to set
the stage for the rest of the migration.

There are a few TODO items here, and if there are better ways to handle
them, I am all ears. If not, I will remove my comments when I merge.

Feedback welcome/needed!
@xsawyerx xsawyerx self-assigned this Jan 29, 2026
Copy link
Copy Markdown
Contributor

@cromedome cromedome left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a lot of churn, but I find the result to be a codebase that's easier to read and maintain. I realize that may be personal preference, but it's a huge win IMO.

A couple of things:

  • I realize that you've eradicated File::Specfrom most tests. It is still being used in t/strict_config.t - might as well be consistent here
  • I have not retested on Windows, and I am not in a position to, so the test failures identified by @SysPete might still apply.

This is great!

@xsawyerx
Copy link
Copy Markdown
Member Author

xsawyerx commented Feb 1, 2026

I realize that you've eradicated File::Specfrom most tests. It is still being used in t/strict_config.t - might as well be consistent here

t/strict_config.t is in a different branch, not here. Once we merge this, I'll update that.

I have not retested on Windows, and I am not in a position to, so the test failures identified by @SysPete might still apply.

We do need to have this tested on Windows. @wchristian is also an expert on the matter and uncovered much of the mistakes in the previous attempt. (And those mistakes might not have been fixed...)

@SysPete
Copy link
Copy Markdown
Member

SysPete commented Feb 1, 2026

Great piece of work @xsawyerx , many thanks! Looking forward to seeing this merged. 👍 from me

@wchristian
Copy link
Copy Markdown
Contributor

I'll have a look next week. :)

@cromedome
Copy link
Copy Markdown
Contributor

Several Windows test failures to resolve:

t/00-compile.t ......................................... ok
t/00-report-prereqs.t .................................. ok
===(      64;15  1/?  0/?  0/?  0/? )===================================
#   Failed test 'Correct caller for app'
#   at t/app.t line 271.
#          got: 't\app.t'
#     expected: 't/app.t'
# Looks like you failed 1 test of 41.
t/app.t ................................................ Dubious, test returned 1 (wstat 256, 0x100)
Failed 1/41 subtests
t/app_alone.t .......................................... ok
t/author-distmeta.t .................................... ok
t/author-no-tabs.t ..................................... ok
t/author-pod-syntax.t .................................. ok
t/auto_page.t .......................................... ok
t/caller.t ............................................. ok
t/charset_server.t ..................................... ok
t/classes/Dancer2-Core-Factory/new.t ................... ok
t/classes/Dancer2-Core-Hook/new.t ...................... ok
t/classes/Dancer2-Core-Request/new.t ................... ok
t/classes/Dancer2-Core-Request/serializers.t ........... ok
t/classes/Dancer2-Core-Response-Delayed/after_hooks.t .. ok
t/classes/Dancer2-Core-Response-Delayed/new.t .......... ok
t/classes/Dancer2-Core-Response/new_from.t ............. ok
t/classes/Dancer2-Core-Role-Engine/with.t .............. ok
t/classes/Dancer2-Core-Role-Handler/with.t ............. ok
t/classes/Dancer2-Core-Role-HasConfig/with.t ........... ok
t/classes/Dancer2-Core-Role-HasEnvironment/with.t ...... ok
t/classes/Dancer2-Core-Role-HasLocation/with.t ......... ok
t/classes/Dancer2-Core-Role-Serializer/with.t .......... ok
t/classes/Dancer2-Core-Role-StandardResponses/with.t ... ok
t/classes/Dancer2-Core-Route/base.t .................... ok
t/classes/Dancer2-Core-Route/deprecated_param_keys.t ... ok
t/classes/Dancer2-Core-Route/match.t ................... ok
t/classes/Dancer2-Core-Runner/environment.t ............ ok
t/classes/Dancer2-Core-Runner/new.t .................... ok
t/classes/Dancer2-Core-Runner/psgi_app.t ............... ok
t/classes/Dancer2-Core/camelize.t ...................... ok
t/classes/Dancer2/import-pragmas.t ..................... ok
t/classes/Dancer2/import.t ............................. ok
t/config.t ............................................. ok
t/config_file_extended.t ............................... ok
t/config_many.t ........................................ ok
t/config_many_failure.t ................................ ok
t/config_multiapp.t .................................... ok
t/config_reader.t ...................................... ok
t/config_settings.t .................................... ok
t/config_utils.t ....................................... ok
t/context-in-before.t .................................. ok
t/cookie.t ............................................. ok
t/custom_dsl.t ......................................... ok
t/dancer-test.t ........................................ ok
t/deserialize.t ........................................ ok
t/disp_named_capture.t ................................. ok
===(    1044;28  0/?  0/?  0/? )========================================# Dispatch test 0, for GET /
===(    1045;29   1/20  0/?  0/?  0/? )=================================# Dispatch test 1, for GET /user/Johnny
# Dispatch test 2, for GET /twoohfour
# Dispatch test 3, for GET /haltme
t/dispatcher.t ......................................... ok
t/dsl/any.t ............................................ ok
t/dsl/app.t ............................................ ok
t/dsl/content.t ........................................ ok
t/dsl/delayed.t ........................................ skipped: AnyEvent required for this test
t/dsl/error_template.t ................................. ok
t/dsl/extend.t ......................................... ok
t/dsl/halt.t ........................................... ok
t/dsl/halt_with_param.t ................................ ok
t/dsl/json.t ........................................... ok
t/dsl/mime.t ........................................... ok
t/dsl/parameters.t ..................................... ok
t/dsl/pass.t ........................................... ok
t/dsl/path.t ........................................... ok
t/dsl/pod.t ............................................ ok
t/dsl/request.t ........................................ ok
t/dsl/request_data.t ................................... ok
t/dsl/route_retvals.t .................................. ok
===(    1194;35  1/?  0/?  0/?  0/? )===================================Subroutine from_json redefined at C:\Users\jason\code\Dancer2\.build\SdeS1i_cS8\blib\lib/Dancer2/Serializer/jSoN.pm line 13.
Subroutine to_json redefined at C:\Users\jason\code\Dancer2\.build\SdeS1i_cS8\blib\lib/Dancer2/Serializer/jSoN.pm line 15.
Subroutine decode_json redefined at C:\Users\jason\code\Dancer2\.build\SdeS1i_cS8\blib\lib/Dancer2/Serializer/jSoN.pm line 17.
Subroutine encode_json redefined at C:\Users\jason\code\Dancer2\.build\SdeS1i_cS8\blib\lib/Dancer2/Serializer/jSoN.pm line 23.
Subroutine serialize redefined at C:\Users\jason\code\Dancer2\.build\SdeS1i_cS8\blib\lib/Dancer2/Serializer/jSoN.pm line 30.
Subroutine deserialize redefined at C:\Users\jason\code\Dancer2\.build\SdeS1i_cS8\blib\lib/Dancer2/Serializer/jSoN.pm line 43.
t/dsl/send_as.t ........................................ ok
===(    1201;36  1/?  0/?  0/?  0/? )===================================[StaticContent:6136] error @2026-02-13 22:15:15> Route exception: Error resolving realpath on '/C:/Users/jason/code/Dancer2/.build/SdeS1i_cS8/t/dsl/send_file.t': No such file or directory at C:\Users\jason\code\Dancer2\.build\SdeS1i_cS8\blib\lib/Dancer2/Core/App.pm line 1141. in C:\Users\jason\code\Dancer2\.build\SdeS1i_cS8\blib\lib/Dancer2/Core/App.pm l. 1645

    #   Failed test 'send_file set status to 200 (no streaming)'
    #   at t/dsl/send_file.t line 123.
    #          got: '500'
    #     expected: '200'

    #   Failed test 'no streaming - content'
    #   at t/dsl/send_file.t line 124.
    #                   '<!DOCTYPE html>
    # <html lang="en">
    # <head>
    #   <meta charset="UTF-8">
    #   <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
    #   <title>Error 500 - Internal Server Error</title>
    #   <link rel="stylesheet" href="http://localhost/css/error.css">
    # </head>
    # <body>
    # <h1>Error 500 - Internal Server Error</h1>
    # <div id="content">
    # Wooops, something went wrong
    # </div>
    # <div id="footer">
    # Powered by <a href="http://perldancer.org/">Dancer2</a> 2.0.1
    # </div>
    # </body>
    # </html>
    # '
    #     doesn't match '(?^:package StaticContent)'
    # Looks like you failed 2 tests of 2.

#   Failed test 'no streaming'
#   at t/dsl/send_file.t line 125.
[StaticContent:6136] error @2026-02-13 22:15:15> Route exception: Error resolving realpath on '/C:/Users/jason/code/Dancer2/.build/SdeS1i_cS8/t/dsl/send_file.t': No such file or directory at C:\Users\jason\code\Dancer2\.build\SdeS1i_cS8\blib\lib/Dancer2/Core/App.pm line 1141. in C:\Users\jason\code\Dancer2\.build\SdeS1i_cS8\blib\lib/Dancer2/Core/App.pm l. 1645

    #   Failed test 'send_file set status to 200 (options streaming)'
    #   at t/dsl/send_file.t line 129.
    #          got: '500'
    #     expected: '200'

    #   Failed test 'options streaming - content'
    #   at t/dsl/send_file.t line 130.
    #                   '<!DOCTYPE html>
    # <html lang="en">
    # <head>
    #   <meta charset="UTF-8">
    #   <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
    #   <title>Error 500 - Internal Server Error</title>
    #   <link rel="stylesheet" href="http://localhost/css/error.css">
    # </head>
    # <body>
    # <h1>Error 500 - Internal Server Error</h1>
    # <div id="content">
    # Wooops, something went wrong
    # </div>
    # <div id="footer">
    # Powered by <a href="http://perldancer.org/">Dancer2</a> 2.0.1
    # </div>
    # </body>
    # </html>
    # '
    #     doesn't match '(?^:package StaticContent)'
    # Looks like you failed 2 tests of 2.

#   Failed test 'options streaming'
#   at t/dsl/send_file.t line 131.
[StaticContent:6136] error @2026-02-13 22:15:15> Route exception: Error resolving realpath on '/C:/Users/jason/AppData/Local/Temp/JIR0Y4HAc1': No such file or directory at C:\Users\jason\code\Dancer2\.build\SdeS1i_cS8\blib\lib/Dancer2/Core/App.pm line 1141. in C:\Users\jason\code\Dancer2\.build\SdeS1i_cS8\blib\lib/Dancer2/Core/App.pm l. 1645

    #   Failed test 'send_file returns success'
    #   at t/dsl/send_file.t line 136.

    #   Failed test 'send_file returns correct content_type'
    #   at t/dsl/send_file.t line 137.
    #          got: 'text/html'
    #     expected: 'image/png'
    # Looks like you failed 2 tests of 2.

#   Failed test 'send_file returns correct content type'
#   at t/dsl/send_file.t line 138.
# Looks like you failed 3 tests of 11.
t/dsl/send_file.t ...................................... Dubious, test returned 3 (wstat 768, 0x300)
Failed 3/11 subtests
t/dsl/splat.t .......................................... ok
t/dsl/to_app.t ......................................... ok
t/dsl/uri_for.t ........................................ ok
t/dsl/uri_for_route.t .................................. ok
t/dsl/yaml.t ........................................... ok
t/engine.t ............................................. ok
t/error.t .............................................. ok
t/examples/hello_world.t ............................... ok
===(    1253;39  0/?  0/?  0/? )========================================
#   Failed test 'path_or_empty on an existing path'
#   at t/file_utils.t line 57.
#          got: 'C:/Users/jason/AppData/Local/Temp/SgX8QZirCJ'
#     expected: 'C:\Users\jason\AppData\Local\Temp\SgX8QZirCJ'
# Looks like you failed 1 test of 14.
t/examples/simple_calculator.t ......................... ok
t/factory.t ............................................ ok
t/file_utils.t ......................................... Dubious, test returned 1 (wstat 256, 0x100)
Failed 1/14 subtests
t/forward.t ............................................ ok
t/forward_before_hook.t ................................ ok
t/forward_hmv_params.t ................................. ok
t/forward_test_tcp.t ................................... ok
===(    1329;41  0/?  0/?  0/? )========================================Useless use of a variable in void context at t/hooks.t line 317.
===(    1330;42  1/?  0/?  0/?  0/? )===================================
    #   Failed test 'before_file_render was called'
    #   at t/hooks.t line 260.
    #          got: undef
    #     expected: '1'

    #   Failed test 'after_file_render was called'
    #   at t/hooks.t line 261.
    #          got: undef
    #     expected: '1'
    # Looks like you failed 2 tests of 2.

#   Failed test 'file render hooks'
#   at t/hooks.t line 262.
# Looks like you failed 1 test of 9.
t/hooks.t .............................................. Dubious, test returned 1 (wstat 256, 0x100)
Failed 1/9 subtests
t/http_methods.t ....................................... ok
t/http_status.t ........................................ ok
t/issues/gh-1013/gh-1013.t ............................. ok
t/issues/gh-1046/gh-1046.t ............................. ok
t/issues/gh-1070.t ..................................... ok
t/issues/gh-1098.t ..................................... ok
t/issues/gh-1216/gh-1216.t ............................. ok
t/issues/gh-1226/gh-1226.t ............................. ok
t/issues/gh-1230/gh-1230.t ............................. ok
t/issues/gh-1232.t ..................................... ok
t/issues/gh-1289.t ..................................... ok
t/issues/gh-1564.t ..................................... ok
t/issues/gh-1621/gh-1621.t ............................. ok
t/issues/gh-1712/gh-1712.t ............................. ok
t/issues/gh-596.t ...................................... ok
t/issues/gh-634.t ...................................... ok
t/issues/gh-639/fails/issue.t .......................... ok
t/issues/gh-639/succeeds/issue.t ....................... ok
t/issues/gh-650/gh-650.t ............................... ok
t/issues/gh-723.t ...................................... ok
t/issues/gh-730.t ...................................... ok
t/issues/gh-762.t ...................................... ok
t/issues/gh-794.t ...................................... ok
t/issues/gh-797.t ...................................... ok
t/issues/gh-799.t ...................................... ok
t/issues/gh-811.t ...................................... skipped: Dancer2::Session::Cookie probably missing.
t/issues/gh-931.t ...................................... ok
t/issues/gh-936.t ...................................... ok
t/issues/gh-944.t ...................................... ok
t/issues/gh-975/gh-975.t ............................... ok
t/issues/memleak/die_in_hooks.t ........................ ok
t/issues/vars-in-forward.t ............................. ok
t/log_die_before_hook.t ................................ ok
t/log_levels.t ......................................... ok
t/logger.t ............................................. ok
t/logger_console.t ..................................... ok
t/memory_cycles.t ...................................... ok
t/mime.t ............................................... ok
t/multi_apps.t ......................................... ok
t/multi_apps_forward.t ................................. ok
t/multiapp_template_hooks.t ............................ ok
t/multipart_content.t .................................. ok
t/named_apps.t ......................................... ok
t/named_routes.t ....................................... ok
t/no_default_middleware.t .............................. ok
t/plugin2/app_dsl_cb/app_dsl_cb.t ...................... ok
t/plugin2/basic-2.t .................................... ok
t/plugin2/basic.t ...................................... ok
t/plugin2/define-keywords.t ............................ ok
t/plugin2/find_plugin.t ................................ ok
t/plugin2/from-config.t ................................ ok
t/plugin2/hooks.t ...................................... ok
t/plugin2/inside-plugin.t .............................. ok
t/plugin2/keywords-hooks-namespace.t ................... ok
t/plugin2/memory_cycles.t .............................. ok
t/plugin2/no-app-munging.t ............................. ok
t/plugin2/no-clobbering.t .............................. ok
t/plugin2/no-config.t .................................. ok
t/plugin2/with-plugins.t ............................... ok
t/plugin_import.t ...................................... ok
t/plugin_multiple_apps.t ............................... ok
t/plugin_register.t .................................... ok
t/plugin_syntax.t ...................................... ok
t/prepare_app.t ........................................ ok
===(    1624;64   1/25  0/?  0/?  0/? )=================================# If you want extra speed, install CGI::Deurl::XS
t/psgi_app.t ........................................... ok
t/psgi_app_forward_and_pass.t .......................... ok
t/redirect.t ........................................... ok
t/request.t ............................................ ok
===(    1782;65  0/?  0/?  0/? )========================================# If you want extra speed, install CGI::Deurl::XS
t/request_make_forward_to.t ............................ ok
t/request_multipart_formdata.t ......................... ok
===(    1786;66  1/?  0/?  0/?  0/? )===================================cannot unlink file for C:\Users\jason\AppData\Local\Temp\B0g9x\5HyiqHbHuL: Permission denied at C:/Strawberry/perl/lib/File/Temp.pm line 2643.
cannot unlink file for C:\Users\jason\AppData\Local\Temp\B0g9x\5MiPbT51y9: Permission denied at C:/Strawberry/perl/lib/File/Temp.pm line 2643.
cannot unlink file for C:\Users\jason\AppData\Local\Temp\B0g9x\ik5IHBfDxy: Permission denied at C:/Strawberry/perl/lib/File/Temp.pm line 2643.
cannot unlink file for C:\Users\jason\AppData\Local\Temp\B0g9x\tq4cr1TnPM: Permission denied at C:/Strawberry/perl/lib/File/Temp.pm line 2643.
cannot remove directory for C:/Users/jason/AppData/Local/Temp/B0g9x: Directory not empty at C:/Strawberry/perl/lib/File/Temp.pm line 2643.
cannot unlink file for C:\Users\jason\AppData\Local\Temp\K09ra\GVQvXQMtly: Permission denied at C:/Strawberry/perl/lib/File/Temp.pm line 2643.
cannot unlink file for C:\Users\jason\AppData\Local\Temp\K09ra\lD_JhkVyMZ: Permission denied at C:/Strawberry/perl/lib/File/Temp.pm line 2643.
cannot unlink file for C:\Users\jason\AppData\Local\Temp\K09ra\lL157iPU0E: Permission denied at C:/Strawberry/perl/lib/File/Temp.pm line 2643.
cannot unlink file for C:\Users\jason\AppData\Local\Temp\K09ra\Qor8czhBbb: Permission denied at C:/Strawberry/perl/lib/File/Temp.pm line 2643.
cannot remove directory for C:/Users/jason/AppData/Local/Temp/K09ra: Directory not empty at C:/Strawberry/perl/lib/File/Temp.pm line 2643.
t/request_upload.t ..................................... ok
t/response.t ........................................... ok
t/roles/hook.t ......................................... ok
t/route-pod-coverage/route-pod-coverage.t .............. ok
t/scope_problems/dispatcher_internal_request.t ......... ok
t/scope_problems/keywords_before_template_hook.t ....... ok
t/scope_problems/session_is_cleared.t .................. ok
t/scope_problems/with_return_dies.t .................... ok
t/serializer.t ......................................... ok
t/serializer_json.t .................................... ok
t/serializer_mutable.t ................................. ok
t/serializer_mutable_custom.t .......................... ok
t/session_bad_client_cookie.t .......................... ok
t/session_config.t ..................................... ok
t/session_engines.t .................................... ok
t/session_forward.t .................................... ok
t/session_hooks.t ...................................... ok
t/session_hooks_no_change_id.t ......................... ok
t/session_in_template.t ................................ ok
t/session_lifecycle.t .................................. ok
t/session_object.t ..................................... ok
t/session_yaml_object.t ................................ ok
t/shared_engines.t ..................................... ok
t/static_content.t ..................................... ok
t/template.t ........................................... ok
t/template_default_tokens.t ............................ ok
t/template_ext.t ....................................... ok
t/template_name.t ...................................... ok
t/time.t ............................................... ok
t/types.t .............................................. ok
t/vars.t ............................................... ok

Test Summary Report
-------------------
t/app.t                                              (Wstat: 256 (exited 1) Tests: 41 Failed: 1)
  Failed test:  41
  Non-zero exit status: 1
t/dsl/send_file.t                                    (Wstat: 768 (exited 3) Tests: 11 Failed: 3)
  Failed tests:  6-8
  Non-zero exit status: 3
t/file_utils.t                                       (Wstat: 256 (exited 1) Tests: 14 Failed: 1)
  Failed test:  8
  Non-zero exit status: 1
t/hooks.t                                            (Wstat: 256 (exited 1) Tests: 9 Failed: 1)
  Failed test:  2
  Non-zero exit status: 1
Files=184, Tests=2093, 78 wallclock secs ( 1.12 usr +  1.05 sys =  2.17 CPU)
Result: FAIL
Failed 4/184 test programs. 6/2093 subtests failed.
gmake: *** [makefile:1075: test_dynamic] Error 255
error running gmake test

I can try and tackle them, but I won't have an opportunity until at least Tuesday.

xsawyerx added 15 commits March 2, 2026 21:31
We're using "realpath" because it converts to absolute.
There are two important changes here.

The first defends against empty paths from the environment. We can
see that there was a situation in which we would have returned
an empty string (in case none of the three options worked) and
that's bad. We should fix it in the future.

The second is what might be a security issue. We use FileUtils'
path() which returns path based on root (/) if it's empty. There
is a situation in which the environments_location is empty (see
paragraph above) and in that case, we will accidentally use the
root directory. This does nothing if the file does not exist, but
this is now officially fixed with Path::Tiny and checking whether
it's empty or not.
This was a bit hard to figure out, but with the help of @wchristian,
it seems as though this merges path from argument 1 onto argument 2.

I have this replaced it with a single path() call. All tests pass,
so I hope this is okay. We should run these tests under Windows to
make sure it works there as well.
We're already using File::Basename. Why are we trying to lazily
load it now?
Instead of moving from Path::Tiny back to string as early as possible,
we're keeping it for a bit longer.

The defined() and -f checks can be done with Path::Tiny too. Then we
can just call openr_raw() instead of creating another Path::Tiny object.

Once we're done with that, we can finally stringify and go back to string
form.

Eventually, we would want to make all of our internals assume on Path::Tiny.
If someone were to send a file to send_file() that includes '../',
then we would allow them to reach outside the directory we choose.
This is a possible security issue. (One can argue the user should
sanitize their input, but I think we simply shouldn't allow it.)

The problem is that Path::Tiny does the right thing and allows us to
reach there. To prevent that, we're resolving paths using
Path::Tiny's realpath() method and then subsumes() to see that the
file is within the original directory.

Otherwise, we send a 403 forbidden. There is also a test that verifies
this is done correctly.
Integrate Path::Tiny even further into the core, using path semantics
like child/exists/is_file, and fewer conversions between string paths
and Path::Tiny objects.

The public path/config APIs are still strings.

* Now using Path::Tiny attrs in config locations, app paths, views,
public dir, sessions, logger, and template resolution.
* Public settings still return strings.
* File/static handling, session file paths, and logger file paths now use
  Path::Tiny for joins/existence checks, with stringification at
  boundaries.
* Error handling uses Path::Tiny for static error pages and backtrace file
  reads.
* CLI generator now emits string paths derived from Path::Tiny.
* Tests were updated to replace File::Spec with Path::Tiny, and the runtime
  dependency on File::Spec was removed from cpanfile.
@xsawyerx xsawyerx force-pushed the cleanup/path-tiny branch from 71cff85 to adc3493 Compare March 2, 2026 21:10
@xsawyerx
Copy link
Copy Markdown
Member Author

xsawyerx commented Mar 2, 2026

Rebased and applied a fix in App.pm and other minor fixes in tests. @cromedome if you can rerun this on some Windows, that'd be great.

@cromedome
Copy link
Copy Markdown
Contributor

All tests pass :) Thanks @xsawyerx!

@wchristian
Copy link
Copy Markdown
Contributor

i ran the test suite based on your branch and found 3 things that don't look like path issues and raised those as their own tickets

otherwise, the tests relating to path stuff seem to pass, however, the following smells like the time between file handles being closed and the test program ended is too little, resulting in a race condition when file::temp tries to clean up

t/request_upload.t ..................................... 1/? cannot unlink file for C:\Users\Mithaldu\AppData\Local\Temp\UL_XV\HsfybzjX17: Permission denied at D:/cpan/strawberry538/perl/lib/File/Temp.pm line 2643.
cannot unlink file for C:\Users\Mithaldu\AppData\Local\Temp\UL_XV\itApj9JgAF: Permission denied at D:/cpan/strawberry538/perl/lib/File/Temp.pm line 2643.
cannot unlink file for C:\Users\Mithaldu\AppData\Local\Temp\UL_XV\vbA6atPcnI: Permission denied at D:/cpan/strawberry538/perl/lib/File/Temp.pm line 2643.
cannot unlink file for C:\Users\Mithaldu\AppData\Local\Temp\UL_XV\wpCOS6ZpVU: Permission denied at D:/cpan/strawberry538/perl/lib/File/Temp.pm line 2643.
cannot remove directory for C:/Users/Mithaldu/AppData/Local/Temp/UL_XV: Directory not empty at D:/cpan/strawberry538/perl/lib/File/Temp.pm line 2643.
t/request_upload.t ..................................... ok

@cromedome
Copy link
Copy Markdown
Contributor

Rebased and merged. Thanks everyone!

@cromedome cromedome closed this Mar 5, 2026
@cromedome cromedome deleted the cleanup/path-tiny branch March 5, 2026 13:07
@wchristian
Copy link
Copy Markdown
Contributor

??? the request_upload.t errors still occur

@xsawyerx
Copy link
Copy Markdown
Member Author

xsawyerx commented Mar 5, 2026

@wchristian Created #1777 to track this so it doesn't get lost.

@cromedome
Copy link
Copy Markdown
Contributor

??? the request_upload.t errors still occur

It was easier to merge the branch and deal with the problems later than to keep rebasing the branch as we prepare to release this feature. Sorry for not saying as much when I merged this, and thanks @xsawyerx for doing the work I should have done to start with ;-)

cromedome added a commit that referenced this pull request Mar 12, 2026
    [ BUG FIXES ]
    * GH #686: Fix: to_json is double encoding UTF8 (Sawyer X)
    * GH #863: Fix case insensitive system confusion (Sawyer X)
    * GH #1124: Fix: charset config option is mostly ignored (Sawyer X)
    * GH #1143: Fix utf8 in URL (Sawyer X, Sorin Pop)
    * GH #1449, 1630: Make plugin DSL keyword app-specific (Sawyer X)
    * GH #1772: t/dsl/send_file.t fails with content_type-related errors
      (Sawyer X)
    * GH #1773: t/dsl/send_as.t throws json-related warnings (Jason
      A. Crome)
    * GH #1774: t/hooks.t throws void warnings (Jason A. Crome)
    * GH #1777: Properly unlink file uploads on Windows (Sawyer X)

    [ ENHANCEMENTS ]
    * GH #763: Strict config mode; warn on unknown config keys with
      opt-out (Sawyer X)
    * GH #763: Default strict config to off, but scaffold new apps with
      stict config enabled (Jason A. Crome)
    * GH #1073: Get multiple session cookie values at once with clear
      method (Sawyer X)
    * GH #1264: Move to Path::Tiny (Sawyer X)
    * GH #1323: Allow fully qualified namespaces for all engines (Russell
      @veryrusty Jenkins)
    * GH #1594: Use Unicode::UTF8 if available (Sawyer X)
    * GH #1664: Stop sending double server headers (Sawyer X)
    * GH #1709: `send_as` should use the full serializer, including hooks
      (Sawyer X)
    * PR #1757: Remove api_version, improve dispatching loop (Sawyer X)
    * PR #1758: Move MIME ownership to app (Sawyer X)
    * PR #1767: Turn off strict_config for noisy tests (Jason A. Crome)
    * PR #1780: Add package name capability to logger output (Mikko
      Koivunalho)

    [ DOCUMENTATION ]
    * GH #1431: Better document behavior of views setting (Jason A. Crome)
    * PR #1749, #1750: Fix broken manual and tutorial links (Gil Magno,
      Jason A. Crome)
    * PR #1753: Fix structure of config docs (Mikko Koivunalho)
    * PR #1762: Remove keyword logger from DSL document (Mikko Koivunalho)

    [ DEPRECATED ]
    * None

    [ MISC ]
    * PR #1776: Remove "Powered by..." from the error page (Jason A. Crome)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants