Synced from monorepo

Changes:
- grok-shell: request workspaces:read/write OAuth2 scopes
- security: fix SSRF bypass via HTTP redirect in hook runner
- fix(grok-build): enterprise STT WSS URL + API-key voice bearer
- Harden identity-change purge and sync-marker invariants
- sandbox + workspace-server: delete the legacy ready-file arm
- Show billing URL when browser cannot open
- fix(pager): show folder-trust UI in minimal mode
- fix(pager): drain task_backgrounded before no-wait headless exit
- grok-agent-sdk: stop SDK-spawned agents from staging self-updates they can never adopt
- Split settings_modal into directory module
- Delegate VS Code SSH file links
- grok-shell: release the workspace session binding when a session is removed
- keep skills reachable when their name collides with a client builtin
- Preserve semantic link targets
This commit is contained in:
grokkybara[bot] 2026-07-16 20:27:30 +01:00
commit 8adf9013a0
117 changed files with 16998 additions and 14540 deletions

View file

@ -2520,9 +2520,13 @@ fn apply_user_info_enrichment_preserves_token_fields() {
/// Regression: async provider calls must drive `auth()` so tool requests get refreshed tokens.
#[tokio::test]
#[serial_test::serial] // reaches `resolve_static_api_key`, which reads the key env vars
async fn current_api_key_async_drives_refresh_chain() {
use xai_grok_test_support::EnvGuard;
use xai_grok_tools::types::ApiKeyProvider;
let _xai = EnvGuard::unset("XAI_API_KEY");
let _legacy = EnvGuard::unset("GROK_CODE_XAI_API_KEY");
let dir = tempfile::tempdir().unwrap();
let mgr = Arc::new(AuthManager::new(dir.path(), GrokComConfig::default()));
mgr.hot_swap(GrokAuth {
@ -3390,6 +3394,198 @@ async fn shared_api_key_provider_resolves_live_bearer() {
);
}
/// No OAuth session → env or auth.json `xai::api_key` for voice/tools.
#[tokio::test]
#[serial_test::serial]
async fn shared_api_key_provider_static_fallthrough() {
use xai_grok_test_support::EnvGuard;
let dir = tempfile::tempdir().unwrap();
let mgr = Arc::new(AuthManager::new(dir.path(), GrokComConfig::default()));
let provider = shared_api_key_provider(mgr.clone());
{
let _legacy = EnvGuard::unset("GROK_CODE_XAI_API_KEY");
let _key = EnvGuard::set("XAI_API_KEY", "env-only-key");
assert_eq!(
provider.current_api_key_async().await.as_deref(),
Some("env-only-key")
);
}
{
let _xai = EnvGuard::unset("XAI_API_KEY");
let _legacy = EnvGuard::unset("GROK_CODE_XAI_API_KEY");
crate::auth::store_api_key(dir.path(), "disk-api-key").unwrap();
assert_eq!(
provider.current_api_key_async().await.as_deref(),
Some("disk-api-key")
);
}
{
let _key = EnvGuard::set("XAI_API_KEY", "env-should-lose");
mgr.hot_swap(GrokAuth {
key: "session-bearer".into(),
expires_at: Some(Utc::now() + Duration::hours(1)),
create_time: Utc::now(),
..GrokAuth::test_default()
});
assert_eq!(
provider.current_api_key_async().await.as_deref(),
Some("session-bearer")
);
}
}
#[tokio::test]
#[serial_test::serial]
async fn shared_api_key_provider_kill_switch_blocks_static() {
use xai_grok_test_support::EnvGuard;
let _key = EnvGuard::set("XAI_API_KEY", "blocked");
let dir = tempfile::tempdir().unwrap();
let mgr = Arc::new(AuthManager::new(
dir.path(),
GrokComConfig {
disable_api_key_auth: Some(true),
..GrokComConfig::default()
},
));
assert_eq!(
shared_api_key_provider(mgr).current_api_key_async().await,
None
);
}
#[tokio::test]
#[serial_test::serial]
async fn shared_api_key_provider_oidc_preferred_blocks_static() {
use xai_grok_test_support::EnvGuard;
let _key = EnvGuard::set("XAI_API_KEY", "should-not-use");
let dir = tempfile::tempdir().unwrap();
let mgr = Arc::new(AuthManager::new(
dir.path(),
GrokComConfig {
preferred_method: Some(crate::auth::PreferredAuthMethod::Oidc),
..GrokComConfig::default()
},
));
assert_eq!(
shared_api_key_provider(mgr).current_api_key_async().await,
None
);
}
/// preferred_method=api_key: leftover session must not beat static API key.
#[tokio::test]
#[serial_test::serial]
async fn shared_api_key_provider_api_key_preferred_skips_session() {
use xai_grok_test_support::EnvGuard;
let _legacy = EnvGuard::unset("GROK_CODE_XAI_API_KEY");
let _key = EnvGuard::set("XAI_API_KEY", "static-preferred");
let dir = tempfile::tempdir().unwrap();
let mgr = Arc::new(AuthManager::new(
dir.path(),
GrokComConfig {
preferred_method: Some(crate::auth::PreferredAuthMethod::ApiKey),
..GrokComConfig::default()
},
));
mgr.hot_swap(GrokAuth {
key: "leftover-oidc".into(),
expires_at: Some(Utc::now() + Duration::hours(1)),
create_time: Utc::now(),
..GrokAuth::test_default()
});
assert_eq!(
shared_api_key_provider(mgr)
.current_api_key_async()
.await
.as_deref(),
Some("static-preferred")
);
}
/// Expired OAuth must not block static fallthrough on the sync path.
#[tokio::test]
#[serial_test::serial]
async fn shared_api_key_provider_sync_falls_through_when_session_expired() {
use xai_grok_test_support::EnvGuard;
let _legacy = EnvGuard::unset("GROK_CODE_XAI_API_KEY");
let _key = EnvGuard::set("XAI_API_KEY", "static-after-expiry");
let dir = tempfile::tempdir().unwrap();
let mgr = Arc::new(AuthManager::new(dir.path(), GrokComConfig::default()));
mgr.hot_swap(GrokAuth {
key: "expired-oidc".into(),
auth_mode: AuthMode::Oidc,
refresh_token: Some("rt".into()),
expires_at: Some(Utc::now() - Duration::hours(1)),
..GrokAuth::test_default()
});
let provider = shared_api_key_provider(mgr);
assert_eq!(
provider.current_api_key().as_deref(),
Some("static-after-expiry"),
"sync path must not return a dead session token over a live static key"
);
assert_eq!(
provider.current_api_key_async().await.as_deref(),
Some("static-after-expiry")
);
}
/// A session inside the early-invalidation buffer is still wire-valid and
/// must beat a static key on the sync path.
#[tokio::test]
#[serial_test::serial]
async fn shared_api_key_provider_sync_buffered_session_beats_static() {
use xai_grok_test_support::EnvGuard;
use xai_grok_tools::types::ApiKeyProvider;
let _legacy = EnvGuard::unset("GROK_CODE_XAI_API_KEY");
let _key = EnvGuard::set("XAI_API_KEY", "leftover-static");
let dir = tempfile::tempdir().unwrap();
let mgr = Arc::new(AuthManager::new(dir.path(), GrokComConfig::default()));
// Two minutes out: inside the 5-minute buffer, but accepted on the wire.
mgr.hot_swap(GrokAuth {
key: "buffered-oidc".into(),
auth_mode: AuthMode::Oidc,
refresh_token: Some("rt".into()),
expires_at: Some(Utc::now() + Duration::minutes(2)),
..GrokAuth::test_default()
});
let provider = super::SharedAuthKeyProvider(mgr);
assert_eq!(provider.current_api_key().as_deref(), Some("buffered-oidc"));
}
/// Auth.json create, rewrite (including same-length, caught by the inode in
/// the memo stamp), and logout must all invalidate the disk static-key memo.
#[tokio::test]
#[serial_test::serial]
async fn shared_api_key_provider_disk_memo_follows_rewrites() {
use xai_grok_test_support::EnvGuard;
let _xai = EnvGuard::unset("XAI_API_KEY");
let _legacy = EnvGuard::unset("GROK_CODE_XAI_API_KEY");
let dir = tempfile::tempdir().unwrap();
let mgr = Arc::new(AuthManager::new(dir.path(), GrokComConfig::default()));
let provider = shared_api_key_provider(mgr);
assert_eq!(provider.current_api_key_async().await, None);
for key in ["first-key", "fresh-key", "second-key-rotated"] {
crate::auth::store_api_key(dir.path(), key).unwrap();
assert_eq!(provider.current_api_key_async().await.as_deref(), Some(key));
}
crate::auth::clear_api_key(dir.path()).unwrap();
assert_eq!(provider.current_api_key_async().await, None);
}
fn expired_oidc() -> GrokAuth {
GrokAuth {
key: "expired-key".into(),