Stricter disable_ai
overrides (#35977)
Settings overrides (e.g. local project settings, server settings) can no longer change `disable_ai` to `false` if it was `true`; they can only change it to `true`. In other words, settings can only cause AI to be *more* disabled, they can't undo the user's preference for no AI (or the project's requirement not to use AI). Release Notes: - Settings overrides (such as local project settings) can now only override `disable_ai` to become `true`; they can no longer cause otherwise-disabled AI to become re-enabled. --------- Co-authored-by: Assistant <assistant@anthropic.com> Co-authored-by: David Kleingeld <git@davidsk.dev>
This commit is contained in:
parent
abb64d2320
commit
6478e66e7a
1 changed files with 163 additions and 8 deletions
|
@ -962,14 +962,19 @@ impl settings::Settings for DisableAiSettings {
|
|||
type FileContent = Option<bool>;
|
||||
|
||||
fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
|
||||
Ok(Self {
|
||||
disable_ai: sources
|
||||
.user
|
||||
.or(sources.server)
|
||||
.copied()
|
||||
.flatten()
|
||||
.unwrap_or(sources.default.ok_or_else(Self::missing_default)?),
|
||||
})
|
||||
// For security reasons, settings can only make AI restrictions MORE strict, not less.
|
||||
// (For example, if someone is working on a project that contractually
|
||||
// requires no AI use, that should override the user's setting which
|
||||
// permits AI use.)
|
||||
// This also prevents an attacker from using project or server settings to enable AI when it should be disabled.
|
||||
let disable_ai = sources
|
||||
.project
|
||||
.iter()
|
||||
.chain(sources.user.iter())
|
||||
.chain(sources.server.iter())
|
||||
.any(|disabled| **disabled == Some(true));
|
||||
|
||||
Ok(Self { disable_ai })
|
||||
}
|
||||
|
||||
fn import_from_vscode(_vscode: &settings::VsCodeSettings, _current: &mut Self::FileContent) {}
|
||||
|
@ -5508,3 +5513,153 @@ fn provide_inline_values(
|
|||
|
||||
variables
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod disable_ai_settings_tests {
|
||||
use super::*;
|
||||
use gpui::TestAppContext;
|
||||
use settings::{Settings, SettingsSources};
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_disable_ai_settings_security(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| {
|
||||
// Test 1: Default is false (AI enabled)
|
||||
let sources = SettingsSources {
|
||||
default: &Some(false),
|
||||
global: None,
|
||||
extensions: None,
|
||||
user: None,
|
||||
release_channel: None,
|
||||
operating_system: None,
|
||||
profile: None,
|
||||
server: None,
|
||||
project: &[],
|
||||
};
|
||||
let settings = DisableAiSettings::load(sources, cx).unwrap();
|
||||
assert_eq!(settings.disable_ai, false, "Default should allow AI");
|
||||
|
||||
// Test 2: Global true, local false -> still disabled (local cannot re-enable)
|
||||
let global_true = Some(true);
|
||||
let local_false = Some(false);
|
||||
let sources = SettingsSources {
|
||||
default: &Some(false),
|
||||
global: None,
|
||||
extensions: None,
|
||||
user: Some(&global_true),
|
||||
release_channel: None,
|
||||
operating_system: None,
|
||||
profile: None,
|
||||
server: None,
|
||||
project: &[&local_false],
|
||||
};
|
||||
let settings = DisableAiSettings::load(sources, cx).unwrap();
|
||||
assert_eq!(
|
||||
settings.disable_ai, true,
|
||||
"Local false cannot override global true"
|
||||
);
|
||||
|
||||
// Test 3: Global false, local true -> disabled (local can make more restrictive)
|
||||
let global_false = Some(false);
|
||||
let local_true = Some(true);
|
||||
let sources = SettingsSources {
|
||||
default: &Some(false),
|
||||
global: None,
|
||||
extensions: None,
|
||||
user: Some(&global_false),
|
||||
release_channel: None,
|
||||
operating_system: None,
|
||||
profile: None,
|
||||
server: None,
|
||||
project: &[&local_true],
|
||||
};
|
||||
let settings = DisableAiSettings::load(sources, cx).unwrap();
|
||||
assert_eq!(
|
||||
settings.disable_ai, true,
|
||||
"Local true can override global false"
|
||||
);
|
||||
|
||||
// Test 4: Server can only make more restrictive (set to true)
|
||||
let user_false = Some(false);
|
||||
let server_true = Some(true);
|
||||
let sources = SettingsSources {
|
||||
default: &Some(false),
|
||||
global: None,
|
||||
extensions: None,
|
||||
user: Some(&user_false),
|
||||
release_channel: None,
|
||||
operating_system: None,
|
||||
profile: None,
|
||||
server: Some(&server_true),
|
||||
project: &[],
|
||||
};
|
||||
let settings = DisableAiSettings::load(sources, cx).unwrap();
|
||||
assert_eq!(
|
||||
settings.disable_ai, true,
|
||||
"Server can set to true even if user is false"
|
||||
);
|
||||
|
||||
// Test 5: Server false cannot override user true
|
||||
let user_true = Some(true);
|
||||
let server_false = Some(false);
|
||||
let sources = SettingsSources {
|
||||
default: &Some(false),
|
||||
global: None,
|
||||
extensions: None,
|
||||
user: Some(&user_true),
|
||||
release_channel: None,
|
||||
operating_system: None,
|
||||
profile: None,
|
||||
server: Some(&server_false),
|
||||
project: &[],
|
||||
};
|
||||
let settings = DisableAiSettings::load(sources, cx).unwrap();
|
||||
assert_eq!(
|
||||
settings.disable_ai, true,
|
||||
"Server false cannot override user true"
|
||||
);
|
||||
|
||||
// Test 6: Multiple local settings, any true disables AI
|
||||
let global_false = Some(false);
|
||||
let local_false3 = Some(false);
|
||||
let local_true2 = Some(true);
|
||||
let local_false4 = Some(false);
|
||||
let sources = SettingsSources {
|
||||
default: &Some(false),
|
||||
global: None,
|
||||
extensions: None,
|
||||
user: Some(&global_false),
|
||||
release_channel: None,
|
||||
operating_system: None,
|
||||
profile: None,
|
||||
server: None,
|
||||
project: &[&local_false3, &local_true2, &local_false4],
|
||||
};
|
||||
let settings = DisableAiSettings::load(sources, cx).unwrap();
|
||||
assert_eq!(
|
||||
settings.disable_ai, true,
|
||||
"Any local true should disable AI"
|
||||
);
|
||||
|
||||
// Test 7: All three sources can independently disable AI
|
||||
let user_false2 = Some(false);
|
||||
let server_false2 = Some(false);
|
||||
let local_true3 = Some(true);
|
||||
let sources = SettingsSources {
|
||||
default: &Some(false),
|
||||
global: None,
|
||||
extensions: None,
|
||||
user: Some(&user_false2),
|
||||
release_channel: None,
|
||||
operating_system: None,
|
||||
profile: None,
|
||||
server: Some(&server_false2),
|
||||
project: &[&local_true3],
|
||||
};
|
||||
let settings = DisableAiSettings::load(sources, cx).unwrap();
|
||||
assert_eq!(
|
||||
settings.disable_ai, true,
|
||||
"Local can disable even if user and server are false"
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue