fix: pre-release audit fixes — Twitter OAuth, DataMasking perf, Prompt versioning

- Twitter like/retweet: return explicit unavailable error instead of
  sending doomed Bearer token requests (would 403 on Twitter API v2)
- DataMasking: pre-compile regex patterns with LazyLock (was compiling
  6 patterns on every mask() call)
- Prompt version: fix get_version handler ignoring version path param,
  add service::get_version_by_number for correct per-version retrieval
This commit is contained in:
iven
2026-04-09 16:43:24 +08:00
parent f2d6a3b6b7
commit 3f2acb49fb
4 changed files with 99 additions and 79 deletions

View File

@@ -497,62 +497,34 @@ impl TwitterHand {
}
/// Execute like action — PUT /2/users/:id/likes
///
/// **NOTE**: Twitter API v2 requires OAuth 1.0a user context for like/retweet.
/// Bearer token (app-only auth) is not sufficient and will return 403.
/// This action is currently unavailable until OAuth 1.0a signing is implemented.
async fn execute_like(&self, tweet_id: &str) -> Result<Value> {
let creds = self.get_credentials().await
.ok_or_else(|| zclaw_types::ZclawError::HandError("Twitter credentials not configured".to_string()))?;
let client = reqwest::Client::new();
// Note: For like/retweet, we need OAuth 1.0a user context
// Using Bearer token as fallback (may not work for all endpoints)
let url = "https://api.twitter.com/2/users/me/likes";
let response = client.post(url)
.header("Authorization", format!("Bearer {}", creds.bearer_token.as_deref().unwrap_or("")))
.header("Content-Type", "application/json")
.header("User-Agent", "ZCLAW/1.0")
.json(&json!({"tweet_id": tweet_id}))
.send()
.await
.map_err(|e| zclaw_types::ZclawError::HandError(format!("Like failed: {}", e)))?;
let status = response.status();
let response_text = response.text().await.unwrap_or_default();
let _ = tweet_id;
tracing::warn!("[TwitterHand] like action requires OAuth 1.0a user context — not yet supported");
Ok(json!({
"success": status.is_success(),
"tweet_id": tweet_id,
"action": "liked",
"status_code": status.as_u16(),
"message": if status.is_success() { "Tweet liked" } else { &response_text }
"success": false,
"action": "like",
"error": "OAuth 1.0a user context required. Like action is not yet supported with app-only Bearer token.",
"suggestion": "Configure OAuth 1.0a credentials (access_token + access_token_secret) to enable write actions."
}))
}
/// Execute retweet action — POST /2/users/:id/retweets
///
/// **NOTE**: Twitter API v2 requires OAuth 1.0a user context for retweet.
/// Bearer token (app-only auth) is not sufficient and will return 403.
/// This action is currently unavailable until OAuth 1.0a signing is implemented.
async fn execute_retweet(&self, tweet_id: &str) -> Result<Value> {
let creds = self.get_credentials().await
.ok_or_else(|| zclaw_types::ZclawError::HandError("Twitter credentials not configured".to_string()))?;
let client = reqwest::Client::new();
let url = "https://api.twitter.com/2/users/me/retweets";
let response = client.post(url)
.header("Authorization", format!("Bearer {}", creds.bearer_token.as_deref().unwrap_or("")))
.header("Content-Type", "application/json")
.header("User-Agent", "ZCLAW/1.0")
.json(&json!({"tweet_id": tweet_id}))
.send()
.await
.map_err(|e| zclaw_types::ZclawError::HandError(format!("Retweet failed: {}", e)))?;
let status = response.status();
let response_text = response.text().await.unwrap_or_default();
let _ = tweet_id;
tracing::warn!("[TwitterHand] retweet action requires OAuth 1.0a user context — not yet supported");
Ok(json!({
"success": status.is_success(),
"tweet_id": tweet_id,
"action": "retweeted",
"status_code": status.as_u16(),
"message": if status.is_success() { "Tweet retweeted" } else { &response_text }
"success": false,
"action": "retweet",
"error": "OAuth 1.0a user context required. Retweet action is not yet supported with app-only Bearer token.",
"suggestion": "Configure OAuth 1.0a credentials (access_token + access_token_secret) to enable write actions."
}))
}