Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 32 additions & 24 deletions cargo_crap_baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@
{
"file": "./src/main.rs",
"function": "run_command",
"line": 172,
"cyclomatic": 10.0,
"coverage": 21.428571428571427,
"crap": 58.505830903790084
"line": 182,
"cyclomatic": 11.0,
"coverage": 20.689655172413794,
"crap": 71.36356554184265
},
{
"file": "./src/prompt.rs",
Expand Down Expand Up @@ -181,7 +181,7 @@
{
"file": "./src/allowlist.rs",
"function": "check_task_allowed",
"line": 144,
"line": 148,
"cyclomatic": 12.0,
"coverage": 66.66666666666666,
"crap": 17.333333333333343
Expand All @@ -202,6 +202,14 @@
"coverage": 75.0,
"crap": 17.0625
},
{
"file": "./src/allowlist.rs",
"function": "evaluate_task_against_allowlist",
"line": 75,
"cyclomatic": 17.0,
"coverage": 100.0,
"crap": 17.0
},
{
"file": "./src/commands/allow_command.rs",
"function": "execute",
Expand Down Expand Up @@ -234,14 +242,6 @@
"coverage": 92.3076923076923,
"crap": 15.10241238051889
},
{
"file": "./src/allowlist.rs",
"function": "evaluate_task_against_allowlist",
"line": 75,
"cyclomatic": 14.0,
"coverage": 95.65217391304348,
"crap": 14.016109147694584
},
{
"file": "./src/mcp/server.rs",
"function": "DelaMcpServer::task_output",
Expand Down Expand Up @@ -293,7 +293,7 @@
{
"file": "./src/main.rs",
"function": "main",
"line": 209,
"line": 220,
"cyclomatic": 3.0,
"coverage": 0.0,
"crap": 12.0
Expand Down Expand Up @@ -682,6 +682,14 @@
"coverage": 100.0,
"crap": 6.0
},
{
"file": "./src/commands/deny.rs",
"function": "execute",
"line": 7,
"cyclomatic": 6.0,
"coverage": 100.0,
"crap": 6.0
},
{
"file": "./src/commands/mcp.rs",
"function": "Editor::config_path",
Expand Down Expand Up @@ -757,7 +765,7 @@
{
"file": "./src/allowlist.rs",
"function": "is_task_allowed",
"line": 132,
"line": 136,
"cyclomatic": 5.0,
"coverage": 100.0,
"crap": 5.0
Expand Down Expand Up @@ -1066,10 +1074,18 @@
"coverage": 100.0,
"crap": 3.0
},
{
"file": "./src/allowlist.rs",
"function": "allowlist_entry_for_task",
"line": 119,
"cyclomatic": 3.0,
"coverage": 100.0,
"crap": 3.0
},
{
"file": "./src/allowlist.rs",
"function": "check_task_allowed_with_scope",
"line": 189,
"line": 193,
"cyclomatic": 3.0,
"coverage": 100.0,
"crap": 3.0
Expand Down Expand Up @@ -1594,14 +1610,6 @@
"coverage": 100.0,
"crap": 2.0
},
{
"file": "./src/allowlist.rs",
"function": "allowlist_entry_for_task",
"line": 115,
"cyclomatic": 2.0,
"coverage": 100.0,
"crap": 2.0
},
{
"file": "./src/task_discovery.rs",
"function": "discover_tasks",
Expand Down
2 changes: 1 addition & 1 deletion dev_docs/project_plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ This plan outlines the major development phases and tasks for building `dela`, a
- [x] [DTKT-32] Persist decisions in the allowlist.
- [ ] [DTKT-33] Have `dela run` take an optional `--allow` flag to allow a task without prompting.
- [x] [DTKT-109] Implement `dela allow` command to add allowlist entries.
- [ ] [DTKT-110] Implement `dela deny` command to add denylist entries.
- [x] [DTKT-110] Implement `dela deny` command to add denylist entries.
- [ ] [DTKT-111] Implement `dela run --allow-once` command to run a command once.

- [x] **Runtime Checks**
Expand Down
44 changes: 39 additions & 5 deletions src/allowlist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,14 @@ pub fn evaluate_task_against_allowlist(task: &Task, allowlist: &Allowlist) -> Al

// First pass: Check for deny entries (highest precedence)
for entry in &allowlist.entries {
if let AllowScope::Deny = entry.scope
&& path_matches(task_path, &entry.path, true)
{
return AllowlistMatch::Denied;
if let AllowScope::Deny = entry.scope {
if let Some(ref tasks) = entry.tasks {
if path_matches(task_path, &entry.path, false) && tasks.contains(&task.name) {
return AllowlistMatch::Denied;
}
} else if path_matches(task_path, &entry.path, true) {
return AllowlistMatch::Denied;
}
}
}

Expand Down Expand Up @@ -113,7 +117,7 @@ pub fn evaluate_task_against_allowlist(task: &Task, allowlist: &Allowlist) -> Al
}

pub fn allowlist_entry_for_task(task: &Task, scope: AllowScope) -> AllowlistEntry {
let tasks = if scope == AllowScope::Task {
let tasks = if scope == AllowScope::Task || scope == AllowScope::Deny {
Some(vec![task.name.clone()])
} else {
None
Expand Down Expand Up @@ -432,6 +436,36 @@ mod tests {
reset_to_real_environment();
}

#[test]
#[serial]
fn test_is_task_allowed_deny_task_scope() {
let (temp_dir, task) = setup_test_env();

// Create allowlist with deny scope for specific task
let mut allowlist = Allowlist::default();
let entry = AllowlistEntry {
path: PathBuf::from("Makefile"),
scope: AllowScope::Deny,
tasks: Some(vec!["test-task".to_string()]),
};
allowlist.entries.push(entry);
save_allowlist(&allowlist).unwrap();

// Task should be denied
assert_eq!(is_task_allowed(&task).unwrap(), (false, true));

// A different task should NOT be denied
let other_task = create_test_task("other-task", PathBuf::from("Makefile"));
assert_eq!(is_task_allowed(&other_task).unwrap(), (false, false));

// A task from a different Makefile should NOT be denied
let other_path_task = create_test_task("test-task", PathBuf::from("other/Makefile"));
assert_eq!(is_task_allowed(&other_path_task).unwrap(), (false, false));

drop(temp_dir);
reset_to_real_environment();
}

#[test]
#[serial]
fn test_is_task_allowed_uses_definition_path_for_composed_tasks() {
Expand Down
6 changes: 6 additions & 0 deletions src/commands/allow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,22 @@ mod tests {
struct TestEnvGuard {
project_dir: TempDir,
home_dir: TempDir,
original_cwd: std::path::PathBuf,
}

impl TestEnvGuard {
fn new() -> Self {
let original_cwd = env::current_dir().unwrap_or_else(|_| std::env::temp_dir());
let (project_dir, home_dir) = setup_test_env();
Self {
project_dir,
home_dir,
original_cwd,
}
}

fn new_uninitialized() -> Self {
let original_cwd = env::current_dir().unwrap_or_else(|_| std::env::temp_dir());
// Create a temp dir for HOME but don't create the dela config directory
let home_dir = TempDir::new().expect("Failed to create temp HOME directory");

Expand All @@ -74,12 +78,14 @@ mod tests {
Self {
project_dir: TempDir::new().expect("Failed to create temp directory"),
home_dir,
original_cwd,
}
}
}

impl Drop for TestEnvGuard {
fn drop(&mut self) {
let _ = env::set_current_dir(&self.original_cwd);
reset_to_real_environment();
}
}
Expand Down
Loading