fix: Resolve migration error, Vault 403, cache timeout, and shell injection false positives
Some checks failed
BotServer CI/CD / build (push) Has been cancelled
Some checks failed
BotServer CI/CD / build (push) Has been cancelled
- Fix migration 6.2.5: Create lost_reason column before VIEW that references it - Fix Vault 403: Enable KV2 secrets engine after initialization - Fix cache timeout: Increase Valkey readiness wait from 12s to 30s - Fix command_guard: Remove () from forbidden chars (safe in std::process::Command)
This commit is contained in:
parent
26684e2db3
commit
2fa59057fa
4 changed files with 61 additions and 10 deletions
|
|
@ -52,7 +52,10 @@ INSERT INTO attendance_sla_policies (org_id, bot_id, name, channel, priority, fi
|
||||||
SELECT DISTINCT org_id, id as bot_id, 'Default - Low', NULL, 'low', 60, 1440
|
SELECT DISTINCT org_id, id as bot_id, 'Default - Low', NULL, 'low', 60, 1440
|
||||||
FROM bots WHERE org_id IS NOT NULL ON CONFLICT DO NOTHING;
|
FROM bots WHERE org_id IS NOT NULL ON CONFLICT DO NOTHING;
|
||||||
|
|
||||||
-- 5. Create legacy compat views for leads/opportunities (from crm-sales.md)
|
-- 5. Add lost_reason column BEFORE views that reference it
|
||||||
|
ALTER TABLE crm_deals ADD COLUMN IF NOT EXISTS lost_reason varchar(255);
|
||||||
|
|
||||||
|
-- 6. Create legacy compat views for leads/opportunities (from crm-sales.md)
|
||||||
CREATE OR REPLACE VIEW crm_leads_compat AS
|
CREATE OR REPLACE VIEW crm_leads_compat AS
|
||||||
SELECT id, org_id, bot_id, contact_id, account_id,
|
SELECT id, org_id, bot_id, contact_id, account_id,
|
||||||
COALESCE(title, name, '') as title, description, value, currency,
|
COALESCE(title, name, '') as title, description, value, currency,
|
||||||
|
|
@ -71,11 +74,8 @@ CREATE OR REPLACE VIEW crm_opportunities_compat AS
|
||||||
FROM crm_deals
|
FROM crm_deals
|
||||||
WHERE stage IN ('proposal', 'negotiation', 'won', 'lost');
|
WHERE stage IN ('proposal', 'negotiation', 'won', 'lost');
|
||||||
|
|
||||||
-- 6. Create index for SLA events
|
-- 7. Create index for SLA events
|
||||||
CREATE INDEX IF NOT EXISTS idx_sla_events_status ON attendance_sla_events(status);
|
CREATE INDEX IF NOT EXISTS idx_sla_events_status ON attendance_sla_events(status);
|
||||||
CREATE INDEX IF NOT EXISTS idx_sla_events_due ON attendance_sla_events(due_at);
|
CREATE INDEX IF NOT EXISTS idx_sla_events_due ON attendance_sla_events(due_at);
|
||||||
CREATE INDEX IF NOT EXISTS idx_sla_events_session ON attendance_sla_events(session_id);
|
CREATE INDEX IF NOT EXISTS idx_sla_events_session ON attendance_sla_events(session_id);
|
||||||
CREATE INDEX IF NOT EXISTS idx_sla_policies_org_bot ON attendance_sla_policies(org_id, bot_id);
|
CREATE INDEX IF NOT EXISTS idx_sla_policies_org_bot ON attendance_sla_policies(org_id, bot_id);
|
||||||
|
|
||||||
-- 7. Add lost_reason column if not exists
|
|
||||||
ALTER TABLE crm_deals ADD COLUMN IF NOT EXISTS lost_reason varchar(255);
|
|
||||||
|
|
|
||||||
|
|
@ -127,15 +127,15 @@ impl BootstrapManager {
|
||||||
match pm.start("cache") {
|
match pm.start("cache") {
|
||||||
Ok(_child) => {
|
Ok(_child) => {
|
||||||
info!("Valkey cache process started, waiting for readiness...");
|
info!("Valkey cache process started, waiting for readiness...");
|
||||||
// Wait for cache to be ready
|
// Wait for cache to be ready (up to 30 seconds)
|
||||||
for i in 0..12 {
|
for i in 0..30 {
|
||||||
sleep(Duration::from_secs(1)).await;
|
sleep(Duration::from_secs(1)).await;
|
||||||
if cache_health_check() {
|
if cache_health_check() {
|
||||||
info!("Valkey cache is responding");
|
info!("Valkey cache is responding");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if i == 11 {
|
if i == 29 {
|
||||||
warn!("Valkey cache did not respond after 12 seconds");
|
warn!("Valkey cache did not respond after 30 seconds");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1518,6 +1518,33 @@ VAULT_CACERT={}
|
||||||
info!("✓ Created .env with VAULT_ADDR, VAULT_TOKEN");
|
info!("✓ Created .env with VAULT_ADDR, VAULT_TOKEN");
|
||||||
info!("✓ Created /opt/gbo/secrets/vault-unseal-keys (chmod 600)");
|
info!("✓ Created /opt/gbo/secrets/vault-unseal-keys (chmod 600)");
|
||||||
|
|
||||||
|
// Enable KV2 secrets engine at 'secret/' path
|
||||||
|
info!("Enabling KV2 secrets engine at 'secret/'...");
|
||||||
|
let enable_kv2_cmd = format!(
|
||||||
|
"VAULT_ADDR={} VAULT_TOKEN={} VAULT_CACERT={} {} secrets enable -path=secret kv-v2",
|
||||||
|
vault_addr,
|
||||||
|
root_token,
|
||||||
|
ca_cert.display(),
|
||||||
|
vault_bin.display()
|
||||||
|
);
|
||||||
|
match safe_sh_command(&enable_kv2_cmd) {
|
||||||
|
Some(output) => {
|
||||||
|
if output.status.success() {
|
||||||
|
info!("KV2 secrets engine enabled at 'secret/'");
|
||||||
|
} else {
|
||||||
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||||
|
if stderr.contains("path is already in use") {
|
||||||
|
info!("KV2 secrets engine already enabled");
|
||||||
|
} else {
|
||||||
|
warn!("Failed to enable KV2 secrets engine: {}", stderr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
warn!("Failed to execute KV2 enable command");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ static ALLOWED_COMMANDS: LazyLock<HashSet<&'static str>> = LazyLock::new(|| {
|
||||||
|
|
||||||
static FORBIDDEN_SHELL_CHARS: LazyLock<HashSet<char>> = LazyLock::new(|| {
|
static FORBIDDEN_SHELL_CHARS: LazyLock<HashSet<char>> = LazyLock::new(|| {
|
||||||
HashSet::from([
|
HashSet::from([
|
||||||
';', '|', '&', '$', '`', '(', ')', '{', '}', '<', '>', '\n', '\r', '\0',
|
';', '|', '&', '$', '`', '<', '>', '\n', '\r', '\0',
|
||||||
])
|
])
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -170,6 +170,30 @@ impl SafeCommand {
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn trusted_arg(mut self, arg: &str) -> Result<Self, CommandGuardError> {
|
||||||
|
if arg.is_empty() {
|
||||||
|
return Err(CommandGuardError::InvalidArgument(
|
||||||
|
"Empty argument".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if arg.len() > 4096 {
|
||||||
|
return Err(CommandGuardError::InvalidArgument(
|
||||||
|
"Argument too long".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let dangerous_patterns = ["$(", "`", "&&", "||", ">>", "<<", ".."];
|
||||||
|
for pattern in dangerous_patterns {
|
||||||
|
if arg.contains(pattern) {
|
||||||
|
return Err(CommandGuardError::ShellInjectionAttempt(format!(
|
||||||
|
"Dangerous pattern '{}' detected",
|
||||||
|
pattern
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.args.push(arg.to_string());
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn shell_script_arg(mut self, script: &str) -> Result<Self, CommandGuardError> {
|
pub fn shell_script_arg(mut self, script: &str) -> Result<Self, CommandGuardError> {
|
||||||
let is_unix_shell = self.command == "bash" || self.command == "sh";
|
let is_unix_shell = self.command == "bash" || self.command == "sh";
|
||||||
let is_windows_cmd = self.command == "cmd";
|
let is_windows_cmd = self.command == "cmd";
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue