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
27 changes: 23 additions & 4 deletions src/crates/assembly/core/src/external_sources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5616,8 +5616,14 @@ mod tests {

#[test]
fn opencode_registry_owns_low_friction_defaults_and_safety_ceilings() {
let policy = integration_policy_snapshot(&ExternalSourcesConfig::default(), None)
.expect("built-in policy is valid");
let mut config = ExternalSourcesConfig::default();
config
.integration_policy
.known_mut()
.expect("the built-in policy schema is known")
.user_defaults
.enabled = true;
let policy = integration_policy_snapshot(&config, None).expect("built-in policy is valid");
let descriptor = policy
.registered_ecosystems
.iter()
Expand Down Expand Up @@ -5662,8 +5668,15 @@ mod tests {

#[test]
fn active_capability_sets_are_scoped_per_ecosystem_for_every_asset_kind() {
let mut policy = integration_policy_snapshot(&ExternalSourcesConfig::default(), None)
.expect("built-in policy is valid");
let mut config = ExternalSourcesConfig::default();
config
.integration_policy
.known_mut()
.expect("the built-in policy schema is known")
.user_defaults
.enabled = true;
let mut policy =
integration_policy_snapshot(&config, None).expect("built-in policy is valid");
let template_descriptor = policy.registered_ecosystems[0].clone();
let template_effective = policy
.effective
Expand Down Expand Up @@ -6022,6 +6035,12 @@ mod tests {
approved_tool_targets: BTreeSet::from([approval_key.to_string()]),
..ExternalSourcesConfig::default()
};
config
.integration_policy
.known_mut()
.expect("the built-in policy schema is known")
.user_defaults
.enabled = true;

config.suppressed_source_keys.push(source.preference_key());
assert!(!external_tool_invocation_is_authorized_by(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ pub struct ExternalIntegrationPolicySettings {
impl Default for ExternalIntegrationPolicySettings {
fn default() -> Self {
Self {
enabled: true,
enabled: false,
ecosystems: BTreeMap::new(),
extensions: BTreeMap::new(),
}
Expand Down Expand Up @@ -453,11 +453,11 @@ impl Default for ExternalIntegrationPolicySnapshot {
),
workspace_override: None,
global_effective: EffectiveExternalIntegrationPolicy {
enabled: true,
enabled: false,
ecosystems: BTreeMap::new(),
},
effective: EffectiveExternalIntegrationPolicy {
enabled: true,
enabled: false,
ecosystems: BTreeMap::new(),
},
registered_ecosystems: Vec::new(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -969,7 +969,7 @@ fn test_external_integration_ecosystems() -> Vec<ExternalIntegrationEcosystemDes
}

#[test]
fn recommended_external_integration_policy_is_low_friction_and_fail_closed() {
fn external_integration_policy_is_disabled_by_default() {
let effective = evaluate_external_integration_policy(
&ExternalIntegrationPolicyDocument::default(),
Some("workspace-a"),
Expand All @@ -981,6 +981,38 @@ fn recommended_external_integration_policy_is_low_friction_and_fail_closed() {
.get(&EcosystemId::new(TEST_ECOSYSTEM_ID).unwrap())
.expect("test ecosystem is registered");

assert!(!effective.enabled);
assert_eq!(opencode.mode, ExternalIntegrationMode::Disabled);
for capability in [
EXTERNAL_CAPABILITY_COMMAND,
EXTERNAL_CAPABILITY_TOOL,
EXTERNAL_CAPABILITY_SUBAGENT,
EXTERNAL_CAPABILITY_MCP,
] {
assert_eq!(
opencode.capabilities[&external_capability(capability)],
ExternalIntegrationAccess::Disabled
);
}
}

#[test]
fn explicitly_enabled_recommended_policy_keeps_registered_access_defaults() {
let mut document = ExternalIntegrationPolicyDocument::default();
document.user_defaults.enabled = true;

let effective = evaluate_external_integration_policy(
&document,
Some("workspace-a"),
&test_external_integration_ecosystems(),
)
.expect("enabled recommended policy evaluates");
let opencode = effective
.ecosystems
.get(&EcosystemId::new(TEST_ECOSYSTEM_ID).unwrap())
.expect("test ecosystem is registered");

assert!(effective.enabled);
assert_eq!(opencode.mode, ExternalIntegrationMode::Recommended);
assert_eq!(
opencode.capabilities[&external_capability(EXTERNAL_CAPABILITY_COMMAND)],
Expand All @@ -1002,6 +1034,7 @@ fn recommended_external_integration_policy_is_low_friction_and_fail_closed() {
fn workspace_policy_overrides_only_the_fields_the_user_changed() {
let ecosystem = EcosystemId::new(TEST_ECOSYSTEM_ID).unwrap();
let mut document = ExternalIntegrationPolicyDocument::default();
document.user_defaults.enabled = true;
document.user_defaults.ecosystems.insert(
ecosystem.clone(),
ExternalEcosystemPolicy {
Expand Down Expand Up @@ -1065,6 +1098,7 @@ fn high_risk_auto_access_is_limited_by_the_capability_owner() {
let ecosystem = EcosystemId::new(TEST_ECOSYSTEM_ID).unwrap();
let mcp = external_capability(EXTERNAL_CAPABILITY_MCP);
let mut document = ExternalIntegrationPolicyDocument::default();
document.user_defaults.enabled = true;
document.user_defaults.ecosystems.insert(
ecosystem.clone(),
ExternalEcosystemPolicy {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,33 @@ describe('ExternalSourcesConfig', () => {
expect(toolRow?.textContent).not.toContain('policy.access.auto');
});

it('fails closed when an older host omits the stored enabled flag', async () => {
getSnapshotMock.mockResolvedValue({
...snapshot,
integrationPolicy: {
...integrationPolicy,
userDefaults: {
...integrationPolicy.userDefaults,
enabled: undefined,
},
workspaceOverride: {
enabled: undefined,
ecosystems: {},
},
},
});

await act(async () => {
root.render(<ExternalSourcesConfig />);
await Promise.resolve();
});

const policyToggle = container.querySelector(
'.bitfun-external-sources-config__policy-card input[type="checkbox"]',
) as HTMLInputElement;
expect(policyToggle.checked).toBe(false);
});

it('requires one explicit conflict choice and persists source toggles', async () => {
await act(async () => {
root.render(<ExternalSourcesConfig />);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -819,8 +819,8 @@ const ExternalSourcesConfig: React.FC = () => {
|| Boolean(workspace?.connectionId);
const policy = snapshot?.integrationPolicy;
const selectedPolicyEnabled = policyScope === 'workspace'
? policy?.workspaceOverride?.enabled ?? policy?.userDefaults.enabled ?? true
: policy?.userDefaults.enabled ?? true;
? policy?.workspaceOverride?.enabled ?? policy?.userDefaults.enabled ?? false
: policy?.userDefaults.enabled ?? false;
const selectedPolicyEffective = policyScope === 'workspace'
? policy?.effective
: policy?.globalEffective;
Expand Down