Skip to content

Commit 5110c2a

Browse files
lpcoxCopilot
andcommitted
fix(guard): cover deprecated tool aliases, enable_toolset DIFC rule, and pre-emptive CLI entries
- Add 5 deprecated MCP tool aliases to write classification: run_workflow, delete_workflow_run_logs, add_project_item, delete_project_item (WRITE_OPERATIONS), update_project_item (READ_WRITE_OPERATIONS) - Add DIFC labeling rules for deprecated aliases by extending existing match arms (projects_write, actions_run_trigger) - Add explicit enable_toolset DIFC rule with writer-level integrity to prevent low-trust agents from self-escalating - Add 6 pre-emptive CLI entries: update_issue_comment, delete_issue_comment, create_release, edit_release, delete_release, delete_gist - Add DIFC labeling rules for all pre-emptive CLI entries - Add tests for new classifications Fixes #3720 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 7e1e8aa commit 5110c2a

2 files changed

Lines changed: 98 additions & 2 deletions

File tree

guards/github-guard/rust-guard/src/labels/tool_rules.rs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,9 @@ pub fn apply_tool_labels(
591591
}
592592

593593
// === Projects write operations (org-scoped) ===
594-
"projects_write" => {
594+
"projects_write"
595+
// Deprecated aliases that map to projects_write
596+
| "add_project_item" | "update_project_item" | "delete_project_item" => {
595597
// Projects are org-scoped; write responses carry the same labels as reads.
596598
// I = approved:<owner>
597599
if !owner.is_empty() {
@@ -609,7 +611,9 @@ pub fn apply_tool_labels(
609611
}
610612

611613
// === Actions: Workflow run triggers ===
612-
"actions_run_trigger" => {
614+
"actions_run_trigger"
615+
// Deprecated aliases that map to actions_run_trigger
616+
| "run_workflow" | "delete_workflow_run_logs" => {
613617
// Triggering a workflow run returns repo-scoped metadata.
614618
// S = S(repo); I = writer
615619
secrecy = apply_repo_visibility_secrecy(&owner, &repo, repo_id, secrecy, ctx);
@@ -670,6 +674,16 @@ pub fn apply_tool_labels(
670674
integrity = writer_integrity(repo_id, ctx);
671675
}
672676

677+
// === Dynamic toolset enablement (capability expansion) ===
678+
"enable_toolset" => {
679+
// Enabling a toolset expands the agent's runtime capability set.
680+
// Requires writer-level integrity to prevent low-trust agents from
681+
// self-escalating by enabling additional tool groups.
682+
// S = public (empty — no repository-scoped data); I = writer (global)
683+
baseline_scope = "github".to_string();
684+
integrity = writer_integrity("github", ctx);
685+
}
686+
673687
// === Star/unstar operations (public metadata) ===
674688
"star_repository" | "unstar_repository" => {
675689
// Starring is a public action; response is minimal metadata.
@@ -679,6 +693,30 @@ pub fn apply_tool_labels(
679693
integrity = project_github_label(ctx);
680694
}
681695

696+
// === Issue/PR comment editing/deletion (pre-emptive) ===
697+
"update_issue_comment" | "delete_issue_comment" => {
698+
// Editing or deleting an issue/PR comment is a repo-scoped write.
699+
// S = S(repo); I = writer
700+
secrecy = apply_repo_visibility_secrecy(&owner, &repo, repo_id, secrecy, ctx);
701+
integrity = writer_integrity(repo_id, ctx);
702+
}
703+
704+
// === Release management (pre-emptive) ===
705+
"create_release" | "edit_release" | "delete_release" => {
706+
// Release operations are repo-scoped writes.
707+
// S = S(repo); I = writer
708+
secrecy = apply_repo_visibility_secrecy(&owner, &repo, repo_id, secrecy, ctx);
709+
integrity = writer_integrity(repo_id, ctx);
710+
}
711+
712+
// === Gist deletion (pre-emptive) ===
713+
"delete_gist" => {
714+
// Gist deletion is a write on user-scoped content.
715+
// S = public (gists can be public/secret); I = writer (global)
716+
baseline_scope = "github".to_string();
717+
integrity = writer_integrity("github", ctx);
718+
}
719+
682720
_ => {
683721
// Default: inherit provided labels
684722
}

guards/github-guard/rust-guard/src/tools.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,20 @@ pub const WRITE_OPERATIONS: &[&str] = &[
5959
// Pre-emptive: gh repo deploy-key add/delete — SSH key with optional write access
6060
"add_deploy_key",
6161
"delete_deploy_key",
62+
// Deprecated alias coverage (guard sees alias name before backend resolves it)
63+
"run_workflow", // deprecated alias for actions_run_trigger (POST workflow dispatch)
64+
"delete_workflow_run_logs", // deprecated alias for actions_run_trigger (DELETE run logs)
65+
"add_project_item", // deprecated alias for projects_write (addProjectV2ItemById)
66+
"delete_project_item", // deprecated alias for projects_write (deleteProjectV2Item)
67+
// Pre-emptive: issue/PR comment editing/deletion (gh issue/pr comment --edit/--delete)
68+
"update_issue_comment", // PATCH /repos/.../issues/comments/{id}
69+
"delete_issue_comment", // DELETE /repos/.../issues/comments/{id}
70+
// Pre-emptive: release management (gh release create/edit/delete)
71+
"create_release", // POST /repos/.../releases
72+
"edit_release", // PATCH /repos/.../releases/{id}
73+
"delete_release", // DELETE /repos/.../releases/{id}
74+
// Pre-emptive: gist deletion (gh gist delete)
75+
"delete_gist", // DELETE /gists/{gist_id}
6276
];
6377

6478
/// Read-write operations that both read and modify data
@@ -73,6 +87,8 @@ pub const READ_WRITE_OPERATIONS: &[&str] = &[
7387
// Pre-emptive entries for anticipated future MCP tools (no equivalent tool today)
7488
// gh agent-task create — creates a Copilot coding-agent job (branch + PR); blocked as unsupported
7589
"create_agent_task",
90+
// Deprecated alias coverage
91+
"update_project_item", // deprecated alias for projects_write (updateProjectV2ItemFieldValue)
7692
];
7793

7894
/// Check if a tool is a write operation
@@ -249,4 +265,46 @@ mod tests {
249265
"create_agent_task should not be in WRITE_OPERATIONS (it is in READ_WRITE_OPERATIONS)"
250266
);
251267
}
268+
269+
#[test]
270+
fn test_deprecated_alias_write_operations() {
271+
for op in &[
272+
"run_workflow",
273+
"delete_workflow_run_logs",
274+
"add_project_item",
275+
"delete_project_item",
276+
] {
277+
assert!(
278+
is_write_operation(op),
279+
"{} (deprecated alias) must be classified as a write operation",
280+
op
281+
);
282+
}
283+
}
284+
285+
#[test]
286+
fn test_deprecated_alias_read_write_operations() {
287+
assert!(
288+
is_read_write_operation("update_project_item"),
289+
"update_project_item (deprecated alias) must be classified as a read-write operation"
290+
);
291+
}
292+
293+
#[test]
294+
fn test_preemptive_cli_write_operations() {
295+
for op in &[
296+
"update_issue_comment",
297+
"delete_issue_comment",
298+
"create_release",
299+
"edit_release",
300+
"delete_release",
301+
"delete_gist",
302+
] {
303+
assert!(
304+
is_write_operation(op),
305+
"{} (pre-emptive CLI) must be classified as a write operation",
306+
op
307+
);
308+
}
309+
}
252310
}

0 commit comments

Comments
 (0)