Repository: mesonbuild/meson

Issue:
Wrap target directory not checked
No check is performed on the target directory for `wrap` files.
Necessary `subdir()` checks are applied too late in process.

Initial clone can lead to `out-of-tree` directory creation in arbitrary location.
Large amounts of data may be downloaded to unusable location.


Discussion:
A python runtime error arises when no `directory` value is set at all.
The (at the moment) working variant `directory=../<dirname>` can be accepted by a filter.

My suggestion would be to generate a target directory derived from a wraps filename at a default location inside the build directory (e.g `<builddir>/meson-wraps/<wrapname>`). This would not conflict with existing projects and at the same time gracefully pave the way for **real** out-of-tree builds.

Context:
- As pointed out by @nirbheek in #1399, wrapped subprojects are considered sources at the moment.
- Modifying the source directory violates the concept of a **pure** out-of-tree build.

Resulting problems:
- The mentioned sharing allows one build to disrupt (`git pull`, `hg pull`) the consistency of another even with no manual changes to the source directory.
- Workarounds are required if the `meson` user has no write access to source directories.

Considering semantics I would argue that a wrapped subproject may (as a whole) be regarded on par with **external** dependencies. As such it's referenced **contents** are **not** required to be recreated inside the **source** directory.
The hacky version `directory=../<dirname>` stopped working because `meson` now misattributes local sources as belonging to a different subproject.
Absolute value for `directory` is caught on first `subdir()` in subproject.

Out-of-tree vulnerability (absolute **and** relative) still applies (clone succeeds) because validity checks are applied far too late.
But easy fix via "*dirname must be valid subdirectory*"-check can now be used without risking further backward breakage.
Unset `directory` still ~~crashes python~~ available to place checkout in build directory.
Interpreter crash on missing directory (now) mitigated/resolved by default name in 76ac4f5 (ruling out `<builddir>/meson-wraps/<wrapname>` checkout trigger).
Is there still an issue here with the recent refactoring of the wrap file handling?
The *out-of-tree* write on wrap checkout (can write anywhere, the *severe* problem) due to `directory` target location checks being applied too late still persists (*initial issue title and description*).

The interpreter crash had no (or unknown) security implications. That 2nd (minor) issue I thoughtlessly mixed in is now fixed. :+1:
*I just assumed the new default value might have been the relevant change.*
I guess the fix here is to simply check that `os.path.dirname(directory) ==''`. I don't think we ever intentionally allowed the directory to be a path, it was supposed to just be a name. Right?

The other point here is to checkout subprojects inside builddir. That seems to make sense. I guess wrap file would have a `fetch_in_builddir=True` value?
An error is already thrown if `directory` not inside the local `subproject` folder at a later time. But then *clone*/*checkout* already downloaded files to a `wrap` file controlled location.
`meson` should refuse an operation which is guaranteed to cause errors in a later step (after wasting resources).

It may even be a nice way to drop a `~/ssh/authorized_keys` file on a badly managed CI server (or similar action).

Resolving patch:
diff --git a/mesonbuild/wrap/wrap.py b/mesonbuild/wrap/wrap.py
--- a/mesonbuild/wrap/wrap.py
+++ b/mesonbuild/wrap/wrap.py
@@ -114,6 +114,8 @@ def resolve(self, packagename):
         self.wrap = self.load_wrap()
         if self.wrap and 'directory' in self.wrap.values:
             self.directory = self.wrap.get('directory')
+            if os.path.dirname(self.directory):
+                raise WrapException('Directory key must be a name and not a path')
         self.dirname = os.path.join(self.subdir_root, self.directory)
         meson_file = os.path.join(self.dirname, 'meson.build')
 


Test patch: