fix(security): enforce company scoped updates
This commit is contained in:
1 parent
fccb0d8586
commit
41e1dec647
9 files changed
+74
-26
No files matched your search
@@ -20,11 +20,16 @@ export function buildAutomationMethods(
|
||||
}) {
|
||||
const id = ensureId(input.id ?? null);
|
||||
const ts = nowIso();
|
||||
const existing = getDb().prepare("select id from automation_rules where id = ?").get(id) as { id: string } | undefined;
|
||||
const existing = getDb().prepare(
|
||||
"select company_id as companyId from automation_rules where id = ?",
|
||||
).get(id) as { companyId: string } | undefined;
|
||||
if (existing && existing.companyId !== input.companyId) {
|
||||
throw new Error(`Automation rule ${id} does not belong to company ${input.companyId}`);
|
||||
}
|
||||
if (existing) {
|
||||
getDb().prepare(
|
||||
"update automation_rules set name = ?, description = ?, trigger = ?, conditions_json = ?, action = ?, action_config_json = ?, source_department = ?, target_department = ?, priority = ?, status = ?, updated_at = ? where id = ?",
|
||||
).run(input.name, input.description ?? "", input.trigger, input.conditionsJson ?? "{}", input.action, input.actionConfigJson ?? "{}", input.sourceDepartment ?? null, input.targetDepartment ?? null, input.priority ?? 50, input.status ?? "active", ts, id);
|
||||
"update automation_rules set name = ?, description = ?, trigger = ?, conditions_json = ?, action = ?, action_config_json = ?, source_department = ?, target_department = ?, priority = ?, status = ?, updated_at = ? where id = ? and company_id = ?",
|
||||
).run(input.name, input.description ?? "", input.trigger, input.conditionsJson ?? "{}", input.action, input.actionConfigJson ?? "{}", input.sourceDepartment ?? null, input.targetDepartment ?? null, input.priority ?? 50, input.status ?? "active", ts, id, input.companyId);
|
||||
} else {
|
||||
getDb().prepare(
|
||||
"insert into automation_rules (id, company_id, name, description, trigger, conditions_json, action, action_config_json, source_department, target_department, priority, status, execution_count, created_at, updated_at) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?)",
|
||||
@@ -55,7 +60,7 @@ export function buildAutomationMethods(
|
||||
const id = randomUUID();
|
||||
const ts = nowIso();
|
||||
getDb().prepare("insert into automation_log (id, company_id, rule_id, rule_name, trigger, action, detail, executed_at) values (?, ?, ?, ?, ?, ?, ?, ?)").run(id, companyId, ruleId, ruleName, trigger, action, detail, ts);
|
||||
getDb().prepare("update automation_rules set execution_count = execution_count + 1, last_executed_at = ? where id = ?").run(ts, ruleId);
|
||||
getDb().prepare("update automation_rules set execution_count = execution_count + 1, last_executed_at = ? where id = ? and company_id = ?").run(ts, ruleId, companyId);
|
||||
},
|
||||
|
||||
getAutomationLog(companyId: string) {
|
||||
@@ -82,11 +87,16 @@ export function buildAutomationMethods(
|
||||
}) {
|
||||
const id = ensureId(input.id ?? null);
|
||||
const ts = nowIso();
|
||||
const existing = getDb().prepare("select id from workflow_pipelines where id = ?").get(id) as { id: string } | undefined;
|
||||
const existing = getDb().prepare(
|
||||
"select company_id as companyId from workflow_pipelines where id = ?",
|
||||
).get(id) as { companyId: string } | undefined;
|
||||
if (existing && existing.companyId !== input.companyId) {
|
||||
throw new Error(`Workflow ${id} does not belong to company ${input.companyId}`);
|
||||
}
|
||||
if (existing) {
|
||||
getDb().prepare(
|
||||
"update workflow_pipelines set name = ?, description = ?, steps_json = ?, trigger_type = ?, trigger_config_json = ?, status = ?, updated_at = ? where id = ?",
|
||||
).run(input.name, input.description ?? "", input.stepsJson ?? "[]", input.triggerType, input.triggerConfigJson ?? "{}", input.status ?? "draft", ts, id);
|
||||
"update workflow_pipelines set name = ?, description = ?, steps_json = ?, trigger_type = ?, trigger_config_json = ?, status = ?, updated_at = ? where id = ? and company_id = ?",
|
||||
).run(input.name, input.description ?? "", input.stepsJson ?? "[]", input.triggerType, input.triggerConfigJson ?? "{}", input.status ?? "draft", ts, id, input.companyId);
|
||||
} else {
|
||||
getDb().prepare(
|
||||
"insert into workflow_pipelines (id, company_id, name, description, steps_json, trigger_type, trigger_config_json, status, current_step_index, run_count, created_at, updated_at) values (?, ?, ?, ?, ?, ?, ?, ?, 0, 0, ?, ?)",
|
||||
|
||||
@@ -64,11 +64,16 @@ export function buildDocumentMethods(
|
||||
}) {
|
||||
const id = ensureId(input.id ?? null);
|
||||
const ts = nowIso();
|
||||
const existing = getDb().prepare("select id from documents where id = ?").get(id) as { id: string } | undefined;
|
||||
const existing = getDb().prepare(
|
||||
"select company_id as companyId from documents where id = ?",
|
||||
).get(id) as { companyId: string } | undefined;
|
||||
if (existing && existing.companyId !== input.companyId) {
|
||||
throw new Error(`Document ${id} does not belong to company ${input.companyId}`);
|
||||
}
|
||||
if (existing) {
|
||||
getDb().prepare(
|
||||
"update documents set type = ?, title = ?, content = ?, author_agent_id = ?, reviewer_agent_id = ?, project_id = ?, goal_id = ?, parent_doc_id = ?, version = ?, status = ?, tags_json = ?, source_run_id = coalesce(?, source_run_id), updated_at = ? where id = ?",
|
||||
).run(input.type, input.title, input.content ?? "", input.authorAgentId ?? null, input.reviewerAgentId ?? null, input.projectId ?? null, input.goalId ?? null, input.parentDocId ?? null, input.version ?? 1, input.status ?? "draft", input.tagsJson ?? "[]", input.sourceRunId ?? null, ts, id);
|
||||
"update documents set type = ?, title = ?, content = ?, author_agent_id = ?, reviewer_agent_id = ?, project_id = ?, goal_id = ?, parent_doc_id = ?, version = ?, status = ?, tags_json = ?, source_run_id = coalesce(?, source_run_id), updated_at = ? where id = ? and company_id = ?",
|
||||
).run(input.type, input.title, input.content ?? "", input.authorAgentId ?? null, input.reviewerAgentId ?? null, input.projectId ?? null, input.goalId ?? null, input.parentDocId ?? null, input.version ?? 1, input.status ?? "draft", input.tagsJson ?? "[]", input.sourceRunId ?? null, ts, id, input.companyId);
|
||||
} else {
|
||||
getDb().prepare(
|
||||
"insert into documents (id, company_id, type, title, content, author_agent_id, reviewer_agent_id, project_id, goal_id, parent_doc_id, version, status, tags_json, source_run_id, created_at, updated_at) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
@@ -129,15 +134,15 @@ export function buildDocumentMethods(
|
||||
"INSERT INTO document_revisions (id, company_id, document_id, revision_number, body, change_summary, created_by_agent_id, created_by_user_id, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
).run(id, input.companyId, input.documentId, revisionNumber, input.body, input.changeSummary ?? null, input.createdByAgentId ?? null, input.createdByUserId ?? null, ts);
|
||||
getDb().prepare(
|
||||
"UPDATE documents SET version = ?, latest_revision_id = ?, updated_at = ? WHERE id = ?",
|
||||
).run(revisionNumber, id, ts, input.documentId);
|
||||
"UPDATE documents SET version = ?, latest_revision_id = ?, updated_at = ? WHERE id = ? AND company_id = ?",
|
||||
).run(revisionNumber, id, ts, input.documentId, input.companyId);
|
||||
return { id, revisionNumber };
|
||||
},
|
||||
|
||||
getDocumentLatestRevisionId(documentId: string): string | null {
|
||||
getDocumentLatestRevisionId(documentId: string, companyId: string): string | null {
|
||||
const row = getDb().prepare(
|
||||
"SELECT latest_revision_id FROM documents WHERE id = ?",
|
||||
).get(documentId) as { latest_revision_id: string | null } | undefined;
|
||||
"SELECT latest_revision_id FROM documents WHERE id = ? AND company_id = ?",
|
||||
).get(documentId, companyId) as { latest_revision_id: string | null } | undefined;
|
||||
return row?.latest_revision_id ?? null;
|
||||
},
|
||||
|
||||
|
||||
@@ -95,11 +95,16 @@ export function buildKnowledgeMethods(
|
||||
}) {
|
||||
const id = ensureId(input.id ?? null);
|
||||
const ts = nowIso();
|
||||
const existing = getDb().prepare("select id from meetings where id = ?").get(id) as { id: string } | undefined;
|
||||
const existing = getDb().prepare(
|
||||
"select company_id as companyId from meetings where id = ?",
|
||||
).get(id) as { companyId: string } | undefined;
|
||||
if (existing && existing.companyId !== input.companyId) {
|
||||
throw new Error(`Meeting ${id} does not belong to company ${input.companyId}`);
|
||||
}
|
||||
if (existing) {
|
||||
getDb().prepare(
|
||||
"update meetings set type = ?, title = ?, organizer_agent_id = ?, participant_agent_ids = ?, scheduled_at = ?, duration_minutes = ?, agenda_json = ?, notes_json = ?, decisions_json = ?, action_items_json = ?, status = ?, updated_at = ? where id = ?",
|
||||
).run(input.type, input.title, input.organizerAgentId ?? null, input.participantAgentIds ?? "[]", input.scheduledAt, input.durationMinutes ?? 30, input.agendaJson ?? "[]", input.notesJson ?? "[]", input.decisionsJson ?? "[]", input.actionItemsJson ?? "[]", input.status ?? "scheduled", ts, id);
|
||||
"update meetings set type = ?, title = ?, organizer_agent_id = ?, participant_agent_ids = ?, scheduled_at = ?, duration_minutes = ?, agenda_json = ?, notes_json = ?, decisions_json = ?, action_items_json = ?, status = ?, updated_at = ? where id = ? and company_id = ?",
|
||||
).run(input.type, input.title, input.organizerAgentId ?? null, input.participantAgentIds ?? "[]", input.scheduledAt, input.durationMinutes ?? 30, input.agendaJson ?? "[]", input.notesJson ?? "[]", input.decisionsJson ?? "[]", input.actionItemsJson ?? "[]", input.status ?? "scheduled", ts, id, input.companyId);
|
||||
} else {
|
||||
getDb().prepare(
|
||||
"insert into meetings (id, company_id, type, title, organizer_agent_id, participant_agent_ids, scheduled_at, duration_minutes, agenda_json, notes_json, decisions_json, action_items_json, status, created_at, updated_at) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
@@ -120,11 +125,16 @@ export function buildKnowledgeMethods(
|
||||
}) {
|
||||
const id = ensureId(input.id ?? null);
|
||||
const ts = nowIso();
|
||||
const existing = getDb().prepare("select id from knowledge_base where id = ?").get(id) as { id: string } | undefined;
|
||||
const existing = getDb().prepare(
|
||||
"select company_id as companyId from knowledge_base where id = ?",
|
||||
).get(id) as { companyId: string } | undefined;
|
||||
if (existing && existing.companyId !== input.companyId) {
|
||||
throw new Error(`Knowledge entry ${id} does not belong to company ${input.companyId}`);
|
||||
}
|
||||
if (existing) {
|
||||
getDb().prepare(
|
||||
"update knowledge_base set category = ?, topic = ?, content = ?, author_agent_id = ?, importance = ?, tags_json = ?, referenced_entity_type = ?, referenced_entity_id = ?, source_run_id = coalesce(?, source_run_id), updated_at = ? where id = ?",
|
||||
).run(input.category, input.topic, input.content, input.authorAgentId ?? null, input.importance ?? "medium", input.tagsJson ?? "[]", input.referencedEntityType ?? null, input.referencedEntityId ?? null, input.sourceRunId ?? null, ts, id);
|
||||
"update knowledge_base set category = ?, topic = ?, content = ?, author_agent_id = ?, importance = ?, tags_json = ?, referenced_entity_type = ?, referenced_entity_id = ?, source_run_id = coalesce(?, source_run_id), updated_at = ? where id = ? and company_id = ?",
|
||||
).run(input.category, input.topic, input.content, input.authorAgentId ?? null, input.importance ?? "medium", input.tagsJson ?? "[]", input.referencedEntityType ?? null, input.referencedEntityId ?? null, input.sourceRunId ?? null, ts, id, input.companyId);
|
||||
} else {
|
||||
getDb().prepare(
|
||||
"insert into knowledge_base (id, company_id, category, topic, content, author_agent_id, importance, tags_json, referenced_entity_type, referenced_entity_id, source_run_id, created_at, updated_at) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
@@ -155,11 +165,16 @@ export function buildKnowledgeMethods(
|
||||
}) {
|
||||
const id = ensureId(input.id ?? null);
|
||||
const ts = nowIso();
|
||||
const existing = getDb().prepare("select id from sprints where id = ?").get(id) as { id: string } | undefined;
|
||||
const existing = getDb().prepare(
|
||||
"select company_id as companyId from sprints where id = ?",
|
||||
).get(id) as { companyId: string } | undefined;
|
||||
if (existing && existing.companyId !== input.companyId) {
|
||||
throw new Error(`Sprint ${id} does not belong to company ${input.companyId}`);
|
||||
}
|
||||
if (existing) {
|
||||
getDb().prepare(
|
||||
"update sprints set name = ?, goal = ?, start_date = ?, end_date = ?, status = ?, retrospective_notes = ?, velocity_points = ?, completed_points = ?, updated_at = ? where id = ?",
|
||||
).run(input.name, input.goal ?? "", input.startDate, input.endDate, input.status ?? "planning", input.retrospectiveNotes ?? "", input.velocityPoints ?? 0, input.completedPoints ?? 0, ts, id);
|
||||
"update sprints set name = ?, goal = ?, start_date = ?, end_date = ?, status = ?, retrospective_notes = ?, velocity_points = ?, completed_points = ?, updated_at = ? where id = ? and company_id = ?",
|
||||
).run(input.name, input.goal ?? "", input.startDate, input.endDate, input.status ?? "planning", input.retrospectiveNotes ?? "", input.velocityPoints ?? 0, input.completedPoints ?? 0, ts, id, input.companyId);
|
||||
} else {
|
||||
getDb().prepare(
|
||||
"insert into sprints (id, company_id, name, goal, start_date, end_date, status, retrospective_notes, velocity_points, completed_points, created_at, updated_at) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
|
||||
Reference in new issue
Block a user