Merge pull request #1020 from zeroclaw-labs/fix/code-scanning-alerts
fix(security): address CodeQL code-scanning alerts
This commit is contained in:
commit
9de77df235
5 changed files with 53 additions and 17 deletions
|
|
@ -602,9 +602,12 @@ mod tests {
|
||||||
assert_eq!(msgs[0].content, "First part\nSecond part");
|
assert_eq!(msgs[0].content, "First part\nSecond part");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fixture secret used exclusively in signature-verification unit tests (not a real credential).
|
||||||
|
const TEST_WEBHOOK_SECRET: &str = "test_webhook_secret";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn linq_signature_verification_valid() {
|
fn linq_signature_verification_valid() {
|
||||||
let secret = "test_webhook_secret";
|
let secret = TEST_WEBHOOK_SECRET;
|
||||||
let body = r#"{"event_type":"message.received"}"#;
|
let body = r#"{"event_type":"message.received"}"#;
|
||||||
let now = chrono::Utc::now().timestamp().to_string();
|
let now = chrono::Utc::now().timestamp().to_string();
|
||||||
|
|
||||||
|
|
@ -621,7 +624,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn linq_signature_verification_invalid() {
|
fn linq_signature_verification_invalid() {
|
||||||
let secret = "test_webhook_secret";
|
let secret = TEST_WEBHOOK_SECRET;
|
||||||
let body = r#"{"event_type":"message.received"}"#;
|
let body = r#"{"event_type":"message.received"}"#;
|
||||||
let now = chrono::Utc::now().timestamp().to_string();
|
let now = chrono::Utc::now().timestamp().to_string();
|
||||||
|
|
||||||
|
|
@ -635,7 +638,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn linq_signature_verification_stale_timestamp() {
|
fn linq_signature_verification_stale_timestamp() {
|
||||||
let secret = "test_webhook_secret";
|
let secret = TEST_WEBHOOK_SECRET;
|
||||||
let body = r#"{"event_type":"message.received"}"#;
|
let body = r#"{"event_type":"message.received"}"#;
|
||||||
// 10 minutes ago — stale
|
// 10 minutes ago — stale
|
||||||
let stale_ts = (chrono::Utc::now().timestamp() - 600).to_string();
|
let stale_ts = (chrono::Utc::now().timestamp() - 600).to_string();
|
||||||
|
|
@ -656,7 +659,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn linq_signature_verification_accepts_sha256_prefix() {
|
fn linq_signature_verification_accepts_sha256_prefix() {
|
||||||
let secret = "test_webhook_secret";
|
let secret = TEST_WEBHOOK_SECRET;
|
||||||
let body = r#"{"event_type":"message.received"}"#;
|
let body = r#"{"event_type":"message.received"}"#;
|
||||||
let now = chrono::Utc::now().timestamp().to_string();
|
let now = chrono::Utc::now().timestamp().to_string();
|
||||||
|
|
||||||
|
|
@ -672,7 +675,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn linq_signature_verification_accepts_uppercase_hex() {
|
fn linq_signature_verification_accepts_uppercase_hex() {
|
||||||
let secret = "test_webhook_secret";
|
let secret = TEST_WEBHOOK_SECRET;
|
||||||
let body = r#"{"event_type":"message.received"}"#;
|
let body = r#"{"event_type":"message.received"}"#;
|
||||||
let now = chrono::Utc::now().timestamp().to_string();
|
let now = chrono::Utc::now().timestamp().to_string();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -262,8 +262,8 @@ impl MatrixChannel {
|
||||||
if hinted != &whoami.user_id {
|
if hinted != &whoami.user_id {
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
"Matrix configured user_id '{}' does not match whoami '{}'; using whoami.",
|
"Matrix configured user_id '{}' does not match whoami '{}'; using whoami.",
|
||||||
hinted,
|
crate::security::redact(hinted),
|
||||||
whoami.user_id
|
crate::security::redact(&whoami.user_id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -282,8 +282,8 @@ impl MatrixChannel {
|
||||||
if whoami_device_id != hinted {
|
if whoami_device_id != hinted {
|
||||||
tracing::warn!(
|
tracing::warn!(
|
||||||
"Matrix configured device_id '{}' does not match whoami '{}'; using whoami.",
|
"Matrix configured device_id '{}' does not match whoami '{}'; using whoami.",
|
||||||
hinted,
|
crate::security::redact(hinted),
|
||||||
whoami_device_id
|
crate::security::redact(whoami_device_id)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
whoami_device_id.clone()
|
whoami_device_id.clone()
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ pub fn is_assistant_autosave_key(key: &str) -> bool {
|
||||||
normalized == "assistant_resp" || normalized.starts_with("assistant_resp_")
|
normalized == "assistant_resp" || normalized.starts_with("assistant_resp_")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Clone, PartialEq, Eq)]
|
||||||
struct ResolvedEmbeddingConfig {
|
struct ResolvedEmbeddingConfig {
|
||||||
provider: String,
|
provider: String,
|
||||||
model: String,
|
model: String,
|
||||||
|
|
@ -90,6 +90,17 @@ struct ResolvedEmbeddingConfig {
|
||||||
api_key: Option<String>,
|
api_key: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for ResolvedEmbeddingConfig {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("ResolvedEmbeddingConfig")
|
||||||
|
.field("provider", &self.provider)
|
||||||
|
.field("model", &self.model)
|
||||||
|
.field("dimensions", &self.dimensions)
|
||||||
|
.field("api_key", &self.api_key.as_ref().map(|_| "[REDACTED]"))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_embedding_config(
|
fn resolve_embedding_config(
|
||||||
config: &MemoryConfig,
|
config: &MemoryConfig,
|
||||||
embedding_routes: &[EmbeddingRouteConfig],
|
embedding_routes: &[EmbeddingRouteConfig],
|
||||||
|
|
|
||||||
|
|
@ -797,9 +797,13 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// AWS documentation example key for SigV4 test vectors (not a real credential).
|
||||||
|
const TEST_VECTOR_SECRET: &str = "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hmac_sha256_known_input() {
|
fn hmac_sha256_known_input() {
|
||||||
let result = hmac_sha256(b"key", b"message");
|
let test_key: &[u8] = b"key";
|
||||||
|
let result = hmac_sha256(test_key, b"message");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
hex::encode(&result),
|
hex::encode(&result),
|
||||||
"6e9ef29b75fffc5b7abae527d58fdadb2fe42e7219011976917343065f58ed4a"
|
"6e9ef29b75fffc5b7abae527d58fdadb2fe42e7219011976917343065f58ed4a"
|
||||||
|
|
@ -810,7 +814,7 @@ mod tests {
|
||||||
fn derive_signing_key_structure() {
|
fn derive_signing_key_structure() {
|
||||||
// Verify the key derivation produces a 32-byte key (SHA-256 output).
|
// Verify the key derivation produces a 32-byte key (SHA-256 output).
|
||||||
let key = derive_signing_key(
|
let key = derive_signing_key(
|
||||||
"wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
|
TEST_VECTOR_SECRET,
|
||||||
"20150830",
|
"20150830",
|
||||||
"us-east-1",
|
"us-east-1",
|
||||||
"iam",
|
"iam",
|
||||||
|
|
@ -821,10 +825,8 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn derive_signing_key_known_test_vector() {
|
fn derive_signing_key_known_test_vector() {
|
||||||
// AWS SigV4 test vector from documentation.
|
// AWS SigV4 test vector from documentation.
|
||||||
// Secret: "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY"
|
|
||||||
// Date: "20150830", Region: "us-east-1", Service: "iam"
|
|
||||||
let key = derive_signing_key(
|
let key = derive_signing_key(
|
||||||
"wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
|
TEST_VECTOR_SECRET,
|
||||||
"20150830",
|
"20150830",
|
||||||
"us-east-1",
|
"us-east-1",
|
||||||
"iam",
|
"iam",
|
||||||
|
|
|
||||||
|
|
@ -197,7 +197,7 @@ struct MinimaxOauthBaseResponse {
|
||||||
status_msg: Option<String>,
|
status_msg: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Default)]
|
#[derive(Clone, Deserialize, Default)]
|
||||||
struct QwenOauthCredentials {
|
struct QwenOauthCredentials {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
access_token: Option<String>,
|
access_token: Option<String>,
|
||||||
|
|
@ -209,6 +209,17 @@ struct QwenOauthCredentials {
|
||||||
expiry_date: Option<i64>,
|
expiry_date: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for QwenOauthCredentials {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("QwenOauthCredentials")
|
||||||
|
.field("access_token", &self.access_token.as_ref().map(|_| "[REDACTED]"))
|
||||||
|
.field("refresh_token", &self.refresh_token.as_ref().map(|_| "[REDACTED]"))
|
||||||
|
.field("resource_url", &self.resource_url)
|
||||||
|
.field("expiry_date", &self.expiry_date)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
struct QwenOauthTokenResponse {
|
struct QwenOauthTokenResponse {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
@ -225,12 +236,21 @@ struct QwenOauthTokenResponse {
|
||||||
error_description: Option<String>,
|
error_description: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
struct QwenOauthProviderContext {
|
struct QwenOauthProviderContext {
|
||||||
credential: Option<String>,
|
credential: Option<String>,
|
||||||
base_url: Option<String>,
|
base_url: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for QwenOauthProviderContext {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("QwenOauthProviderContext")
|
||||||
|
.field("credential", &self.credential.as_ref().map(|_| "[REDACTED]"))
|
||||||
|
.field("base_url", &self.base_url)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn read_non_empty_env(name: &str) -> Option<String> {
|
fn read_non_empty_env(name: &str) -> Option<String> {
|
||||||
std::env::var(name)
|
std::env::var(name)
|
||||||
.ok()
|
.ok()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue