{"id":16986033,"url":"https://github.com/rayne/virtual-path","last_synced_at":"2025-04-12T03:32:36.025Z","repository":{"id":62533597,"uuid":"106043097","full_name":"Rayne/virtual-path","owner":"Rayne","description":"The VirtualPath library normalises paths and prevents directory traversal attacks without querying a file system","archived":false,"fork":false,"pushed_at":"2019-11-10T19:16:33.000Z","size":39,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-25T23:07:36.707Z","etag":null,"topics":["composer-library","jailed-path","path-normalisation","path-normalization","php-library"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Rayne.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-10-06T19:33:46.000Z","updated_at":"2023-09-08T07:24:58.000Z","dependencies_parsed_at":"2022-11-02T16:01:13.555Z","dependency_job_id":null,"html_url":"https://github.com/Rayne/virtual-path","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rayne%2Fvirtual-path","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rayne%2Fvirtual-path/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rayne%2Fvirtual-path/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Rayne%2Fvirtual-path/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Rayne","download_url":"https://codeload.github.com/Rayne/virtual-path/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248512987,"owners_count":21116725,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["composer-library","jailed-path","path-normalisation","path-normalization","php-library"],"created_at":"2024-10-14T02:44:41.523Z","updated_at":"2025-04-12T03:32:35.993Z","avatar_url":"https://github.com/Rayne.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Rayne\\VirtualPath\n\nThe `VirtualPath` library normalises paths and\nprevents directory traversal attacks\nwithout querying a file system.\n\n[![Latest Stable Version](https://poser.pugx.org/rayne/virtual-path/v/stable)](https://packagist.org/packages/rayne/virtual-path)\n[![Code Coverage](https://scrutinizer-ci.com/g/rayne/virtual-path/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/rayne/virtual-path/?branch=master)\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/rayne/virtual-path/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/rayne/virtual-path/?branch=master)\n[![License](https://poser.pugx.org/rayne/virtual-path/license)](https://packagist.org/packages/rayne/virtual-path)\n\n## Contents\n\n* [Installation](#installation)\n* [Dependencies](#dependencies)\n* [Usage](#usage)\n* [Examples](#examples)\n* [Implementation Details](#implementation-details)\n* [Tests](#tests)\n\n## Installation\n\nIt's recommended to use the dependency manager\n[Composer](https://getcomposer.org/download)\nto install `rayne/virtual-path`.\n\n```bash\ncomposer require rayne/virtual-path\n```\n\n## Dependencies\n\n* PHP 5.6 or better\n\n## Usage\n\nThe `VirtualPath` class normalises inputs to absolute virtual paths\nwithout querying any file system.\nIt also detects and flags directory traversal attacks.\n\nThe `JailedPath` class utilises `VirtualPath` to build safe paths\nwhich can be used for working with real files.\nThe normalisation is done relative to a `jail` called path\nwhich is used as virtual root for any path entered by the user.\nAs `JailedPath` does not query the file system\nit's suited for working with local, remote or fictional paths.\n\nPlease read the [Implementation Details](#implementation-details) section for more details.\n\n**TL;DR** *Use the `JailedPath` class when in doubt.*\n\n## Examples\n\n### `JailedPath`\n\nIn this example website visitors are allowed to download any file\nfrom the local directory `/test`\nby specifying the relative path as `GET` parameter.\nTo prevent users from leaving the directory with directory traversal attacks,\n`JailedPath` is used with `/test` as the virtual root directory.\n\n```php\n\u003c?php\n\nuse Rayne\\VirtualPath\\JailedPath;\n\n$jailedPath = new JailedPath('/test', $_GET['path'] ?? '');\n\nif ($jailedPath-\u003ehasJailbreakAttempt()) {\n    // Log jailbreak attempt, ban user, …\n    return;\n}\n\nif (is_file($jailedPath-\u003egetAbsolutePath())) {\n    @readfile($jailedPath-\u003egetAbsolutePath());\n}\n```\n\nThe following table shows how user defined paths are normalised\nand how they are interpreted relative to the virtual root.\n\nUser Input       | `hasJailbreakAttempt()` | `getAbsolutePath()` | `getRelativePath()`\n-----------------|:-----------------------:|---------------------|---\nEmpty String     | `false`                 | `/test`             | Empty String\n`.`              | `false`                 | `/test`             | Empty String\n`a.png/../b.png` | `false`                 | `/test/b.png`       | `b.png`\n`/a/./b`         | `false`                 | `/test/a/b`         | `a/b`\n`..`             | `true`                  | `/test`             | Empty String\n`../example`     | `true`                  | `/test/example`     | `example`\n`../etc/passwd`  | `true`                  | `/test/etc/passwd`  | `etc/passwd`\nArray            | `true`                  | `/test`             | Empty String\n\n### `VirtualPath`\n\nIf a fixed prefix or the sugar coating of `JailedPath` isn't required,\nthen `VirtualPath` is sufficient as it is the class used for normalising paths.\n`VirtualPath` normalises the input and provides a trusted\n(normalised, with a leading `/`)\nand an untrusted\n(a string representation of the probably malicious user input)\npath.\n\nThe previous example can be easily recreated with `VirtualPath`\nwhen the instance of `VirtualPath` (which is `(string)` cast-able)\nis appended to the virtual root directory.\n\n```php\n\u003c?php\n\nuse Rayne\\VirtualPath\\VirtualPath;\n\n$path = new VirtualPath($_GET['path'] ?? '');\n$absolutePath = '/test' . $path;\n```\n\nDepending on the usage scenario it's sometimes useful to work with the\nnormalised trusted path even if the original input is not trustworthy,\ne.g. when explicitly supporting relative paths\nand giving the user the benefit of doubt when *accidentally*\ntrying to access files outside of the virtual path.\n\n**Note**: `VirtualPath` returns the normalised path with a leading `/`.\nWhen working with files it's recommended to add a trusted path as prefix\n(see code example in the current section)\nas otherwise files relative to the file system's root would be referenced.\n*To not forget to add the prefix use the `JailedPath` class instead when working with real files.*\n\nInput                 |  `isTrusted()` |  `getTrustedPath()` | `getUntrustedPath()`\n----------------------|:--------------:|---------------------|-------------------\nArray                 | `false`        | `/`                 | Empty String\nEmpty String          | `true`         | `/`                 | Empty String\n`../articles`         | `false`        | `/articles`         | `../articles`\n`tags/../../articles` | `false`        | `/articles`         | `tags/../../articles`\n`tags/../articles`    | `true`         | `/articles`         | `tags/../articles`\n`../etc/passwd`       | `false`        | `/etc/passwd`       | `../etc/passwd`\n`/etc/passwd`         | `true`         | `/etc/passwd`       | `/etc/passwd`\n`etc/passwd`          | `true`         | `/etc/passwd`       | `etc/passwd`\n\n## Implementation Details\n\nUsing a pure virtual normalised path has different benefits:\n\n* Path normalisation is done without querying a file system\n\n* It's impossible to forge timing attacks for files\n  outside of the scope of the virtual path\n\n* No complex comparisons are required to limit directory traversals\n  to a specific directory and its children\n\n* Only `.`, `..`, `\\` (normalised to `/`) and `/` are interpreted for path normalisation\n\n* No unexpected and information leaking `~` expansions as seen in other libraries\n\nThe implementation of `VirtualPath` does not interpret,\nalter or remove control characters and Unicode:\n\n* Directory and file paths are allowed to contain control characters on some systems\n\n* Removing control characters is out of scope for the library\n\n## Tests\n\n1. Clone the repository\n\n   ```bash\n   git clone https://github.com/rayne/virtual-path.git\n   ```\n\n2. Install the development dependencies\n\n   ```bash\n   composer install --dev\n   ```\n\n3. Run the tests\n\n   ```bash\n   composer test\n   ```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frayne%2Fvirtual-path","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frayne%2Fvirtual-path","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frayne%2Fvirtual-path/lists"}