Skip to content

Commit

Permalink
feat: support overlapping permissions (#6561)
Browse files Browse the repository at this point in the history
* feat: support overlapping permissions

* cleanup
  • Loading branch information
mattsse authored Dec 10, 2023
1 parent e911d22 commit 0ae39ea
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 2 deletions.
43 changes: 41 additions & 2 deletions crates/config/src/fs_permissions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ impl FsPermissions {
Self { permissions: permissions.into_iter().collect() }
}

/// Adds a new permission
pub fn add(&mut self, permission: PathPermission) {
self.permissions.push(permission)
}

/// Returns true if access to the specified path is allowed with the specified.
///
/// This first checks permission, and only if it is granted, whether the path is allowed.
Expand All @@ -37,9 +42,28 @@ impl FsPermissions {
self.find_permission(path).map(|perm| perm.is_granted(kind)).unwrap_or_default()
}

/// Returns the permission for the matching path
/// Returns the permission for the matching path.
///
/// This finds the longest matching path, e.g. if we have the following permissions:
///
/// `./out` = `read`
/// `./out/contracts` = `read-write`
///
/// And we check for `./out/contracts/MyContract.sol` we will get `read-write` as permission.
pub fn find_permission(&self, path: &Path) -> Option<FsAccessPermission> {
self.permissions.iter().find(|perm| path.starts_with(&perm.path)).map(|perm| perm.access)
let mut permission: Option<&PathPermission> = None;
for perm in &self.permissions {
if path.starts_with(&perm.path) {
if let Some(active_perm) = permission.as_ref() {
// the longest path takes precedence
if perm.path < active_perm.path {
continue;
}
}
permission = Some(perm);
}
}
permission.map(|perm| perm.access)
}

/// Updates all `allowed_paths` and joins ([`Path::join`]) the `root` with all entries
Expand Down Expand Up @@ -238,4 +262,19 @@ mod tests {
assert_eq!(FsAccessPermission::Read, "read".parse().unwrap());
assert_eq!(FsAccessPermission::Write, "write".parse().unwrap());
}

#[test]
fn nested_permissions() {
let permissions = FsPermissions::new(vec![
PathPermission::read("./"),
PathPermission::write("./out"),
PathPermission::read_write("./out/contracts"),
]);

let permission =
permissions.find_permission(Path::new("./out/contracts/MyContract.sol")).unwrap();
assert_eq!(FsAccessPermission::ReadWrite, permission);
let permission = permissions.find_permission(Path::new("./out/MyContract.sol")).unwrap();
assert_eq!(FsAccessPermission::Write, permission);
}
}
18 changes: 18 additions & 0 deletions crates/forge/tests/it/repros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ macro_rules! test_repro {
}
}
};
($issue_number:literal; |$config:ident| $e:expr $(,)?) => {
paste::paste! {
#[tokio::test(flavor = "multi_thread")]
async fn [< issue_ $issue_number >]() {
let mut $config = repro_config($issue_number, false, None).await;
$e
$config.run().await;
}
}
};
}

async fn repro_config(issue: usize, should_fail: bool, sender: Option<Address>) -> TestConfig {
Expand Down Expand Up @@ -267,3 +277,11 @@ test_repro!(6501, false, None, |res| {
);
}
});

// https://github.com/foundry-rs/foundry/issues/6554
test_repro!(6554; |config| {
let mut cheats_config = config.runner.cheats_config.as_ref().clone();
let path = cheats_config.root.join("out/Issue6554.t.sol");
cheats_config.fs_permissions.add(PathPermission::read_write(path));
config.runner.cheats_config = std::sync::Arc::new(cheats_config);
});
16 changes: 16 additions & 0 deletions testdata/repros/Issue6554.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity 0.8.18;

import "ds-test/test.sol";
import "../cheats/Vm.sol";

// https://github.com/foundry-rs/foundry/issues/6554
contract Issue6554Test is DSTest {
Vm constant vm = Vm(HEVM_ADDRESS);

function testPermissions() public {
vm.writeFile("./out/Issue6554.t.sol/cachedFile.txt", "cached data");
string memory content = vm.readFile("./out/Issue6554.t.sol/cachedFile.txt");
assertEq(content, "cached data");
}
}

0 comments on commit 0ae39ea

Please sign in to comment.