fix(security): resolve all HIGH/MEDIUM/LOW audit findings
Rust fixes (HIGH): - symbols.rs: path validation (reject near-root, 50K file limit, symlink filter) - memory.rs: FTS5 query quoting (prevent operator injection), 1000 fragment cap, content length limit, transaction wrapping - budget.rs: atomic check-and-reserve via transaction, input validation, index on budget_log - export.rs: safe UTF-8 truncation via chars().take() - git_context.rs: reject paths starting with '-' (flag injection) - branch_policy.rs: action validation (block|warn only), path validation Rust fixes (MEDIUM): - export.rs: named column access (positional→named) - budget.rs: named column access, negative value guards Svelte fixes: - AccountSwitcher: 2-step confirmation before account switch - ProjectMemory: expand/collapse content, 2-step delete confirm, tags split fix - CodeIntelligence: min 2-char symbol query, CodeSymbol rename, aria-labels - BudgetManager: 10M upper bound, aria-label on input, named constants - SessionExporter: timeout cleanup on destroy, aria-live feedback - AnalyticsDashboard: SVG aria-label, removed unused import, named constant
This commit is contained in:
parent
0324f813e2
commit
738574b9f0
13 changed files with 280 additions and 91 deletions
|
|
@ -44,17 +44,19 @@ pub fn pro_analytics_summary(project_id: String, days: Option<i64>) -> Result<An
|
|||
let cutoff = now_epoch() - (period * 86400);
|
||||
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT COUNT(*), COALESCE(SUM(cost_usd), 0), COALESCE(SUM(peak_tokens), 0),
|
||||
COALESCE(SUM(turn_count), 0), COALESCE(SUM(tool_call_count), 0)
|
||||
"SELECT COUNT(*) AS cnt, COALESCE(SUM(cost_usd), 0) AS total_cost,
|
||||
COALESCE(SUM(peak_tokens), 0) AS total_tokens,
|
||||
COALESCE(SUM(turn_count), 0) AS total_turns,
|
||||
COALESCE(SUM(tool_call_count), 0) AS total_tools
|
||||
FROM session_metrics WHERE project_id = ?1 AND end_time >= ?2"
|
||||
).map_err(|e| format!("Query failed: {e}"))?;
|
||||
|
||||
let result = stmt.query_row(rusqlite::params![project_id, cutoff], |row| {
|
||||
let count: i64 = row.get(0)?;
|
||||
let cost: f64 = row.get(1)?;
|
||||
let tokens: i64 = row.get(2)?;
|
||||
let turns: i64 = row.get(3)?;
|
||||
let tools: i64 = row.get(4)?;
|
||||
let count: i64 = row.get("cnt")?;
|
||||
let cost: f64 = row.get("total_cost")?;
|
||||
let tokens: i64 = row.get("total_tokens")?;
|
||||
let turns: i64 = row.get("total_turns")?;
|
||||
let tools: i64 = row.get("total_tools")?;
|
||||
Ok(AnalyticsSummary {
|
||||
total_sessions: count,
|
||||
total_cost_usd: cost,
|
||||
|
|
@ -77,9 +79,11 @@ pub fn pro_analytics_daily(project_id: String, days: Option<i64>) -> Result<Vec<
|
|||
let cutoff = now_epoch() - (period * 86400);
|
||||
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT date(end_time, 'unixepoch') as day,
|
||||
COUNT(*), COALESCE(SUM(cost_usd), 0), COALESCE(SUM(peak_tokens), 0),
|
||||
COALESCE(SUM(turn_count), 0), COALESCE(SUM(tool_call_count), 0)
|
||||
"SELECT date(end_time, 'unixepoch') AS day,
|
||||
COUNT(*) AS cnt, COALESCE(SUM(cost_usd), 0) AS total_cost,
|
||||
COALESCE(SUM(peak_tokens), 0) AS total_tokens,
|
||||
COALESCE(SUM(turn_count), 0) AS total_turns,
|
||||
COALESCE(SUM(tool_call_count), 0) AS total_tools
|
||||
FROM session_metrics
|
||||
WHERE project_id = ?1 AND end_time >= ?2
|
||||
GROUP BY day ORDER BY day ASC"
|
||||
|
|
@ -87,12 +91,12 @@ pub fn pro_analytics_daily(project_id: String, days: Option<i64>) -> Result<Vec<
|
|||
|
||||
let rows = stmt.query_map(rusqlite::params![project_id, cutoff], |row| {
|
||||
Ok(DailyStats {
|
||||
date: row.get(0)?,
|
||||
session_count: row.get(1)?,
|
||||
cost_usd: row.get(2)?,
|
||||
tokens: row.get(3)?,
|
||||
turns: row.get(4)?,
|
||||
tool_calls: row.get(5)?,
|
||||
date: row.get("day")?,
|
||||
session_count: row.get("cnt")?,
|
||||
cost_usd: row.get("total_cost")?,
|
||||
tokens: row.get("total_tokens")?,
|
||||
turns: row.get("total_turns")?,
|
||||
tool_calls: row.get("total_tools")?,
|
||||
})
|
||||
}).map_err(|e| format!("Query failed: {e}"))?
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
|
|
@ -108,21 +112,22 @@ pub fn pro_analytics_model_breakdown(project_id: String, days: Option<i64>) -> R
|
|||
let cutoff = now_epoch() - (period * 86400);
|
||||
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT COALESCE(model, 'unknown'), COUNT(*), COALESCE(SUM(cost_usd), 0),
|
||||
COALESCE(SUM(peak_tokens), 0)
|
||||
"SELECT COALESCE(model, 'unknown') AS model_name, COUNT(*) AS cnt,
|
||||
COALESCE(SUM(cost_usd), 0) AS total_cost,
|
||||
COALESCE(SUM(peak_tokens), 0) AS total_tokens
|
||||
FROM session_metrics
|
||||
WHERE project_id = ?1 AND end_time >= ?2
|
||||
GROUP BY model ORDER BY SUM(cost_usd) DESC"
|
||||
).map_err(|e| format!("Query failed: {e}"))?;
|
||||
|
||||
let rows = stmt.query_map(rusqlite::params![project_id, cutoff], |row| {
|
||||
let count: i64 = row.get(1)?;
|
||||
let cost: f64 = row.get(2)?;
|
||||
let count: i64 = row.get("cnt")?;
|
||||
let cost: f64 = row.get("total_cost")?;
|
||||
Ok(ModelBreakdown {
|
||||
model: row.get(0)?,
|
||||
model: row.get("model_name")?,
|
||||
session_count: count,
|
||||
total_cost_usd: cost,
|
||||
total_tokens: row.get(3)?,
|
||||
total_tokens: row.get("total_tokens")?,
|
||||
avg_cost_per_session: if count > 0 { cost / count as f64 } else { 0.0 },
|
||||
})
|
||||
}).map_err(|e| format!("Query failed: {e}"))?
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue