fix(api): fail-safe прод-старт, транзакции и санитизация install-скриптов
- прод-режим отказывается стартовать без AUTH_REQUIRED и реальных секретов (opt-out через EVOFW_ALLOW_UNSAFE) - CORS: whitelist через CORS_ORIGINS вместо origin:true; CSP для раздаваемого SPA - транзакции для setAgentPolicySets, reorderPolicyRules, replaceResolvedForRule, replaceIpListEntries - install-скрипты: Zod-валидация имени ссылки, экранирование $ и контрольных символов в RouterOS-рендере - constant-time сравнение enroll-seed - опциональное шифрование токена EvoBGP в БД (EVOFW_SECRET_KEY, AES-256-GCM) и маскирование per-list api_token в ответах - graceful shutdown (SIGTERM/SIGINT) + тесты
This commit is contained in:
@@ -60,17 +60,19 @@ export function countEntriesByListIds(
|
||||
}
|
||||
|
||||
export function replaceIpListEntries(db: Db, listId: string, cidrs: string[]) {
|
||||
db.delete(ipListEntries).where(eq(ipListEntries.listId, listId)).run()
|
||||
if (cidrs.length === 0) return
|
||||
const now = new Date().toISOString()
|
||||
db.insert(ipListEntries)
|
||||
.values(
|
||||
cidrs.map((cidr) => ({
|
||||
id: crypto.randomUUID(),
|
||||
listId,
|
||||
cidr,
|
||||
createdAt: now,
|
||||
})),
|
||||
)
|
||||
.run()
|
||||
db.transaction((tx) => {
|
||||
tx.delete(ipListEntries).where(eq(ipListEntries.listId, listId)).run()
|
||||
if (cidrs.length === 0) return
|
||||
const now = new Date().toISOString()
|
||||
tx.insert(ipListEntries)
|
||||
.values(
|
||||
cidrs.map((cidr) => ({
|
||||
id: crypto.randomUUID(),
|
||||
listId,
|
||||
cidr,
|
||||
createdAt: now,
|
||||
})),
|
||||
)
|
||||
.run()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -170,11 +170,13 @@ export function setAgentPolicySets(db: Db, agentId: string, setIds: string[]) {
|
||||
}
|
||||
}
|
||||
|
||||
db.delete(agentPolicySets).where(eq(agentPolicySets.agentId, agentId)).run()
|
||||
setIds.forEach((setId, i) => {
|
||||
db.insert(agentPolicySets)
|
||||
.values({ agentId, setId, sort: i * 10 })
|
||||
.run()
|
||||
db.transaction((tx) => {
|
||||
tx.delete(agentPolicySets).where(eq(agentPolicySets.agentId, agentId)).run()
|
||||
setIds.forEach((setId, i) => {
|
||||
tx.insert(agentPolicySets)
|
||||
.values({ agentId, setId, sort: i * 10 })
|
||||
.run()
|
||||
})
|
||||
})
|
||||
bumpAgentGeneration(db, agentId)
|
||||
}
|
||||
@@ -271,20 +273,22 @@ export function reorderPolicyRules(
|
||||
throw new Error('ordered_ids must list every rule in the set exactly once')
|
||||
}
|
||||
// Temporary priorities to avoid UNIQUE collisions
|
||||
orderedIds.forEach((id, i) => {
|
||||
db.update(policyRules)
|
||||
.set({ priority: 9000 + i, updatedAt: new Date().toISOString() })
|
||||
.where(eq(policyRules.id, id))
|
||||
.run()
|
||||
})
|
||||
orderedIds.forEach((id, i) => {
|
||||
db.update(policyRules)
|
||||
.set({
|
||||
priority: (i + 1) * 10,
|
||||
updatedAt: new Date().toISOString(),
|
||||
})
|
||||
.where(eq(policyRules.id, id))
|
||||
.run()
|
||||
db.transaction((tx) => {
|
||||
orderedIds.forEach((id, i) => {
|
||||
tx.update(policyRules)
|
||||
.set({ priority: 9000 + i, updatedAt: new Date().toISOString() })
|
||||
.where(eq(policyRules.id, id))
|
||||
.run()
|
||||
})
|
||||
orderedIds.forEach((id, i) => {
|
||||
tx.update(policyRules)
|
||||
.set({
|
||||
priority: (i + 1) * 10,
|
||||
updatedAt: new Date().toISOString(),
|
||||
})
|
||||
.where(eq(policyRules.id, id))
|
||||
.run()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -316,21 +320,23 @@ export function listResolvedForRule(db: Db, ruleId: string) {
|
||||
}
|
||||
|
||||
export function replaceResolvedForRule(db: Db, ruleId: string, cidrs: string[]) {
|
||||
db.delete(policyRuleResolved)
|
||||
.where(eq(policyRuleResolved.ruleId, ruleId))
|
||||
.run()
|
||||
if (cidrs.length === 0) return
|
||||
const now = new Date().toISOString()
|
||||
db.insert(policyRuleResolved)
|
||||
.values(
|
||||
cidrs.map((cidr) => ({
|
||||
id: crypto.randomUUID(),
|
||||
ruleId,
|
||||
cidr,
|
||||
resolvedAt: now,
|
||||
})),
|
||||
)
|
||||
.run()
|
||||
db.transaction((tx) => {
|
||||
tx.delete(policyRuleResolved)
|
||||
.where(eq(policyRuleResolved.ruleId, ruleId))
|
||||
.run()
|
||||
if (cidrs.length === 0) return
|
||||
const now = new Date().toISOString()
|
||||
tx.insert(policyRuleResolved)
|
||||
.values(
|
||||
cidrs.map((cidr) => ({
|
||||
id: crypto.randomUUID(),
|
||||
ruleId,
|
||||
cidr,
|
||||
resolvedAt: now,
|
||||
})),
|
||||
)
|
||||
.run()
|
||||
})
|
||||
}
|
||||
|
||||
export function listOverrides(db: Db, agentId: string) {
|
||||
|
||||
Reference in New Issue
Block a user