Add trusted plugin script attribution (#35016)

## What changed

- Build a set of active, verified curated and remote plugin roots from loaded plugins.
- Resolve direct and safely wrapped script commands to a plugin ID and normalized plugin-relative path.
- Leave complex or ambiguous commands, local overrides, missing files, overlapping roots, and symlink escapes unattributed.
- Add a shared validator for the safe cross-platform shape of serialized plugin-relative paths.

## Testing

- Cover trusted-root selection, supported interpreters and shell wrappers, normalized paths, and fail-closed cases.

GitOrigin-RevId: 6e4199a241fd6dfadfec3df0845e7cb615352a49
This commit is contained in:
Kyle Brown
2026-07-23 21:50:17 +00:00
committed by copyberry
parent ceb2ffb793
commit 5bdbd3ee90
7 changed files with 660 additions and 1 deletions
+40
View File
@@ -180,6 +180,24 @@ impl From<ExecCommandStatus> for CommandExecutionStatus {
}
}
/// Returns whether a path is safe to serialize as a trusted plugin-relative path.
///
/// This validates the cross-platform wire shape only. The trusted plugin resolver
/// remains responsible for establishing that the path actually came from a plugin root.
pub fn is_safe_plugin_relative_path(path: &str) -> bool {
!path.is_empty()
&& !path.starts_with('/')
&& !path.contains('\\')
&& path.split('/').all(|component| {
!component.is_empty()
&& !matches!(component, "." | "..")
&& !matches!(
component.as_bytes(),
[drive, b':', ..] if drive.is_ascii_alphabetic()
)
})
}
#[derive(Debug, Clone, Deserialize, Serialize, TS, JsonSchema, PartialEq)]
pub struct CommandExecutionItem {
pub id: String,
@@ -704,6 +722,28 @@ mod tests {
);
}
#[test]
fn plugin_relative_paths_use_safe_wire_shape() {
assert!(is_safe_plugin_relative_path("scripts/run.py"));
for path in [
"",
"/home/user/.codex/plugins/cache/sample/scripts/run.py",
"C:/Users/user/.codex/plugins/cache/sample/scripts/run.py",
"scripts/C:/run.py",
r"\\server\share\sample\scripts\run.py",
r"scripts\run.py",
"scripts//run.py",
"scripts/./run.py",
"scripts/../run.py",
] {
assert!(
!is_safe_plugin_relative_path(path),
"unsafe plugin-relative path should be rejected: {path:?}"
);
}
}
#[test]
fn hook_prompt_roundtrips_multiple_fragments() {
let original = vec![