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
34 changes: 34 additions & 0 deletions guards/github-guard/rust-guard/src/labels/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4770,6 +4770,40 @@ mod tests {
}
}

#[test]
fn test_apply_tool_labels_set_issue_fields_writer_integrity() {
let ctx = default_ctx();
let repo_id = "github/copilot";
let tool_args = json!({
"owner": "github",
"repo": "copilot",
"issue_number": 1,
"fields": [
{
"field_id": "PVTSSF_example",
"text_value": "In progress"
}
]
});

let (secrecy, integrity, _desc) = apply_tool_labels(
"set_issue_fields",
&tool_args,
repo_id,
vec![],
vec![],
String::new(),
&ctx,
);

assert_eq!(secrecy, vec![] as Vec<String>, "set_issue_fields secrecy mismatch");
assert_eq!(
integrity,
writer_integrity(repo_id, &ctx),
"set_issue_fields should have writer integrity"
);
}

#[test]
fn test_apply_tool_labels_granular_sub_issue_tools_writer_integrity() {
let ctx = default_ctx();
Expand Down
9 changes: 9 additions & 0 deletions guards/github-guard/rust-guard/src/labels/tool_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,15 @@ pub fn apply_tool_labels(
integrity = writer_integrity(repo_id, ctx);
}

// === Issue custom fields mutation (repo-scoped write) ===
"set_issue_fields" => {
// Field definitions are organization-level, but the mutation targets a specific
// issue in owner/repo and returns issue-scoped metadata.
// S = S(repo); I = writer
secrecy = apply_repo_visibility_secrecy(&owner, &repo, repo_id, secrecy, ctx);
integrity = writer_integrity(repo_id, ctx);
}

// === Granular repo-scoped write operations ===
// Covers granular issue PATCH tools, sub-issue management, granular PR PATCH tools,
// and PR review tools. All follow: S = S(repo), I = writer.
Expand Down
18 changes: 18 additions & 0 deletions guards/github-guard/rust-guard/src/tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ pub const READ_WRITE_OPERATIONS: &[&str] = &[
"update_issue_title", // PATCH — modifies issue title
"update_issue_type", // PATCH — modifies issue type

// Issue custom field mutation (field definitions are org-level; target issue is repo-scoped)
"set_issue_fields", // GraphQL — sets custom field values on a specific repository issue

// Sub-issue management tools (alongside sub_issue_write composite)
"add_sub_issue", // POST /repos/.../issues/{number}/sub_issues
"remove_sub_issue", // DELETE/POST — remove sub-issue link
Expand Down Expand Up @@ -362,6 +365,21 @@ mod tests {
}
}

#[test]
fn test_set_issue_fields_is_read_write_operation() {
let op = "set_issue_fields";
assert!(
is_read_write_operation(op),
"{} must be classified as a read-write operation",
op
);
assert!(
!is_write_operation(op),
"{} should not be in WRITE_OPERATIONS (it is in READ_WRITE_OPERATIONS)",
op
);
}

#[test]
fn test_sub_issue_management_tools_are_read_write_operations() {
for op in &["add_sub_issue", "remove_sub_issue", "reprioritize_sub_issue"] {
Expand Down
Loading