ci: sync Bazel clippy lints and fix uncovered violations (#16351)

## Why

Follow-up to #16345, the Bazel clippy rollout in #15955, and the cleanup
pass in #16353.

`cargo clippy` was enforcing the workspace deny-list from
`codex-rs/Cargo.toml` because the member crates opt into `[lints]
workspace = true`, but Bazel clippy was only using `rules_rust` plus
`clippy.toml`. That left the Bazel lane vulnerable to drift:
`clippy.toml` can tune lint behavior, but it cannot set
allow/warn/deny/forbid levels.

This PR now closes both sides of the follow-up. It keeps `.bazelrc` in
sync with `[workspace.lints.clippy]`, and it fixes the real clippy
violations that the newly-synced Windows Bazel lane surfaced once that
deny-list started matching Cargo.

## What Changed

- added `.github/scripts/verify_bazel_clippy_lints.py`, a Python check
that parses `codex-rs/Cargo.toml` with `tomllib`, reads the Bazel
`build:clippy` `clippy_flag` entries from `.bazelrc`, and reports
missing, extra, or mismatched lint levels
- ran that verifier from the lightweight `ci.yml` workflow so the sync
check does not depend on a Rust toolchain being installed first
- expanded the `.bazelrc` comment to explain the Cargo `workspace =
true` linkage and why Bazel needs the deny-list duplicated explicitly
- fixed the Windows-only `codex-windows-sandbox` violations that Bazel
clippy reported after the sync, using the same style as #16353: inline
`format!` args, method references instead of trivial closures, removed
redundant clones, and replaced SID conversion `unwrap` and `expect`
calls with proper errors
- cleaned up the remaining cross-platform violations the Bazel lane
exposed in `codex-backend-client` and `core_test_support`

## Testing

Key new test introduced by this PR:

`python3 .github/scripts/verify_bazel_clippy_lints.py`
This commit is contained in:
Michael Bolin
2026-03-31 17:09:48 -07:00
committed by GitHub
Unverified
parent ae057e0bb9
commit 2e942ce830
22 changed files with 347 additions and 61 deletions
+1 -1
View File
@@ -309,7 +309,7 @@ where
D: Deserializer<'de>,
T: Deserialize<'de>,
{
Option::<Vec<T>>::deserialize(deserializer).map(|opt| opt.unwrap_or_default())
Option::<Vec<T>>::deserialize(deserializer).map(Option::unwrap_or_default)
}
#[derive(Clone, Debug, Deserialize)]
+3 -3
View File
@@ -287,7 +287,7 @@ where
F: Fn(&codex_protocol::protocol::EventMsg) -> Option<T>,
{
let ev = wait_for_event(codex, |ev| matcher(ev).is_some()).await;
matcher(&ev).unwrap()
matcher(&ev).expect("EventMsg should match matcher predicate")
}
pub async fn wait_for_event_with_timeout<F>(
@@ -417,7 +417,7 @@ pub mod fs_wait {
let deadline = Instant::now() + timeout;
loop {
if path.exists() {
return Ok(path.clone());
return Ok(path);
}
let now = Instant::now();
if now >= deadline {
@@ -427,7 +427,7 @@ pub mod fs_wait {
match rx.recv_timeout(remaining) {
Ok(Ok(_event)) => {
if path.exists() {
return Ok(path.clone());
return Ok(path);
}
}
Ok(Err(err)) => return Err(err.into()),
+2
View File
@@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used)]
use std::collections::VecDeque;
use std::sync::Arc;
use std::sync::Mutex;
+3 -3
View File
@@ -270,7 +270,7 @@ fn docker_command_success<const N: usize>(args: [&str; N]) -> Result<()> {
let output = Command::new("docker")
.args(args)
.output()
.with_context(|| format!("run docker {:?}", args))?;
.with_context(|| format!("run docker {args:?}"))?;
if !output.status.success() {
return Err(anyhow!(
"docker {:?} failed: stdout={} stderr={}",
@@ -286,7 +286,7 @@ fn docker_command_capture_stdout<const N: usize>(args: [&str; N]) -> Result<Stri
let output = Command::new("docker")
.args(args)
.output()
.with_context(|| format!("run docker {:?}", args))?;
.with_context(|| format!("run docker {args:?}"))?;
if !output.status.success() {
return Err(anyhow!(
"docker {:?} failed: stdout={} stderr={}",
@@ -346,7 +346,7 @@ impl TestCodexBuilder {
pub fn with_model(self, model: &str) -> Self {
let new_model = model.to_string();
self.with_config(move |config| {
config.model = Some(new_model.clone());
config.model = Some(new_model);
})
}
+4 -4
View File
@@ -327,13 +327,13 @@ unsafe fn ensure_allow_mask_aces_with_inheritance_impl(
if !p_sd.is_null() {
LocalFree(p_sd as HLOCAL);
}
return Err(anyhow!("SetNamedSecurityInfoW failed: {}", code3));
return Err(anyhow!("SetNamedSecurityInfoW failed: {code3}"));
}
} else {
if !p_sd.is_null() {
LocalFree(p_sd as HLOCAL);
}
return Err(anyhow!("SetEntriesInAclW failed: {}", code2));
return Err(anyhow!("SetEntriesInAclW failed: {code2}"));
}
}
if !p_sd.is_null() {
@@ -401,7 +401,7 @@ pub unsafe fn add_allow_ace(path: &Path, psid: *mut c_void) -> Result<bool> {
&mut p_sd,
);
if code != ERROR_SUCCESS {
return Err(anyhow!("GetNamedSecurityInfoW failed: {}", code));
return Err(anyhow!("GetNamedSecurityInfoW failed: {code}"));
}
// Already has write? Skip costly DACL rewrite.
if dacl_has_write_allow_for_sid(p_dacl, psid) {
@@ -467,7 +467,7 @@ pub unsafe fn add_deny_write_ace(path: &Path, psid: *mut c_void) -> Result<bool>
&mut p_sd,
);
if code != ERROR_SUCCESS {
return Err(anyhow!("GetNamedSecurityInfoW failed: {}", code));
return Err(anyhow!("GetNamedSecurityInfoW failed: {code}"));
}
let mut added = false;
if !dacl_has_write_deny_for_sid(p_dacl, psid) {
+2 -3
View File
@@ -194,8 +194,7 @@ pub fn audit_everyone_writable(
}
crate::logging::log_note(
&format!(
"AUDIT: world-writable scan FAILED; cwd={cwd:?}; checked={checked}; duration_ms={elapsed_ms}; flagged:{}",
list
"AUDIT: world-writable scan FAILED; cwd={cwd:?}; checked={checked}; duration_ms={elapsed_ms}; flagged:{list}",
),
logs_base_dir,
);
@@ -229,7 +228,7 @@ pub fn apply_world_writable_scan_and_denies(
logs_base_dir,
) {
log_note(
&format!("AUDIT: failed to apply capability deny ACEs: {}", err),
&format!("AUDIT: failed to apply capability deny ACEs: {err}"),
logs_base_dir,
);
}
+1 -1
View File
@@ -34,7 +34,7 @@ fn make_random_cap_sid_string() -> String {
let b = rng.next_u32();
let c = rng.next_u32();
let d = rng.next_u32();
format!("S-1-5-21-{}-{}-{}-{}", a, b, c, d)
format!("S-1-5-21-{a}-{b}-{c}-{d}")
}
fn persist_caps(path: &Path, caps: &CapSids) -> Result<()> {
@@ -480,7 +480,7 @@ pub fn main() -> Result<()> {
let _ = send_error(&pipe_write, "spawn_failed", err.to_string());
return Err(err);
}
let log_dir_owned = log_dir.map(|p| p.to_path_buf());
let log_dir_owned = log_dir.map(Path::to_path_buf);
let out_thread = spawn_output_reader(
Arc::clone(&pipe_write),
stdout_handle,
@@ -144,7 +144,7 @@ pub fn read_frame<R: Read>(mut reader: R) -> Result<Option<FramedMessage>> {
}
let len = u32::from_le_bytes(len_buf) as usize;
if len > MAX_FRAME_LEN {
anyhow::bail!("frame too large: {}", len);
anyhow::bail!("frame too large: {len}");
}
let mut payload = vec![0u8; len];
reader.read_exact(&mut payload)?;
@@ -93,7 +93,7 @@ mod windows_impl {
} else {
cur.join(gitdir)
};
return resolved.parent().map(|p| p.to_path_buf()).or(Some(cur));
return resolved.parent().map(Path::to_path_buf).or(Some(cur));
}
return Some(cur);
}
@@ -270,17 +270,22 @@ mod windows_impl {
}
let caps = load_or_create_cap_sids(codex_home)?;
let (psid_to_use, cap_sids) = match &policy {
SandboxPolicy::ReadOnly { .. } => (
unsafe { convert_string_sid_to_sid(&caps.readonly).unwrap() },
vec![caps.readonly.clone()],
),
SandboxPolicy::WorkspaceWrite { .. } => (
unsafe { convert_string_sid_to_sid(&caps.workspace).unwrap() },
vec![
caps.workspace.clone(),
crate::cap::workspace_cap_sid_for_cwd(codex_home, cwd)?,
],
),
SandboxPolicy::ReadOnly { .. } => {
#[allow(clippy::unwrap_used)]
let psid = unsafe { convert_string_sid_to_sid(&caps.readonly).unwrap() };
(psid, vec![caps.readonly])
}
SandboxPolicy::WorkspaceWrite { .. } => {
#[allow(clippy::unwrap_used)]
let psid = unsafe { convert_string_sid_to_sid(&caps.workspace).unwrap() };
(
psid,
vec![
caps.workspace,
crate::cap::workspace_cap_sid_for_cwd(codex_home, cwd)?,
],
)
}
SandboxPolicy::DangerFullAccess | SandboxPolicy::ExternalSandbox { .. } => {
unreachable!("DangerFullAccess handled above")
}
@@ -302,7 +307,7 @@ mod windows_impl {
let runner_exe = find_runner_exe(codex_home, logs_base_dir);
let runner_cmdline = runner_exe
.to_str()
.map(|s| s.to_string())
.map(ToString::to_string)
.unwrap_or_else(|| "codex-command-runner.exe".to_string());
let runner_full_cmd = format!(
"{} {} {}",
@@ -365,7 +370,7 @@ mod windows_impl {
),
logs_base_dir,
);
return Err(anyhow::anyhow!("CreateProcessWithLogonW failed: {}", err));
return Err(anyhow::anyhow!("CreateProcessWithLogonW failed: {err}"));
}
if let Err(err) = connect_pipe(h_pipe_in) {
@@ -451,7 +456,7 @@ mod windows_impl {
if exit_code == 0 {
log_success(&command, logs_base_dir);
} else {
log_failure(&command, &format!("exit code {}", exit_code), logs_base_dir);
log_failure(&command, &format!("exit code {exit_code}"), logs_base_dir);
}
Ok(CaptureResult {
+4 -4
View File
@@ -48,7 +48,7 @@ fn prepend_path(env_map: &mut HashMap<String, String>, prefix: &str) {
.cloned()
.or_else(|| env::var("PATH").ok())
.unwrap_or_default();
let parts: Vec<String> = existing.split(';').map(|s| s.to_string()).collect();
let parts: Vec<String> = existing.split(';').map(ToString::to_string).collect();
if parts
.first()
.map(|p| p.eq_ignore_ascii_case(prefix))
@@ -74,7 +74,7 @@ fn reorder_pathext_for_stubs(env_map: &mut HashMap<String, String>) {
let exts: Vec<String> = default
.split(';')
.filter(|e| !e.is_empty())
.map(|s| s.to_string())
.map(ToString::to_string)
.collect();
let exts_norm: Vec<String> = exts.iter().map(|e| e.to_ascii_uppercase()).collect();
let want = [".BAT", ".CMD"];
@@ -110,7 +110,7 @@ fn ensure_denybin(tools: &[&str], denybin_dir: Option<&Path>) -> Result<PathBuf>
fs::create_dir_all(&base)?;
for tool in tools {
for ext in [".bat", ".cmd"] {
let path = base.join(format!("{}{}", tool, ext));
let path = base.join(format!("{tool}{ext}"));
if !path.exists() {
let mut f = File::create(&path)?;
f.write_all(b"@echo off\\r\\nexit /b 1\\r\\n")?;
@@ -162,7 +162,7 @@ pub fn apply_no_network_to_env(env_map: &mut HashMap<String, String>) -> Result<
let base = ensure_denybin(&["ssh", "scp"], /*denybin_dir*/ None)?;
for tool in ["curl", "wget"] {
for ext in [".bat", ".cmd"] {
let p = base.join(format!("{}{}", tool, ext));
let p = base.join(format!("{tool}{ext}"));
if p.exists() {
let _ = fs::remove_file(&p);
}
+3 -3
View File
@@ -122,7 +122,7 @@ fn select_identity(
};
let password = decode_password(&chosen)?;
Ok(Some(SandboxIdentity {
username: chosen.username.clone(),
username: chosen.username,
password,
}))
}
@@ -187,8 +187,8 @@ pub fn require_logon_sandbox_creds(
proxy_enforced,
},
crate::setup::SetupRootOverrides {
read_roots: Some(needed_read.clone()),
write_roots: Some(needed_write.clone()),
read_roots: Some(needed_read),
write_roots: Some(needed_write),
},
)?;
identity = select_identity(network_identity, codex_home)?;
+13 -5
View File
@@ -335,14 +335,20 @@ mod windows_impl {
let (h_token, psid_generic, psid_workspace): (HANDLE, *mut c_void, Option<*mut c_void>) = unsafe {
match &policy {
SandboxPolicy::ReadOnly { .. } => {
let psid = convert_string_sid_to_sid(&caps.readonly).unwrap();
#[allow(clippy::expect_used)]
let psid =
convert_string_sid_to_sid(&caps.readonly).expect("valid readonly SID");
let (h, _) = super::token::create_readonly_token_with_cap(psid)?;
(h, psid, None)
}
SandboxPolicy::WorkspaceWrite { .. } => {
let psid_generic = convert_string_sid_to_sid(&caps.workspace).unwrap();
#[allow(clippy::expect_used)]
let psid_generic =
convert_string_sid_to_sid(&caps.workspace).expect("valid workspace SID");
let ws_sid = workspace_cap_sid_for_cwd(codex_home, cwd)?;
let psid_workspace = convert_string_sid_to_sid(&ws_sid).unwrap();
#[allow(clippy::expect_used)]
let psid_workspace =
convert_string_sid_to_sid(&ws_sid).expect("valid workspace SID");
let base = super::token::get_current_token_for_restriction()?;
let h_res = create_workspace_write_token_with_caps_from(
base,
@@ -363,7 +369,7 @@ mod windows_impl {
&& let Ok(base) = super::token::get_current_token_for_restriction()
{
if let Ok(bytes) = super::token::get_logon_sid_bytes(base) {
let mut tmp = bytes.clone();
let mut tmp = bytes;
let psid2 = tmp.as_mut_ptr() as *mut c_void;
allow_null_device(psid2);
}
@@ -536,7 +542,7 @@ mod windows_impl {
if exit_code == 0 {
log_success(&command, logs_base_dir);
} else {
log_failure(&command, &format!("exit code {}", exit_code), logs_base_dir);
log_failure(&command, &format!("exit code {exit_code}"), logs_base_dir);
}
if !persist_aces {
@@ -569,9 +575,11 @@ mod windows_impl {
ensure_codex_home_exists(codex_home)?;
let caps = load_or_create_cap_sids(codex_home)?;
#[allow(clippy::expect_used)]
let psid_generic =
unsafe { convert_string_sid_to_sid(&caps.workspace) }.expect("valid workspace SID");
let ws_sid = workspace_cap_sid_for_cwd(codex_home, cwd)?;
#[allow(clippy::expect_used)]
let psid_workspace =
unsafe { convert_string_sid_to_sid(&ws_sid) }.expect("valid workspace SID");
let current_dir = cwd.to_path_buf();
+2 -2
View File
@@ -43,7 +43,7 @@ pub fn make_env_block(env: &HashMap<String, String>) -> Vec<u16> {
});
let mut w: Vec<u16> = Vec::new();
for (k, v) in items {
let mut s = to_wide(format!("{}={}", k, v));
let mut s = to_wide(format!("{k}={v}"));
s.pop();
w.extend_from_slice(&s);
w.push(0);
@@ -149,7 +149,7 @@ pub unsafe fn create_process_as_user(
creation_flags,
);
logging::debug_log(&msg, logs_base_dir);
return Err(anyhow!("CreateProcessAsUserW failed: {}", err));
return Err(anyhow!("CreateProcessAsUserW failed: {err}"));
}
Ok(CreatedProcess {
process_info: pi,
@@ -35,7 +35,7 @@ pub fn read_acl_mutex_exists() -> Result<bool> {
if err == ERROR_FILE_NOT_FOUND {
return Ok(false);
}
return Err(anyhow::anyhow!("OpenMutexW failed: {}", err));
return Err(anyhow::anyhow!("OpenMutexW failed: {err}"));
}
unsafe {
CloseHandle(handle);
@@ -312,8 +312,7 @@ fn lock_sandbox_dir(
);
if set != 0 {
return Err(anyhow::anyhow!(
"SetEntriesInAclW sandbox dir failed: {}",
set
"SetEntriesInAclW sandbox dir failed: {set}",
));
}
let path_w = to_wide(dir.as_os_str());
@@ -328,8 +327,7 @@ fn lock_sandbox_dir(
);
if res != 0 {
return Err(anyhow::anyhow!(
"SetNamedSecurityInfoW sandbox dir failed: {}",
res
"SetNamedSecurityInfoW sandbox dir failed: {res}",
));
}
if !new_dacl.is_null() {
@@ -424,7 +422,7 @@ fn real_main() -> Result<()> {
});
let report = SetupErrorReport {
code: failure.code,
message: failure.message.clone(),
message: failure.message,
};
if let Err(write_err) = write_setup_error_report(&payload.codex_home, &report) {
let _ = log_line(
@@ -496,7 +494,7 @@ fn run_read_acl_only(payload: &Payload, log: &mut File) -> Result<()> {
if !refresh_errors.is_empty() {
log_line(
log,
&format!("read ACL run completed with errors: {:?}", refresh_errors),
&format!("read ACL run completed with errors: {refresh_errors:?}"),
)?;
if payload.refresh_only {
anyhow::bail!("read ACL run had errors");
@@ -637,7 +635,7 @@ fn run_setup_full(payload: &Payload, log: &mut File, sbx_dir: &Path) -> Result<(
}
}
let cap_sid_str = caps.workspace.clone();
let cap_sid_str = caps.workspace;
let sandbox_group_sid_str =
string_from_sid_bytes(&sandbox_group_sid).map_err(anyhow::Error::msg)?;
let write_mask =
@@ -895,7 +893,7 @@ fn run_setup_full(payload: &Payload, log: &mut File, sbx_dir: &Path) -> Result<(
if refresh_only && !refresh_errors.is_empty() {
log_line(
log,
&format!("setup refresh completed with errors: {:?}", refresh_errors),
&format!("setup refresh completed with errors: {refresh_errors:?}"),
)?;
anyhow::bail!("setup refresh had errors");
}
@@ -201,7 +201,7 @@ fn run_setup_refresh_inner(
&format!("setup refresh: exited with status {status:?}"),
Some(&sandbox_dir(request.codex_home)),
);
return Err(anyhow!("setup refresh failed with status {}", status));
return Err(anyhow!("setup refresh failed with status {status}"));
}
Ok(())
}
+3 -4
View File
@@ -78,7 +78,7 @@ unsafe fn set_default_dacl(h_token: HANDLE, sids: &[*mut c_void]) -> Result<()>
&mut p_new_dacl,
);
if res != ERROR_SUCCESS {
return Err(anyhow!("SetEntriesInAclW failed: {}", res));
return Err(anyhow!("SetEntriesInAclW failed: {res}"));
}
let mut info = TokenDefaultDaclInfo {
default_dacl: p_new_dacl,
@@ -95,8 +95,7 @@ unsafe fn set_default_dacl(h_token: HANDLE, sids: &[*mut c_void]) -> Result<()>
LocalFree(p_new_dacl as HLOCAL);
}
return Err(anyhow!(
"SetTokenInformation(TokenDefaultDacl) failed: {}",
err
"SetTokenInformation(TokenDefaultDacl) failed: {err}",
));
}
if !p_new_dacl.is_null() {
@@ -277,7 +276,7 @@ unsafe fn enable_single_privilege(h_token: HANDLE, name: &str) -> Result<()> {
}
let err = GetLastError();
if err != 0 {
return Err(anyhow!("AdjustTokenPrivileges error {}", err));
return Err(anyhow!("AdjustTokenPrivileges error {err}"));
}
Ok(())
}
+1 -1
View File
@@ -83,7 +83,7 @@ pub fn format_last_error(err: i32) -> String {
std::ptr::null_mut(),
);
if len == 0 || buf_ptr.is_null() {
return format!("Win32 error {}", err);
return format!("Win32 error {err}");
}
let slice = std::slice::from_raw_parts(buf_ptr, len as usize);
let mut s = String::from_utf16_lossy(slice);