Skip to content

Commit d246e49

Browse files
kenodegardjaimergpbeckermr
authored
Merge commit from fork
* Downgrade necessary permissions for build script * Use atomic moves to minimize attack window * add news * minimize windows further * use opener * Provide new helper context manager to handle atomic writes and permission setting * pre-commit * clarify need for umask reset * use secrets.token_urlsafe with 64 characters * Revert "Fix transitive subpackage dependency resolution (#5603)" (#5647) * Update conda_build/utils.py Co-authored-by: Ken Odegard <kodegard@anaconda.com> * Update conda_build/utils.py --------- Co-authored-by: jaimergp <jaimergp@users.noreply.github.com> Co-authored-by: Matthew R. Becker <beckermr@users.noreply.github.com>
1 parent 70e79f2 commit d246e49

File tree

4 files changed

+75
-6
lines changed

4 files changed

+75
-6
lines changed

conda_build/build.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
)
7272
from .utils import (
7373
CONDA_PACKAGE_EXTENSIONS,
74+
create_file_with_permissions,
7475
env_var,
7576
glob,
7677
on_mac,
@@ -3063,24 +3064,24 @@ def write_build_scripts(m, script, build_file):
30633064

30643065
work_file = join(m.config.work_dir, "conda_build.sh")
30653066
env_file = join(m.config.work_dir, "build_env_setup.sh")
3066-
with open(env_file, "w") as bf:
3067+
3068+
with create_file_with_permissions(env_file, 0o600) as bf:
30673069
for k, v in env.items():
30683070
if v != "" and v is not None:
30693071
bf.write(f'export {k}="{v}"\n')
3070-
30713072
if m.activate_build_script:
30723073
_write_sh_activation_text(bf, m)
3073-
with open(work_file, "w") as bf:
3074+
3075+
with create_file_with_permissions(work_file, 0o700) as bf:
30743076
# bf.write('set -ex\n')
30753077
bf.write("if [ -z ${CONDA_BUILD+x} ]; then\n")
3076-
bf.write(f" source {env_file}\n")
3078+
bf.write(f" source '{env_file}'\n")
30773079
bf.write("fi\n")
30783080
if script:
30793081
bf.write(script)
30803082
if isfile(build_file) and not script:
3081-
bf.write(open(build_file).read())
3083+
bf.write(Path(build_file).read_text())
30823084

3083-
os.chmod(work_file, 0o766)
30843085
return work_file, env_file
30853086

30863087

conda_build/utils.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import mmap
1212
import os
1313
import re
14+
import secrets
1415
import shutil
1516
import stat
1617
import subprocess
@@ -2260,3 +2261,39 @@ def is_conda_pkg(pkg_path: str) -> bool:
22602261

22612262
def package_record_to_requirement(prec: PackageRecord) -> str:
22622263
return f"{prec.name} {prec.version} {prec.build}"
2264+
2265+
2266+
@contextlib.contextmanager
2267+
def set_umask(mask: int = 0) -> Iterator[None]:
2268+
current = os.umask(mask)
2269+
yield
2270+
os.umask(current)
2271+
2272+
2273+
@contextlib.contextmanager
2274+
def create_file_with_permissions(path: str, permissions: int):
2275+
"""
2276+
Opens a new file for writing, with permissions set from creation time.
2277+
This is achieved by creating a temporary directory in the same parent
2278+
directory, opening a new file inside with the right permissions,
2279+
yielding the descriptor so the caller can add the necessary contents,
2280+
and then moving the temporary file to the target location, with preserved
2281+
permissions.
2282+
2283+
The umask is temporarily reset during this process, and then restored.
2284+
This is needed so permissions can be applied as intended. Without a zeroed
2285+
umask, the system umask might filter the passed value to a different one.
2286+
For example, given a system umask=022, passing 666 will result in a file
2287+
with permissions 644.
2288+
"""
2289+
2290+
def opener(path, flags):
2291+
return os.open(path, flags, mode=permissions)
2292+
2293+
dirname = os.path.dirname(path)
2294+
with set_umask(), TemporaryDirectory(dir=dirname) as tmpdir:
2295+
tmp_path = os.path.join(tmpdir, secrets.token_urlsafe(64))
2296+
with open(tmp_path, "w", opener=opener) as fh:
2297+
yield fh
2298+
2299+
shutil.move(tmp_path, path)

news/build-script-permissions

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
### Enhancements
2+
3+
* <news item>
4+
5+
### Bug fixes
6+
7+
* Use more adequate permissions for temporary build scripts written to `$SRC_DIR`.
8+
9+
### Deprecations
10+
11+
* <news item>
12+
13+
### Docs
14+
15+
* <news item>
16+
17+
### Other
18+
19+
* <news item>

tests/test_api_build.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2179,3 +2179,15 @@ def test_api_build_pytorch_cpu_issue5644(tmp_path, testing_config):
21792179
os.environ["CONDA_SUBDIR"] = old_subdir
21802180
else:
21812181
del os.environ["CONDA_SUBDIR"]
2182+
2183+
2184+
def test_build_script_permissions(testing_config):
2185+
recipe = os.path.join(metadata_dir, "_noarch_python")
2186+
metadata = api.render(
2187+
recipe, config=testing_config, dirty=True, remove_work_dir=False
2188+
)[0][0]
2189+
api.build(metadata, notest=True)
2190+
build_script = os.path.join(metadata.config.work_dir, "conda_build.sh")
2191+
assert (os.stat(build_script).st_mode & 0o777) == 0o700
2192+
env_script = os.path.join(metadata.config.work_dir, "build_env_setup.sh")
2193+
assert (os.stat(env_script).st_mode & 0o777) == 0o600

0 commit comments

Comments
 (0)