-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Enforce read-only-ness of package directory even when it's inside writable directories #3996
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enforce read-only-ness of package directory even when it's inside writable directories #3996
Conversation
…p and other writable directories The sandbox supports a list of `writableDirectories`, which are treated as path component prefixes after package resolution. The are applied to the sandbox rules after the default-deny and everything-is-read-only rules, carving out cones of writability in specific areas of the file system. But if the package directory is under one of these directories that is allowed by default, then it is implicitly writable even in the absence `--allow-writing-to-package-directory`. It also makes unit tests awkward, since they usually create temporary test fixtures inside the temporary directory. This adds a set of `readOnlyDirectories` in addition to the writable ones, so that the package directory remains unwritable regardless of where it is located. It also adds some documentation in the Sandbox source file, and it removes a somewhat confusing `--block-writing-to-package-directory` flag that was added mostly for unit tests but that is no longer needed.
@@ -895,10 +895,6 @@ extension SwiftPackageTool { | |||
help: "Allow the plugin to write to an additional directory") | |||
var additionalAllowedWritableDirectories: [String] = [] | |||
|
|||
@Flag(name: .customLong("block-writing-to-temporary-directory"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This flag was just added, so nobody is depending on it yet, and it was mostly for unit tests — it seems much clearer to always make the package directory read-only, even for packages checked out to /tmp
or other implicitly writable places.
@@ -979,13 +975,6 @@ extension SwiftPackageTool { | |||
if allowWritingToPackageDirectory { | |||
writableDirectories.append(package.path) | |||
} | |||
if !blockWritingToTemporaryDirectory { | |||
writableDirectories.append(AbsolutePath("/tmp")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The temporary directories are now again handled by the strictness.
if allowWritingToPackageDirectory { | ||
writableDirectories.append(package.path) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was actually redundant with lines 975-977 above.
// Make sure that the package path is read-only unless it's covered by any of the explicitly writable directories. | ||
let readOnlyDirectories = writableDirectories.contains{ package.path.isDescendantOfOrEqual(to: $0) } ? [] : [package.path] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is subject to debate. It avoids marking the package path as readonly if it's covered by any of the directories marked as explicitly writable. Maybe what should be done here in the long term is to have a single list of directory-specific permissions, each of which could be either block
or allow
and then the caller can craft their sandbox directories as they want to. For example, it might make sense to allow a plugin to modify only a subdirectory of the package. We can extend this in the future — probably better to keep it simple for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
starting simple makes sense to me. having the sandbox API accepts a list of allow/deny and having the call site define that make sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
// swift-tools-version: 999.0 | ||
// swift-tools-version: 5.6 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an incidental fix that will be needed once we mark the 5.6 branch as no longer a development branch (so it stops accepting 999). The PR that added this was still open when all the plugins were changed from 999 to 5.6 after evolution proposal acceptance.
@swift-ci please smoke test |
case manifest_pre_53 // backwards compatibility for manifests | ||
/// Like `default`, but also makes temporary-files directories (such as `/tmp`) on the platform writable. | ||
case writableTemporaryDirectory |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would it better to have the call site pass the temp dirs as writable locations instead of defining a new strictness option for that? alternatively, should this be an argument on "default" strictness instead of it's own case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had that (passing in the temporary directories from the client) initially actually, but that pushes the platform-specific nature of these paths onto the client. In addition to the paths, there could actually be a varying number of them on different platforms. But I agree that this shouldn't really be part of the strictness, but rather an option on the default strictness as you suggest. I think there are more of those in the future, such as whether or not to allow network, etc.
So yes, in retrospect I think it was a mistake to add the temporary directory handling as a separate strictness level, and it's something to be unwound in the future.
Really what I'd like to do is to change the Sandbox
struct to a SandboxProfile
or some other name that suggests that it's a set of options, and then that profile could be configured once and passed around so it can be applied to multiple calls later without having to thread through all the individual parameters (and perhaps also cached using operating system calls based on the contents of that profile). Then as we add more parameters, none of the intermediate code has to change. The code that calls the subprocess could just apply the same profile to one command after another. I'm hoping to get to that improvement soonish, in particular since I'm hopeful that it could allow compilation of the profile on macOS just once and the reuse of the compiled profile, saving time when starting sandboxed subprocesses.
At that point it would make sense to get rid of strictness, I think, and just have a set of individually controllable options, and then a convenience initializer to create a SandboxProfile
that matches the SwiftPM 5.3 semantics. That special case could then exist outside of the SandboxProfile
code itself as an extension, which seems more correct than leaking it into the sandbox API.
All of course for a future PR, but hopefully not too long in the future.
…table directories (swiftlang#3996) * Enforce read-only-ness of package directory even when it's inside /tmp and other writable directories The sandbox supports a list of `writableDirectories`, which are treated as path component prefixes after package resolution. The are applied to the sandbox rules after the default-deny and everything-is-read-only rules, carving out cones of writability in specific areas of the file system. But if the package directory is under one of these directories that is allowed by default, then it is implicitly writable even in the absence `--allow-writing-to-package-directory`. It also makes unit tests awkward, since they usually create temporary test fixtures inside the temporary directory. This adds a set of `readOnlyDirectories` in addition to the writable ones, so that the package directory remains unwritable regardless of where it is located. It also adds some documentation in the Sandbox source file, and it removes a somewhat confusing `--block-writing-to-package-directory` flag that was added mostly for unit tests but that is no longer needed. (cherry picked from commit c117c86)
…table directories (#3996) (#3999) * Enforce read-only-ness of package directory even when it's inside /tmp and other writable directories The sandbox supports a list of `writableDirectories`, which are treated as path component prefixes after package resolution. The are applied to the sandbox rules after the default-deny and everything-is-read-only rules, carving out cones of writability in specific areas of the file system. But if the package directory is under one of these directories that is allowed by default, then it is implicitly writable even in the absence `--allow-writing-to-package-directory`. It also makes unit tests awkward, since they usually create temporary test fixtures inside the temporary directory. This adds a set of `readOnlyDirectories` in addition to the writable ones, so that the package directory remains unwritable regardless of where it is located. It also adds some documentation in the Sandbox source file, and it removes a somewhat confusing `--block-writing-to-package-directory` flag that was added mostly for unit tests but that is no longer needed. (cherry picked from commit c117c86)
…der `<pkgdir>/.build` The sandbox rules introduced in swiftlang#3996 made that the entire package directory read-only, but that isn't appropriate when `.build` is inside the package directory. This changes the order of the operations so that the `allow` comes after the `deny`, but still after the temporary directories. In order to make this more flexible this coalesces the lists of writable and read-only directories into a single `pathRules` list of a new enum type that allows writing or read-only paths, and allows the caller to control the order. Also, writability of temporary directories is pulled into an option on the `default` strictness. rdar://87417780
…der `<pkgdir>/.build` The sandbox rules introduced in swiftlang#3996 made the entire package directory read-only, but that isn't appropriate when `.build` is inside the package directory. This change turns the stateless Sandbox enum into a SandboxProfile struct that can be configured and passed around well before being applied, and it makes the configuration more flexible in several regards: - there is a list of path rules allowing a mixed order of `allow` and `deny` rules - the choice of allowing writing to temporary directories is independent of anything other choice - the defaults are built into the initializer rather than a separate `strictness` parameter — this means that they can be queried once the SandboxProfile has been created and are expressed in terms of the choices available to all sandbox profiles. Having the sandbox profile be a struct that generates the platform specifics as needed also provides a place with which to associate any cached/compiled representation for profiles that are largely static. As cleanup, this commit also removes the pre-SwiftPM 5.3 specialities which were specific to running the package manifest in an interpreter rather than compiling and executing it. This functionality is no longer relevant since it isn't possible to run any manifest in the interpreter. In this commit, the call sites have been adjusted so that they use the modified SandboxProfile API, but still construct the profiles on-the-fly as before. A future change could make them instead be configured at an early point and then applied later when the sandboxed process is actually launched. Another future change could add the sandbox profile to Process as a property so that there is no API assumption that applying a sandbox necessarily involves just modifying the command line. rdar://87417780
…der `<pkgdir>/.build` The sandbox rules introduced in swiftlang#3996 made the entire package directory read-only, but that isn't appropriate when `.build` is inside the package directory. This change turns the stateless Sandbox enum into a SandboxProfile struct that can be configured and passed around well before being applied, and it makes the configuration more flexible in several regards: - there is a list of path rules allowing a mixed order of `allow` and `deny` rules - the choice of allowing writing to temporary directories is independent of anything other choice - the defaults are built into the initializer rather than a separate `strictness` parameter — this means that they can be queried once the SandboxProfile has been created and are expressed in terms of the choices available to all sandbox profiles. Having the sandbox profile be a struct that generates the platform specifics as needed also provides a place with which to associate any cached/compiled representation for profiles that are largely static. As cleanup, this commit also removes the pre-SwiftPM 5.3 specialities which were specific to running the package manifest in an interpreter rather than compiling and executing it. This functionality is no longer relevant since it isn't possible to run any manifest in the interpreter. In this commit, the call sites have been adjusted so that they use the modified SandboxProfile API, but still construct the profiles on-the-fly as before. A future change could make them instead be configured at an early point and then applied later when the sandboxed process is actually launched. Another future change could add the sandbox profile to Process as a property so that there is no API assumption that applying a sandbox necessarily involves just modifying the command line. rdar://87417780
…der `<pkgdir>/.build` The sandbox rules introduced in swiftlang#3996 made the entire package directory read-only, but that isn't appropriate when `.build` is inside the package directory. The rules for applying read-only directories were intended to shadow the implicit rules added by specifying a writable temporary directory, not to block the ones specified by explicit writable directories. This fixes that ordering, and adds the missing unit test that would have caught the problem. A future change reworks Sandbox completely so that it becomes a struct that can carry around the sandbox profile configuration until it is applied — this change is intended to be small enough to be nominatable rdar://87417780
…der `<pkgdir>/.build` The sandbox rules introduced in swiftlang#3996 made the entire package directory read-only, but that isn't appropriate when `.build` is inside the package directory. The rules for applying read-only directories were intended to shadow the implicit rules added by specifying a writable temporary directory, not to block the ones specified by explicit writable directories. This fixes that ordering, and adds the missing unit test that would have caught the problem. A future change reworks Sandbox completely so that it becomes a struct that can carry around the sandbox profile configuration until it is applied — this change is intended to be small enough to be nominatable rdar://87417780
…der `<pkgdir>/.build` (#4009) The sandbox rules introduced in #3996 made the entire package directory read-only, but that isn't appropriate when `.build` is inside the package directory. The rules for applying read-only directories were intended to shadow the implicit rules added by specifying a writable temporary directory, not to block the ones specified by explicit writable directories. This fixes that ordering, and adds the missing unit test that would have caught the problem. A future change reworks Sandbox completely so that it becomes a struct that can carry around the sandbox profile configuration until it is applied — this change is intended to be small enough to be nominatable rdar://87417780
…t's under `<pkgdir>/.build` The sandbox rules introduced in swiftlang#3996 made the entire package directory read-only, but that isn't appropriate when `.build` is inside the package directory. The rules for applying read-only directories were intended to shadow the implicit rules added by specifying a writable temporary directory, not to block the ones specified by explicit writable directories. This fixes that ordering, and adds the missing unit test that would have caught the problem. rdar://87417780 (cherry picked from commit c3f5261)
…t's under `<pkgdir>/.build` (#4018) The sandbox rules introduced in #3996 made the entire package directory read-only, but that isn't appropriate when `.build` is inside the package directory. The rules for applying read-only directories were intended to shadow the implicit rules added by specifying a writable temporary directory, not to block the ones specified by explicit writable directories. This fixes that ordering, and adds the missing unit test that would have caught the problem. rdar://87417780 (cherry picked from commit c3f5261)
…der `<pkgdir>/.build` The sandbox rules introduced in swiftlang#3996 made the entire package directory read-only, but that isn't appropriate when `.build` is inside the package directory. This change turns the stateless Sandbox enum into a SandboxProfile struct that can be configured and passed around well before being applied, and it makes the configuration more flexible in several regards: - there is a list of path rules allowing a mixed order of `allow` and `deny` rules - the choice of allowing writing to temporary directories is independent of anything other choice - the defaults are built into the initializer rather than a separate `strictness` parameter — this means that they can be queried once the SandboxProfile has been created and are expressed in terms of the choices available to all sandbox profiles. Having the sandbox profile be a struct that generates the platform specifics as needed also provides a place with which to associate any cached/compiled representation for profiles that are largely static. As cleanup, this commit also removes the pre-SwiftPM 5.3 specialities which were specific to running the package manifest in an interpreter rather than compiling and executing it. This functionality is no longer relevant since it isn't possible to run any manifest in the interpreter. In this commit, the call sites have been adjusted so that they use the modified SandboxProfile API, but still construct the profiles on-the-fly as before. A future change could make them instead be configured at an early point and then applied later when the sandboxed process is actually launched. Another future change could add the sandbox profile to Process as a property so that there is no API assumption that applying a sandbox necessarily involves just modifying the command line. rdar://87417780
Enforce read-only-ness of package directory even when it's inside
/tmp
and other writable directories to the number of reduce special cases.Motivation
The package directory should always be read-only unless specifically requested to be writable, even if it happens to be in
/tmp
or other implicitly writable locations.Details
The sandbox supports a list of
writableDirectories
, which are treated as path component prefixes after package resolution. The are applied to the sandbox rules after the default-deny and everything-is-read-only rules, carving out cones of writability in specific areas of the file system.But if the package directory is under one of these directories that is allowed by default, then it is implicitly writable even in the absence
--allow-writing-to-package-directory
. It also makes unit tests awkward, since they usually create temporary test fixtures inside the temporary directory.Modifications:
Sandbox
with a list of read-only locationswritable-directory
parameterstrictness
for temporary-directory writability (cleaner)Result:
There are fewer edge cases with respect to when the package directory is writable.