Skip to main content

Module write_guard

Module write_guard 

Source
Expand description

Confine the files that a run writes on request (baselines, snapshots and report files) to the project.

A path must resolve inside the project root, or inside the Git work tree that contains the root when the working directory is inside that tree too, or inside a shared directory: the CI workspace (GITHUB_WORKSPACE, GitLab CI_PROJECT_DIR) and the temp directories (RUNNER_TEMP, the system temp directory), each when it is set. An existing character device or named pipe (/dev/null, /dev/stdout, process substitution) is also allowed, because a write to it cannot put a file anywhere. On Windows the null device (NUL) is allowed for the same reason.

The command line layer checks each path before the analysis starts and then records the scope with confine. The writers call create_file or write_file, which resolve and check the path again right before the write and do not follow a symlink at the final component.

This narrows the window for a path that another local user swaps for a symlink, from the whole analysis to the moment of the write. It does not close the window for an intermediate directory: a directory swapped between the last parent check and the open call is still followed. On Windows the final component is checked just before the open, so a small window stays there too.

Symlinks are resolved on both sides: on the part of the path that exists, and on each allowed directory. So a link inside the root that points outside it does not open a way out, and /var and /private/var on macOS compare equal.

Structs§

WriteScope
The directories a run may write into.

Enums§

WriteFailure
Why a confined write did not happen.
WriteTarget
What kind of file a write targets. The kind selects the scope that the write is checked against.

Functions§

confine
Record the scopes that later writes are checked against. The first call wins. Without a call, writes are not confined, but they still do not follow a symlink at the final component that appears after the path was resolved.
create_file
Create or truncate path for writing, after the checks this module describes. Missing parent directories are created.
resolve
Resolve path the way a write would reach it: symlinks and .. in the part that exists are resolved by the file system, and the rest, which a write would create, is normalised lexically. A dangling symlink is followed to its target, because a write through it lands there.
shared_dirs
The shared directories a run may write into: each directory in GITHUB_WORKSPACE, CI_PROJECT_DIR and RUNNER_TEMP that is set and not empty, and the system temp directory. The CI workspace keeps a job working that checks the repository out into a subdirectory and writes its report beside it. A directory that does not exist is skipped, because it cannot be resolved.
write_file
Write contents to path through create_file.